Commit 3781f83b authored by Stan Hu's avatar Stan Hu

Fix project deploy key creation and deletion as admin

In EE, the DeployKeyController reloads the user's accesible key so that
it can audit changes. However, this has a side effect of preventing
admins from deleting keys in the interface.  Note that the deletion
problem does not happen in CE because auditing is not performed there.

We can clean up the code by reusing the key that was created or destroyed
and pushing the logic into the service.

Closes https://gitlab.com/gitlab-org/gitlab-ce/issues/53783
parent 5d3eace2
...@@ -48,9 +48,9 @@ class Projects::DeployKeysController < Projects::ApplicationController ...@@ -48,9 +48,9 @@ class Projects::DeployKeysController < Projects::ApplicationController
end end
def enable def enable
load_key key = Projects::EnableDeployKeyService.new(@project, current_user, params).execute
Projects::EnableDeployKeyService.new(@project, current_user, params).execute
log_audit_event(@key.title, action: :create) return render_404 unless key
respond_to do |format| respond_to do |format|
format.html { redirect_to_repository_settings(@project, anchor: 'js-deploy-keys-settings') } format.html { redirect_to_repository_settings(@project, anchor: 'js-deploy-keys-settings') }
...@@ -58,21 +58,16 @@ class Projects::DeployKeysController < Projects::ApplicationController ...@@ -58,21 +58,16 @@ class Projects::DeployKeysController < Projects::ApplicationController
end end
end end
# rubocop: disable CodeReuse/ActiveRecord
def disable def disable
deploy_key_project = @project.deploy_keys_projects.find_by(deploy_key_id: params[:id]) deploy_key_project = Projects::DisableDeployKeyService.new(@project, current_user, params).execute
return render_404 unless deploy_key_project
load_key return render_404 unless deploy_key_project
deploy_key_project.destroy!
log_audit_event(@key.title, action: :destroy)
respond_to do |format| respond_to do |format|
format.html { redirect_to_repository_settings(@project, anchor: 'js-deploy-keys-settings') } format.html { redirect_to_repository_settings(@project, anchor: 'js-deploy-keys-settings') }
format.json { head :ok } format.json { head :ok }
end end
end end
# rubocop: enable CodeReuse/ActiveRecord
protected protected
...@@ -99,8 +94,4 @@ class Projects::DeployKeysController < Projects::ApplicationController ...@@ -99,8 +94,4 @@ class Projects::DeployKeysController < Projects::ApplicationController
AuditEventService.new(current_user, @project, options) AuditEventService.new(current_user, @project, options)
.for_deploy_key(key_title).security_event .for_deploy_key(key_title).security_event
end end
def load_key
@key ||= current_user.accessible_deploy_keys.find(params[:id])
end
end end
# frozen_string_literal: true
module Projects
class DisableDeployKeyService < BaseService
def execute
# rubocop: disable CodeReuse/ActiveRecord
deploy_key_project = project.deploy_keys_projects.find_by(deploy_key_id: params[:id])
# rubocop: enable CodeReuse/ActiveRecord
deploy_key_project&.destroy!
end
end
end
Projects::DisableDeployKeyService.prepend(EE::Projects::DisableDeployKeyService)
...@@ -2,9 +2,10 @@ ...@@ -2,9 +2,10 @@
module Projects module Projects
class EnableDeployKeyService < BaseService class EnableDeployKeyService < BaseService
# rubocop: disable CodeReuse/ActiveRecord
def execute def execute
key = accessible_keys.find_by(id: params[:key_id] || params[:id]) key_id = params[:key_id] || params[:id]
key = find_accessible_key(key_id)
return unless key return unless key
unless project.deploy_keys.include?(key) unless project.deploy_keys.include?(key)
...@@ -13,12 +14,17 @@ module Projects ...@@ -13,12 +14,17 @@ module Projects
key key
end end
# rubocop: enable CodeReuse/ActiveRecord
private private
def accessible_keys def find_accessible_key(key_id)
current_user.accessible_deploy_keys if current_user.admin?
DeployKey.find_by_id(key_id)
else
current_user.accessible_deploy_keys.find_by_id(key_id)
end
end end
end end
end end
Projects::EnableDeployKeyService.prepend(EE::Projects::EnableDeployKeyService)
# frozen_string_literal: true
module EE::Projects::DisableDeployKeyService
extend ActiveSupport::Concern
extend ::Gitlab::Utils::Override
override :execute
def execute
super.tap do |deploy_key_project|
break unless deploy_key_project
log_audit_event(deploy_key_project.deploy_key.title, action: :destroy)
end
end
private
def log_audit_event(key_title, options = {})
AuditEventService.new(current_user, project, options)
.for_deploy_key(key_title).security_event
end
end
# frozen_string_literal: true
module EE::Projects::EnableDeployKeyService
extend ActiveSupport::Concern
extend ::Gitlab::Utils::Override
override :execute
def execute
super.tap do |key|
break unless key
log_audit_event(key.title, action: :create)
end
end
private
def log_audit_event(key_title, options = {})
AuditEventService.new(current_user, project, options)
.for_deploy_key(key_title).security_event
end
end
---
title: Fix project deploy key creation and deletion as admin
merge_request: 8432
author:
type: fixed
...@@ -27,12 +27,8 @@ describe Projects::DeployKeysController do ...@@ -27,12 +27,8 @@ describe Projects::DeployKeysController do
let(:project2) { create(:project, :internal)} let(:project2) { create(:project, :internal)}
let(:project_private) { create(:project, :private)} let(:project_private) { create(:project, :private)}
let(:deploy_key_internal) do let(:deploy_key_internal) { create(:deploy_key) }
create(:deploy_key, key: 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQCdMHEHyhRjbhEZVddFn6lTWdgEy5Q6Bz4nwGB76xWZI5YT/1WJOMEW+sL5zYd31kk7sd3FJ5L9ft8zWMWrr/iWXQikC2cqZK24H1xy+ZUmrRuJD4qGAaIVoyyzBL+avL+lF8J5lg6YSw8gwJY/lX64/vnJHUlWw2n5BF8IFOWhiw== dummy@gitlab.com') let(:deploy_key_actual) { create(:deploy_key) }
end
let(:deploy_key_actual) do
create(:deploy_key, key: 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDNd/UJWhPrpb+b/G5oL109y57yKuCxE+WUGJGYaj7WQKsYRJmLYh1mgjrl+KVyfsWpq4ylOxIfFSnN9xBBFN8mlb0Fma5DC7YsSsibJr3MZ19ZNBprwNcdogET7aW9I0In7Wu5f2KqI6e5W/spJHCy4JVxzVMUvk6Myab0LnJ2iQ== dummy@gitlab.com')
end
let!(:deploy_key_public) { create(:deploy_key, public: true) } let!(:deploy_key_public) { create(:deploy_key, public: true) }
let!(:deploy_keys_project_internal) do let!(:deploy_keys_project_internal) do
...@@ -63,4 +59,147 @@ describe Projects::DeployKeysController do ...@@ -63,4 +59,147 @@ describe Projects::DeployKeysController do
end end
end end
end end
describe '/enable/:id' do
let(:deploy_key) { create(:deploy_key) }
let(:project2) { create(:project) }
let!(:deploy_keys_project_internal) do
create(:deploy_keys_project, project: project2, deploy_key: deploy_key)
end
context 'with anonymous user' do
before do
sign_out(:user)
end
it 'redirects to login' do
expect do
put :enable, id: deploy_key.id, namespace_id: project.namespace, project_id: project
end.not_to change { DeployKeysProject.count }
expect(response).to have_http_status(302)
expect(response).to redirect_to(new_user_session_path)
end
end
context 'with user with no permission' do
before do
sign_in(create(:user))
end
it 'returns 404' do
expect do
put :enable, id: deploy_key.id, namespace_id: project.namespace, project_id: project
end.not_to change { DeployKeysProject.count }
expect(response).to have_http_status(404)
end
end
context 'with user with permission' do
before do
project2.add_maintainer(user)
end
it 'returns 302' do
expect do
put :enable, id: deploy_key.id, namespace_id: project.namespace, project_id: project
end.to change { DeployKeysProject.count }.by(1)
expect(DeployKeysProject.where(project_id: project.id, deploy_key_id: deploy_key.id).count).to eq(1)
expect(response).to have_http_status(302)
expect(response).to redirect_to(namespace_project_settings_repository_path(anchor: 'js-deploy-keys-settings'))
end
it 'returns 404' do
put :enable, id: 0, namespace_id: project.namespace, project_id: project
expect(response).to have_http_status(404)
end
end
context 'with admin' do
before do
sign_in(create(:admin))
end
it 'returns 302' do
expect do
put :enable, id: deploy_key.id, namespace_id: project.namespace, project_id: project
end.to change { DeployKeysProject.count }.by(1)
.and change { AuditEvent.count }.by(1) ## EE only
expect(DeployKeysProject.where(project_id: project.id, deploy_key_id: deploy_key.id).count).to eq(1)
expect(response).to have_http_status(302)
expect(response).to redirect_to(namespace_project_settings_repository_path(anchor: 'js-deploy-keys-settings'))
end
end
end
describe '/disable/:id' do
let(:deploy_key) { create(:deploy_key) }
let!(:deploy_key_project) { create(:deploy_keys_project, project: project, deploy_key: deploy_key) }
context 'with anonymous user' do
before do
sign_out(:user)
end
it 'redirects to login' do
put :disable, id: deploy_key.id, namespace_id: project.namespace, project_id: project
expect(response).to have_http_status(302)
expect(response).to redirect_to(new_user_session_path)
expect(DeployKey.find(deploy_key.id)).to eq(deploy_key)
end
end
context 'with user with no permission' do
before do
sign_in(create(:user))
end
it 'returns 404' do
put :disable, id: deploy_key.id, namespace_id: project.namespace, project_id: project
expect(response).to have_http_status(404)
expect(DeployKey.find(deploy_key.id)).to eq(deploy_key)
end
end
context 'with user with permission' do
it 'returns 302' do
put :disable, id: deploy_key.id, namespace_id: project.namespace, project_id: project
expect(response).to have_http_status(302)
expect(response).to redirect_to(namespace_project_settings_repository_path(anchor: 'js-deploy-keys-settings'))
expect { DeployKey.find(deploy_key.id) }.to raise_error(ActiveRecord::RecordNotFound)
end
it 'returns 404' do
put :disable, id: 0, namespace_id: project.namespace, project_id: project
expect(response).to have_http_status(404)
end
end
context 'with admin' do
before do
sign_in(create(:admin))
end
it 'returns 302' do
expect do
put :disable, id: deploy_key.id, namespace_id: project.namespace, project_id: project
end.to change { DeployKey.count }.by(-1)
.and change { AuditEvent.count }.by(1) ## EE only
expect(response).to have_http_status(302)
expect(response).to redirect_to(namespace_project_settings_repository_path(anchor: 'js-deploy-keys-settings'))
expect { DeployKey.find(deploy_key.id) }.to raise_error(ActiveRecord::RecordNotFound)
end
end
end
end end
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