Skip to content
代码片段 群组 项目
未验证 提交 5818a3d2 编辑于 作者: Joe Woodward's avatar Joe Woodward 提交者: GitLab
浏览文件

Merge branch 'fix/tag-api-delete-auth' into 'master'

No related branches found
No related tags found
无相关合并请求
# frozen_string_literal: true
module Gitlab
module Git
class TagPolicy < BasePolicy
delegate { project }
condition(:protected_tag, scope: :subject) do
ProtectedTag.protected?(project, @subject.name)
end
rule { can?(:admin_tag) & (~protected_tag | can?(:maintainer_access)) }.enable :delete_tag
def project
@subject.repository.container
end
end
end
end
...@@ -2,21 +2,17 @@ ...@@ -2,21 +2,17 @@
module Tags module Tags
class DestroyService < BaseService class DestroyService < BaseService
def execute(tag_name) def execute(tag_name, skip_find: false)
repository = project.repository repository = project.repository
tag = repository.find_tag(tag_name)
unless tag # If we've found the tag upstream we don't need to refind it so we can
return error('No such tag', 404) # pass skip_find: true
end return error('No such tag', 404) unless skip_find || tag_exists?(tag_name)
if repository.rm_tag(current_user, tag_name) if repository.rm_tag(current_user, tag_name)
## # When a tag in a repository is destroyed, release assets will be
# When a tag in a repository is destroyed, # destroyed too.
# release assets will be destroyed too. destroy_releases(tag_name)
Releases::DestroyService
.new(project, current_user, tag: tag_name)
.execute
unlock_artifacts(tag_name) unlock_artifacts(tag_name)
...@@ -38,6 +34,14 @@ def success(message) ...@@ -38,6 +34,14 @@ def success(message)
private private
def tag_exists?(tag_name)
repository.find_tag(tag_name)
end
def destroy_releases(tag_name)
Releases::DestroyService.new(project, current_user, tag: tag_name).execute
end
def unlock_artifacts(tag_name) def unlock_artifacts(tag_name)
Ci::RefDeleteUnlockArtifactsWorker.perform_async(project.id, current_user.id, "#{::Gitlab::Git::TAG_REF_PREFIX}#{tag_name}") Ci::RefDeleteUnlockArtifactsWorker.perform_async(project.id, current_user.id, "#{::Gitlab::Git::TAG_REF_PREFIX}#{tag_name}")
end end
......
...@@ -119,6 +119,7 @@ def find_releases(tags) ...@@ -119,6 +119,7 @@ def find_releases(tags)
desc 'Delete a repository tag' do desc 'Delete a repository tag' do
success code: 204 success code: 204
failure [ failure [
{ code: 400, message: 'Bad request' },
{ code: 403, message: 'Unauthenticated' }, { code: 403, message: 'Unauthenticated' },
{ code: 404, message: 'Not found' }, { code: 404, message: 'Not found' },
{ code: 412, message: 'Precondition failed' } { code: 412, message: 'Precondition failed' }
...@@ -129,16 +130,14 @@ def find_releases(tags) ...@@ -129,16 +130,14 @@ def find_releases(tags)
requires :tag_name, type: String, desc: 'The name of the tag' requires :tag_name, type: String, desc: 'The name of the tag'
end end
delete ':id/repository/tags/:tag_name', requirements: TAG_ENDPOINT_REQUIREMENTS, feature_category: :source_code_management do delete ':id/repository/tags/:tag_name', requirements: TAG_ENDPOINT_REQUIREMENTS, feature_category: :source_code_management do
authorize_admin_tag
tag = user_project.repository.find_tag(params[:tag_name]) tag = user_project.repository.find_tag(params[:tag_name])
not_found!('Tag') unless tag not_found!('Tag') unless tag
authorize!(:delete_tag, tag)
commit = user_project.repository.commit(tag.dereferenced_target) commit = user_project.repository.commit(tag.dereferenced_target)
destroy_conditionally!(commit, last_updated: commit.authored_date) do destroy_conditionally!(commit, last_updated: commit.authored_date) do
result = ::Tags::DestroyService.new(user_project, current_user) result = ::Tags::DestroyService.new(user_project, current_user).execute(params[:tag_name], skip_find: true)
.execute(params[:tag_name])
if result[:status] != :success if result[:status] != :success
render_api_error!(result[:message], result[:return_code]) render_api_error!(result[:message], result[:return_code])
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::Git::TagPolicy, feature_category: :source_code_management do
let_it_be(:project) { create(:project, :public, :repository) }
let_it_be(:guest) { create(:user, guest_of: project) }
let_it_be(:developer) { create(:user, developer_of: project) }
let_it_be(:maintainer) { create(:user, maintainer_of: project) }
let_it_be(:owner) { create(:user, owner_of: project) }
let_it_be(:tag) { project.repository.tags.first }
subject { described_class.new(user, tag) }
context 'when user is a project guest' do
let(:user) { guest }
it { is_expected.to be_disallowed(:delete_tag) }
end
context 'when user is a project developer' do
let(:user) { developer }
it { is_expected.to be_allowed(:delete_tag) }
context 'when the tag is protected' do
let_it_be(:protected_tag) { create(:protected_tag, project: project, name: tag.name) }
it { is_expected.to be_disallowed(:delete_tag) }
end
end
context 'when user is a project maintainer' do
let(:user) { maintainer }
it { is_expected.to be_allowed(:delete_tag) }
context 'when the tag is protected' do
let_it_be(:protected_tag) { create(:protected_tag, project: project, name: tag.name) }
it { is_expected.to be_allowed(:delete_tag) }
end
end
context 'when user is a project owner' do
let(:user) { owner }
it { is_expected.to be_allowed(:delete_tag) }
context 'when the tag is protected' do
let_it_be(:protected_tag) { create(:protected_tag, project: project, name: tag.name) }
it { is_expected.to be_allowed(:delete_tag) }
end
end
end
...@@ -537,8 +537,16 @@ ...@@ -537,8 +537,16 @@
end end
end end
context 'when authenticated', 'as a maintainer' do context 'when authenticated as a guest' do
let(:current_user) { user } let(:current_user) { create(:user, guest_of: project) }
it_behaves_like '403 response' do
let(:request) { delete api(route, current_user) }
end
end
context 'when authenticated as a developer' do
let(:current_user) { create(:user, developer_of: project) }
it_behaves_like 'repository delete tag' it_behaves_like 'repository delete tag'
...@@ -547,6 +555,42 @@ ...@@ -547,6 +555,42 @@
it_behaves_like 'repository delete tag' it_behaves_like 'repository delete tag'
end end
context 'when the tag is protected' do
before do
create(:protected_tag, project: project, name: tag_name)
end
it_behaves_like '403 response' do
let(:request) { delete api(route, current_user) }
end
end
end
context 'when authenticated as a maintainer' do
let(:current_user) { create(:user, maintainer_of: project) }
it_behaves_like 'repository delete tag'
context 'when the tag is protected' do
before do
create(:protected_tag, project: project, name: tag_name)
end
it_behaves_like 'repository delete tag'
end
end
context 'when authenticated as an owner' do
let(:current_user) { create(:user, owner_of: project) }
context 'when the tag is protected' do
before do
create(:protected_tag, project: project, name: tag_name)
end
it_behaves_like 'repository delete tag'
end
end end
end end
......
...@@ -7,25 +7,46 @@ ...@@ -7,25 +7,46 @@
let(:repository) { project.repository } let(:repository) { project.repository }
let(:user) { create(:user) } let(:user) { create(:user) }
let(:service) { described_class.new(project, user) } let(:service) { described_class.new(project, user) }
let(:skip_find) { false }
describe '#execute' do describe '#execute(tag_name, skip_find: false)' do
subject { service.execute(tag_name) } subject(:execute) { service.execute(tag_name, skip_find: skip_find) }
before do before do
allow(Ci::RefDeleteUnlockArtifactsWorker).to receive(:perform_async) allow(Ci::RefDeleteUnlockArtifactsWorker).to receive(:perform_async)
end end
it 'removes the tag' do context 'with tag named v1.1.0' do
expect(repository).to receive(:before_remove_tag) let(:tag_name) { 'v1.1.0' }
expect(service).to receive(:success)
service.execute('v1.1.0') it 'removes the tag' do
end allow(repository).to receive(:before_remove_tag)
allow(service).to receive(:success)
execute
expect(repository).to have_received(:before_remove_tag)
expect(service).to have_received(:success)
end
context 'when skip_find is true' do
let(:skip_find) { true }
it 'calls the RefDeleteUnlockArtifactsWorker' do before do
expect(Ci::RefDeleteUnlockArtifactsWorker).to receive(:perform_async).with(project.id, user.id, 'refs/tags/v1.1.0') allow(repository).to receive(:find_tag)
execute
end
service.execute('v1.1.0') it 'does not verify the tag exists in the repository' do
expect(repository).not_to have_received(:find_tag)
end
end
it 'calls the RefDeleteUnlockArtifactsWorker' do
expect(Ci::RefDeleteUnlockArtifactsWorker).to receive(:perform_async).with(project.id, user.id, "refs/tags/#{tag_name}")
execute
end
end end
context 'when there is an associated release on the tag' do context 'when there is an associated release on the tag' do
......
0% 加载中 .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册