Skip to content

Commit 4df1faa

Browse files
committed
Merge branch 'release/17.0' into release/17.1
2 parents 94013a0 + aad8941 commit 4df1faa

8 files changed

Lines changed: 322 additions & 29 deletions

File tree

app/policies/query_policy.rb

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ def cache(query)
3636
hash[cached_query] = {
3737
show: viewable?(cached_query),
3838
update: persisted_and_own_or_public?(cached_query),
39-
destroy: persisted_and_own_or_public?(cached_query),
39+
destroy: destroy_allowed?(cached_query),
4040
create: create_allowed?(cached_query),
4141
create_new: create_new_allowed?(cached_query),
4242
publicize: publicize_allowed?(cached_query),
@@ -126,4 +126,9 @@ def share_via_ical_allowed?(query)
126126
user.allowed_in_any_project?(:share_calendars)
127127
end
128128
end
129+
130+
def destroy_allowed?(query)
131+
(query.persisted? && !query.project && query.user == user && user.logged?) ||
132+
persisted_and_own_or_public?(query)
133+
end
129134
end

modules/boards/app/models/boards/grid.rb

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,9 @@
2929
module Boards
3030
class Grid < ::Grids::Grid
3131
belongs_to :project
32-
validates_presence_of :name
32+
validates :name, presence: true
3333

34-
before_destroy :delete_queries
34+
before_destroy :delete_queries, prepend: true
3535

3636
set_acts_as_attachable_options view_permission: :show_board_views,
3737
delete_permission: :manage_board_views,
@@ -62,7 +62,11 @@ def board_type_attribute
6262
private
6363

6464
def delete_queries
65-
contained_queries.delete_all
65+
policy = QueryPolicy.new(User.current)
66+
67+
contained_queries
68+
.select { |q| policy.allowed?(q, :destroy) }
69+
.each(&:delete)
6670
end
6771

6872
def contained_query_ids

modules/boards/spec/models/boards/grid_spec.rb

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,4 +83,48 @@
8383
end
8484
end
8585
end
86+
87+
describe "#destroy" do
88+
context "with an associated query" do
89+
let(:project) { create(:project) }
90+
let(:instance) { described_class.new name: "foo", row_count: 2, column_count: 2, project: }
91+
let(:query) do
92+
create(:query,
93+
public: true,
94+
project: project)
95+
end
96+
97+
current_user { build_stubbed(:user) }
98+
99+
before do
100+
widget = Grids::Widget.new(identifier: "work_package_query",
101+
start_row: 1,
102+
end_row: 2,
103+
start_column: 1,
104+
end_column: 2,
105+
options: { "queryId" => query.id })
106+
107+
instance.widgets = [widget]
108+
instance.save!
109+
end
110+
111+
context "when the user has the permissions to manage queries" do
112+
before do
113+
mock_permissions_for(current_user) do |mock|
114+
mock.allow_in_project :manage_public_queries, project:
115+
end
116+
end
117+
118+
it "deletes the query" do
119+
expect { instance.destroy }.to change(Query, :count).by(-1)
120+
end
121+
end
122+
123+
context "when the user lacks the permissions to manage queries" do
124+
it "keeps the query" do
125+
expect { instance.destroy }.not_to change(Query, :count)
126+
end
127+
end
128+
end
129+
end
86130
end

modules/grids/lib/grids/configuration/in_project_base_registration.rb

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,11 @@ class InProjectBaseRegistration < ::Grids::Configuration::Registration
1515
"custom_text"
1616

1717
remove_query_lambda = -> {
18-
::Query.find_by(id: options[:queryId])&.destroy
18+
@query = ::Query.find_by(id: options["queryId"])
19+
20+
next unless @query && QueryPolicy.new(User.current).allowed?(@query, :destroy)
21+
22+
@query.destroy!
1923
}
2024

2125
save_or_manage_queries_lambda = ->(user, project) {

modules/my_page/lib/my_page/grid_registration.rb

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,13 @@ class GridRegistration < ::Grids::Configuration::Registration
1616
"news"
1717

1818
wp_table_strategy_proc = Proc.new do
19-
after_destroy -> { ::Query.find_by(id: options[:queryId])&.destroy }
19+
after_destroy -> {
20+
@query = ::Query.find_by(id: options["queryId"])
21+
22+
next unless @query && QueryPolicy.new(User.current).allowed?(@query, :destroy)
23+
24+
@query.destroy!
25+
}
2026

2127
allowed ->(user, _project) { user.allowed_in_any_project?(:save_queries) }
2228

@@ -26,7 +32,13 @@ class GridRegistration < ::Grids::Configuration::Registration
2632
# Allow users without save_queries permission to access the widgets
2733
# but they are not allowed to update the underlying query
2834
wp_static_table_strategy_proc = Proc.new do
29-
after_destroy -> { ::Query.find_by(id: options[:queryId])&.destroy }
35+
after_destroy -> {
36+
@query = ::Query.find_by(id: options["queryId"])
37+
38+
next unless @query && QueryPolicy.new(User.current).allowed?(@query, :destroy)
39+
40+
@query.destroy!
41+
}
3042

3143
options_representer "::API::V3::Grids::Widgets::QueryOptionsRepresenter"
3244
end

modules/my_page/spec/models/grids/my_page_spec.rb

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -45,31 +45,53 @@
4545
end
4646

4747
context "altering widgets" do
48-
context "when removing a work_packages_table widget" do
49-
let(:user) { create(:user) }
48+
shared_examples_for "removing a query widget" do |identifier|
49+
let(:query_user) { create(:user) }
5050
let(:query) do
5151
create(:query,
52-
user:)
52+
user: query_user,
53+
project: nil)
5354
end
5455

5556
before do
56-
widget = Grids::Widget.new(identifier: "work_packages_table",
57+
widget = Grids::Widget.new(identifier:,
5758
start_row: 1,
5859
end_row: 2,
5960
start_column: 1,
6061
end_column: 2,
61-
options: { queryId: query.id })
62+
options: { "queryId" => query.id })
6263

6364
instance.widgets = [widget]
6465
instance.save!
6566
end
6667

67-
it "removes the widget's query" do
68-
instance.widgets = []
68+
context "when the query is owned by the user" do
69+
current_user { query_user }
6970

70-
expect(Query.find_by(id: query.id))
71-
.to be_nil
71+
it "removes the widget's query" do
72+
instance.widgets = []
73+
74+
expect(Query.find_by(id: query.id))
75+
.to be_nil
76+
end
77+
end
78+
79+
context "when the query is not owned by the user" do
80+
current_user { create(:user) }
81+
82+
it "removes the widget but keeps the query" do
83+
instance.widgets = []
84+
85+
expect(Query.find_by(id: query.id))
86+
.to eql query
87+
end
7288
end
7389
end
90+
91+
it_behaves_like "removing a query widget", "work_packages_table"
92+
it_behaves_like "removing a query widget", "work_packages_assigned"
93+
it_behaves_like "removing a query widget", "work_packages_accountable"
94+
it_behaves_like "removing a query widget", "work_packages_watched"
95+
it_behaves_like "removing a query widget", "work_packages_created"
7496
end
7597
end
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
# frozen_string_literal: true
2+
3+
# -- copyright
4+
# OpenProject is an open source project management software.
5+
# Copyright (C) the OpenProject GmbH
6+
#
7+
# This program is free software; you can redistribute it and/or
8+
# modify it under the terms of the GNU General Public License version 3.
9+
#
10+
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
11+
# Copyright (C) 2006-2013 Jean-Philippe Lang
12+
# Copyright (C) 2010-2013 the ChiliProject Team
13+
#
14+
# This program is free software; you can redistribute it and/or
15+
# modify it under the terms of the GNU General Public License
16+
# as published by the Free Software Foundation; either version 2
17+
# of the License, or (at your option) any later version.
18+
#
19+
# This program is distributed in the hope that it will be useful,
20+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
21+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22+
# GNU General Public License for more details.
23+
#
24+
# You should have received a copy of the GNU General Public License
25+
# along with this program; if not, write to the Free Software
26+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
27+
#
28+
# See COPYRIGHT and LICENSE files for more details.
29+
# ++
30+
31+
require "spec_helper"
32+
33+
RSpec.describe Grids::Overview do
34+
let(:instance) { described_class.new(row_count: 5, column_count: 5) }
35+
36+
context "when altering widgets" do
37+
shared_examples_for "removing a query widget" do |identifier|
38+
let(:project) { create(:project) }
39+
let(:query) do
40+
create(:query,
41+
public: true,
42+
project:)
43+
end
44+
45+
current_user { build_stubbed(:user) }
46+
47+
before do
48+
widget = Grids::Widget.new(identifier:,
49+
start_row: 1,
50+
end_row: 2,
51+
start_column: 1,
52+
end_column: 2,
53+
options: { "queryId" => query.id })
54+
55+
instance.widgets = [widget]
56+
instance.save!
57+
end
58+
59+
context "when the current user has the permission to manage public queries" do
60+
before do
61+
mock_permissions_for(current_user) do |mock|
62+
mock.allow_in_project :manage_public_queries, project:
63+
end
64+
end
65+
66+
it "removes the widget's query" do
67+
instance.widgets = []
68+
69+
expect(Query.find_by(id: query.id))
70+
.to be_nil
71+
end
72+
end
73+
74+
context "when the current user lacks the permission to manage public queries" do
75+
current_user { create(:user) }
76+
77+
it "removes the widget but keeps the query" do
78+
instance.widgets = []
79+
80+
expect(Query.find_by(id: query.id))
81+
.to eql query
82+
end
83+
end
84+
end
85+
86+
it_behaves_like "removing a query widget", "work_packages_table"
87+
it_behaves_like "removing a query widget", "work_packages_graph"
88+
end
89+
end

0 commit comments

Comments
 (0)