Commit 9c2682cf authored by Etienne Baqué's avatar Etienne Baqué

Merge branch 'secure-file-project-deletion' into 'master'

Remove Secure Files when a project is destroyed

See merge request gitlab-org/gitlab!82443
parents d8f75667 6ea7db76
# frozen_string_literal: true
class Projects::Ci::SecureFilesController < Projects::ApplicationController
before_action :check_can_collaborate!
before_action :authorize_read_secure_files!
feature_category :pipeline_authoring
def show
end
private
def check_can_collaborate!
render_404 unless can_collaborate_with_project?(project)
end
end
......@@ -413,6 +413,7 @@ class ProjectPolicy < BasePolicy
enable :admin_feature_flag
enable :admin_feature_flags_user_lists
enable :update_escalation_status
enable :read_secure_files
end
rule { can?(:developer_access) & user_confirmed? }.policy do
......@@ -462,6 +463,7 @@ class ProjectPolicy < BasePolicy
enable :register_project_runners
enable :update_runners_registration_token
enable :admin_project_google_cloud
enable :admin_secure_files
end
rule { public_project & metrics_dashboard_allowed }.policy do
......
# frozen_string_literal: true
module Ci
class DestroySecureFileService < BaseService
def execute(secure_file)
raise Gitlab::Access::AccessDeniedError unless can?(current_user, :admin_secure_files, secure_file.project)
secure_file.destroy!
end
end
end
......@@ -200,6 +200,10 @@ module Projects
::Ci::DestroyPipelineService.new(project, current_user).execute(pipeline)
end
project.secure_files.find_each(batch_size: BATCH_SIZE) do |secure_file| # rubocop: disable CodeReuse/ActiveRecord
::Ci::DestroySecureFileService.new(project, current_user).execute(secure_file)
end
deleted_count = ::CommitStatus.for_project(project).delete_all
Gitlab::AppLogger.info(
......
......@@ -7,8 +7,8 @@ module API
before do
authenticate!
authorize! :admin_build, user_project
feature_flag_enabled?
authorize! :read_secure_files, user_project
end
feature_category :pipeline_authoring
......@@ -52,39 +52,44 @@ module API
body secure_file.file.read
end
desc 'Upload a Secure File'
params do
requires :name, type: String, desc: 'The name of the file'
requires :file, types: [Rack::Multipart::UploadedFile, ::API::Validations::Types::WorkhorseFile], desc: 'The secure file to be uploaded'
optional :permissions, type: String, desc: 'The file permissions', default: 'read_only', values: %w[read_only read_write execute]
end
route_setting :authentication, basic_auth_personal_access_token: true, job_token_allowed: true
post ':id/secure_files' do
secure_file = user_project.secure_files.new(
name: params[:name],
permissions: params[:permissions] || :read_only
)
secure_file.file = params[:file]
file_too_large! unless secure_file.file.size < ::Ci::SecureFile::FILE_SIZE_LIMIT.to_i
resource do
before do
authorize! :admin_secure_files, user_project
end
if secure_file.save
present secure_file, with: Entities::Ci::SecureFile
else
render_validation_error!(secure_file)
desc 'Upload a Secure File'
params do
requires :name, type: String, desc: 'The name of the file'
requires :file, types: [Rack::Multipart::UploadedFile, ::API::Validations::Types::WorkhorseFile], desc: 'The secure file to be uploaded'
optional :permissions, type: String, desc: 'The file permissions', default: 'read_only', values: %w[read_only read_write execute]
end
route_setting :authentication, basic_auth_personal_access_token: true, job_token_allowed: true
post ':id/secure_files' do
secure_file = user_project.secure_files.new(
name: params[:name],
permissions: params[:permissions] || :read_only
)
secure_file.file = params[:file]
file_too_large! unless secure_file.file.size < ::Ci::SecureFile::FILE_SIZE_LIMIT.to_i
if secure_file.save
present secure_file, with: Entities::Ci::SecureFile
else
render_validation_error!(secure_file)
end
end
end
desc 'Delete an individual Secure File'
route_setting :authentication, basic_auth_personal_access_token: true, job_token_allowed: true
delete ':id/secure_files/:secure_file_id' do
secure_file = user_project.secure_files.find(params[:secure_file_id])
desc 'Delete an individual Secure File'
route_setting :authentication, basic_auth_personal_access_token: true, job_token_allowed: true
delete ':id/secure_files/:secure_file_id' do
secure_file = user_project.secure_files.find(params[:secure_file_id])
secure_file.destroy!
::Ci::DestroySecureFileService.new(user_project, current_user).execute(secure_file)
no_content!
no_content!
end
end
end
......
This diff is collapsed.
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe ::Ci::DestroySecureFileService do
let_it_be(:maintainer_user) { create(:user) }
let_it_be(:developer_user) { create(:user) }
let_it_be(:project) { create(:project) }
let_it_be(:secure_file) { create(:ci_secure_file, project: project) }
let_it_be(:project_member) { create(:project_member, :maintainer, user: maintainer_user, project: project) }
let_it_be(:project_member2) { create(:project_member, :developer, user: developer_user, project: project) }
subject { described_class.new(project, user).execute(secure_file) }
context 'user is a maintainer' do
let(:user) { maintainer_user }
it 'destroys the secure file' do
subject
expect { secure_file.reload }.to raise_error(ActiveRecord::RecordNotFound)
end
end
context 'user is a developer' do
let(:user) { developer_user }
it 'raises an exception' do
expect { subject }.to raise_error(Gitlab::Access::AccessDeniedError)
end
end
end
......@@ -43,6 +43,7 @@ RSpec.describe Projects::DestroyService, :aggregate_failures, :event_store_publi
let!(:report_result) { create(:ci_build_report_result, build: build) }
let!(:pending_state) { create(:ci_build_pending_state, build: build) }
let!(:pipeline_artifact) { create(:ci_pipeline_artifact, pipeline: pipeline) }
let!(:secure_file) { create(:ci_secure_file, project: project) }
it 'deletes build and pipeline related records' do
expect { destroy_project(project, user, {}) }
......@@ -56,6 +57,7 @@ RSpec.describe Projects::DestroyService, :aggregate_failures, :event_store_publi
.and change { Ci::BuildReportResult.count }.by(-1)
.and change { Ci::BuildRunnerSession.count }.by(-1)
.and change { Ci::Pipeline.count }.by(-1)
.and change { Ci::SecureFile.count }.by(-1)
end
it 'avoids N+1 queries' do
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment