diff --git a/app/services/pages/delete_service.rb b/app/services/pages/delete_service.rb index 96b451aeba42ea09251e3c7a4f801d4cfc666896..ba0889d1a902f56377979c92b32e3a84eb4b5a7d 100644 --- a/app/services/pages/delete_service.rb +++ b/app/services/pages/delete_service.rb @@ -3,6 +3,11 @@ module Pages class DeleteService < BaseService def execute + if current_user.is_a?(User) && !can_remove_pages? + return ServiceResponse.error(message: _('The current user is not authorized to remove the Pages deployment'), + reason: :forbidden) + end + PagesDeployment.deactivate_all(project) # project.pages_domains.delete_all will just nullify project_id: @@ -27,5 +32,9 @@ def publish_deleted_event Gitlab::EventStore.publish(event) end + + def can_remove_pages? + can?(current_user, :remove_pages, project) + end end end diff --git a/app/services/pages/update_service.rb b/app/services/pages/update_service.rb index 01113da037b27d015af54a5579a18e2dc7a07871..bfe5d11b41f71b7975e2459f73e7c16a5fdab9ad 100644 --- a/app/services/pages/update_service.rb +++ b/app/services/pages/update_service.rb @@ -34,7 +34,7 @@ def update_pages_https_only! end def can_update_page_settings? - current_user&.can_read_all_resources? && can?(current_user, :update_pages, project) + can?(current_user, :update_pages, project) end end end diff --git a/app/workers/pages/delete_pages_deployment_worker.rb b/app/workers/pages/delete_pages_deployment_worker.rb index 2737be7d7bac45c52e577bd8c831d4c878aee920..ff516edf8f1269f831aef7a78d3caa89907f3f2f 100644 --- a/app/workers/pages/delete_pages_deployment_worker.rb +++ b/app/workers/pages/delete_pages_deployment_worker.rb @@ -12,7 +12,10 @@ def handle_event(event) project = Project.find_by_id(event.data['project_id']) return unless project - ::Pages::DeleteService.new(project).execute + user = project.owner + return unless user + + ::Pages::DeleteService.new(project, user).execute end end end diff --git a/doc/api/pages.md b/doc/api/pages.md index 7dbdabade203dbdf9b5b6921ef740face4e52e3d..6a8c062fc623fa4d26c2dfb37b3829460eb58d1e 100644 --- a/doc/api/pages.md +++ b/doc/api/pages.md @@ -15,9 +15,11 @@ The GitLab Pages feature must be enabled to use these endpoints. Find out more a ## Unpublish Pages +> - [Changed](https://gitlab.com/gitlab-org/gitlab/-/issues/498658) the minimum required role from administrator access to the Maintainer role in GitLab 17.7 + Prerequisites: -- You must have administrator access to the instance. +- You must have at least the Maintainer role for the project. Remove Pages. @@ -105,10 +107,11 @@ Example response: ## Update Pages settings for a project > - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/147227) in GitLab 17.0. +> - [Changed](https://gitlab.com/gitlab-org/gitlab/-/issues/498658) the minimum required role from administrator access to the Maintainer role in GitLab 17.7 Prerequisites: -- You must have administrator access to the instance. +- You must have at least the Maintainer role for the project. Update Pages settings for the project. diff --git a/lib/api/pages.rb b/lib/api/pages.rb index 8f3b944b60bf2325bcc86fb2ff6cadb329bf758b..aa3d8ac410061ec6d049a414a41dfb3b6e235b75 100644 --- a/lib/api/pages.rb +++ b/lib/api/pages.rb @@ -23,7 +23,6 @@ class Pages < ::API::Base tags %w[pages] end delete ':id/pages' do - authenticated_with_can_read_all_resources! authorize! :remove_pages, user_project ::Pages::DeleteService.new(user_project, current_user).execute @@ -46,7 +45,6 @@ class Pages < ::API::Base optional :pages_primary_domain, type: String, desc: 'Set pages primary domain' end patch ':id/pages' do - authenticated_with_can_read_all_resources! authorize! :update_pages, user_project break not_found! unless user_project.pages_enabled? diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 767d31d47272e000ae638ef674cf46b048f951c8..888a21f00e092991c4b555d0259c7f4509221dd4 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -57323,6 +57323,9 @@ msgstr "" msgid "The current user is not authorized to manage the pipeline trigger token" msgstr "" +msgid "The current user is not authorized to remove the Pages deployment" +msgstr "" + msgid "The current user is not authorized to set pipeline schedule variables" msgstr "" diff --git a/spec/requests/api/pages/pages_spec.rb b/spec/requests/api/pages/pages_spec.rb index e701238701ddabcc8fbd1577d7293abc3a26fd6e..b3755da09e61cd3d5f05f77ad411e1996cd6388c 100644 --- a/spec/requests/api/pages/pages_spec.rb +++ b/spec/requests/api/pages/pages_spec.rb @@ -15,7 +15,8 @@ stub_pages_setting(enabled: true) end - let(:succes_status_code) { :no_content } + let(:success_status_code) { :no_content } + let(:failed_status_code) { :not_found } end context 'when Pages is disabled' do diff --git a/spec/requests/api/pages_spec.rb b/spec/requests/api/pages_spec.rb index 5c7c997709b9e8d8567ccdada83cb16d6ae12633..3226663160e4496cebcc2e2db1fc666eb32ae700 100644 --- a/spec/requests/api/pages_spec.rb +++ b/spec/requests/api/pages_spec.rb @@ -100,7 +100,8 @@ context 'when the user is authorized' do context 'and the update succeeds' do it 'updates the pages settings and returns 200' do - patch api(path, admin, admin_mode: true), params: params + project.add_maintainer(user) + patch api(path, user), params: params expect(response).to have_gitlab_http_status(:ok) expect(json_response['force_https']).to eq(true) @@ -136,7 +137,8 @@ end it 'returns 403 forbidden with the passed along error message' do - patch api(path, admin, admin_mode: true), params: params + project.add_maintainer(user) + patch api(path, user), params: params expect(response).to have_gitlab_http_status(:forbidden) expect(response.body).to include(service_error_message) @@ -146,6 +148,7 @@ context 'when the user is unauthorized' do it 'returns a 403 forbidden' do + project.add_developer(user) patch api(path, user), params: params expect(response).to have_gitlab_http_status(:forbidden) @@ -158,7 +161,7 @@ end it 'returns a 404 not found' do - patch api(path, admin, admin_mode: true), params: params + patch api(path, user), params: params expect(response).to have_gitlab_http_status(:not_found) end diff --git a/spec/services/pages/delete_service_spec.rb b/spec/services/pages/delete_service_spec.rb index 86b1bd5bde2012d50b2ba67fcce549ee61d7c9d8..5f045066e121373b24deea146eff1e10478745c9 100644 --- a/spec/services/pages/delete_service_spec.rb +++ b/spec/services/pages/delete_service_spec.rb @@ -3,50 +3,69 @@ require 'spec_helper' RSpec.describe Pages::DeleteService, feature_category: :pages do - let_it_be(:admin) { create(:admin) } - + let_it_be(:user) { create(:user) } let(:project) { create(:project, path: "my.project") } - let(:service) { described_class.new(project, admin) } - - it 'marks pages as not deployed' do - create(:pages_deployment, project: project) + let(:service) { described_class.new(project, user) } - expect { service.execute } - .to change { project.reload.pages_deployed? } - .from(true).to(false) + before do + project.add_maintainer(user) end - it 'deletes all domains' do - domain = create(:pages_domain, project: project) - unrelated_domain = create(:pages_domain) + describe '#execute' do + context 'when user has permission' do + it 'marks pages as not deployed' do + create(:pages_deployment, project: project) - service.execute + expect { service.execute }.to change { project.pages_deployed? }.from(true).to(false) + end - expect(PagesDomain.find_by_id(domain.id)).to eq(nil) - expect(PagesDomain.find_by_id(unrelated_domain.id)).to be_present - end + it 'deletes domains' do + domain = create(:pages_domain, project: project) + other_domain = create(:pages_domain) - it 'schedules a destruction of pages deployments' do - expect(DestroyPagesDeploymentsWorker) - .to(receive(:perform_async).with(project.id)) + expect { service.execute }.to change { PagesDomain.count }.by(-1) + expect { domain.reload }.to raise_error(ActiveRecord::RecordNotFound) + expect { other_domain.reload }.not_to raise_error + end - service.execute - end + it 'schedules destruction of pages deployments' do + expect(DestroyPagesDeploymentsWorker).to receive(:perform_async).with(project.id) - it 'removes pages deployments', :sidekiq_inline do - create(:pages_deployment, project: project) + service.execute + end - expect { service.execute } - .to change { PagesDeployment.count }.by(-1) - end + it 'removes pages deployments', :sidekiq_inline do + create(:pages_deployment, project: project) + + expect { service.execute }.to change { PagesDeployment.count }.by(-1) + end + + it 'publishes a ProjectDeleted event with project id and namespace id' do + expected_data = { + project_id: project.id, + namespace_id: project.namespace_id, + root_namespace_id: project.root_namespace.id + } + + expect { service.execute }.to publish_event(Pages::PageDeletedEvent).with(expected_data) + end + end + + context 'when user does not have permission' do + before do + project.add_developer(user) + end + + it 'does not delete pages' do + expect { service.execute }.not_to change { project.pages_deployed? } + end - it 'publishes a ProjectDeleted event with project id and namespace id' do - expected_data = { - project_id: project.id, - namespace_id: project.namespace_id, - root_namespace_id: project.root_namespace.id - } + it 'returns an error message' do + result = service.execute - expect { service.execute }.to publish_event(Pages::PageDeletedEvent).with(expected_data) + expect(result).to be_error + expect(result.message).to eq('The current user is not authorized to remove the Pages deployment') + end + end end end diff --git a/spec/services/pages/update_service_spec.rb b/spec/services/pages/update_service_spec.rb index c2a83c82dce16814d8a2e3bca72eaa28577d76e5..8de12184041f022f4cfe6b81ee98f3d32a68ca73 100644 --- a/spec/services/pages/update_service_spec.rb +++ b/spec/services/pages/update_service_spec.rb @@ -3,7 +3,6 @@ require 'spec_helper' RSpec.describe Pages::UpdateService, feature_category: :pages do - let_it_be(:admin) { create(:admin) } let_it_be(:user) { create(:user) } let_it_be_with_reload(:project) { create(:project) } let(:domain) { 'my.domain.com' } @@ -22,11 +21,10 @@ describe '#execute' do context 'with sufficient permissions' do - let(:service) { described_class.new(project, admin, params) } + let(:service) { described_class.new(project, user, params) } before do - allow(admin).to receive(:can_read_all_resources?).and_return(true) - allow(service).to receive(:can?).with(admin, :update_pages, project).and_return(true) + allow(service).to receive(:can?).with(user, :update_pages, project).and_return(true) end context 'when updating page setting succeeds' do