From 46e1fdb8bbdaf149371334f1a1757ba4d68fe020 Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Wed, 13 Dec 2023 18:16:51 +0000 Subject: Add latest changes from gitlab-org/gitlab@master --- .../dependency_proxy_authentication_service.rb | 30 +++++++++-- .../variables_base_save_service.rb | 52 +++++++++++++++++++ .../pipeline_schedules/variables_create_service.rb | 24 +++++++++ .../pipeline_schedules/variables_update_service.rb | 24 +++++++++ .../personal_access_tokens/rotate_service.rb | 44 ++++++++-------- .../project_access_tokens/rotate_service.rb | 58 ++++++++++++++++++++++ app/services/work_items/delete_task_service.rb | 45 ----------------- .../task_list_reference_removal_service.rb | 56 --------------------- 8 files changed, 205 insertions(+), 128 deletions(-) create mode 100644 app/services/ci/pipeline_schedules/variables_base_save_service.rb create mode 100644 app/services/ci/pipeline_schedules/variables_create_service.rb create mode 100644 app/services/ci/pipeline_schedules/variables_update_service.rb create mode 100644 app/services/project_access_tokens/rotate_service.rb delete mode 100644 app/services/work_items/delete_task_service.rb delete mode 100644 app/services/work_items/task_list_reference_removal_service.rb (limited to 'app/services') diff --git a/app/services/auth/dependency_proxy_authentication_service.rb b/app/services/auth/dependency_proxy_authentication_service.rb index 164594d6f6c..29f5a50d809 100644 --- a/app/services/auth/dependency_proxy_authentication_service.rb +++ b/app/services/auth/dependency_proxy_authentication_service.rb @@ -5,10 +5,11 @@ module Auth AUDIENCE = 'dependency_proxy' HMAC_KEY = 'gitlab-dependency-proxy' DEFAULT_EXPIRE_TIME = 1.minute + REQUIRED_ABILITIES = %i[read_container_image create_container_image].freeze def execute(authentication_abilities:) return error('dependency proxy not enabled', 404) unless ::Gitlab.config.dependency_proxy.enabled - return error('access forbidden', 403) unless valid_user_actor? + return error('access forbidden', 403) unless valid_user_actor?(authentication_abilities) { token: authorized_token.encoded } end @@ -33,8 +34,27 @@ module Auth private - def valid_user_actor? - current_user || valid_deploy_token? + def valid_user_actor?(authentication_abilities) + feature_user = deploy_token&.user || current_user + if Feature.enabled?(:packages_dependency_proxy_containers_scope_check, feature_user) + if deploy_token + deploy_token.valid_for_dependency_proxy? + elsif current_user&.project_bot? + group_access_token&.active? && has_required_abilities?(authentication_abilities) + else + current_user + end + else + current_user || valid_deploy_token? + end + end + + def has_required_abilities?(authentication_abilities) + (REQUIRED_ABILITIES & authentication_abilities).size == REQUIRED_ABILITIES.size + end + + def group_access_token + PersonalAccessTokensFinder.new(state: 'active').find_by_token(raw_token) end def valid_deploy_token? @@ -52,5 +72,9 @@ module Auth def deploy_token params[:deploy_token] end + + def raw_token + params[:raw_token] + end end end diff --git a/app/services/ci/pipeline_schedules/variables_base_save_service.rb b/app/services/ci/pipeline_schedules/variables_base_save_service.rb new file mode 100644 index 00000000000..fb3ba52b36e --- /dev/null +++ b/app/services/ci/pipeline_schedules/variables_base_save_service.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +module Ci + module PipelineSchedules + class VariablesBaseSaveService + include Gitlab::Utils::StrongMemoize + + def execute + variable.assign_attributes(params) + + return forbidden_to_update_pipeline_schedule unless allowed_to_update_pipeline_schedule? + return forbidden_to_save_variables unless allowed_to_save_variables? + + if variable.save + ServiceResponse.success(payload: variable) + else + ServiceResponse.error(payload: variable, message: variable.errors.full_messages) + end + end + + private + + attr_reader :project, :user, :params, :variable, :pipeline_schedule + + def allowed_to_update_pipeline_schedule? + user.can?(:update_pipeline_schedule, pipeline_schedule) + end + + def forbidden_to_update_pipeline_schedule + # We add the error to the base object too + # because model errors are used in the API responses and the `form_errors` helper. + pipeline_schedule.errors.add(:base, authorize_message) + + ServiceResponse.error(payload: pipeline_schedule, message: [authorize_message], reason: :forbidden) + end + + def allowed_to_save_variables? + user.can?(:set_pipeline_variables, project) + end + + def forbidden_to_save_variables + message = _('The current user is not authorized to set pipeline schedule variables') + + # We add the error to the base object too + # because model errors are used in the API responses and the `form_errors` helper. + pipeline_schedule.errors.add(:base, message) + + ServiceResponse.error(payload: pipeline_schedule, message: [message], reason: :forbidden) + end + end + end +end diff --git a/app/services/ci/pipeline_schedules/variables_create_service.rb b/app/services/ci/pipeline_schedules/variables_create_service.rb new file mode 100644 index 00000000000..7d81b0771dc --- /dev/null +++ b/app/services/ci/pipeline_schedules/variables_create_service.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +module Ci + module PipelineSchedules + class VariablesCreateService < VariablesBaseSaveService + AUTHORIZE = :update_pipeline_schedule + + def initialize(pipeline_schedule, user, params) + @variable = pipeline_schedule.variables.new + @user = user + @pipeline_schedule = pipeline_schedule + @project = pipeline_schedule.project + @params = params + end + + private + + def authorize_message + _('The current user is not authorized to create the pipeline schedule variables') + end + strong_memoize_attr :authorize_message + end + end +end diff --git a/app/services/ci/pipeline_schedules/variables_update_service.rb b/app/services/ci/pipeline_schedules/variables_update_service.rb new file mode 100644 index 00000000000..f0e3a03c37a --- /dev/null +++ b/app/services/ci/pipeline_schedules/variables_update_service.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +module Ci + module PipelineSchedules + class VariablesUpdateService < VariablesBaseSaveService + AUTHORIZE = :update_pipeline_schedule + + def initialize(pipeline_schedule_variable, user, params) + @variable = pipeline_schedule_variable + @user = user + @pipeline_schedule = pipeline_schedule_variable.pipeline_schedule + @project = pipeline_schedule.project + @params = params + end + + private + + def authorize_message + _('The current user is not authorized to update the pipeline schedule variables') + end + strong_memoize_attr :authorize_message + end + end +end diff --git a/app/services/personal_access_tokens/rotate_service.rb b/app/services/personal_access_tokens/rotate_service.rb index 55eff1e69aa..e381d86fbed 100644 --- a/app/services/personal_access_tokens/rotate_service.rb +++ b/app/services/personal_access_tokens/rotate_service.rb @@ -10,27 +10,19 @@ module PersonalAccessTokens end def execute(params = {}) - return error_response(message: _('token already revoked')) if token.revoked? + return error_response(_('token already revoked')) if token.revoked? response = ServiceResponse.success PersonalAccessToken.transaction do unless token.revoke! - response = error_response + response = error_response(_('failed to revoke token')) raise ActiveRecord::Rollback end - target_user = token.user - new_token = target_user.personal_access_tokens.create(create_token_params(token, params)) + response = create_access_token(params) - if new_token.persisted? - response = error_response unless update_bot_membership(target_user, new_token.expires_at) - response = success_response(new_token) - else - response = error_response(message: new_token.errors.full_messages.to_sentence) - - raise ActiveRecord::Rollback - end + raise ActiveRecord::Rollback unless response.success? end response @@ -40,6 +32,16 @@ module PersonalAccessTokens attr_reader :current_user, :token + def create_access_token(params) + target_user = token.user + + new_token = target_user.personal_access_tokens.create(create_token_params(token, params)) + + return success_response(new_token) if new_token.persisted? + + error_response(new_token.errors.full_messages.to_sentence) + end + def expires_at(params) return params[:expires_at] if params[:expires_at] @@ -50,22 +52,16 @@ module PersonalAccessTokens ServiceResponse.success(payload: { personal_access_token: new_token }) end - def error_response(message: _('failed to revoke token')) + def error_response(message) ServiceResponse.error(message: message) end def create_token_params(token, params) - { name: token.name, - previous_personal_access_token_id: token.id, - impersonation: token.impersonation, - scopes: token.scopes, - expires_at: expires_at(params) } - end - - def update_bot_membership(target_user, expires_at) - return unless target_user.project_bot? - - target_user.members.first.update(expires_at: expires_at) + { name: token.name, + previous_personal_access_token_id: token.id, + impersonation: token.impersonation, + scopes: token.scopes, + expires_at: expires_at(params) } end end end diff --git a/app/services/project_access_tokens/rotate_service.rb b/app/services/project_access_tokens/rotate_service.rb new file mode 100644 index 00000000000..63d8d2a82cc --- /dev/null +++ b/app/services/project_access_tokens/rotate_service.rb @@ -0,0 +1,58 @@ +# frozen_string_literal: true + +module ProjectAccessTokens + class RotateService < ::PersonalAccessTokens::RotateService + extend ::Gitlab::Utils::Override + + def initialize(current_user, token, resource = nil) + @current_user = current_user + @token = token + @project = resource + end + + def execute(params = {}) + super + end + + attr_reader :project + + private + + override :create_access_token + def create_access_token(params) + target_user = token.user + + unless valid_access_level? + return error_response( + _("Not eligible to rotate token with access level higher than the user") + ) + end + + new_token = target_user.personal_access_tokens.create(create_token_params(token, params)) + + if new_token.persisted? + update_bot_membership(target_user, new_token.expires_at) + + return success_response(new_token) + end + + error_response(new_token.errors.full_messages.to_sentence) + end + + def update_bot_membership(target_user, expires_at) + target_user.members.update(expires_at: expires_at) + end + + def valid_access_level? + return true if current_user.can_admin_all_resources? + return false unless current_user.can?(:manage_resource_access_tokens, project) + + token_access_level = project.team.max_member_access(token.user.id).to_i + current_user_access_level = project.team.max_member_access(current_user.id).to_i + + return true if token_access_level.to_i <= current_user_access_level + + false + end + end +end diff --git a/app/services/work_items/delete_task_service.rb b/app/services/work_items/delete_task_service.rb deleted file mode 100644 index 4c0ee2f827d..00000000000 --- a/app/services/work_items/delete_task_service.rb +++ /dev/null @@ -1,45 +0,0 @@ -# frozen_string_literal: true - -module WorkItems - class DeleteTaskService - def initialize(work_item:, lock_version:, current_user: nil, task_params: {}) - @work_item = work_item - @current_user = current_user - @task_params = task_params - @lock_version = lock_version - @task = task_params[:task] - @errors = [] - end - - def execute - transaction_result = ::WorkItem.transaction do - replacement_result = TaskListReferenceRemovalService.new( - work_item: @work_item, - task: @task, - line_number_start: @task_params[:line_number_start], - line_number_end: @task_params[:line_number_end], - lock_version: @lock_version, - current_user: @current_user - ).execute - - next ::ServiceResponse.error(message: replacement_result.errors, http_status: 422) if replacement_result.error? - - delete_result = ::WorkItems::DeleteService.new( - container: @task.project, - current_user: @current_user - ).execute(@task) - - if delete_result.error? - @errors += delete_result.errors - raise ActiveRecord::Rollback - end - - delete_result - end - - return transaction_result if transaction_result - - ::ServiceResponse.error(message: @errors, http_status: 422) - end - end -end diff --git a/app/services/work_items/task_list_reference_removal_service.rb b/app/services/work_items/task_list_reference_removal_service.rb deleted file mode 100644 index 843b03906ac..00000000000 --- a/app/services/work_items/task_list_reference_removal_service.rb +++ /dev/null @@ -1,56 +0,0 @@ -# frozen_string_literal: true - -module WorkItems - class TaskListReferenceRemovalService - STALE_OBJECT_MESSAGE = 'Stale work item. Check lock version' - - def initialize(work_item:, task:, line_number_start:, line_number_end:, lock_version:, current_user:) - @work_item = work_item - @task = task - @line_number_start = line_number_start - @line_number_end = line_number_end - @lock_version = lock_version - @current_user = current_user - @task_reference = /#{Regexp.escape(@task.to_reference)}(?!\d)\+/ - end - - def execute - return ::ServiceResponse.error(message: 'line_number_start must be greater than 0') if @line_number_start < 1 - return ::ServiceResponse.error(message: "Work item description can't be blank") if @work_item.description.blank? - - if @line_number_end < @line_number_start - return ::ServiceResponse.error(message: 'line_number_end must be greater or equal to line_number_start') - end - - source_lines = @work_item.description.split("\n") - - line_matches_reference = (@line_number_start..@line_number_end).any? do |line_number| - markdown_line = source_lines[line_number - 1] - - if @task_reference.match?(markdown_line) - markdown_line.sub!(@task_reference, @task.title) - end - end - - unless line_matches_reference - return ::ServiceResponse.error( - message: "Unable to detect a task on lines #{@line_number_start}-#{@line_number_end}" - ) - end - - ::WorkItems::UpdateService.new( - container: @work_item.project, - current_user: @current_user, - params: { description: source_lines.join("\n"), lock_version: @lock_version } - ).execute(@work_item) - - if @work_item.valid? - ::ServiceResponse.success - else - ::ServiceResponse.error(message: @work_item.errors.full_messages) - end - rescue ActiveRecord::StaleObjectError - ::ServiceResponse.error(message: STALE_OBJECT_MESSAGE) - end - end -end -- cgit v1.2.3