diff --git a/app/Containers/AppSection/Authentication/Actions/VerifyEmailAction.php b/app/Containers/AppSection/Authentication/Actions/VerifyEmailAction.php index c060ac185..042a6a664 100644 --- a/app/Containers/AppSection/Authentication/Actions/VerifyEmailAction.php +++ b/app/Containers/AppSection/Authentication/Actions/VerifyEmailAction.php @@ -2,18 +2,42 @@ namespace App\Containers\AppSection\Authentication\Actions; +use App\Containers\AppSection\Authentication\Exceptions\InvalidEmailVerificationDataException; use App\Containers\AppSection\Authentication\Notifications\EmailVerified; use App\Containers\AppSection\Authentication\UI\API\Requests\VerifyEmailRequest; +use App\Containers\AppSection\User\Models\User; +use App\Containers\AppSection\User\Tasks\FindUserByIdTask; +use App\Ship\Exceptions\NotFoundException; use App\Ship\Parents\Actions\Action; +use Throwable; class VerifyEmailAction extends Action { + /** + * @param VerifyEmailRequest $request + * @throws NotFoundException + * @throws Throwable + */ public function run(VerifyEmailRequest $request): void { - if (!$request->user()->hasVerifiedEmail()) { - $request->user()->markEmailAsVerified(); + $user = app(FindUserByIdTask::class)->run($request->id); - $request->user()->notify(new EmailVerified()); + throw_unless($this->validateData($request, $user), InvalidEmailVerificationDataException::class); + + if (!$user->hasVerifiedEmail()) { + $user->markEmailAsVerified(); + + $user->notify(new EmailVerified()); } } + + /** + * @param VerifyEmailRequest $request + * @param User $user + * @return bool + */ + private function validateData(VerifyEmailRequest $request, User $user): bool + { + return hash_equals((string)$request->hash, sha1($user->getEmailForVerification())); + } } diff --git a/app/Containers/AppSection/Authentication/Exceptions/InvalidEmailVerificationDataException.php b/app/Containers/AppSection/Authentication/Exceptions/InvalidEmailVerificationDataException.php new file mode 100644 index 000000000..d4cb78c6d --- /dev/null +++ b/app/Containers/AppSection/Authentication/Exceptions/InvalidEmailVerificationDataException.php @@ -0,0 +1,12 @@ +unverified()->create(); - // enable email verification - config(['appSection-authentication.require_email_verification' => true]); - $request = VerifyEmailRequest::injectData([], $unverifiedUser); - - - app(VerifyEmailAction::class)->run($request); - - $this->assertTrue($unverifiedUser->hasVerifiedEmail()); - Notification::assertSentTo($unverifiedUser, EmailVerified::class); - } -} diff --git a/app/Containers/AppSection/Authentication/UI/API/Controllers/VerifyEmailController.php b/app/Containers/AppSection/Authentication/UI/API/Controllers/VerifyEmailController.php index 5f553d93c..027052918 100644 --- a/app/Containers/AppSection/Authentication/UI/API/Controllers/VerifyEmailController.php +++ b/app/Containers/AppSection/Authentication/UI/API/Controllers/VerifyEmailController.php @@ -4,11 +4,17 @@ use App\Containers\AppSection\Authentication\Actions\VerifyEmailAction; use App\Containers\AppSection\Authentication\UI\API\Requests\VerifyEmailRequest; +use App\Ship\Exceptions\NotFoundException; use App\Ship\Parents\Controllers\ApiController; use Illuminate\Http\JsonResponse; +use Throwable; class VerifyEmailController extends ApiController { + /** + * @throws NotFoundException + * @throws Throwable + */ public function verifyEmail(VerifyEmailRequest $request): JsonResponse { app(VerifyEmailAction::class)->run($request); diff --git a/app/Containers/AppSection/Authentication/UI/API/Requests/VerifyEmailRequest.php b/app/Containers/AppSection/Authentication/UI/API/Requests/VerifyEmailRequest.php index f99b18eb9..bfb2b2e9d 100644 --- a/app/Containers/AppSection/Authentication/UI/API/Requests/VerifyEmailRequest.php +++ b/app/Containers/AppSection/Authentication/UI/API/Requests/VerifyEmailRequest.php @@ -2,13 +2,10 @@ namespace App\Containers\AppSection\Authentication\UI\API\Requests; -use App\Containers\AppSection\Authorization\Traits\IsResourceOwnerTrait; use App\Ship\Parents\Requests\Request; class VerifyEmailRequest extends Request { - use IsResourceOwnerTrait; - /** * Define which Roles and/or Permissions has access to this request. */ @@ -38,7 +35,7 @@ class VerifyEmailRequest extends Request public function rules(): array { return [ - 'id' => 'required', + // 'id' => 'required', ]; } @@ -49,13 +46,6 @@ public function authorize(): bool { return $this->check([ 'hasAccess', - 'isResourceOwner', - 'isHashEqualsUserEmail', ]); } - - protected function isHashEqualsUserEmail(): bool - { - return hash_equals((string)$this->hash, sha1($this->user()->getEmailForVerification())); - } } diff --git a/app/Containers/AppSection/Authentication/UI/API/Routes/VerifyEmail.v1.public.php b/app/Containers/AppSection/Authentication/UI/API/Routes/VerifyEmail.v1.public.php index f57375349..980d20892 100644 --- a/app/Containers/AppSection/Authentication/UI/API/Routes/VerifyEmail.v1.public.php +++ b/app/Containers/AppSection/Authentication/UI/API/Routes/VerifyEmail.v1.public.php @@ -4,11 +4,11 @@ * @apiGroup Authentication * @apiName VerifyEmail * - * @api {GET} /v1/email/verify/:id/:hash Verify Email + * @api {POST} /v1/email/verify/:id/:hash Verify Email * @apiDescription Verify user email * * @apiVersion 1.0.0 - * @apiPermission Authenticated + * @apiPermission none * * @apiSuccessExample {json} Success-Response: * HTTP/1.1 200 OK @@ -18,6 +18,5 @@ use App\Containers\AppSection\Authentication\UI\API\Controllers\VerifyEmailController; use Illuminate\Support\Facades\Route; -Route::post('email/verify/{id}/{hash}', [VerifyEmailController::class, 'verifyEmail']) - ->middleware(['auth:api']); +Route::post('email/verify/{id}/{hash}', [VerifyEmailController::class, 'verifyEmail']); diff --git a/app/Containers/AppSection/Authentication/UI/API/Tests/Functional/VerifyEmailTest.php b/app/Containers/AppSection/Authentication/UI/API/Tests/Functional/VerifyEmailTest.php new file mode 100644 index 000000000..a10f59beb --- /dev/null +++ b/app/Containers/AppSection/Authentication/UI/API/Tests/Functional/VerifyEmailTest.php @@ -0,0 +1,41 @@ + '', + 'permissions' => '', + ]; + + public function testVerifyEmail(): void + { + Notification::fake(); + $unverifiedUser = User::factory()->unverified()->create(); + // enable email verification + config(['appSection-authentication.require_email_verification' => true]); + + $response = $this->injectId($unverifiedUser->id) + ->injectId(sha1($unverifiedUser->getEmailForVerification()), skipEncoding: true, replace: '{hash}') + ->makeCall(); + + $response->assertStatus(200); + $unverifiedUser->refresh(); + $this->assertTrue($unverifiedUser->hasVerifiedEmail()); + Notification::assertSentTo($unverifiedUser, EmailVerified::class); + } +}