diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb index 51f01728b..f91c708c0 100644 --- a/app/controllers/admin/users_controller.rb +++ b/app/controllers/admin/users_controller.rb @@ -2,7 +2,7 @@ module Admin class UsersController < Admin::BaseController layout 'admin', except: :js_search - before_action :get_user, only: [:edit, :update, :destroy, :become] + before_action :get_user, only: [:edit, :update, :become, :soft_delete] # Used by dialog def js_search @@ -42,12 +42,14 @@ def update end end - def destroy - salesforce_ids = [@user.salesforce_contact_id, @user.salesforce_lead_id].collect(&:to_s) - security_log :user_deleted_by_admin, user_id: params[:id], uuid: @user.uuid, salesforce_ids: salesforce_ids - @user.destroy + def soft_delete + result = SoftDeleteUser.call(@user) + + security_log :user_deleted_by_admin, user: @user, admin_id: @current_user.id + + # redirect_to admin_users_path + flash[:alert] = "Authentications and PII removed from account." redirect_to admin_users_path - flash[:alert] = "User account has been deleted" end def become diff --git a/app/models/application_user.rb b/app/models/application_user.rb index fe693a0a5..75eb2fc6c 100644 --- a/app/models/application_user.rb +++ b/app/models/application_user.rb @@ -1,7 +1,7 @@ class ApplicationUser < ApplicationRecord belongs_to :application, class_name: 'Doorkeeper::Application', inverse_of: :application_users - belongs_to :user, inverse_of: :application_users + belongs_to :user, inverse_of: :application_users, optional: true belongs_to :default_contact_info, class_name: 'ContactInfo' diff --git a/app/models/authentication.rb b/app/models/authentication.rb index 6415cfdc9..797cb6726 100644 --- a/app/models/authentication.rb +++ b/app/models/authentication.rb @@ -1,5 +1,7 @@ +include UserSessionManagement + class Authentication < ApplicationRecord - belongs_to :user, inverse_of: :authentications + belongs_to :user, inverse_of: :authentications, optional: true validates :provider, presence: true, uniqueness: { scope: :user_id }, @@ -20,6 +22,8 @@ def display_name protected def check_not_last + return if user.is_deleted? + if user.present? && user.authentications.size <= 1 && user.activated? throw(:abort) end diff --git a/app/models/contact_info.rb b/app/models/contact_info.rb index 7a794471b..d626711be 100644 --- a/app/models/contact_info.rb +++ b/app/models/contact_info.rb @@ -63,7 +63,7 @@ def strip end def check_if_last_verified - if verified? and not user.contact_infos.verified.many? and not destroyed_by_association + if verified? and not user.contact_infos.verified.many? and not destroyed_by_association and not user.is_deleted? errors.add(:user, :last_verified) throw(:abort) end diff --git a/app/routines/soft_delete_user.rb b/app/routines/soft_delete_user.rb new file mode 100644 index 000000000..ce550abcd --- /dev/null +++ b/app/routines/soft_delete_user.rb @@ -0,0 +1,33 @@ +class SoftDeleteUser + + lev_routine + + protected + + def exec(user) + return if user.nil? + + # Make sure object up to date, esp before dependent destroy stuff kicks in + user.reload + + user.is_deleted = true + user.save! + + user.external_ids.destroy_all + user.external_uuids.destroy_all + user.authentications.destroy_all + user.application_users.destroy_all + user.contact_infos.destroy_all + user.save! + + user.reload + user.first_name = 'Deleted' + user.last_name = 'User' + user.save! + + # security logs are read-only, but they contain PII so we force delete them for the user + user.security_logs.delete_all + + end + +end diff --git a/app/views/admin/users/_form.html.erb b/app/views/admin/users/_form.html.erb index d211bc9b1..09de326b9 100644 --- a/app/views/admin/users/_form.html.erb +++ b/app/views/admin/users/_form.html.erb @@ -50,6 +50,12 @@ <%= form_for [:admin, @user], html: {id: 'admin-user-form', class: 'form-horizontal'} do |f| %> <%= render "shared/error_messages", :target => @user %> + <% if @user.is_deleted? %> +