From: Dan Brown Date: Wed, 14 Jul 2021 19:50:36 +0000 (+0100) Subject: Added command to reset user MFA X-Git-Tag: v21.08~1^2~26^2~16 X-Git-Url: http://source.bookstackapp.com/bookstack/commitdiff_plain/7c86c26cd0585a063ca4905a1302301e3c1d2e19 Added command to reset user MFA Includes tests to cover the command. --- diff --git a/app/Console/Commands/ResetMfa.php b/app/Console/Commands/ResetMfa.php new file mode 100644 index 000000000..feb477943 --- /dev/null +++ b/app/Console/Commands/ResetMfa.php @@ -0,0 +1,74 @@ +option('id'); + $email = $this->option('email'); + if (!$id && !$email) { + $this->error('Either a --id= or --email= option must be provided.'); + return 1; + } + + /** @var User $user */ + $field = $id ? 'id' : 'email'; + $value = $id ?: $email; + $user = User::query() + ->where($field, '=', $value) + ->first(); + + if (!$user) { + $this->error("A user where {$field}={$value} could not be found."); + return 1; + } + + $this->info("This will delete any configure multi-factor authentication methods for user: \n- ID: {$user->id}\n- Name: {$user->name}\n- Email: {$user->email}\n"); + $this->info('If multi-factor authentication is required for this user they will be asked to reconfigure their methods on next login.'); + $confirm = $this->confirm('Are you sure you want to proceed?'); + if ($confirm) { + $user->mfaValues()->delete(); + $this->info('User MFA methods have been reset.'); + return 0; + } + + return 1; + } +} diff --git a/tests/Commands/ResetMfaCommandTest.php b/tests/Commands/ResetMfaCommandTest.php new file mode 100644 index 000000000..550f6d390 --- /dev/null +++ b/tests/Commands/ResetMfaCommandTest.php @@ -0,0 +1,65 @@ +artisan('bookstack:reset-mfa') + ->expectsOutput('Either a --id= or --email= option must be provided.') + ->assertExitCode(1); + } + + public function test_command_runs_with_provided_email() + { + /** @var User $user */ + $user = User::query()->first(); + MfaValue::upsertWithValue($user, MfaValue::METHOD_TOTP, 'test'); + + $this->assertEquals(1, $user->mfaValues()->count()); + $this->artisan("bookstack:reset-mfa --email={$user->email}") + ->expectsQuestion('Are you sure you want to proceed?', true) + ->expectsOutput('User MFA methods have been reset.') + ->assertExitCode(0); + $this->assertEquals(0, $user->mfaValues()->count()); + } + + public function test_command_runs_with_provided_id() + { + /** @var User $user */ + $user = User::query()->first(); + MfaValue::upsertWithValue($user, MfaValue::METHOD_TOTP, 'test'); + + $this->assertEquals(1, $user->mfaValues()->count()); + $this->artisan("bookstack:reset-mfa --id={$user->id}") + ->expectsQuestion('Are you sure you want to proceed?', true) + ->expectsOutput('User MFA methods have been reset.') + ->assertExitCode(0); + $this->assertEquals(0, $user->mfaValues()->count()); + } + + public function test_saying_no_to_confirmation_does_not_reset_mfa() + { + /** @var User $user */ + $user = User::query()->first(); + MfaValue::upsertWithValue($user, MfaValue::METHOD_TOTP, 'test'); + + $this->assertEquals(1, $user->mfaValues()->count()); + $this->artisan("bookstack:reset-mfa --id={$user->id}") + ->expectsQuestion('Are you sure you want to proceed?', false) + ->assertExitCode(1); + $this->assertEquals(1, $user->mfaValues()->count()); + } + + public function test_giving_non_existing_user_shows_error_message() + { + $this->artisan("bookstack:reset-mfa --email=donkeys@example.com") + ->expectsOutput('A user where email=donkeys@example.com could not be found.') + ->assertExitCode(1); + } +}