diff --git a/app/assets/javascripts/filtered_search/filtered_search_visual_tokens.js b/app/assets/javascripts/filtered_search/filtered_search_visual_tokens.js index fba31f16d654dd44ed047f944128b50a37352f8e..5090b0bdc3c3e57731a5e76da0b4eebbf79b691a 100644 --- a/app/assets/javascripts/filtered_search/filtered_search_visual_tokens.js +++ b/app/assets/javascripts/filtered_search/filtered_search_visual_tokens.js @@ -163,7 +163,7 @@ export default class FilteredSearchVisualTokens { const tokenValueElement = tokenValueContainer.querySelector('.value'); tokenValueElement.innerText = tokenValue; - if (tokenValue === 'none' || tokenValue === 'any') { + if (['none', 'any'].includes(tokenValue.toLowerCase())) { return; } diff --git a/app/controllers/dashboard/milestones_controller.rb b/app/controllers/dashboard/milestones_controller.rb index 9484e4d30cd83d1ccf55bd9b6cce2d0ce250b521..912036da0eaf1826c5dd101673c2f2ea79e3fd12 100644 --- a/app/controllers/dashboard/milestones_controller.rb +++ b/app/controllers/dashboard/milestones_controller.rb @@ -25,8 +25,6 @@ class Dashboard::MilestonesController < Dashboard::ApplicationController private def group_milestones - groups = GroupsFinder.new(current_user, all_available: false).execute - DashboardGroupMilestone.build_collection(groups, params) end @@ -45,6 +43,6 @@ class Dashboard::MilestonesController < Dashboard::ApplicationController end def groups - @groups ||= GroupsFinder.new(current_user, state_all: true).execute + @groups ||= GroupsFinder.new(current_user, all_available: false).execute end end diff --git a/app/models/board.rb b/app/models/board.rb index a137863456cf38760a77e31a831388fe1bd5070a..758a71d6903b95bb1ba6c441878ac9af2abf11d4 100644 --- a/app/models/board.rb +++ b/app/models/board.rb @@ -21,6 +21,10 @@ class Board < ActiveRecord::Base group_id.present? end + def project_board? + project_id.present? + end + def backlog_list lists.merge(List.backlog).take end diff --git a/app/policies/board_policy.rb b/app/policies/board_policy.rb index 46db008421fe7c2f3f72db7c97265b8e6d86d3ad..4bf1e7bd3e1b648726a822ed520da26b1585dae3 100644 --- a/app/policies/board_policy.rb +++ b/app/policies/board_policy.rb @@ -4,10 +4,12 @@ class BoardPolicy < BasePolicy delegate { @subject.parent } condition(:is_group_board) { @subject.group_board? } + condition(:is_project_board) { @subject.project_board? } - rule { is_group_board ? can?(:read_group) : can?(:read_project) }.enable :read_parent + rule { is_project_board & can?(:read_project) }.enable :read_parent rule { is_group_board & can?(:read_group) }.policy do + enable :read_parent enable :read_milestone enable :read_issue end diff --git a/changelogs/unreleased/56332-exclude-public-group-milestones-from-count.yml b/changelogs/unreleased/56332-exclude-public-group-milestones-from-count.yml new file mode 100644 index 0000000000000000000000000000000000000000..50ca9c94173823f418d95e85c8a06a9e92b828d2 --- /dev/null +++ b/changelogs/unreleased/56332-exclude-public-group-milestones-from-count.yml @@ -0,0 +1,5 @@ +--- +title: Fix counts in milestones dashboard +merge_request: 25230 +author: +type: fixed diff --git a/changelogs/unreleased/sh-fix-board-user-assigns.yml b/changelogs/unreleased/sh-fix-board-user-assigns.yml new file mode 100644 index 0000000000000000000000000000000000000000..89c228107f0b5cd89a247c0de6bdfc76671de1fe --- /dev/null +++ b/changelogs/unreleased/sh-fix-board-user-assigns.yml @@ -0,0 +1,5 @@ +--- +title: Fix 403 errors when adding an assignee list in project boards +merge_request: 25263 +author: +type: fixed diff --git a/doc/user/permissions.md b/doc/user/permissions.md index 019652b2408340e3659e6b130dd7483cbec50a6e..8f3d2a18ef60ad3a3b20a67cad1c7f7a7fb6147d 100644 --- a/doc/user/permissions.md +++ b/doc/user/permissions.md @@ -61,6 +61,7 @@ The following table depicts the various user permission levels in a project. | Manage related issues **[STARTER]** | | ✓ | ✓ | ✓ | ✓ | | Lock issue discussions | | ✓ | ✓ | ✓ | ✓ | | Create issue from vulnerability **[ULTIMATE]** | | ✓ | ✓ | ✓ | ✓ | +| View Error Tracking list | | ✓ | ✓ | ✓ | ✓ | | Lock merge request discussions | | | ✓ | ✓ | ✓ | | Create new environments | | | ✓ | ✓ | ✓ | | Stop environments | | | ✓ | ✓ | ✓ | @@ -101,6 +102,7 @@ The following table depicts the various user permission levels in a project. | Manage clusters | | | | ✓ | ✓ | | Manage license policy **[ULTIMATE]** | | | | ✓ | ✓ | | Edit comments (posted by any user) | | | | ✓ | ✓ | +| Manage Error Tracking | | | | ✓ | ✓ | | Switch visibility level | | | | | ✓ | | Transfer project to another namespace | | | | | ✓ | | Remove project | | | | | ✓ | diff --git a/doc/user/project/operations/error_tracking.md b/doc/user/project/operations/error_tracking.md index fe4b36062f7d1f803edad32e1ce14e17720dcef1..90bb92d2062aee59693e3c00bc6ba15418e6dbeb 100644 --- a/doc/user/project/operations/error_tracking.md +++ b/doc/user/project/operations/error_tracking.md @@ -14,10 +14,14 @@ You may sign up to the cloud hosted or deploy your own [on-p ### Enabling Sentry +NOTE: **Note:** +You will need at least Maintainer [permissions](../../permissions.md) to enable the Sentry integration. + GitLab provides an easy way to connect Sentry to your project: 1. Sign up to Sentry.io or [deploy your own](#deploying-sentry) Sentry instance. 1. [Find or generate](https://docs.sentry.io/api/auth/) a Sentry auth token for your Sentry project. +Make sure to give the token at least the following scopes: `event:read` and `project:read`. 1. Navigate to your project’s **Settings > Operations** and provide the Sentry API URL and auth token. 1. Ensure that the 'Active' checkbox is set. 1. Click **Save changes** for the changes to take effect. @@ -25,6 +29,9 @@ GitLab provides an easy way to connect Sentry to your project: ## Error Tracking List +NOTE: **Note:** +You will need at least Reporter [permissions](../../permissions.md) to view the Error Tracking list. + The Error Tracking list may be found at **Operations > Error Tracking** in your project's sidebar. ![Error Tracking list](img/error_tracking_list.png) diff --git a/spec/controllers/dashboard/milestones_controller_spec.rb b/spec/controllers/dashboard/milestones_controller_spec.rb index 8b176e07bc87fd3153cad1eb583ca221566c3239..4b164d0aa6bd5e6641583745dedb77c35f9e03a7 100644 --- a/spec/controllers/dashboard/milestones_controller_spec.rb +++ b/spec/controllers/dashboard/milestones_controller_spec.rb @@ -3,11 +3,9 @@ require 'spec_helper' describe Dashboard::MilestonesController do let(:project) { create(:project) } let(:group) { create(:group) } - let(:public_group) { create(:group, :public) } let(:user) { create(:user) } let(:project_milestone) { create(:milestone, project: project) } let(:group_milestone) { create(:milestone, group: group) } - let!(:public_milestone) { create(:milestone, group: public_group) } let(:milestone) do DashboardMilestone.build( [project], @@ -45,6 +43,9 @@ describe Dashboard::MilestonesController do end describe "#index" do + let(:public_group) { create(:group, :public) } + let!(:public_milestone) { create(:milestone, group: public_group) } + render_views it 'returns group and project milestones to which the user belongs' do @@ -74,10 +75,10 @@ describe Dashboard::MilestonesController do expect(response.body).not_to include(project_milestone.title) end - it 'should contain group and project milestones to which the user belongs to' do + it 'should show counts of group and project milestones to which the user belongs to' do get :index - expect(response.body).to include("Open\n3") + expect(response.body).to include("Open\n2") expect(response.body).to include("Closed\n0") end end diff --git a/spec/javascripts/filtered_search/filtered_search_visual_tokens_spec.js b/spec/javascripts/filtered_search/filtered_search_visual_tokens_spec.js index 9aa3cbaa23198db794da22ee0e40c8f2cdcff22d..6230da77f49f248b7f2e5e921a994fc137180f4b 100644 --- a/spec/javascripts/filtered_search/filtered_search_visual_tokens_spec.js +++ b/spec/javascripts/filtered_search/filtered_search_visual_tokens_spec.js @@ -755,6 +755,17 @@ describe('Filtered Search Visual Tokens', () => { expect(updateUserTokenAppearanceSpy.calls.count()).toBe(0); }); + it('does not update user token appearance for `None` filter', () => { + const { tokenNameElement } = findElements(authorToken); + + const tokenName = tokenNameElement.innerText; + const tokenValue = 'None'; + + subject.renderVisualTokenValue(authorToken, tokenName, tokenValue); + + expect(updateUserTokenAppearanceSpy.calls.count()).toBe(0); + }); + it('does not update user token appearance for `none` filter', () => { const { tokenNameElement } = findElements(authorToken); diff --git a/spec/policies/board_policy_spec.rb b/spec/policies/board_policy_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..4b76d65ef690f7bd7398441a009aa8cd38c38cd4 --- /dev/null +++ b/spec/policies/board_policy_spec.rb @@ -0,0 +1,67 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe BoardPolicy do + let(:user) { create(:user) } + let(:project) { create(:project, :private) } + let(:group) { create(:group, :private) } + let(:group_board) { create(:board, group: group) } + let(:project_board) { create(:board, project: project) } + + let(:board_permissions) do + [ + :read_parent, + :read_milestone, + :read_issue + ] + end + + def expect_allowed(*permissions) + permissions.each { |p| is_expected.to be_allowed(p) } + end + + def expect_disallowed(*permissions) + permissions.each { |p| is_expected.not_to be_allowed(p) } + end + + context 'group board' do + subject { described_class.new(user, group_board) } + + context 'user has access' do + before do + group.add_developer(user) + end + + it do + expect_allowed(*board_permissions) + end + end + + context 'user does not have access' do + it do + expect_disallowed(*board_permissions) + end + end + end + + context 'project board' do + subject { described_class.new(user, project_board) } + + context 'user has access' do + before do + project.add_developer(user) + end + + it do + expect_allowed(*board_permissions) + end + end + + context 'user does not have access' do + it do + expect_disallowed(*board_permissions) + end + end + end +end