diff --git a/app/Containers/AppSection/Authentication/UI/API/Requests/RegisterUserRequest.php b/app/Containers/AppSection/Authentication/UI/API/Requests/RegisterUserRequest.php index eb06d7ce6..96da64e2b 100644 --- a/app/Containers/AppSection/Authentication/UI/API/Requests/RegisterUserRequest.php +++ b/app/Containers/AppSection/Authentication/UI/API/Requests/RegisterUserRequest.php @@ -4,6 +4,7 @@ use App\Ship\Parents\Requests\Request; use Illuminate\Validation\Rule; +use Illuminate\Validation\Rules\Password; class RegisterUserRequest extends Request { @@ -33,14 +34,25 @@ class RegisterUserRequest extends Request public function rules(): array { return [ - 'email' => 'required|email|max:40|unique:users,email', - 'password' => 'required|min:6|max:32', + 'email' => 'required|email|unique:users,email', + 'password' => [ + 'required', + Password::min(8) + ->letters() + ->mixedCase() + ->numbers() + ->symbols(), + ], 'name' => 'min:2|max:50', 'gender' => 'in:male,female,unspecified', 'birth' => 'date', - 'verification_url' => Rule::requiredIf(function () { - return config('appSection-authentication.require_email_verification'); - }) . '|url|' . Rule::in(config('appSection-authentication.allowed-verify-email-urls')), + 'verification_url' => [ + 'url', + Rule::requiredIf(function () { + return config('appSection-authentication.require_email_verification'); + }), + Rule::in(config('appSection-authentication.allowed-verify-email-urls')), + ], ]; } diff --git a/app/Containers/AppSection/Authentication/UI/API/Requests/ResetPasswordRequest.php b/app/Containers/AppSection/Authentication/UI/API/Requests/ResetPasswordRequest.php index d216ea338..6e3069146 100644 --- a/app/Containers/AppSection/Authentication/UI/API/Requests/ResetPasswordRequest.php +++ b/app/Containers/AppSection/Authentication/UI/API/Requests/ResetPasswordRequest.php @@ -3,6 +3,7 @@ namespace App\Containers\AppSection\Authentication\UI\API\Requests; use App\Ship\Parents\Requests\Request; +use Illuminate\Validation\Rules\Password; class ResetPasswordRequest extends Request { @@ -32,9 +33,16 @@ class ResetPasswordRequest extends Request public function rules(): array { return [ - 'token' => 'required|max:255', - 'email' => 'required|email|max:255', - 'password' => 'required|min:6|max:255', + 'token' => 'required', + 'email' => 'required|email', + 'password' => [ + 'required', + Password::min(8) + ->letters() + ->mixedCase() + ->numbers() + ->symbols(), + ], ]; } diff --git a/app/Containers/AppSection/Authentication/UI/API/Routes/RegisterUser.v1.private.php b/app/Containers/AppSection/Authentication/UI/API/Routes/RegisterUser.v1.private.php index 0893e673a..0bf399c2a 100644 --- a/app/Containers/AppSection/Authentication/UI/API/Routes/RegisterUser.v1.private.php +++ b/app/Containers/AppSection/Authentication/UI/API/Routes/RegisterUser.v1.private.php @@ -10,7 +10,18 @@ * @apiPermission none * * @apiParam {String} email - * @apiParam {String} password min:6|max:40 + * @apiParam {String} password min: 8 + * + * at least one character of the following: + * + * upper case letter + * + * lower case letter + * + * number + * + * special character + * * @apiParam {String} [name] min:2|max:50 * @apiParam {String="male,female,unspecified"} [gender] * @apiParam {Date} [birth] format: Y-m-d / e.g. 2015-10-15 diff --git a/app/Containers/AppSection/Authentication/UI/API/Routes/ResetPassword.v1.public.php b/app/Containers/AppSection/Authentication/UI/API/Routes/ResetPassword.v1.public.php index 0ca7d2147..443ef2649 100644 --- a/app/Containers/AppSection/Authentication/UI/API/Routes/ResetPassword.v1.public.php +++ b/app/Containers/AppSection/Authentication/UI/API/Routes/ResetPassword.v1.public.php @@ -10,9 +10,19 @@ * @apiVersion 1.0.0 * @apiPermission none * - * @apiParam {String} email - * @apiParam {String} token from the forgot password email - * @apiParam {String} password min:6|max:255 the new password + * @apiParam {String} email + * @apiParam {String} token from the forgot password email + * @apiParam {String} password min: 8 + * + * at least one character of the following: + * + * upper case letter + * + * lower case letter + * + * number + * + * special character * * @apiSuccessExample {json} Success-Response: * HTTP/1.1 204 No Content diff --git a/app/Containers/AppSection/Authentication/UI/API/Tests/Functional/RegisterUserTest.php b/app/Containers/AppSection/Authentication/UI/API/Tests/Functional/RegisterUserTest.php index 68ee6778b..3926b5513 100644 --- a/app/Containers/AppSection/Authentication/UI/API/Tests/Functional/RegisterUserTest.php +++ b/app/Containers/AppSection/Authentication/UI/API/Tests/Functional/RegisterUserTest.php @@ -29,7 +29,7 @@ public function testGivenEmailVerificationEnabled_RegisterNewUserWithCredentials $data = [ 'email' => 'apiato@mail.test', - 'password' => 'secretpass', + 'password' => 's3cr3tPa$$', 'verification_url' => 'http://some.test/known/url', ]; @@ -37,7 +37,7 @@ public function testGivenEmailVerificationEnabled_RegisterNewUserWithCredentials $response->assertStatus(200); $response->assertJson( - fn (AssertableJson $json) => $json->has('data') + fn(AssertableJson $json) => $json->has('data') ->where('data.email', $data['email']) ->etc() ); @@ -48,14 +48,14 @@ public function testGivenEmailVerificationDisabled_RegisterNewUserWithCredential config(['appSection-authentication.require_email_verification' => false]); $data = [ 'email' => 'apiato@mail.test', - 'password' => 'secretpass', + 'password' => 's3cr3tPa$$', ]; $response = $this->makeCall($data); $response->assertStatus(200); $response->assertJson( - fn (AssertableJson $json) => $json->has('data') + fn(AssertableJson $json) => $json->has('data') ->where('data.email', $data['email']) ->etc() ); @@ -72,7 +72,7 @@ public function testRegisterNewUserUsingGetVerb(): void $response->assertStatus(405); $response->assertJson( - fn (AssertableJson $json) => $json->has('message') + fn(AssertableJson $json) => $json->has('message') ->where('message', 'The GET method is not supported for this route. Supported methods: POST.') ->etc() ); @@ -96,7 +96,7 @@ public function testRegisterExistingUser(): void $response->assertStatus(422); $response->assertJson( - fn (AssertableJson $json) => $json->has('errors') + fn(AssertableJson $json) => $json->has('errors') ->where('errors.email.0', 'The email has already been taken.') ->etc() ); @@ -112,23 +112,21 @@ public function testRegisterNewUserWithoutData(): void if (config('appSection-authentication.require_email_verification')) { $response->assertJson( - fn (AssertableJson $json) => $json->hasAll(['message', 'errors' => 3]) + fn(AssertableJson $json) => $json->hasAll(['message', 'errors' => 3]) ->has( 'errors', - fn (AssertableJson $json) => - $json->where('email.0', 'The email field is required.') - ->where('password.0', 'The password field is required.') - ->where('verification_url.0', 'The verification url field is required.') + fn(AssertableJson $json) => $json->where('email.0', 'The email field is required.') + ->where('password.0', 'The password field is required.') + ->where('verification_url.0', 'The verification url field is required.') ) ); } else { $response->assertJson( - fn (AssertableJson $json) => $json->hasAll(['message', 'errors' => 2]) + fn(AssertableJson $json) => $json->hasAll(['message', 'errors' => 2]) ->has( 'errors', - fn (AssertableJson $json) => - $json->where('email.0', 'The email field is required.') - ->where('password.0', 'The password field is required.') + fn(AssertableJson $json) => $json->where('email.0', 'The email field is required.') + ->where('password.0', 'The password field is required.') ) ); } @@ -138,19 +136,40 @@ public function testRegisterNewUserWithInvalidEmail(): void { $data = [ 'email' => 'missing-at.test', - 'password' => 'secret', ]; $response = $this->makeCall($data); $response->assertStatus(422); $response->assertJson( - fn (AssertableJson $json) => $json->has('errors') + fn(AssertableJson $json) => $json->has('errors') ->where('errors.email.0', 'The email must be a valid email address.') ->etc() ); } + public function testRegisterNewUserWithInvalidPassword(): void + { + $data = [ + 'password' => '((((()))))', + ]; + + $response = $this->makeCall($data); + + $response->assertStatus(422); + $response->assertJson( + fn(AssertableJson $json) => $json->has('errors') + ->has( + 'errors.password', + fn(AssertableJson $json) => $json + ->where('0', 'The password must contain at least one uppercase and one lowercase letter.') + ->where('1', 'The password must contain at least one letter.') + ->where('2', 'The password must contain at least one number.') + ) + ->etc() + ); + } + public function testRegisterNewUserWithNotAllowedVerificationUrl(): void { if (!config('appSection-authentication.require_email_verification')) { @@ -161,7 +180,7 @@ public function testRegisterNewUserWithNotAllowedVerificationUrl(): void $data = [ 'email' => 'test@test.test', - 'password' => 'secret', + 'password' => 's3cr3tPa$$', 'verification_url' => 'http://notallowed.test/wrong', ]; @@ -169,7 +188,7 @@ public function testRegisterNewUserWithNotAllowedVerificationUrl(): void $response->assertStatus(422); $response->assertJson( - fn (AssertableJson $json) => $json->hasAll(['message', 'errors' => 1]) + fn(AssertableJson $json) => $json->hasAll(['message', 'errors' => 1]) ->where('errors.verification_url.0', 'The selected verification url is invalid.') ); } diff --git a/app/Containers/AppSection/Authentication/UI/API/Tests/Functional/ResetPasswordTest.php b/app/Containers/AppSection/Authentication/UI/API/Tests/Functional/ResetPasswordTest.php index c52add201..a6eea5f32 100644 --- a/app/Containers/AppSection/Authentication/UI/API/Tests/Functional/ResetPasswordTest.php +++ b/app/Containers/AppSection/Authentication/UI/API/Tests/Functional/ResetPasswordTest.php @@ -4,6 +4,7 @@ use App\Containers\AppSection\Authentication\Tasks\CreatePasswordResetTokenTask; use App\Containers\AppSection\Authentication\UI\API\Tests\ApiTestCase; +use Illuminate\Testing\Fluent\AssertableJson; /** * Class ResetPasswordTest. @@ -29,7 +30,7 @@ public function testResetPassword(): void $token = app(CreatePasswordResetTokenTask::class)->run($this->testingUser); $data = [ 'email' => $this->testingUser->email, - 'password' => 'new pass', + 'password' => 's3cr3tPa$$', 'token' => $token, ]; @@ -37,4 +38,42 @@ public function testResetPassword(): void $response->assertStatus(204); } + + public function testResetPasswordWithInvalidEmail(): void + { + $data = [ + 'email' => 'missing-at.test', + ]; + + $response = $this->makeCall($data); + + $response->assertStatus(422); + $response->assertJson( + fn(AssertableJson $json) => $json->has('errors') + ->where('errors.email.0', 'The email must be a valid email address.') + ->etc() + ); + } + + public function testResetPasswordWithInvalidPassword(): void + { + $data = [ + 'password' => '((((()))))', + ]; + + $response = $this->makeCall($data); + + $response->assertStatus(422); + $response->assertJson( + fn(AssertableJson $json) => $json->has('errors') + ->has( + 'errors.password', + fn(AssertableJson $json) => $json + ->where('0', 'The password must contain at least one uppercase and one lowercase letter.') + ->where('1', 'The password must contain at least one letter.') + ->where('2', 'The password must contain at least one number.') + ) + ->etc() + ); + } }