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.
This commit is contained in:
Felipe Artur 2019-09-04 16:33:02 +00:00 committed by Michael Kozono
parent 24de5d6573
commit 13fc0efa57
14 changed files with 168 additions and 6 deletions

View File

@ -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"

View File

@ -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;

View File

@ -16,7 +16,7 @@ export const visibilityLevelDescriptions = {
),
};
const featureAccessLevel = {
export const featureAccessLevel = {
NOT_ENABLED: 0,
PROJECT_MEMBERS: 10,
EVERYONE: 20,

View File

@ -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,

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,5 @@
---
title: Let project reporters create issue from group boards
merge_request: 29866
author:
type: fixed

View File

@ -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` |

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)