From a4f41b8f8dfaa81048e1a77fc4cc604dd7349d91 Mon Sep 17 00:00:00 2001 From: miguel456 Date: Mon, 7 Mar 2022 18:14:42 +0000 Subject: [PATCH] fix: add constraint actions to db structure This commit adds several missing "cascade delete" actions to relationships on database tables. This effectively fixes errors while trying to delete user accounts because of pending child records. Additionally, the observers for applications and vacancies were removed, since they are now obsolete. The account deletion system was also refactored. --- app/Http/Controllers/Auth/LoginController.php | 10 +- app/Http/Controllers/UserController.php | 3 +- app/Jobs/ProcessAccountDelete.php | 51 +++++++ app/Mail/UserAccountDeleteConfirmation.php | 23 ++- app/Observers/ApplicationObserver.php | 109 -------------- app/Observers/UserObserver.php | 24 +-- app/Observers/VacancyObserver.php | 94 ------------ app/Providers/AppServiceProvider.php | 10 +- app/Providers/EventServiceProvider.php | 8 +- app/Services/AccountSuspensionService.php | 51 +++++++ app/Traits/HandlesAccountDeletion.php | 137 ++++++++++++++++++ app/Traits/HandlesAccountTokens.php | 62 -------- app/Traits/ReceivesAccountTokens.php | 113 --------------- app/User.php | 13 +- ...020_04_29_022245_create_profiles_table.php | 3 +- ...04_29_022402_create_applications_table.php | 3 +- .../2020_04_29_022421_create_votes_table.php | 3 +- ...04_29_022442_create_appointments_table.php | 3 +- ...20_04_29_030107_create_responses_table.php | 3 +- ...020_04_29_195848_votes_has_application.php | 4 +- .../2020_06_08_153602_create_bans_table.php | 3 +- ...020_06_20_210255_create_comments_table.php | 6 +- ...022_02_02_060702_create_absences_table.php | 6 +- ...180241_remove_account_tokens_from_user.php | 32 ++++ .../user/profile/useraccount.blade.php | 14 +- .../views/mail/deleted-account.blade.php | 26 ++-- routes/web.php | 2 +- 27 files changed, 353 insertions(+), 463 deletions(-) create mode 100644 app/Jobs/ProcessAccountDelete.php delete mode 100755 app/Observers/ApplicationObserver.php delete mode 100755 app/Observers/VacancyObserver.php create mode 100755 app/Traits/HandlesAccountDeletion.php delete mode 100755 app/Traits/HandlesAccountTokens.php delete mode 100755 app/Traits/ReceivesAccountTokens.php create mode 100644 database/migrations/2022_03_07_180241_remove_account_tokens_from_user.php diff --git a/app/Http/Controllers/Auth/LoginController.php b/app/Http/Controllers/Auth/LoginController.php index e9f81a1..15dc202 100755 --- a/app/Http/Controllers/Auth/LoginController.php +++ b/app/Http/Controllers/Auth/LoginController.php @@ -22,6 +22,7 @@ namespace App\Http\Controllers\Auth; use App\Http\Controllers\Controller; +use App\Services\AccountSuspensionService; use App\User; use Illuminate\Foundation\Auth\AuthenticatesUsers; use Illuminate\Http\Request; @@ -65,13 +66,16 @@ class LoginController extends Controller // We can't customise the error message, since that would imply overriding the login method, which is large. // Also, the user should never know that they're banned. - public function attemptLogin(Request $request) + public function attemptLogin(Request $request): bool { + $service = new AccountSuspensionService; $user = User::where('email', $request->email)->first(); if ($user) { - $isBanned = $user->isBanned(); - if ($isBanned) { + $isBanned = $service->isSuspended($user); + $isLocked = $service->isLocked($user); + + if ($isBanned || $isLocked) { return false; } else { return $this->originalAttemptLogin($request); diff --git a/app/Http/Controllers/UserController.php b/app/Http/Controllers/UserController.php index 25c5c7a..edec917 100755 --- a/app/Http/Controllers/UserController.php +++ b/app/Http/Controllers/UserController.php @@ -33,6 +33,7 @@ use App\Http\Requests\UpdateUserRequest; use App\Notifications\ChangedPassword; use App\Notifications\EmailChanged; use App\Traits\DisablesFeatures; +use App\Traits\HandlesAccountDeletion; use App\Traits\ReceivesAccountTokens; use App\User; use Google2FA; @@ -44,7 +45,7 @@ use Spatie\Permission\Models\Role; class UserController extends Controller { - use ReceivesAccountTokens; + use HandlesAccountDeletion; public function showUsers() { diff --git a/app/Jobs/ProcessAccountDelete.php b/app/Jobs/ProcessAccountDelete.php new file mode 100644 index 0000000..7cd8bb9 --- /dev/null +++ b/app/Jobs/ProcessAccountDelete.php @@ -0,0 +1,51 @@ +user = $user; + } + + /** + * Execute the job. + * + * @return void + */ + public function handle() + { + // It shouldn't need the suspension service, because if it was dispatched, the account was already locked + + Log::alert('[Worker] Processing account deletion request', [ + 'email' => $this->user->email + ]); + + $this->user->delete(); + } +} diff --git a/app/Mail/UserAccountDeleteConfirmation.php b/app/Mail/UserAccountDeleteConfirmation.php index 33b021f..c4baf0a 100755 --- a/app/Mail/UserAccountDeleteConfirmation.php +++ b/app/Mail/UserAccountDeleteConfirmation.php @@ -30,27 +30,23 @@ class UserAccountDeleteConfirmation extends Mailable { use Queueable, SerializesModels; - public $deleteToken; + public string + $approveLink, + $cancelLink, + $name, + $userID; - public $cancelToken; - - public $originalIP; - - public $name; - - public $userID; /** * Create a new message instance. * * @return void */ - public function __construct(User $user, array $tokens, string $originalIP) + public function __construct(User $user, array $links) { - $this->deleteToken = $tokens['delete']; - $this->cancelToken = $tokens['cancel']; + $this->approveLink = $links['approveURL']; + $this->cancelLink = $links['cancelURL']; - $this->originalIP = $originalIP; $this->name = $user->name; $this->userID = $user->id; } @@ -62,6 +58,7 @@ class UserAccountDeleteConfirmation extends Mailable */ public function build() { - return $this->view('mail.deleted-account'); + return $this->subject('[ACTION REQUIRED] Please confirm account removal') + ->view('mail.deleted-account'); } } diff --git a/app/Observers/ApplicationObserver.php b/app/Observers/ApplicationObserver.php deleted file mode 100755 index 16b6615..0000000 --- a/app/Observers/ApplicationObserver.php +++ /dev/null @@ -1,109 +0,0 @@ -. - */ - -namespace App\Observers; - -use App\Application; -use Illuminate\Support\Facades\Log; - -class ApplicationObserver -{ - /** - * Handle the application "created" event. - * - * @param \App\Application $application - * @return void - */ - public function created(Application $application) - { - // - } - - /** - * Handle the application "updated" event. - * - * @param \App\Application $application - * @return void - */ - public function updated(Application $application) - { - // - } - - public function deleting(Application $application) - { - $application->response()->delete(); - $votes = $application->votes; - - foreach ($votes as $vote) { - Log::debug('Referential integrity cleanup: Deleting and detaching vote '.$vote->id); - $vote->application()->detach($application->id); - $vote->delete(); - } - - if (! is_null($application->appointment)) { - Log::debug('RIC: Deleting appointment!'); - $application->appointment()->delete(); - } - - if (! $application->comments->isEmpty()) { - Log::debug('RIC: Deleting comments!'); - foreach ($application->comments as $comment) { - $comment->delete(); - } - } - - // application can now be deleted - } - - /** - * Handle the application "deleted" event. - * - * @param \App\Application $application - * @return void - */ - public function deleted(Application $application) - { - // - } - - /** - * Handle the application "restored" event. - * - * @param \App\Application $application - * @return void - */ - public function restored(Application $application) - { - // - } - - /** - * Handle the application "force deleted" event. - * - * @param \App\Application $application - * @return void - */ - public function forceDeleted(Application $application) - { - // - } -} diff --git a/app/Observers/UserObserver.php b/app/Observers/UserObserver.php index 16d2cdb..486e257 100755 --- a/app/Observers/UserObserver.php +++ b/app/Observers/UserObserver.php @@ -40,6 +40,7 @@ class UserObserver */ public function created(User $user) { + // TODO: Make sure that the profile is created, throw an exception if otherwise, or try to recreate the profile. Profile::create([ 'profileShortBio' => 'Write a one-liner about you here!', @@ -61,29 +62,6 @@ class UserObserver // } - public function deleting(User $user) - { - Log::debug("Deleting observer running"); - if ($user->isForceDeleting()) { - $user->profile->delete(); - Log::debug('Referential integrity cleanup: Deleted profile!'); - $applications = $user->applications; - - if (! $applications->isEmpty()) { - Log::debug('RIC: Now trying to delete applications and responses...'); - foreach ($applications as $application) { - // code moved to Application observer, where it gets rid of attached elements individually - Log::debug('RIC: Deleting application '.$application->id); - $application->delete(); - } - } - } else { - Log::debug('RIC: Not cleaning up soft deleted models!'); - } - - Log::debug('RIC: Cleanup done!'); - } - /** * Handle the user "deleted" event. * diff --git a/app/Observers/VacancyObserver.php b/app/Observers/VacancyObserver.php deleted file mode 100755 index c0752c0..0000000 --- a/app/Observers/VacancyObserver.php +++ /dev/null @@ -1,94 +0,0 @@ -. - */ - -namespace App\Observers; - -use App\Application; -use App\Vacancy; -use Illuminate\Support\Facades\Log; - -class VacancyObserver -{ - /** - * Handle the vacancy "created" event. - * - * @param \App\Vacancy $vacancy - * @return void - */ - public function created(Vacancy $vacancy) - { - // - } - - /** - * Handle the vacancy "updated" event. - * - * @param \App\Vacancy $vacancy - * @return void - */ - public function updated(Vacancy $vacancy) - { - // - } - - public function deleting(Vacancy $vacancy) - { - foreach(Application::with('response.vacancy')->get() as $app) { - if ($app->response->vacancy->id == $vacancy->id) - { - $app->delete(); - } - } - } - - /** - * Handle the vacancy "deleted" event. - * - * @param \App\Vacancy $vacancy - * @return void - */ - public function deleted(Vacancy $vacancy) - { - - } - - /** - * Handle the vacancy "restored" event. - * - * @param \App\Vacancy $vacancy - * @return void - */ - public function restored(Vacancy $vacancy) - { - // - } - - /** - * Handle the vacancy "force deleted" event. - * - * @param \App\Vacancy $vacancy - * @return void - */ - public function forceDeleted(Vacancy $vacancy) - { - // - } -} diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index c383be6..148fb87 100755 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -38,6 +38,7 @@ use Sentry; class AppServiceProvider extends ServiceProvider { + /** * Register any application services. * @@ -62,11 +63,6 @@ class AppServiceProvider extends ServiceProvider Schema::defaultStringLength(191); Paginator::useBootstrap(); - // Register observers - User::observe(UserObserver::class); - Application::observe(ApplicationObserver::class); - Vacancy::observe(VacancyObserver::class); - $https = ($this->app->environment() != 'local'); $collect = true; @@ -78,6 +74,10 @@ class AppServiceProvider extends ServiceProvider $collect = false; } + // Initialize user observer + User::observe(UserObserver::class); + + $this->app['request']->server->set('HTTPS', $https); View::share('shouldCollect', $collect); diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index c997c76..6eb82d3 100755 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -21,9 +21,15 @@ namespace App\Providers; +use App\Application; use App\Listeners\LogAuthenticationFailure; use App\Listeners\LogAuthenticationSuccess; use App\Listeners\OnUserRegistration; +use App\Observers\ApplicationObserver; +use App\Observers\UserObserver; +use App\Observers\VacancyObserver; +use App\User; +use App\Vacancy; use Illuminate\Auth\Events\Failed; use Illuminate\Auth\Events\Login; use Illuminate\Auth\Events\Registered; @@ -74,7 +80,5 @@ class EventServiceProvider extends ServiceProvider public function boot() { parent::boot(); - - // } } diff --git a/app/Services/AccountSuspensionService.php b/app/Services/AccountSuspensionService.php index 6676efd..9e9137d 100755 --- a/app/Services/AccountSuspensionService.php +++ b/app/Services/AccountSuspensionService.php @@ -62,6 +62,47 @@ class AccountSuspensionService $user->bans->delete(); } + + + + /** + * Sets an administrative lock on a user account. + * Used to prevent logins after a deletion process is initiated, but may be used for + * other things where a suspension is not necessary/warranted, such as a security breach event. + * These locks cannot be overridden manually be administrators. + * + * @param User $user The account to lock + * @return bool + */ + public function lockAccount(User $user): bool + { + Log::alert('User account locked!', [ + 'email' => $user->email + ]); + + $user->administratively_locked = 1; + return $user->save(); + } + + + /** + * Unlocks a user account. Reverse of lockAccount(). + * + * @param User $user + * @return bool + */ + public function unlockAccount(User $user): bool + { + Log::alert('User account unlocked!', [ + 'email' => $user->email + ]); + + $user->administratively_locked = 0; + return $user->save(); + } + + + /** * Checks whether a user is suspended * @@ -73,6 +114,16 @@ class AccountSuspensionService } + /** + * Checks whether an account is locked + * + * @param User $user The user to check + * @return bool Whether the mentioned account is locked + */ + public function isLocked(User $user): bool { + return $user->administratively_locked == 1; + } + /** * Takes a suspension directly and makes it permanent. * diff --git a/app/Traits/HandlesAccountDeletion.php b/app/Traits/HandlesAccountDeletion.php new file mode 100755 index 0000000..054f260 --- /dev/null +++ b/app/Traits/HandlesAccountDeletion.php @@ -0,0 +1,137 @@ +. + */ + +namespace App\Traits; + +use App\Http\Requests\UserDeleteRequest; +use App\Jobs\ProcessAccountDelete; +use App\Mail\UserAccountDeleteConfirmation; +use App\Services\AccountSuspensionService; +use App\User; +use Carbon\Carbon; +use Illuminate\Http\Request; +use Illuminate\Support\Facades\Auth; +use Illuminate\Support\Facades\Log; +use Illuminate\Support\Facades\Mail; +use Illuminate\Support\Facades\URL; + +trait HandlesAccountDeletion +{ + + /** + * Starts the user account deletion process. + * + * @param AccountSuspensionService $suspensionService + * @param UserDeleteRequest $request + * @return \Illuminate\Http\RedirectResponse + */ + public function userDelete(AccountSuspensionService $suspensionService, UserDeleteRequest $request) + { + if (config('demo.is_enabled')) + { + return redirect() + ->back() + ->with('error', 'This feature is disabled'); + } + + $links = [ + 'approveURL' => URL::temporarySignedRoute( + 'processDeleteConfirmation', now()->addDays(7), ['accountID' => $request->user()->id, 'action' => 'confirm'] + ), + 'cancelURL' => URL::temporarySignedRoute( + 'processDeleteConfirmation', now()->addDays(7), ['accountID' => $request->user()->id, 'action' => 'cancel'] + ) + ]; + + Mail::to($request->user()) + ->send(new UserAccountDeleteConfirmation($request->user(), $links)); + + // Only locked accounts can be deleted + $suspensionService->lockAccount($request->user()); + Auth::logout(); + + $request->session()->flash('success', __('Please check your email to finish deleting your account.')); + return redirect()->to('/'); + } + + + /** + * Dispatches the correct jobs and events to delete the specified user account + * + * @param Request $request + * @param AccountSuspensionService $suspensionService + * @param $accountID + * @param $action + * @return \Illuminate\Http\RedirectResponse|void + */ + public function processDeleteConfirmation(Request $request, AccountSuspensionService $suspensionService, $accountID, $action) + { + if (config('demo.is_enabled') || !$request->hasValidSignature()) + { + abort(403); + } + + // It's almost impossible for this to fail, unless the model has already been deleted by someone else, because: + // The request URL can't be tampered with and the request can't be initiated without a valid account in the first place + $account = User::find($accountID); + + if (!is_null($account)) + { + if (!$suspensionService->isLocked($account)) { + abort(403); + } + + Log::alert('Signed account deletion request received!', [ + 'user' => $account->name, + 'email' => $account->name, + 'created_at' => $account->created_at, + 'updated_at' => $account->updated_at, + 'deleted_at' => Carbon::now(), + 'ipAddress' => $request->ip(), + 'userAgent' => $request->userAgent(), + + ]); + + if ($action == 'confirm') { + // dispatch event (for notifications) and job (for async processing) + ProcessAccountDelete::dispatch($account); + + $request->session()->flash('success', __('Thank you for confirming. Your account will now be deleted shortly.')); + + return redirect() + ->to('/'); + } + + $suspensionService->unlockAccount($account); + $request->session()->flash('success', __('Account removal request cancelled. Your account has been unlocked and you can now sign in.')); + + return redirect() + ->route('login'); + + } + + Log::error("Cannot delete account that doesn't exist!", [ + 'validSignature' => $request->hasValidSignature() + ]); + abort(400); + + } +} diff --git a/app/Traits/HandlesAccountTokens.php b/app/Traits/HandlesAccountTokens.php deleted file mode 100755 index 965e54a..0000000 --- a/app/Traits/HandlesAccountTokens.php +++ /dev/null @@ -1,62 +0,0 @@ -. - */ - -namespace App\Traits; - -use Illuminate\Support\Facades\Hash; - -trait HandlesAccountTokens -{ - public function generateAccountTokens() - { - $deleteToken = bin2hex(openssl_random_pseudo_bytes(32)); - $cancelToken = bin2hex(openssl_random_pseudo_bytes(32)); - - $tokens = [ - - 'delete' => Hash::make($deleteToken), - 'cancel' => Hash::make($cancelToken), - - ]; - - $this->account_tokens = json_encode($tokens); - $this->save(); - - return [ - - 'delete' => $deleteToken, - 'cancel' => $cancelToken, - ]; - } - - public function verifyAccountToken(string $token, string $type): bool - { - $tokens = json_decode($this->account_tokens); - - if ($type == 'deleteToken') { - return Hash::check($token, $tokens->delete); - } elseif ($type == 'cancelToken') { - return Hash::check($token, $tokens->cancel); - } - - return false; - } -} diff --git a/app/Traits/ReceivesAccountTokens.php b/app/Traits/ReceivesAccountTokens.php deleted file mode 100755 index 7ed9de3..0000000 --- a/app/Traits/ReceivesAccountTokens.php +++ /dev/null @@ -1,113 +0,0 @@ -. - */ - -namespace App\Traits; - -use App\Http\Requests\UserDeleteRequest; -use App\Mail\UserAccountDeleteConfirmation; -use App\User; -use Illuminate\Http\Request; -use Illuminate\Support\Facades\Auth; -use Illuminate\Support\Facades\Log; -use Illuminate\Support\Facades\Mail; - -trait ReceivesAccountTokens -{ - public function userDelete(UserDeleteRequest $request) - { - //Fixme: TEMPORARY, PLEASE REMOVE UNTIL FIXED OR DURING DEVELOPMENT - return redirect() - ->back() - ->with('error', 'This feature is disabled'); - - if (config('demo.is_enabled')) - { - return redirect() - ->back() - ->with('error', 'This feature is disabled'); - } - - // a little verbose - $user = User::find(Auth::user()->id); - $tokens = $user->generateAccountTokens(); - - Mail::to($user)->send(new UserAccountDeleteConfirmation($user, $tokens, $request->ip())); - - $user->delete(); - Auth::logout(); - - $request->session()->flash('success', __('Please check your email to finish deleting your account.')); - - return redirect()->to('/'); - } - - public function processDeleteConfirmation(Request $request, $ID, $action, $token) - { - if (config('demo.is_enabled')) - { - return redirect() - ->back() - ->with('error', 'This feature is disabled'); - } - - // We can't rely on Laravel's route model injection, because it'll ignore soft-deleted models, - // so we have to use a special scope to find them ourselves. - $user = User::withTrashed()->findOrFail($ID); - $email = $user->email; - - switch ($action) { - case 'confirm': - - if ($user->verifyAccountToken($token, 'deleteToken')) { - Log::info('SECURITY: User deleted account!', [ - - 'confirmDeleteToken' => $token, - 'ipAddress' => $request->ip(), - 'email' => $user->email, - - ]); - - $user->forceDelete(); - - $request->session()->flash('success', __('Account permanently deleted. Thank you for using our service.')); - - return redirect()->to('/'); - } - - break; - - case 'cancel': - - if ($user->verifyAccountToken($token, 'cancelToken')) { - $user->restore(); - $request->session()->flash('success', __('Account deletion cancelled! You may now login.')); - - return redirect()->to(route('login')); - } - - break; - - default: - - abort(404, __('The page you were trying to access may not exist or may be expired.')); - } - } -} diff --git a/app/User.php b/app/User.php index 752c47b..2bca37a 100755 --- a/app/User.php +++ b/app/User.php @@ -21,6 +21,7 @@ namespace App; +use App\Services\AccountSuspensionService; use App\Traits\HandlesAccountTokens; use Illuminate\Contracts\Auth\MustVerifyEmail; use Illuminate\Database\Eloquent\SoftDeletes; @@ -31,7 +32,7 @@ use Spatie\Permission\Traits\HasRoles; class User extends Authenticatable implements MustVerifyEmail { - use UserHasTeams, Notifiable, HasRoles, SoftDeletes, HandlesAccountTokens; + use UserHasTeams, Notifiable, HasRoles; /** * The attributes that are mass assignable. @@ -101,7 +102,15 @@ class User extends Authenticatable implements MustVerifyEmail // UTILITY LOGIC - public function isBanned() + /** + * Checks if a user is banned. + * + * @deprecated This method is obsolete, as it has been replaced by the suspension service. + * @see AccountSuspensionService::isSuspended() + * + * @return bool Whether the user is banned + */ + public function isBanned(): bool { return ! $this->bans()->get()->isEmpty(); } diff --git a/database/migrations/2020_04_29_022245_create_profiles_table.php b/database/migrations/2020_04_29_022245_create_profiles_table.php index bc21b71..a14ddb2 100755 --- a/database/migrations/2020_04_29_022245_create_profiles_table.php +++ b/database/migrations/2020_04_29_022245_create_profiles_table.php @@ -46,7 +46,8 @@ class CreateProfilesTable extends Migration $table->foreign('userID') ->references('id') - ->on('users'); + ->on('users') + ->cascadeOnDelete(); }); } diff --git a/database/migrations/2020_04_29_022402_create_applications_table.php b/database/migrations/2020_04_29_022402_create_applications_table.php index 529e046..0faf6ef 100755 --- a/database/migrations/2020_04_29_022402_create_applications_table.php +++ b/database/migrations/2020_04_29_022402_create_applications_table.php @@ -53,7 +53,8 @@ class CreateApplicationsTable extends Migration $table->foreign('applicantUserID') ->references('id') - ->on('users'); + ->on('users') + ->cascadeOnDelete(); }); } diff --git a/database/migrations/2020_04_29_022421_create_votes_table.php b/database/migrations/2020_04_29_022421_create_votes_table.php index 6164f56..2838cac 100755 --- a/database/migrations/2020_04_29_022421_create_votes_table.php +++ b/database/migrations/2020_04_29_022421_create_votes_table.php @@ -43,7 +43,8 @@ class CreateVotesTable extends Migration $table->foreign('userID') ->references('id') - ->on('users'); + ->on('users') + ->cascadeOnDelete(); }); } diff --git a/database/migrations/2020_04_29_022442_create_appointments_table.php b/database/migrations/2020_04_29_022442_create_appointments_table.php index 08d60f2..d79ff0e 100755 --- a/database/migrations/2020_04_29_022442_create_appointments_table.php +++ b/database/migrations/2020_04_29_022442_create_appointments_table.php @@ -54,7 +54,8 @@ class CreateAppointmentsTable extends Migration $table->foreign('applicationID') ->references('id') - ->on('applications'); + ->on('applications') + ->cascadeOnDelete(); }); } diff --git a/database/migrations/2020_04_29_030107_create_responses_table.php b/database/migrations/2020_04_29_030107_create_responses_table.php index 02acc46..fd1e053 100755 --- a/database/migrations/2020_04_29_030107_create_responses_table.php +++ b/database/migrations/2020_04_29_030107_create_responses_table.php @@ -41,7 +41,8 @@ class CreateResponsesTable extends Migration // A better way would be to link responses directly to vacancies, that subsquently have a form $table->foreign('responseFormID') ->references('id') - ->on('forms'); + ->on('forms') + ->cascadeOnDelete(); }); } diff --git a/database/migrations/2020_04_29_195848_votes_has_application.php b/database/migrations/2020_04_29_195848_votes_has_application.php index 8ccf13f..32eae4c 100755 --- a/database/migrations/2020_04_29_195848_votes_has_application.php +++ b/database/migrations/2020_04_29_195848_votes_has_application.php @@ -38,8 +38,8 @@ class VotesHasApplication extends Migration $table->bigInteger('application_id')->unsigned(); $table->timestamps(); - $table->foreign('vote_id')->references('id')->on('votes'); - $table->foreign('application_id')->references('id')->on('applications'); + $table->foreign('vote_id')->references('id')->on('votes')->cascadeOnDelete(); + $table->foreign('application_id')->references('id')->on('applications')->cascadeOnDelete(); }); } diff --git a/database/migrations/2020_06_08_153602_create_bans_table.php b/database/migrations/2020_06_08_153602_create_bans_table.php index dd18593..98b645d 100755 --- a/database/migrations/2020_06_08_153602_create_bans_table.php +++ b/database/migrations/2020_06_08_153602_create_bans_table.php @@ -43,7 +43,8 @@ class CreateBansTable extends Migration $table->foreign('userID') ->references('id') - ->on('users'); + ->on('users') + ->cascadeOnDelete(); }); } diff --git a/database/migrations/2020_06_20_210255_create_comments_table.php b/database/migrations/2020_06_20_210255_create_comments_table.php index fc9c1d2..4cd372a 100755 --- a/database/migrations/2020_06_20_210255_create_comments_table.php +++ b/database/migrations/2020_06_20_210255_create_comments_table.php @@ -41,11 +41,13 @@ class CreateCommentsTable extends Migration $table->foreign('authorID') ->references('id') - ->on('users'); + ->on('users') + ->cascadeOnDelete(); $table->foreign('applicationID') ->references('id') - ->on('applications'); + ->on('applications') + ->cascadeOnDelete(); }); } diff --git a/database/migrations/2022_02_02_060702_create_absences_table.php b/database/migrations/2022_02_02_060702_create_absences_table.php index a00d80c..611d675 100755 --- a/database/migrations/2022_02_02_060702_create_absences_table.php +++ b/database/migrations/2022_02_02_060702_create_absences_table.php @@ -27,11 +27,13 @@ class CreateAbsencesTable extends Migration $table->foreign('requesterID') ->references('id') - ->on('users'); + ->on('users') + ->onDelete('cascade'); $table->foreign('reviewer') ->references('id') - ->on('users'); + ->on('users') + ->onDelete('set null'); }); } diff --git a/database/migrations/2022_03_07_180241_remove_account_tokens_from_user.php b/database/migrations/2022_03_07_180241_remove_account_tokens_from_user.php new file mode 100644 index 0000000..fdd2e8f --- /dev/null +++ b/database/migrations/2022_03_07_180241_remove_account_tokens_from_user.php @@ -0,0 +1,32 @@ +dropColumn('account_tokens'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('users', function (Blueprint $table) { + $table->string('account_tokens')->after('password')->nullable(); + }); + } +}; diff --git a/resources/views/dashboard/user/profile/useraccount.blade.php b/resources/views/dashboard/user/profile/useraccount.blade.php index ca66911..d5aedde 100755 --- a/resources/views/dashboard/user/profile/useraccount.blade.php +++ b/resources/views/dashboard/user/profile/useraccount.blade.php @@ -48,13 +48,7 @@
  • {{ __('Server logs of your visits, including IP addresses') }}
  • - -

    {{ __('Feature temporarily unavailable') }}

    - -

    This feature has been temporarily made unavailable while we work to fix underlying issues that are causing our backoffice to crash. We apologize for the inconvenience, and any account/data deletion requests should be forwarded to our data protection officer below.

    -

    dpo@gamescluboficial.com.br

    -
    - +

    {{ __("Note: After you verify your identity, you'll receive an email with more information asking you to confirm this request.") }}

    @@ -63,7 +57,7 @@
    - +

    {{ __('For your security, your password is always required for sensitive operations.') }} {{ __('Forgot your password?') }}

    @@ -71,7 +65,7 @@
    - +

    {{ __('You cannot recover lost 2FA secrets.') }}

    @@ -81,7 +75,7 @@ - + diff --git a/resources/views/mail/deleted-account.blade.php b/resources/views/mail/deleted-account.blade.php index aea3c12..2a9fed8 100755 --- a/resources/views/mail/deleted-account.blade.php +++ b/resources/views/mail/deleted-account.blade.php @@ -3,7 +3,7 @@ - Your account has been deleted + Action required: please confirm whether you'd like to delete your account - + @@ -111,23 +111,23 @@
     
    -

    Hi {{ $name }},

    -

    Someone (hopefully you) has requested that your account at {{ config('app.name') }} be deleted.

    -

    As a security measure, an email is always sent out to make sure you really want to delete your account.

    -

    This is the IP address the request was made from: {{ $originalIP }}.

    -

    If you don't do anything, your account will automatically be permanently deleted in 30 days, and will remain unaccessible for that time period.

    -

    Click one of the buttons below to make a decision.

    +

    Hi {{ $name }},

    +

    We're sorry to see you go! Please let us know if there is any feedback you'd like to share, or if there's anything we can help you with.

    +

    To prevent any accidental loss of data, please confirm whether you really want to delete your account.

    +

    You have 7 days to make a decision, after which the links below will become invalid. You will not be able to sign in or use your account during this time period.

    +

    If you don't make any decision after 7 days, your account will remain permanently locked; Locked accounts are automatically deleted after 30 days. You may, however, contact us after this time period to unlock your account if you have changed your mind.

    +

    Do you really want to delete your account?

    - +
    - - + +
    Delete Account Cancel Deletion No, unlock and keep my account Yes, irreversibly delete my account
    @@ -150,7 +150,7 @@
    - Staff Manager + RBRecruiter | GC
    @@ -164,4 +164,4 @@
    - \ No newline at end of file + diff --git a/routes/web.php b/routes/web.php index 609d485..1168f6a 100755 --- a/routes/web.php +++ b/routes/web.php @@ -94,7 +94,7 @@ Route::group(['prefix' => LaravelLocalization::setLocale(), 'middleware' => ['lo Route::post('/form/contact', [ContactController::class, 'create']) ->name('sendSubmission'); - Route::get('/accounts/danger-zone/{ID}/{action}/{token}', [UserController::class, 'processDeleteConfirmation']) + Route::get('/accounts/{accountID}/dg/process-delete/{action}', [UserController::class, 'processDeleteConfirmation']) ->name('processDeleteConfirmation'); Route::group(['middleware' => ['auth', 'forcelogout', 'passwordexpiration', '2fa', 'verified']], function () {