Merge branch '28694-hard-delete-user-from-admin-panel' into 'master'
Allow users to be hard-deleted from the admin panel Closes #28694 See merge request !11874
This commit is contained in:
commit
7c23c6af6f
15 changed files with 82 additions and 21 deletions
|
@ -138,7 +138,7 @@ class Admin::UsersController < Admin::ApplicationController
|
|||
end
|
||||
|
||||
def destroy
|
||||
DeleteUserWorker.perform_async(current_user.id, user.id)
|
||||
user.delete_async(deleted_by: current_user, params: params.permit(:hard_delete))
|
||||
|
||||
respond_to do |format|
|
||||
format.html { redirect_to admin_users_path, notice: "The user is being deleted." }
|
||||
|
|
|
@ -25,7 +25,7 @@ class RegistrationsController < Devise::RegistrationsController
|
|||
end
|
||||
|
||||
def destroy
|
||||
DeleteUserWorker.perform_async(current_user.id, current_user.id)
|
||||
current_user.delete_async(deleted_by: current_user)
|
||||
|
||||
respond_to do |format|
|
||||
format.html do
|
||||
|
|
|
@ -15,8 +15,7 @@ class AbuseReport < ActiveRecord::Base
|
|||
alias_method :author, :reporter
|
||||
|
||||
def remove_user(deleted_by:)
|
||||
user.block
|
||||
DeleteUserWorker.perform_async(deleted_by.id, user.id, delete_solo_owned_groups: true, hard_delete: true)
|
||||
user.delete_async(deleted_by: deleted_by, params: { hard_delete: true })
|
||||
end
|
||||
|
||||
def notify
|
||||
|
|
|
@ -4,8 +4,7 @@ class SpamLog < ActiveRecord::Base
|
|||
validates :user, presence: true
|
||||
|
||||
def remove_user(deleted_by:)
|
||||
user.block
|
||||
DeleteUserWorker.perform_async(deleted_by.id, user.id, delete_solo_owned_groups: true, hard_delete: true)
|
||||
user.delete_async(deleted_by: deleted_by, params: { hard_delete: true })
|
||||
end
|
||||
|
||||
def text
|
||||
|
|
|
@ -809,6 +809,11 @@ class User < ActiveRecord::Base
|
|||
system_hook_service.execute_hooks_for(self, :destroy)
|
||||
end
|
||||
|
||||
def delete_async(deleted_by:, params: {})
|
||||
block if params[:hard_delete]
|
||||
DeleteUserWorker.perform_async(deleted_by.id, id, params)
|
||||
end
|
||||
|
||||
def notification_service
|
||||
NotificationService.new
|
||||
end
|
||||
|
|
|
@ -6,12 +6,27 @@ module Users
|
|||
@current_user = current_user
|
||||
end
|
||||
|
||||
# Synchronously destroys +user+
|
||||
#
|
||||
# The operation will fail if the user is the sole owner of any groups. To
|
||||
# force the groups to be destroyed, pass `delete_solo_owned_groups: true` in
|
||||
# +options+.
|
||||
#
|
||||
# The user's contributions will be migrated to a global ghost user. To
|
||||
# force the contributions to be destroyed, pass `hard_delete: true` in
|
||||
# +options+.
|
||||
#
|
||||
# `hard_delete: true` implies `delete_solo_owned_groups: true`. To perform
|
||||
# a hard deletion without destroying solo-owned groups, pass
|
||||
# `delete_solo_owned_groups: false, hard_delete: true` in +options+.
|
||||
def execute(user, options = {})
|
||||
delete_solo_owned_groups = options.fetch(:delete_solo_owned_groups, options[:hard_delete])
|
||||
|
||||
unless Ability.allowed?(current_user, :destroy_user, user)
|
||||
raise Gitlab::Access::AccessDeniedError, "#{current_user} tried to destroy user #{user}!"
|
||||
end
|
||||
|
||||
if !options[:delete_solo_owned_groups] && user.solo_owned_groups.present?
|
||||
if !delete_solo_owned_groups && user.solo_owned_groups.present?
|
||||
user.errors[:base] << 'You must transfer ownership or delete groups before you can remove user'
|
||||
return user
|
||||
end
|
||||
|
|
|
@ -34,9 +34,15 @@
|
|||
- if user.access_locked?
|
||||
%li
|
||||
= link_to 'Unlock', unlock_admin_user_path(user), method: :put, class: 'btn-grouped btn btn-xs btn-success', data: { confirm: 'Are you sure?' }
|
||||
- if user.can_be_removed? && can?(current_user, :destroy_user, user)
|
||||
- if can?(current_user, :destroy_user, user)
|
||||
%li.divider
|
||||
- if user.can_be_removed?
|
||||
%li
|
||||
= link_to 'Remove user', admin_user_path(user),
|
||||
data: { confirm: "USER #{user.name} WILL BE REMOVED! Are you sure?" },
|
||||
method: :delete
|
||||
%li
|
||||
= link_to 'Delete user', [:admin, user], data: { confirm: "USER #{user.name} WILL BE REMOVED! All issues, merge requests and groups linked to this user will also be removed! Consider cancelling this deletion and blocking the user instead. Are you sure?" },
|
||||
class: 'btn btn-remove btn-block',
|
||||
method: :delete
|
||||
= link_to 'Remove user and contributions', admin_user_path(user, hard_delete: true),
|
||||
data: { confirm: "USER #{user.name} WILL BE REMOVED! All issues, merge requests and comments authored by this user, and groups owned solely by them, will also be removed! Are you sure?" },
|
||||
class: 'btn btn-remove btn-block',
|
||||
method: :delete
|
||||
|
|
|
@ -177,7 +177,7 @@
|
|||
%p Deleting a user has the following effects:
|
||||
= render 'users/deletion_guidance', user: @user
|
||||
%br
|
||||
= link_to 'Remove user', [:admin, @user], data: { confirm: "USER #{@user.name} WILL BE REMOVED! Are you sure?" }, method: :delete, class: "btn btn-remove"
|
||||
= link_to 'Remove user', admin_user_path(@user), data: { confirm: "USER #{@user.name} WILL BE REMOVED! Are you sure?" }, method: :delete, class: "btn btn-remove"
|
||||
- else
|
||||
- if @user.solo_owned_groups.present?
|
||||
%p
|
||||
|
@ -188,3 +188,22 @@
|
|||
- else
|
||||
%p
|
||||
You don't have access to delete this user.
|
||||
|
||||
.panel.panel-danger
|
||||
.panel-heading
|
||||
Remove user and contributions
|
||||
.panel-body
|
||||
- if can?(current_user, :destroy_user, @user)
|
||||
%p
|
||||
This option deletes the user and any contributions that
|
||||
would usually be moved to the
|
||||
= succeed "." do
|
||||
= link_to "system ghost user", help_page_path("user/profile/account/delete_account")
|
||||
As well as the user's personal projects, groups owned solely by
|
||||
the user, and projects in them, will also be removed. Commits
|
||||
to other projects are unaffected.
|
||||
%br
|
||||
= link_to 'Remove user and contributions', admin_user_path(@user, hard_delete: true), data: { confirm: "USER #{@user.name} WILL BE REMOVED! Are you sure?" }, method: :delete, class: "btn btn-remove"
|
||||
- else
|
||||
%p
|
||||
You don't have access to delete this user.
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
title: Allow users to be hard-deleted from the admin panel
|
||||
merge_request: 11874
|
||||
author:
|
|
@ -25,7 +25,8 @@ Instead of being deleted, these records will be moved to a system-wide
|
|||
When a user is deleted from an abuse report or spam log, these associated
|
||||
records are not ghosted and will be removed, along with any groups the user
|
||||
is a sole owner of. Administrators can also request this behaviour when
|
||||
deleting users from the [API](../../../api/users.md#user-deletion)
|
||||
deleting users from the [API](../../../api/users.md#user-deletion) or the
|
||||
admin area.
|
||||
|
||||
[ce-7393]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/7393
|
||||
[ce-10273]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/10273
|
||||
|
|
|
@ -293,7 +293,7 @@ module API
|
|||
user = User.find_by(id: params[:id])
|
||||
not_found!('User') unless user
|
||||
|
||||
DeleteUserWorker.perform_async(current_user.id, user.id, hard_delete: params[:hard_delete])
|
||||
user.delete_async(deleted_by: current_user, params: params)
|
||||
end
|
||||
|
||||
desc 'Block a user. Available only for admins.'
|
||||
|
|
|
@ -10,15 +10,26 @@ describe Admin::UsersController do
|
|||
|
||||
describe 'DELETE #user with projects' do
|
||||
let(:project) { create(:empty_project, namespace: user.namespace) }
|
||||
let!(:issue) { create(:issue, author: user) }
|
||||
|
||||
before do
|
||||
project.team << [user, :developer]
|
||||
end
|
||||
|
||||
it 'deletes user' do
|
||||
it 'deletes user and ghosts their contributions' do
|
||||
delete :destroy, id: user.username, format: :json
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
expect { User.find(user.id) }.to raise_exception(ActiveRecord::RecordNotFound)
|
||||
expect(User.exists?(user.id)).to be_falsy
|
||||
expect(issue.reload.author).to be_ghost
|
||||
end
|
||||
|
||||
it 'deletes the user and their contributions when hard delete is specified' do
|
||||
delete :destroy, id: user.username, hard_delete: true, format: :json
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
expect(User.exists?(user.id)).to be_falsy
|
||||
expect(Issue.exists?(issue.id)).to be_falsy
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -77,7 +77,7 @@ describe RegistrationsController do
|
|||
end
|
||||
|
||||
it 'schedules the user for destruction' do
|
||||
expect(DeleteUserWorker).to receive(:perform_async).with(user.id, user.id)
|
||||
expect(DeleteUserWorker).to receive(:perform_async).with(user.id, user.id, {})
|
||||
|
||||
post(:destroy)
|
||||
|
||||
|
|
|
@ -22,7 +22,8 @@ describe "Admin::Users", feature: true do
|
|||
expect(page).to have_content(user.email)
|
||||
expect(page).to have_content(user.name)
|
||||
expect(page).to have_link('Block', href: block_admin_user_path(user))
|
||||
expect(page).to have_link('Delete', href: admin_user_path(user))
|
||||
expect(page).to have_link('Remove user', href: admin_user_path(user))
|
||||
expect(page).to have_link('Remove user and contributions', href: admin_user_path(user, hard_delete: true))
|
||||
end
|
||||
|
||||
describe 'Two-factor Authentication filters' do
|
||||
|
@ -116,6 +117,9 @@ describe "Admin::Users", feature: true do
|
|||
|
||||
expect(page).to have_content(user.email)
|
||||
expect(page).to have_content(user.name)
|
||||
expect(page).to have_link('Block user', href: block_admin_user_path(user))
|
||||
expect(page).to have_link('Remove user', href: admin_user_path(user))
|
||||
expect(page).to have_link('Remove user and contributions', href: admin_user_path(user, hard_delete: true))
|
||||
end
|
||||
|
||||
describe 'Impersonation' do
|
||||
|
|
|
@ -28,9 +28,7 @@ RSpec.describe AbuseReport, type: :model do
|
|||
end
|
||||
|
||||
it 'lets a worker delete the user' do
|
||||
expect(DeleteUserWorker).to receive(:perform_async).with(user.id, subject.user.id,
|
||||
delete_solo_owned_groups: true,
|
||||
hard_delete: true)
|
||||
expect(DeleteUserWorker).to receive(:perform_async).with(user.id, subject.user.id, hard_delete: true)
|
||||
|
||||
subject.remove_user(deleted_by: user)
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue