From b22668efa98cf475f5c9359fe0436fd571774086 Mon Sep 17 00:00:00 2001 From: Jason Varga Date: Fri, 29 May 2020 13:42:24 -0400 Subject: [PATCH 1/2] Separate forgot password and account activation --- config/users.php | 16 ++++++++++++ .../views/auth/passwords/reset.blade.php | 8 +++--- routes/web.php | 3 +++ src/Actions/SendPasswordReset.php | 6 ++++- src/Auth/Passwords/PasswordBrokerManager.php | 1 + src/Auth/Passwords/PasswordReset.php | 11 ++++++-- src/Auth/Passwords/TokenRepository.php | 8 ++++-- src/Auth/User.php | 24 ++++++++++++----- .../Controllers/ActivateAccountController.php | 24 +++++++++++++++++ .../Controllers/CP/Users/UsersController.php | 8 ++++-- .../Controllers/ForgotPasswordController.php | 6 +++++ .../Controllers/ResetPasswordController.php | 26 ++++++++++++++++--- src/Notifications/ActivateAccount.php | 2 +- src/Notifications/PasswordReset.php | 4 +-- 14 files changed, 122 insertions(+), 25 deletions(-) create mode 100644 src/Http/Controllers/ActivateAccountController.php diff --git a/config/users.php b/config/users.php index 9348e77d55..43a35901d0 100644 --- a/config/users.php +++ b/config/users.php @@ -62,4 +62,20 @@ // ], + /* + |-------------------------------------------------------------------------- + | Password Brokers + |-------------------------------------------------------------------------- + | + | When resetting passwords, Statamic uses an appropriate password broker. + | Here you may define which broker should be used for each situation. + | You may want a longer expiry for user activations, for example. + | + */ + + 'passwords' => [ + 'resets' => 'resets', + 'activations' => 'resets', + ], + ]; diff --git a/resources/views/auth/passwords/reset.blade.php b/resources/views/auth/passwords/reset.blade.php index df7641f20a..8aba9ba317 100644 --- a/resources/views/auth/passwords/reset.blade.php +++ b/resources/views/auth/passwords/reset.blade.php @@ -3,11 +3,11 @@ @section('content') -

{{ __('Reset Password') }}

+

{{ $title }}

-
+ @csrf @@ -42,9 +42,7 @@
- + diff --git a/routes/web.php b/routes/web.php index 9925d5cc7f..b18cb266cb 100755 --- a/routes/web.php +++ b/routes/web.php @@ -29,6 +29,9 @@ Route::post('password/email', 'ForgotPasswordController@sendResetLinkEmail')->name('password.email'); Route::get('password/reset/{token}', 'ResetPasswordController@showResetForm')->name('password.reset'); Route::post('password/reset', 'ResetPasswordController@reset')->name('password.reset.action'); + + Route::get('activate/{token}', 'ActivateAccountController@showResetForm')->name('account.activate'); + Route::post('activate', 'ActivateAccountController@reset')->name('account.activate.action'); }); Statamic::additionalActionRoutes(); diff --git a/src/Actions/SendPasswordReset.php b/src/Actions/SendPasswordReset.php index 74414d234c..8a5a1c3700 100644 --- a/src/Actions/SendPasswordReset.php +++ b/src/Actions/SendPasswordReset.php @@ -35,6 +35,10 @@ public function buttonText() public function run($users, $values) { - $users->each->generateTokenAndSendPasswordResetNotification(); + $users->each(function ($user) { + $user->password() + ? $user->generateTokenAndSendPasswordResetNotification() + : $user->generateTokenAndSendActivateAccountNotification(); + }); } } diff --git a/src/Auth/Passwords/PasswordBrokerManager.php b/src/Auth/Passwords/PasswordBrokerManager.php index 0c74c8fb39..3e70c7cd14 100644 --- a/src/Auth/Passwords/PasswordBrokerManager.php +++ b/src/Auth/Passwords/PasswordBrokerManager.php @@ -18,6 +18,7 @@ protected function createTokenRepository(array $config) return new TokenRepository( $this->app['files'], $this->app['hash'], + $config['table'], $key, $config['expire'], $config['throttle'] ?? 0 diff --git a/src/Auth/Passwords/PasswordReset.php b/src/Auth/Passwords/PasswordReset.php index 7270065dc0..6424a59e23 100644 --- a/src/Auth/Passwords/PasswordReset.php +++ b/src/Auth/Passwords/PasswordReset.php @@ -4,14 +4,21 @@ class PasswordReset { + const BROKER_RESETS = 'resets'; + const BROKER_ACTIVATIONS = 'activations'; + protected static $url; protected static $redirect; - public static function url($token) + public static function url($token, $broker) { + $route = $broker === self::BROKER_ACTIVATIONS ? 'statamic.account.activate' : 'statamic.password.reset'; + + $defaultUrl = route($route, $token); + $url = static::$url ? sprintf('%s?token=%s', static::$url, $token) - : route('statamic.password.reset', $token); + : $defaultUrl; parse_str(parse_url($url, PHP_URL_QUERY) ?: '', $query); diff --git a/src/Auth/Passwords/TokenRepository.php b/src/Auth/Passwords/TokenRepository.php index 95d281fa23..9b226c8b5f 100644 --- a/src/Auth/Passwords/TokenRepository.php +++ b/src/Auth/Passwords/TokenRepository.php @@ -17,7 +17,7 @@ class TokenRepository extends DatabaseTokenRepository protected $expires; protected $path; - public function __construct(Filesystem $files, HasherContract $hasher, $hashKey, $expires = 60, $throttle = 60) + public function __construct(Filesystem $files, HasherContract $hasher, $table, $hashKey, $expires = 60, $throttle = 60) { $this->files = $files; $this->hasher = $hasher; @@ -25,7 +25,7 @@ public function __construct(Filesystem $files, HasherContract $hasher, $hashKey, $this->expires = $expires * 60; $this->throttle = $throttle; - $this->path = storage_path('statamic/password_resets.yaml'); + $this->path = storage_path("statamic/password_resets/$table.yaml"); } public function create(CanResetPasswordContract $user) @@ -92,6 +92,10 @@ protected function getResets() protected function putResets($resets) { + if (! $this->files->isDirectory($dir = dirname($this->path))) { + $this->files->makeDirectory($dir); + } + $this->files->put($this->path, YAML::dump($resets->all())); } } diff --git a/src/Auth/User.php b/src/Auth/User.php index f516b30938..6036abb8e5 100644 --- a/src/Auth/User.php +++ b/src/Auth/User.php @@ -182,11 +182,12 @@ public function routeNotificationForMail($notification = null) public function sendPasswordResetNotification($token) { - $notification = $this->password() - ? new PasswordResetNotification($token) - : new ActivateAccountNotification($token); + $this->notify(new PasswordResetNotification($token)); + } - $this->notify($notification); + public function sendActivateAccountNotification($token) + { + $this->notify(new ActivateAccountNotification($token)); } public function generateTokenAndSendPasswordResetNotification() @@ -194,14 +195,23 @@ public function generateTokenAndSendPasswordResetNotification() $this->sendPasswordResetNotification($this->generatePasswordResetToken()); } - public function getPasswordResetUrl() + public function generateTokenAndSendActivateAccountNotification() { - return PasswordReset::url($this->generatePasswordResetToken()); + $this->sendActivateAccountNotification($this->generateActivateAccountToken()); } public function generatePasswordResetToken() { - return Password::broker()->createToken($this); + $broker = config('statamic.users.passwords.'.PasswordReset::BROKER_RESETS); + + return Password::broker($broker)->createToken($this); + } + + public function generateActivateAccountToken() + { + $broker = config('statamic.users.passwords.'.PasswordReset::BROKER_ACTIVATIONS); + + return Password::broker($broker)->createToken($this); } public static function __callStatic($method, $parameters) diff --git a/src/Http/Controllers/ActivateAccountController.php b/src/Http/Controllers/ActivateAccountController.php new file mode 100644 index 0000000000..f8f14e68c5 --- /dev/null +++ b/src/Http/Controllers/ActivateAccountController.php @@ -0,0 +1,24 @@ +invitation['send']) { ActivateAccount::subject($request->invitation['subject']); ActivateAccount::body($request->invitation['message']); - $user->generateTokenAndSendPasswordResetNotification(); + $user->generateTokenAndSendActivateAccountNotification(); + $url = null; + } else { + $url = PasswordReset::url($user->generateActivateAccountToken(), PasswordReset::BROKER_ACTIVATIONS); } return [ 'redirect' => $user->editUrl(), - 'activationUrl' => $request->invitation['send'] ? null : $user->getPasswordResetUrl(), + 'activationUrl' => $url, ]; } diff --git a/src/Http/Controllers/ForgotPasswordController.php b/src/Http/Controllers/ForgotPasswordController.php index 9e708fbb58..df7bcb24c8 100644 --- a/src/Http/Controllers/ForgotPasswordController.php +++ b/src/Http/Controllers/ForgotPasswordController.php @@ -3,6 +3,7 @@ namespace Statamic\Http\Controllers; use Illuminate\Http\Request; +use Illuminate\Support\Facades\Password; use Statamic\Auth\Passwords\PasswordReset; use Statamic\Auth\SendsPasswordResetEmails; use Statamic\Facades\URL; @@ -32,4 +33,9 @@ public function sendResetLinkEmail(Request $request) return $this->traitSendResetLinkEmail($request); } + + public function broker() + { + return Password::broker(PasswordReset::BROKER_RESETS); + } } diff --git a/src/Http/Controllers/ResetPasswordController.php b/src/Http/Controllers/ResetPasswordController.php index 2ff6b571ed..24960cf404 100644 --- a/src/Http/Controllers/ResetPasswordController.php +++ b/src/Http/Controllers/ResetPasswordController.php @@ -3,6 +3,8 @@ namespace Statamic\Http\Controllers; use Illuminate\Http\Request; +use Illuminate\Support\Facades\Password; +use Statamic\Auth\Passwords\PasswordReset; use Statamic\Auth\ResetsPasswords; use Statamic\Http\Middleware\RedirectIfAuthenticated; @@ -19,9 +21,22 @@ public function __construct() public function showResetForm(Request $request, $token = null) { - return view('statamic::auth.passwords.reset')->with( - ['token' => $token, 'email' => $request->email] - ); + return view('statamic::auth.passwords.reset')->with([ + 'token' => $token, + 'email' => $request->email, + 'action' => $this->resetFormAction(), + 'title' => $this->resetFormTitle(), + ]); + } + + protected function resetFormAction() + { + return route('statamic.password.reset.action'); + } + + protected function resetFormTitle() + { + return __('Reset Password'); } public function redirectPath() @@ -38,4 +53,9 @@ protected function resetPassword($user, $password) $this->traitResetPassword($user, $password); } + + public function broker() + { + return Password::broker(PasswordReset::BROKER_RESETS); + } } diff --git a/src/Notifications/ActivateAccount.php b/src/Notifications/ActivateAccount.php index 226a511381..921efff35e 100644 --- a/src/Notifications/ActivateAccount.php +++ b/src/Notifications/ActivateAccount.php @@ -39,7 +39,7 @@ public function toMail($notifiable) return (new MailMessage) ->subject(static::$subject ?? __('statamic::messages.activate_account_notification_subject')) ->line(static::$body ?? __('statamic::messages.activate_account_notification_body')) - ->action(__('Activate Account'), PasswordResetManager::url($this->token)); + ->action(__('Activate Account'), PasswordResetManager::url($this->token, PasswordResetManager::BROKER_ACTIVATIONS)); } /** diff --git a/src/Notifications/PasswordReset.php b/src/Notifications/PasswordReset.php index e908eb2c80..63b0cfd712 100644 --- a/src/Notifications/PasswordReset.php +++ b/src/Notifications/PasswordReset.php @@ -5,7 +5,7 @@ use Illuminate\Bus\Queueable; use Illuminate\Notifications\Messages\MailMessage; use Illuminate\Notifications\Notification; -use Statamic\Auth\Passwords\PasswordReset as PasswordResetUrl; +use Statamic\Auth\Passwords\PasswordReset as PasswordResetManager; class PasswordReset extends Notification { @@ -40,7 +40,7 @@ public function toMail($notifiable) return (new MailMessage) ->subject(__('statamic::messages.reset_password_notification_subject')) ->line(__('statamic::messages.reset_password_notification_body')) - ->action(__('Reset Password'), PasswordResetUrl::url($this->token)) + ->action(__('Reset Password'), PasswordResetManager::url($this->token, PasswordResetManager::BROKER_RESETS)) ->line(__('statamic::messages.reset_password_notification_no_action')); } From 2b3bd271b5a46091a38af8e050f84a4f5584f75a Mon Sep 17 00:00:00 2001 From: Jason Varga Date: Fri, 29 May 2020 14:31:26 -0400 Subject: [PATCH 2/2] Adjust default brokers --- config/users.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/users.php b/config/users.php index 43a35901d0..40c26972e8 100644 --- a/config/users.php +++ b/config/users.php @@ -74,8 +74,8 @@ */ 'passwords' => [ - 'resets' => 'resets', - 'activations' => 'resets', + 'resets' => config('auth.defaults.passwords'), + 'activations' => config('auth.defaults.passwords'), ], ];