diff --git a/app/HMS/Entities/Banking/BankTransaction.php b/app/HMS/Entities/Banking/BankTransaction.php index a5e000db4..314a5d36e 100644 --- a/app/HMS/Entities/Banking/BankTransaction.php +++ b/app/HMS/Entities/Banking/BankTransaction.php @@ -4,8 +4,9 @@ use Carbon\Carbon; use HMS\Entities\Snackspace\Transaction; +use HMS\Entities\EntityObfuscatableInterface; -class BankTransaction +class BankTransaction implements EntityObfuscatableInterface { /** * @var int @@ -171,4 +172,20 @@ public function setTransaction($transaction) return $this; } + + /** + * Remove any personal information from the transaction. + * This should only happen after 7 years if the member has deleted their account. + */ + public function obfuscate() { + $historic = Carbon::now(); + $historic->subYears(7); + + if ($this->transactionDate() < $historic) { + // The description may contain names etc. + $this->description = "obfuscated"; + } + + return $this; + } } diff --git a/app/HMS/Entities/EntityObfuscatableInterface.php b/app/HMS/Entities/EntityObfuscatableInterface.php new file mode 100644 index 000000000..4ef98f9d5 --- /dev/null +++ b/app/HMS/Entities/EntityObfuscatableInterface.php @@ -0,0 +1,13 @@ +unlockText = null; + $this->contactNumber = null; + $this->dateOfBirth = Carbon::create(1900, 1, 1, 0, 0, 0); + $this->discordUsername = null; + + return $this; + } } diff --git a/app/HMS/Entities/Snackspace/VendLog.php b/app/HMS/Entities/Snackspace/VendLog.php index 1add77180..b7b202254 100644 --- a/app/HMS/Entities/Snackspace/VendLog.php +++ b/app/HMS/Entities/Snackspace/VendLog.php @@ -4,8 +4,9 @@ use Carbon\Carbon; use HMS\Entities\User; +use HMS\Entities\EntityObfuscatableInterface; -class VendLog +class VendLog implements EntityObfuscatableInterface { /** * @var int @@ -213,4 +214,21 @@ public function setPosition($position) return $this; } + + + /** + * Remove any personal information from the transaction. + * This should only happen after 7 years if the member has deleted their account. + */ + public function obfuscate() { + $historic = Carbon::now(); + $historic->subYears(7); + + if ($this->transactionDate() < $historic) { + $this->rfidSerial = null; + $this->user = null; + } + + return $this; + } } diff --git a/app/HMS/Entities/Tools/Usage.php b/app/HMS/Entities/Tools/Usage.php index 1c20ec168..0bf86ba84 100644 --- a/app/HMS/Entities/Tools/Usage.php +++ b/app/HMS/Entities/Tools/Usage.php @@ -4,8 +4,9 @@ use Carbon\Carbon; use HMS\Entities\User; +use HMS\Entities\EntityObfuscatableInterface; -class Usage +class Usage implements EntityObfuscatableInterface { /** * @var int @@ -179,4 +180,13 @@ public function setStatus($status) return $this; } + + /** + * Disassociate the usage from a specific user + */ + public function obfuscate() { + $this->user = null; + + return $this; + } } diff --git a/app/HMS/Entities/User.php b/app/HMS/Entities/User.php index 5e7f58ec0..34d8b300b 100644 --- a/app/HMS/Entities/User.php +++ b/app/HMS/Entities/User.php @@ -11,6 +11,7 @@ use HMS\Traits\Entities\SoftDeletable; use HMS\Traits\Entities\Timestampable; use HMS\Traits\HasApiTokens; +use HMS\Entities\EntityObfuscatableInterface; use Illuminate\Auth\Passwords\CanResetPassword; use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract; use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract; @@ -23,6 +24,7 @@ use LaravelDoctrine\ACL\Permissions\HasPermissions; use LaravelDoctrine\ACL\Roles\HasRoles; use LaravelDoctrine\ORM\Notifications\Notifiable; +use Carbon\Carbon; class User implements AuthenticatableContract, @@ -30,7 +32,8 @@ class User implements HasRoleContract, HasPermissionsContract, AuthorizableContract, - MustVerifyEmailContract + MustVerifyEmailContract, + EntityObfuscatableInterface { use CanResetPassword, Notifiable, @@ -487,4 +490,14 @@ public function routeNotificationForDiscord() ]) ->id; } + + /** + * Obfuscate personal information + */ + public function obfuscate() + { + $this->email = 'deleted-account+' . $this->username . '@deleted-accounts.local'; + $this->account = null; + return $this; + } } diff --git a/app/Http/Controllers/Auth/DeleteAccountController.php b/app/Http/Controllers/Auth/DeleteAccountController.php new file mode 100644 index 000000000..5e5aa33f4 --- /dev/null +++ b/app/Http/Controllers/Auth/DeleteAccountController.php @@ -0,0 +1,117 @@ +passwordStore = $passwordStore; + $this->userRepository = $userRepository; + $this->profileRepository = $profileRepository; + $this->roleManager = $roleManager; + } + + /** + * Show the form for editing the specified resource. + * + * @return \Illuminate\Http\Response + */ + public function info() + { + return view('user.deleteAccount')->with('user', Auth::user()); + } + + /** + * Initiate the account removal process + * + * @param \Illuminate\Http\Request $request + * + * @return \Illuminate\Http\Response + */ + public function delete(Request $request) + { + $user = Auth::user(); + + $valideCurrentPassword = Auth::guard()->validate([ + 'username' => $user->getUsername(), + 'password' => $request->currentPassword, + ]); + + if (! $valideCurrentPassword) { + flash('Your current password does not matches with the password you provided. Please try again.')->error(); + return redirect()->back(); + } + + // BankTransactions will be handled by audit job. These will + // be obfuscated if they are older than seven years and the + // account has been removed. Same for VendLog. + // Unimportant information from Profile will be removed. + // Email address is removed from User. + + $profile = $user->getProfile(); + + // Remove all roles regardless of whether it's retained. + foreach ($user->getRoles() as $role) { + $this->roleManager->removeUserFromRole($user, $role); + } + + // Obfuscate user and profile and flag user as soft deleted. + // Soft delete will result in logout on next page load. + $user->setDeletedAt(Carbon::now()) + ->obfuscate(); + $profile->obfuscate(); + + // Commit all changes. + $this->userRepository->save($user); + $this->profileRepository->save($profile); + + // event(new AccountDeletion($user)); + + // TODO: illuminate unique is excluding deleted items during validation + // but database insertion fails. i dont want usernames to be recycled anyway. + + return redirect()->route('home'); + } + +} diff --git a/resources/views/user/deleteAccount.blade.php b/resources/views/user/deleteAccount.blade.php new file mode 100644 index 000000000..552017a04 --- /dev/null +++ b/resources/views/user/deleteAccount.blade.php @@ -0,0 +1,77 @@ +@extends('layouts.app') + +@section('pageTitle', 'Account Removal '.$user->getFirstname()) + + @section('content') +