From 4f511db0e833431b396435d5bdc77dd60f71a8c9 Mon Sep 17 00:00:00 2001 From: Francesco Malatesta Date: Thu, 17 Nov 2016 16:42:18 +0100 Subject: [PATCH] Add password reset functionality --- .env.example | 1 + .../Controllers/ResetPasswordController.php | 76 +++++++++++++++++ app/Api/V1/Requests/ResetPasswordRequest.php | 19 +++++ config/boilerplate.php | 9 ++ phpunit.xml | 1 + routes/api.php | 1 + .../ResetPasswordControllerTest.php | 85 +++++++++++++++++++ 7 files changed, 192 insertions(+) create mode 100644 app/Api/V1/Controllers/ResetPasswordController.php create mode 100644 app/Api/V1/Requests/ResetPasswordRequest.php create mode 100644 tests/Functional/Api/V1/Controllers/ResetPasswordControllerTest.php diff --git a/.env.example b/.env.example index cb6fd9f..79fd82b 100644 --- a/.env.example +++ b/.env.example @@ -36,3 +36,4 @@ API_SUBTYPE=app API_VERSION=v1 SIGN_UP_RELEASE_TOKEN=false +PASSWORD_RESET_RELEASE_TOKEN=false diff --git a/app/Api/V1/Controllers/ResetPasswordController.php b/app/Api/V1/Controllers/ResetPasswordController.php new file mode 100644 index 0000000..470970f --- /dev/null +++ b/app/Api/V1/Controllers/ResetPasswordController.php @@ -0,0 +1,76 @@ +broker()->reset( + $this->credentials($request), function ($user, $password) { + $this->reset($user, $password); + } + ); + + if($response !== Password::PASSWORD_RESET) { + throw new HttpException(500); + } + + if(!Config::get('boilerplate.reset_password.release_token')) { + return response()->json([ + 'status' => 'ok', + ]); + } + + $user = User::where('email', '=', $request->get('email'))->first(); + + return response()->json([ + 'status' => 'ok', + 'token' => $JWTAuth->fromUser($user) + ]); + } + + /** + * Get the broker to be used during password reset. + * + * @return \Illuminate\Contracts\Auth\PasswordBroker + */ + public function broker() + { + return Password::broker(); + } + + /** + * Get the password reset credentials from the request. + * + * @param ResetPasswordRequest $request + * @return array + */ + protected function credentials(ResetPasswordRequest $request) + { + return $request->only( + 'email', 'password', 'password_confirmation', 'token' + ); + } + + /** + * Reset the given user's password. + * + * @param \Illuminate\Contracts\Auth\CanResetPassword $user + * @param string $password + * @return void + */ + protected function reset($user, $password) + { + $user->password = $password; + $user->save(); + } +} diff --git a/app/Api/V1/Requests/ResetPasswordRequest.php b/app/Api/V1/Requests/ResetPasswordRequest.php new file mode 100644 index 0000000..7a0a611 --- /dev/null +++ b/app/Api/V1/Requests/ResetPasswordRequest.php @@ -0,0 +1,19 @@ + [ 'email' => 'required|email' ] + ], + + 'reset_password' => [ + 'release_token' => env('PASSWORD_RESET_RELEASE_TOKEN', false), + 'validation_rules' => [ + 'token' => 'required', + 'email' => 'required|email', + 'password' => 'required|confirmed' + ] ] ]; diff --git a/phpunit.xml b/phpunit.xml index aea2a8c..d7b9545 100755 --- a/phpunit.xml +++ b/phpunit.xml @@ -23,6 +23,7 @@ + diff --git a/routes/api.php b/routes/api.php index 86971bb..83b6917 100644 --- a/routes/api.php +++ b/routes/api.php @@ -10,4 +10,5 @@ $api->post('login', 'App\\Api\\V1\\Controllers\\LoginController@login'); $api->post('recovery', 'App\\Api\\V1\\Controllers\\ForgotPasswordController@sendResetEmail'); + $api->post('reset', 'App\\Api\\V1\\Controllers\\ResetPasswordController@resetPassword'); }); diff --git a/tests/Functional/Api/V1/Controllers/ResetPasswordControllerTest.php b/tests/Functional/Api/V1/Controllers/ResetPasswordControllerTest.php new file mode 100644 index 0000000..27d9cde --- /dev/null +++ b/tests/Functional/Api/V1/Controllers/ResetPasswordControllerTest.php @@ -0,0 +1,85 @@ +post('api/reset', [ + 'email' => 'test@email.com', + 'token' => 'my_super_secret_code', + 'password' => 'mynewpass', + 'password_confirmation' => 'mynewpass' + ])->seeJson([ + 'status' => 'ok' + ])->assertResponseOk(); + } + + public function testResetSuccessfullyWithTokenRelease() + { + Config::set('boilerplate.reset_password.release_token', true); + + $this->post('api/reset', [ + 'email' => 'test@email.com', + 'token' => 'my_super_secret_code', + 'password' => 'mynewpass', + 'password_confirmation' => 'mynewpass' + ])->seeJsonStructure([ + 'status', + 'token' + ])->seeJson([ + 'status' => 'ok' + ])->assertResponseOk(); + } + + public function testResetReturnsProcessError() + { + $this->post('api/reset', [ + 'email' => 'unknown@email.com', + 'token' => 'this_code_is_invalid', + 'password' => 'mynewpass', + 'password_confirmation' => 'mynewpass' + ])->seeJsonStructure([ + 'error' + ])->assertResponseStatus(500); + } + + public function testResetReturnsValidationError() + { + $this->post('api/reset', [ + 'email' => 'test@email.com', + 'token' => 'my_super_secret_code', + 'password' => 'mynewpass' + ])->seeJsonStructure([ + 'error' + ])->assertResponseStatus(422); + } + + public function setUp() + { + parent::setUp(); + + $user = new User([ + 'name' => 'Test User', + 'email' => 'test@email.com', + 'password' => '123456' + ]); + $user->save(); + + DB::table('password_resets')->insert([ + 'email' => 'test@email.com', + 'token' => 'my_super_secret_code', + 'created_at' => Carbon::now() + ]); + } +}