summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFelipe Artur <fcardozo@gitlab.com>2019-09-04 16:33:02 +0000
committerMichael Kozono <mkozono@gmail.com>2019-09-04 16:33:02 +0000
commit13fc0efa5725d94cda527dc487d8dfdb7e90ed21 (patch)
treee059e8ad1ffece44218974140ec04cdfe8de5a07
parent24de5d65733de22fd067f150e65e36eb6121d17f (diff)
downloadgitlab-ce-13fc0efa5725d94cda527dc487d8dfdb7e90ed21.tar.gz
Let project reporters create issue from group boards
The current state of group issue boards does not show the "Add issues" button on the UI for users that are reporters of group child projects.
-rw-r--r--app/assets/javascripts/boards/components/board_new_issue.vue2
-rw-r--r--app/assets/javascripts/boards/components/project_select.vue12
-rw-r--r--app/assets/javascripts/pages/projects/shared/permissions/constants.js2
-rw-r--r--app/helpers/boards_helper.rb2
-rw-r--r--app/policies/board_policy.rb18
-rw-r--r--app/policies/concerns/find_group_projects.rb13
-rw-r--r--app/policies/group_policy.rb4
-rw-r--r--changelogs/unreleased/issue_54042.yml5
-rw-r--r--doc/api/groups.md3
-rw-r--r--lib/api/groups.rb2
-rw-r--r--spec/features/boards/new_issue_spec.rb40
-rw-r--r--spec/helpers/boards_helper_spec.rb2
-rw-r--r--spec/policies/board_policy_spec.rb53
-rw-r--r--spec/requests/api/groups_spec.rb16
14 files changed, 168 insertions, 6 deletions
diff --git a/app/assets/javascripts/boards/components/board_new_issue.vue b/app/assets/javascripts/boards/components/board_new_issue.vue
index 4180023b7db..f9284266b72 100644
--- a/app/assets/javascripts/boards/components/board_new_issue.vue
+++ b/app/assets/javascripts/boards/components/board_new_issue.vue
@@ -114,7 +114,7 @@ export default {
name="issue_title"
autocomplete="off"
/>
- <project-select v-if="groupId" :group-id="groupId" />
+ <project-select v-if="groupId" :group-id="groupId" :list="list" />
<div class="clearfix prepend-top-10">
<gl-button
ref="submit-button"
diff --git a/app/assets/javascripts/boards/components/project_select.vue b/app/assets/javascripts/boards/components/project_select.vue
index e8d25e84be1..e5ebb887ce0 100644
--- a/app/assets/javascripts/boards/components/project_select.vue
+++ b/app/assets/javascripts/boards/components/project_select.vue
@@ -6,6 +6,7 @@ import Icon from '~/vue_shared/components/icon.vue';
import { GlLoadingIcon } from '@gitlab/ui';
import eventHub from '../eventhub';
import Api from '../../api';
+import { featureAccessLevel } from '~/pages/projects/shared/permissions/constants';
export default {
name: 'BoardProjectSelect',
@@ -19,6 +20,10 @@ export default {
required: true,
default: 0,
},
+ list: {
+ type: Object,
+ required: true,
+ },
},
data() {
return {
@@ -49,6 +54,12 @@ export default {
selectable: true,
data: (term, callback) => {
this.loading = true;
+ const additionalAttrs = {};
+
+ if (this.list.type && this.list.type !== 'backlog') {
+ additionalAttrs.min_access_level = featureAccessLevel.EVERYONE;
+ }
+
return Api.groupProjects(
this.groupId,
term,
@@ -56,6 +67,7 @@ export default {
with_issues_enabled: true,
with_shared: false,
include_subgroups: true,
+ ...additionalAttrs,
},
projects => {
this.loading = false;
diff --git a/app/assets/javascripts/pages/projects/shared/permissions/constants.js b/app/assets/javascripts/pages/projects/shared/permissions/constants.js
index 73269c6f3ba..6771391254e 100644
--- a/app/assets/javascripts/pages/projects/shared/permissions/constants.js
+++ b/app/assets/javascripts/pages/projects/shared/permissions/constants.js
@@ -16,7 +16,7 @@ export const visibilityLevelDescriptions = {
),
};
-const featureAccessLevel = {
+export const featureAccessLevel = {
NOT_ENABLED: 0,
PROJECT_MEMBERS: 10,
EVERYONE: 20,
diff --git a/app/helpers/boards_helper.rb b/app/helpers/boards_helper.rb
index bbe05f40999..8ef3ed9e8a5 100644
--- a/app/helpers/boards_helper.rb
+++ b/app/helpers/boards_helper.rb
@@ -10,7 +10,7 @@ module BoardsHelper
boards_endpoint: @boards_endpoint,
lists_endpoint: board_lists_path(board),
board_id: board.id,
- disabled: "#{!can?(current_user, :admin_list, current_board_parent)}",
+ disabled: (!can?(current_user, :create_non_backlog_issues, board)).to_s,
issue_link_base: build_issue_link_base,
root_path: root_path,
bulk_update_path: @bulk_issues_path,
diff --git a/app/policies/board_policy.rb b/app/policies/board_policy.rb
index 4bf1e7bd3e1..b8435dad3f1 100644
--- a/app/policies/board_policy.rb
+++ b/app/policies/board_policy.rb
@@ -1,6 +1,8 @@
# frozen_string_literal: true
class BoardPolicy < BasePolicy
+ include FindGroupProjects
+
delegate { @subject.parent }
condition(:is_group_board) { @subject.group_board? }
@@ -13,4 +15,20 @@ class BoardPolicy < BasePolicy
enable :read_milestone
enable :read_issue
end
+
+ condition(:reporter_of_group_projects) do
+ next unless @user
+
+ group_projects_for(user: @user, group: @subject.parent)
+ .visible_to_user_and_access_level(@user, ::Gitlab::Access::REPORTER)
+ .exists?
+ end
+
+ rule { is_group_board & reporter_of_group_projects }.policy do
+ enable :create_non_backlog_issues
+ end
+
+ rule { is_project_board & can?(:admin_issue) }.policy do
+ enable :create_non_backlog_issues
+ end
end
diff --git a/app/policies/concerns/find_group_projects.rb b/app/policies/concerns/find_group_projects.rb
new file mode 100644
index 00000000000..e2cb90079c7
--- /dev/null
+++ b/app/policies/concerns/find_group_projects.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module FindGroupProjects
+ extend ActiveSupport::Concern
+
+ def group_projects_for(user:, group:)
+ GroupProjectsFinder.new(
+ group: group,
+ current_user: user,
+ options: { include_subgroups: true, only_owned: true }
+ ).execute
+ end
+end
diff --git a/app/policies/group_policy.rb b/app/policies/group_policy.rb
index 5d2b74b17a2..c726c7c24a7 100644
--- a/app/policies/group_policy.rb
+++ b/app/policies/group_policy.rb
@@ -1,6 +1,8 @@
# frozen_string_literal: true
class GroupPolicy < BasePolicy
+ include FindGroupProjects
+
desc "Group is public"
with_options scope: :subject, score: 0
condition(:public_group) { @subject.public? }
@@ -22,7 +24,7 @@ class GroupPolicy < BasePolicy
condition(:can_change_parent_share_with_group_lock) { can?(:change_share_with_group_lock, @subject.parent) }
condition(:has_projects) do
- GroupProjectsFinder.new(group: @subject, current_user: @user, options: { include_subgroups: true, only_owned: true }).execute.any?
+ group_projects_for(user: @user, group: @subject).any?
end
with_options scope: :subject, score: 0
diff --git a/changelogs/unreleased/issue_54042.yml b/changelogs/unreleased/issue_54042.yml
new file mode 100644
index 00000000000..465c7426e93
--- /dev/null
+++ b/changelogs/unreleased/issue_54042.yml
@@ -0,0 +1,5 @@
+---
+title: Let project reporters create issue from group boards
+merge_request: 29866
+author:
+type: fixed
diff --git a/doc/api/groups.md b/doc/api/groups.md
index d7f5b1b463b..8b13462b887 100644
--- a/doc/api/groups.md
+++ b/doc/api/groups.md
@@ -156,7 +156,8 @@ Parameters:
| `with_issues_enabled` | boolean | no | Limit by projects with issues feature enabled. Default is `false` |
| `with_merge_requests_enabled` | boolean | no | Limit by projects with merge requests feature enabled. Default is `false` |
| `with_shared` | boolean | no | Include projects shared to this group. Default is `true` |
-| `include_subgroups` | boolean | no | Include projects in subgroups of this group. Default is `false` |
+| `include_subgroups` | boolean | no | Include projects in subgroups of this group. Default is `false` |
+| `min_access_level` | integer | no | Limit to projects where current user has at least this [access level](members.md) |
| `with_custom_attributes` | boolean | no | Include [custom attributes](custom_attributes.md) in response (admins only) |
| `with_security_reports` | boolean | no | **(ULTIMATE)** Return only projects that have security reports artifacts present in any of their builds. This means "projects with security reports enabled". Default is `false` |
diff --git a/lib/api/groups.rb b/lib/api/groups.rb
index 0bcd09d3977..0b086f2e36d 100644
--- a/lib/api/groups.rb
+++ b/lib/api/groups.rb
@@ -75,6 +75,7 @@ module API
).execute
projects = projects.with_issues_available_for_user(current_user) if params[:with_issues_enabled]
projects = projects.with_merge_requests_enabled if params[:with_merge_requests_enabled]
+ projects = projects.visible_to_user_and_access_level(current_user, params[:min_access_level]) if params[:min_access_level]
projects = reorder_projects(projects)
paginate(projects)
end
@@ -213,6 +214,7 @@ module API
optional :with_merge_requests_enabled, type: Boolean, default: false, desc: 'Limit by enabled merge requests feature'
optional :with_shared, type: Boolean, default: true, desc: 'Include projects shared to this group'
optional :include_subgroups, type: Boolean, default: false, desc: 'Includes projects in subgroups of this group'
+ optional :min_access_level, type: Integer, values: Gitlab::Access.all_values, desc: 'Limit by minimum access level of authenticated user on projects'
use :pagination
use :with_custom_attributes
diff --git a/spec/features/boards/new_issue_spec.rb b/spec/features/boards/new_issue_spec.rb
index 36743650270..730887370dd 100644
--- a/spec/features/boards/new_issue_spec.rb
+++ b/spec/features/boards/new_issue_spec.rb
@@ -127,4 +127,44 @@ describe 'Issue Boards new issue', :js do
end
end
end
+
+ context 'group boards' do
+ set(:group) { create(:group, :public) }
+ set(:project) { create(:project, namespace: group) }
+ set(:group_board) { create(:board, group: group) }
+ set(:list) { create(:list, board: group_board, position: 0) }
+
+ context 'for unauthorized users' do
+ before do
+ sign_in(user)
+ visit group_board_path(group, group_board)
+ wait_for_requests
+ end
+
+ it 'displays new issue button in open list' do
+ expect(first('.board')).to have_selector('.issue-count-badge-add-button', count: 1)
+ end
+
+ it 'does not display new issue button in label list' do
+ page.within('.board.is-draggable') do
+ expect(page).not_to have_selector('.issue-count-badge-add-button')
+ end
+ end
+ end
+
+ context 'for authorized users' do
+ it 'display new issue button in label list' do
+ project = create(:project, namespace: group)
+ project.add_reporter(user)
+
+ sign_in(user)
+ visit group_board_path(group, group_board)
+ wait_for_requests
+
+ page.within('.board.is-draggable') do
+ expect(page).to have_selector('.issue-count-badge-add-button')
+ end
+ end
+ end
+ end
end
diff --git a/spec/helpers/boards_helper_spec.rb b/spec/helpers/boards_helper_spec.rb
index f014537eb54..ad088398ce9 100644
--- a/spec/helpers/boards_helper_spec.rb
+++ b/spec/helpers/boards_helper_spec.rb
@@ -40,7 +40,7 @@ describe BoardsHelper do
assign(:project, project)
allow(helper).to receive(:current_user) { user }
- allow(helper).to receive(:can?).with(user, :admin_list, project).and_return(true)
+ allow(helper).to receive(:can?).with(user, :create_non_backlog_issues, board).and_return(true)
end
it 'returns a board_lists_path as lists_endpoint' do
diff --git a/spec/policies/board_policy_spec.rb b/spec/policies/board_policy_spec.rb
index 52c23951e37..35eac8a02c4 100644
--- a/spec/policies/board_policy_spec.rb
+++ b/spec/policies/board_policy_spec.rb
@@ -56,4 +56,57 @@ describe BoardPolicy do
end
end
end
+
+ context 'create_non_backlog_issues' do
+ context 'for project boards' do
+ let!(:current_user) { create(:user) }
+
+ subject { described_class.new(current_user, project_board) }
+
+ context 'when user can admin project issues' do
+ it 'allows to add non backlog issues from issue board' do
+ project.add_reporter(current_user)
+
+ expect_allowed(:create_non_backlog_issues)
+ end
+ end
+
+ context 'when user cannot admin project issues' do
+ it 'does not allow to add non backlog issues from issue board' do
+ project.add_guest(current_user)
+
+ expect_disallowed(:create_non_backlog_issues)
+ end
+ end
+ end
+
+ context 'for group boards' do
+ let!(:current_user) { create(:user) }
+ let!(:project_1) { create(:project, namespace: group) }
+ let!(:project_2) { create(:project, namespace: group) }
+ let!(:group_board) { create(:board, group: group) }
+
+ subject { described_class.new(current_user, group_board) }
+
+ before do
+ project_1.add_guest(current_user)
+ end
+
+ context 'when user is at least reporter in one of the child projects' do
+ it 'allows to add non backlog issues from issue board' do
+ project_2.add_reporter(current_user)
+
+ expect_allowed(:create_non_backlog_issues)
+ end
+ end
+
+ context 'when user is not a reporter from any child projects' do
+ it 'does not allow to add non backlog issues from issue board' do
+ project_2.add_guest(current_user)
+
+ expect_disallowed(:create_non_backlog_issues)
+ end
+ end
+ end
+ end
end
diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb
index 50f36141aed..0893dcb39b6 100644
--- a/spec/requests/api/groups_spec.rb
+++ b/spec/requests/api/groups_spec.rb
@@ -483,6 +483,22 @@ describe API::Groups do
describe "GET /groups/:id/projects" do
context "when authenticated as user" do
+ context 'with min access level' do
+ it 'returns projects with min access level or higher' do
+ group_guest = create(:user)
+ group1.add_guest(group_guest)
+ project4 = create(:project, group: group1)
+ project1.add_guest(group_guest)
+ project3.add_reporter(group_guest)
+ project4.add_developer(group_guest)
+
+ get api("/groups/#{group1.id}/projects", group_guest), params: { min_access_level: Gitlab::Access::REPORTER }
+
+ project_ids = json_response.map { |proj| proj['id'] }
+ expect(project_ids).to match_array([project3.id, project4.id])
+ end
+ end
+
it "returns the group's projects" do
get api("/groups/#{group1.id}/projects", user1)