From 9934bbfe01fceb470bfc7344c2df9cd4442e8071 Mon Sep 17 00:00:00 2001 From: Brivan-26 Date: Wed, 11 Oct 2023 12:34:36 +0100 Subject: [PATCH 1/3] Added base configuration --- app/Http/Controllers/API/AuthController.php | 4 +++ .../Controllers/VerifyEmailController.php | 27 +++++++++++++++++++ app/Models/User.php | 1 + .../2014_10_12_000000_create_users_table.php | 1 + routes/api.php | 14 ++++++++-- 5 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 app/Http/Controllers/VerifyEmailController.php diff --git a/app/Http/Controllers/API/AuthController.php b/app/Http/Controllers/API/AuthController.php index ead6845..37e54c0 100644 --- a/app/Http/Controllers/API/AuthController.php +++ b/app/Http/Controllers/API/AuthController.php @@ -11,12 +11,14 @@ use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Validator; +use Illuminate\Auth\Events\Registered; class AuthController extends BaseController { public function register(Request $request){ $validator = Validator::make($request->all(), [ 'full_name' => 'required|string|unique:users,full_name', + 'email' => 'required|email|unique:users,email', 'track' => 'required|string|exists:tracks,type', 'password' => 'required|string|min:6', ]); @@ -33,6 +35,7 @@ public function register(Request $request){ $user = User::create([ 'full_name' => $request->full_name, + 'email' => $request->email, 'track_id' => $trackID, 'password' => Hash::make($request->password), 'role' => 'participant', @@ -40,6 +43,7 @@ public function register(Request $request){ 'ip' => $request->ip() ]); + event(new Registered($user)); $token = $user->createToken('arizona-platform')->plainTextToken; $result = [ 'user' => new ParticipantResource($user), diff --git a/app/Http/Controllers/VerifyEmailController.php b/app/Http/Controllers/VerifyEmailController.php new file mode 100644 index 0000000..f2cdf87 --- /dev/null +++ b/app/Http/Controllers/VerifyEmailController.php @@ -0,0 +1,27 @@ +route('id')); + + if ($user->hasVerifiedEmail()) { + return redirect(env('FRONT_URL')); + } + + if ($user->markEmailAsVerified()) { + event(new Verified($user)); + } + + return redirect(env('FRONT_URL')); + } +} diff --git a/app/Models/User.php b/app/Models/User.php index f161d4d..cbf3db4 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -20,6 +20,7 @@ class User extends Authenticatable implements MustVerifyEmail */ protected $fillable = [ 'full_name', + 'email', 'password', 'points', 'role', diff --git a/database/migrations/2014_10_12_000000_create_users_table.php b/database/migrations/2014_10_12_000000_create_users_table.php index 582c01e..52aea93 100644 --- a/database/migrations/2014_10_12_000000_create_users_table.php +++ b/database/migrations/2014_10_12_000000_create_users_table.php @@ -17,6 +17,7 @@ public function up() $table->bigIncrements('id'); $table->foreignId('track_id')->nullable()->constrained()->onDelete('cascade'); $table->string('full_name'); + $table->string('email')->unique(); $table->timestamp('email_verified_at')->nullable(); $table->string('password'); $table->float('points')->nullable(); diff --git a/routes/api.php b/routes/api.php index 8c998e9..dbdb7a2 100644 --- a/routes/api.php +++ b/routes/api.php @@ -5,7 +5,9 @@ use App\Http\Controllers\API\JudgeController; use App\Http\Controllers\API\ParticipantController; use App\Http\Controllers\API\GeneralController; +use App\Http\Controllers\VerifyEmailController; use App\Models\Track; +use Illuminate\Http\Request; use Illuminate\Support\Facades\Route; @@ -19,13 +21,21 @@ | is assigned the "api" middleware group. Enjoy building your API! | */ +// Verify email +Route::get('/email/verify/{id}/{hash}', [VerifyEmailController::class, 'verifyEmail'])->middleware(['auth:sanctum', 'signed'])->name('verification.verify'); + +// Resend link to verify email +Route::post('/email/verify/resend', function (Request $request) { + $request->user()->sendEmailVerificationNotification(); + return back()->with('message', 'Verification link sent!'); +})->middleware(['auth:api', 'throttle:6,1'])->name('ication.send'); Route::middleware(['throttle:api'])->group(function() { Route::post('/register', [AuthController::class, 'register']); Route::post('/login', [AuthController::class, 'login']); - Route::get('/tracks', [GeneralController::class, 'get_track_types']); + Route::get('/tracks', [GeneralController::class, 'get_track_types'])->middleware(['auth:sanctum','verified']); Route::post('/logout', [AuthController::class, 'logout'])->middleware('auth:sanctum'); - Route::get('/track/{type}/leaderboard', [ParticipantController::class, 'leaderboard'])->middleware(['auth:sanctum']); + Route::get('/track/{type}/leaderboard', [ParticipantController::class, 'leaderboard'])->middleware(['auth:sanctum', 'verified']); Route::prefix('admin')->middleware(['auth:sanctum', 'hasRole:admin']) ->controller(AdminController::class)->group(function() { From 1e0f7dda77425c0b5592d34532bb53cd7a92f0cb Mon Sep 17 00:00:00 2001 From: Brivan-26 Date: Wed, 18 Oct 2023 23:18:12 +0100 Subject: [PATCH 2/3] Added Email verification --- .../Controllers/VerifyEmailController.php | 36 +++++++++++++++---- app/Providers/AuthServiceProvider.php | 11 +++++- routes/api.php | 14 +++----- routes/web.php | 4 +-- 4 files changed, 45 insertions(+), 20 deletions(-) diff --git a/app/Http/Controllers/VerifyEmailController.php b/app/Http/Controllers/VerifyEmailController.php index f2cdf87..8597adc 100644 --- a/app/Http/Controllers/VerifyEmailController.php +++ b/app/Http/Controllers/VerifyEmailController.php @@ -4,24 +4,46 @@ use App\Models\User; use Illuminate\Auth\Events\Verified; +use Illuminate\Foundation\Auth\EmailVerificationRequest; use Illuminate\Http\Request; use Illuminate\Routing\Controller; class VerifyEmailController extends Controller { - public function __invoke(Request $request) + public function sendVerificationEmail(Request $request) { - $user = User::find($request->route('id')); + if ($request->user()->hasVerifiedEmail()) { + return response()->json([ + 'success' => false, + 'message' => 'email already verified' + ], 400); + } + + $request->user()->sendEmailVerificationNotification(); - if ($user->hasVerifiedEmail()) { - return redirect(env('FRONT_URL')); + return response()->json([ + 'success' => true, + 'message' => 'email verification link has been sent' + ]); + } + + public function verify(EmailVerificationRequest $request) + { + if ($request->user()->hasVerifiedEmail()) { + return response()->json([ + 'success' => false, + 'message' => 'email already verified' + ], 400); } - if ($user->markEmailAsVerified()) { - event(new Verified($user)); + if ($request->user()->markEmailAsVerified()) { + event(new Verified($request->user())); } - return redirect(env('FRONT_URL')); + return response()->json([ + 'success' => true, + 'message' => 'email has been verified' + ]); } } diff --git a/app/Providers/AuthServiceProvider.php b/app/Providers/AuthServiceProvider.php index 33b83f5..61c40a0 100644 --- a/app/Providers/AuthServiceProvider.php +++ b/app/Providers/AuthServiceProvider.php @@ -3,7 +3,10 @@ namespace App\Providers; // use Illuminate\Support\Facades\Gate; + +use Illuminate\Auth\Notifications\VerifyEmail; use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider; +use Illuminate\Notifications\Messages\MailMessage; class AuthServiceProvider extends ServiceProvider { @@ -25,6 +28,12 @@ public function boot() { $this->registerPolicies(); - // + VerifyEmail::toMailUsing(function ($notifiable, $url) { + $spaUrl = env('FRONT_URL').'/email_verification/verify?url='.$url; + return (new MailMessage) + ->subject('Verify Your Email Address') + ->line('Are you real? If so, please click the button below to verify your email address') + ->action('Verify Email Address', $spaUrl); + }); } } diff --git a/routes/api.php b/routes/api.php index dbdb7a2..30ef843 100644 --- a/routes/api.php +++ b/routes/api.php @@ -6,8 +6,6 @@ use App\Http\Controllers\API\ParticipantController; use App\Http\Controllers\API\GeneralController; use App\Http\Controllers\VerifyEmailController; -use App\Models\Track; -use Illuminate\Http\Request; use Illuminate\Support\Facades\Route; @@ -21,19 +19,15 @@ | is assigned the "api" middleware group. Enjoy building your API! | */ -// Verify email -Route::get('/email/verify/{id}/{hash}', [VerifyEmailController::class, 'verifyEmail'])->middleware(['auth:sanctum', 'signed'])->name('verification.verify'); -// Resend link to verify email -Route::post('/email/verify/resend', function (Request $request) { - $request->user()->sendEmailVerificationNotification(); - return back()->with('message', 'Verification link sent!'); -})->middleware(['auth:api', 'throttle:6,1'])->name('ication.send'); +Route::post('/email/verification-notification', [VerifyEmailController::class, 'sendVerificationEmail'])->middleware('auth:sanctum'); +Route::get('/verify-email/{id}/{hash}', [VerifyEmailController::class, 'verify'])->name('verification.verify')->middleware('auth:sanctum'); + Route::middleware(['throttle:api'])->group(function() { Route::post('/register', [AuthController::class, 'register']); Route::post('/login', [AuthController::class, 'login']); - Route::get('/tracks', [GeneralController::class, 'get_track_types'])->middleware(['auth:sanctum','verified']); + Route::get('/tracks', [GeneralController::class, 'get_track_types']); Route::post('/logout', [AuthController::class, 'logout'])->middleware('auth:sanctum'); Route::get('/track/{type}/leaderboard', [ParticipantController::class, 'leaderboard'])->middleware(['auth:sanctum', 'verified']); diff --git a/routes/web.php b/routes/web.php index 443cf8c..f7891f2 100644 --- a/routes/web.php +++ b/routes/web.php @@ -1,7 +1,6 @@ middleware(['signed'])->name('verification.verify'); Route::get('/', function () { return view('welcome'); })->name('welcome'); + + From c5f306dd4a812e77345d12b66a68140f7deda4b9 Mon Sep 17 00:00:00 2001 From: Brivan-26 Date: Sat, 21 Oct 2023 10:56:58 +0100 Subject: [PATCH 3/3] Updated tests --- app/Http/Repositories/UserRepository.php | 7 +- .../Resources/User/AdministratorResource.php | 1 + app/Http/Resources/User/JudgeResource.php | 1 + .../Resources/User/ParticipantResource.php | 1 + composer.lock | 172 +++++++++--------- database/factories/UserFactory.php | 5 +- routes/api.php | 2 +- .../Admin/UserManagment/CreateJudgeTest.php | 39 +++- .../UserManagment/CreateParticipantTest.php | 40 +++- tests/Feature/Auth/RegisterUserTest.php | 31 ++++ 10 files changed, 203 insertions(+), 96 deletions(-) diff --git a/app/Http/Repositories/UserRepository.php b/app/Http/Repositories/UserRepository.php index c32c68c..fec3bf6 100644 --- a/app/Http/Repositories/UserRepository.php +++ b/app/Http/Repositories/UserRepository.php @@ -8,9 +8,6 @@ use App\Models\User; use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Validator; -use Illuminate\Support\Facades\Mail; -use App\Mail\ParticipantAccountCreated; -use App\Mail\JudgeAccountCreated; Class UserRepository { @@ -26,6 +23,7 @@ public function create_participant($request) { $response = []; $validator = Validator::make($request->all(), [ 'full_name' => 'required|unique:users,full_name', + 'email' => 'required|unique:users,email', 'password' => 'required|min:6', 'track' => 'required|exists:tracks,type' ]); @@ -39,6 +37,7 @@ public function create_participant($request) { $trackID = Track::where('type', $request->track)->pluck('id')->first(); $user = User::create([ 'full_name' => $request->full_name, + 'email' => $request->email, 'track_id' => $trackID, 'password' => Hash::make($request->password), 'role' => 'participant', @@ -55,6 +54,7 @@ public function create_judge($request) $response = []; $validator = Validator::make($request->all(), [ 'full_name' => 'required|string|unique:users,full_name', + 'email' => 'required|unique:users,email', 'password' => 'required|string|min:6', 'track' => 'required|exists:tracks,type' ]); @@ -67,6 +67,7 @@ public function create_judge($request) $trackID = Track::where('type', $request->track)->pluck('id')->first(); $user = User::create([ 'full_name' => $request->full_name, + 'email' => $request->email, 'password' => Hash::make($request->password), 'role' => 'judge', 'track_id' => $trackID diff --git a/app/Http/Resources/User/AdministratorResource.php b/app/Http/Resources/User/AdministratorResource.php index 49ccb4c..2723064 100644 --- a/app/Http/Resources/User/AdministratorResource.php +++ b/app/Http/Resources/User/AdministratorResource.php @@ -17,6 +17,7 @@ public function toArray($request) return [ 'id' => $this->id, 'full_name' => $this->full_name, + 'email' => $this->email, 'role' => 'administrator' ]; } diff --git a/app/Http/Resources/User/JudgeResource.php b/app/Http/Resources/User/JudgeResource.php index b8569b8..4716070 100644 --- a/app/Http/Resources/User/JudgeResource.php +++ b/app/Http/Resources/User/JudgeResource.php @@ -17,6 +17,7 @@ public function toArray($request) return [ 'id' => $this->id, 'full_name' => $this->full_name, + 'email' => $this->email, 'role' => 'judge', 'track' => $this->track->type ]; diff --git a/app/Http/Resources/User/ParticipantResource.php b/app/Http/Resources/User/ParticipantResource.php index bb4b9c4..d6c496e 100644 --- a/app/Http/Resources/User/ParticipantResource.php +++ b/app/Http/Resources/User/ParticipantResource.php @@ -18,6 +18,7 @@ public function toArray($request) return [ 'id' => $this->id, 'full_name' => $this->full_name, + 'email' => $this->email, 'points' => $this->points, 'role' => 'participant', 'email_verified' => $this->email_verified_at ? true: false, diff --git a/composer.lock b/composer.lock index 8301f4f..5c1f67c 100644 --- a/composer.lock +++ b/composer.lock @@ -367,16 +367,16 @@ }, { "name": "egulias/email-validator", - "version": "4.0.1", + "version": "4.0.2", "source": { "type": "git", "url": "https://github.com/egulias/EmailValidator.git", - "reference": "3a85486b709bc384dae8eb78fb2eec649bdb64ff" + "reference": "ebaaf5be6c0286928352e054f2d5125608e5405e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/3a85486b709bc384dae8eb78fb2eec649bdb64ff", - "reference": "3a85486b709bc384dae8eb78fb2eec649bdb64ff", + "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/ebaaf5be6c0286928352e054f2d5125608e5405e", + "reference": "ebaaf5be6c0286928352e054f2d5125608e5405e", "shasum": "" }, "require": { @@ -385,8 +385,8 @@ "symfony/polyfill-intl-idn": "^1.26" }, "require-dev": { - "phpunit/phpunit": "^9.5.27", - "vimeo/psalm": "^4.30" + "phpunit/phpunit": "^10.2", + "vimeo/psalm": "^5.12" }, "suggest": { "ext-intl": "PHP Internationalization Libraries are required to use the SpoofChecking validation" @@ -422,7 +422,7 @@ ], "support": { "issues": "https://github.com/egulias/EmailValidator/issues", - "source": "https://github.com/egulias/EmailValidator/tree/4.0.1" + "source": "https://github.com/egulias/EmailValidator/tree/4.0.2" }, "funding": [ { @@ -430,25 +430,25 @@ "type": "github" } ], - "time": "2023-01-14T14:17:03+00:00" + "time": "2023-10-06T06:47:41+00:00" }, { "name": "fruitcake/php-cors", - "version": "v1.2.0", + "version": "v1.3.0", "source": { "type": "git", "url": "https://github.com/fruitcake/php-cors.git", - "reference": "58571acbaa5f9f462c9c77e911700ac66f446d4e" + "reference": "3d158f36e7875e2f040f37bc0573956240a5a38b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/fruitcake/php-cors/zipball/58571acbaa5f9f462c9c77e911700ac66f446d4e", - "reference": "58571acbaa5f9f462c9c77e911700ac66f446d4e", + "url": "https://api.github.com/repos/fruitcake/php-cors/zipball/3d158f36e7875e2f040f37bc0573956240a5a38b", + "reference": "3d158f36e7875e2f040f37bc0573956240a5a38b", "shasum": "" }, "require": { "php": "^7.4|^8.0", - "symfony/http-foundation": "^4.4|^5.4|^6" + "symfony/http-foundation": "^4.4|^5.4|^6|^7" }, "require-dev": { "phpstan/phpstan": "^1.4", @@ -458,7 +458,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.1-dev" + "dev-master": "1.2-dev" } }, "autoload": { @@ -489,7 +489,7 @@ ], "support": { "issues": "https://github.com/fruitcake/php-cors/issues", - "source": "https://github.com/fruitcake/php-cors/tree/v1.2.0" + "source": "https://github.com/fruitcake/php-cors/tree/v1.3.0" }, "funding": [ { @@ -501,7 +501,7 @@ "type": "github" } ], - "time": "2022-02-20T15:07:15+00:00" + "time": "2023-10-12T05:21:21+00:00" }, { "name": "graham-campbell/result-type", @@ -972,16 +972,16 @@ }, { "name": "laravel/framework", - "version": "v9.52.15", + "version": "v9.52.16", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "e3350e87a52346af9cc655a3012d2175d2d05ad7" + "reference": "082345d76fc6a55b649572efe10b11b03e279d24" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/e3350e87a52346af9cc655a3012d2175d2d05ad7", - "reference": "e3350e87a52346af9cc655a3012d2175d2d05ad7", + "url": "https://api.github.com/repos/laravel/framework/zipball/082345d76fc6a55b649572efe10b11b03e279d24", + "reference": "082345d76fc6a55b649572efe10b11b03e279d24", "shasum": "" }, "require": { @@ -1166,7 +1166,7 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2023-08-08T14:28:40+00:00" + "time": "2023-10-03T13:02:30+00:00" }, { "name": "laravel/sanctum", @@ -1553,16 +1553,16 @@ }, { "name": "league/flysystem", - "version": "3.16.0", + "version": "3.18.0", "source": { "type": "git", "url": "https://github.com/thephpleague/flysystem.git", - "reference": "4fdf372ca6b63c6e281b1c01a624349ccb757729" + "reference": "015633a05aee22490495159237a5944091d8281e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/4fdf372ca6b63c6e281b1c01a624349ccb757729", - "reference": "4fdf372ca6b63c6e281b1c01a624349ccb757729", + "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/015633a05aee22490495159237a5944091d8281e", + "reference": "015633a05aee22490495159237a5944091d8281e", "shasum": "" }, "require": { @@ -1580,8 +1580,8 @@ "symfony/http-client": "<5.2" }, "require-dev": { - "async-aws/s3": "^1.5", - "async-aws/simple-s3": "^1.1", + "async-aws/s3": "^1.5 || ^2.0", + "async-aws/simple-s3": "^1.1 || ^2.0", "aws/aws-sdk-php": "^3.220.0", "composer/semver": "^3.0", "ext-fileinfo": "*", @@ -1591,7 +1591,7 @@ "google/cloud-storage": "^1.23", "microsoft/azure-storage-blob": "^1.1", "phpseclib/phpseclib": "^3.0.14", - "phpstan/phpstan": "^0.12.26", + "phpstan/phpstan": "^1.10", "phpunit/phpunit": "^9.5.11|^10.0", "sabre/dav": "^4.3.1" }, @@ -1627,7 +1627,7 @@ ], "support": { "issues": "https://github.com/thephpleague/flysystem/issues", - "source": "https://github.com/thephpleague/flysystem/tree/3.16.0" + "source": "https://github.com/thephpleague/flysystem/tree/3.18.0" }, "funding": [ { @@ -1639,20 +1639,20 @@ "type": "github" } ], - "time": "2023-09-07T19:22:17+00:00" + "time": "2023-10-20T17:59:40+00:00" }, { "name": "league/flysystem-local", - "version": "3.16.0", + "version": "3.18.0", "source": { "type": "git", "url": "https://github.com/thephpleague/flysystem-local.git", - "reference": "ec7383f25642e6fd4bb0c9554fc2311245391781" + "reference": "e7381ef7643f658b87efb7dbe98fe538fb1bbf32" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem-local/zipball/ec7383f25642e6fd4bb0c9554fc2311245391781", - "reference": "ec7383f25642e6fd4bb0c9554fc2311245391781", + "url": "https://api.github.com/repos/thephpleague/flysystem-local/zipball/e7381ef7643f658b87efb7dbe98fe538fb1bbf32", + "reference": "e7381ef7643f658b87efb7dbe98fe538fb1bbf32", "shasum": "" }, "require": { @@ -1687,7 +1687,7 @@ ], "support": { "issues": "https://github.com/thephpleague/flysystem-local/issues", - "source": "https://github.com/thephpleague/flysystem-local/tree/3.16.0" + "source": "https://github.com/thephpleague/flysystem-local/tree/3.18.0" }, "funding": [ { @@ -1699,20 +1699,20 @@ "type": "github" } ], - "time": "2023-08-30T10:23:59+00:00" + "time": "2023-10-19T20:07:13+00:00" }, { "name": "league/mime-type-detection", - "version": "1.13.0", + "version": "1.14.0", "source": { "type": "git", "url": "https://github.com/thephpleague/mime-type-detection.git", - "reference": "a6dfb1194a2946fcdc1f38219445234f65b35c96" + "reference": "b6a5854368533df0295c5761a0253656a2e52d9e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/mime-type-detection/zipball/a6dfb1194a2946fcdc1f38219445234f65b35c96", - "reference": "a6dfb1194a2946fcdc1f38219445234f65b35c96", + "url": "https://api.github.com/repos/thephpleague/mime-type-detection/zipball/b6a5854368533df0295c5761a0253656a2e52d9e", + "reference": "b6a5854368533df0295c5761a0253656a2e52d9e", "shasum": "" }, "require": { @@ -1743,7 +1743,7 @@ "description": "Mime-type detection for Flysystem", "support": { "issues": "https://github.com/thephpleague/mime-type-detection/issues", - "source": "https://github.com/thephpleague/mime-type-detection/tree/1.13.0" + "source": "https://github.com/thephpleague/mime-type-detection/tree/1.14.0" }, "funding": [ { @@ -1755,7 +1755,7 @@ "type": "tidelift" } ], - "time": "2023-08-05T12:09:49+00:00" + "time": "2023-10-17T14:13:20+00:00" }, { "name": "monolog/monolog", @@ -1967,16 +1967,16 @@ }, { "name": "nette/schema", - "version": "v1.2.4", + "version": "v1.2.5", "source": { "type": "git", "url": "https://github.com/nette/schema.git", - "reference": "c9ff517a53903b3d4e29ec547fb20feecb05b8ab" + "reference": "0462f0166e823aad657c9224d0f849ecac1ba10a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nette/schema/zipball/c9ff517a53903b3d4e29ec547fb20feecb05b8ab", - "reference": "c9ff517a53903b3d4e29ec547fb20feecb05b8ab", + "url": "https://api.github.com/repos/nette/schema/zipball/0462f0166e823aad657c9224d0f849ecac1ba10a", + "reference": "0462f0166e823aad657c9224d0f849ecac1ba10a", "shasum": "" }, "require": { @@ -2023,9 +2023,9 @@ ], "support": { "issues": "https://github.com/nette/schema/issues", - "source": "https://github.com/nette/schema/tree/v1.2.4" + "source": "https://github.com/nette/schema/tree/v1.2.5" }, - "time": "2023-08-05T18:56:25+00:00" + "time": "2023-10-05T20:37:59+00:00" }, { "name": "nette/utils", @@ -2744,16 +2744,16 @@ }, { "name": "psy/psysh", - "version": "v0.11.21", + "version": "v0.11.22", "source": { "type": "git", "url": "https://github.com/bobthecow/psysh.git", - "reference": "bcb22101107f3bf770523b65630c9d547f60c540" + "reference": "128fa1b608be651999ed9789c95e6e2a31b5802b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/bobthecow/psysh/zipball/bcb22101107f3bf770523b65630c9d547f60c540", - "reference": "bcb22101107f3bf770523b65630c9d547f60c540", + "url": "https://api.github.com/repos/bobthecow/psysh/zipball/128fa1b608be651999ed9789c95e6e2a31b5802b", + "reference": "128fa1b608be651999ed9789c95e6e2a31b5802b", "shasum": "" }, "require": { @@ -2782,7 +2782,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "0.11.x-dev" + "dev-0.11": "0.11.x-dev" }, "bamarni-bin": { "bin-links": false, @@ -2818,9 +2818,9 @@ ], "support": { "issues": "https://github.com/bobthecow/psysh/issues", - "source": "https://github.com/bobthecow/psysh/tree/v0.11.21" + "source": "https://github.com/bobthecow/psysh/tree/v0.11.22" }, - "time": "2023-09-17T21:15:54+00:00" + "time": "2023-10-14T21:56:36+00:00" }, { "name": "ralouphie/getallheaders", @@ -6070,16 +6070,16 @@ }, { "name": "laravel/pint", - "version": "v1.13.2", + "version": "v1.13.3", "source": { "type": "git", "url": "https://github.com/laravel/pint.git", - "reference": "bbb13460d7f8c5c0cd9a58109beedd79cd7331ff" + "reference": "93b2d0d49719bc6e444ba21cd4dbbccec935413d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/pint/zipball/bbb13460d7f8c5c0cd9a58109beedd79cd7331ff", - "reference": "bbb13460d7f8c5c0cd9a58109beedd79cd7331ff", + "url": "https://api.github.com/repos/laravel/pint/zipball/93b2d0d49719bc6e444ba21cd4dbbccec935413d", + "reference": "93b2d0d49719bc6e444ba21cd4dbbccec935413d", "shasum": "" }, "require": { @@ -6090,7 +6090,7 @@ "php": "^8.1.0" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^3.26.1", + "friendsofphp/php-cs-fixer": "^3.34.1", "illuminate/view": "^10.23.1", "laravel-zero/framework": "^10.1.2", "mockery/mockery": "^1.6.6", @@ -6132,7 +6132,7 @@ "issues": "https://github.com/laravel/pint/issues", "source": "https://github.com/laravel/pint" }, - "time": "2023-09-19T15:55:02+00:00" + "time": "2023-10-10T15:39:09+00:00" }, { "name": "laravel/sail", @@ -7992,35 +7992,35 @@ }, { "name": "spatie/flare-client-php", - "version": "1.4.2", + "version": "1.4.3", "source": { "type": "git", "url": "https://github.com/spatie/flare-client-php.git", - "reference": "5f2c6a7a0d2c1d90c12559dc7828fd942911a544" + "reference": "5db2fdd743c3ede33f2a5367d89ec1a7c9c1d1ec" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/flare-client-php/zipball/5f2c6a7a0d2c1d90c12559dc7828fd942911a544", - "reference": "5f2c6a7a0d2c1d90c12559dc7828fd942911a544", + "url": "https://api.github.com/repos/spatie/flare-client-php/zipball/5db2fdd743c3ede33f2a5367d89ec1a7c9c1d1ec", + "reference": "5db2fdd743c3ede33f2a5367d89ec1a7c9c1d1ec", "shasum": "" }, "require": { - "illuminate/pipeline": "^8.0|^9.0|^10.0", + "illuminate/pipeline": "^8.0|^9.0|^10.0|^11.0", "nesbot/carbon": "^2.62.1", "php": "^8.0", "spatie/backtrace": "^1.5.2", - "symfony/http-foundation": "^5.0|^6.0", - "symfony/mime": "^5.2|^6.0", - "symfony/process": "^5.2|^6.0", - "symfony/var-dumper": "^5.2|^6.0" + "symfony/http-foundation": "^5.2|^6.0|^7.0", + "symfony/mime": "^5.2|^6.0|^7.0", + "symfony/process": "^5.2|^6.0|^7.0", + "symfony/var-dumper": "^5.2|^6.0|^7.0" }, "require-dev": { - "dms/phpunit-arraysubset-asserts": "^0.3.0", - "pestphp/pest": "^1.20", + "dms/phpunit-arraysubset-asserts": "^0.5.0", + "pestphp/pest": "^1.20|^2.0", "phpstan/extension-installer": "^1.1", "phpstan/phpstan-deprecation-rules": "^1.0", "phpstan/phpstan-phpunit": "^1.0", - "spatie/phpunit-snapshot-assertions": "^4.0" + "spatie/phpunit-snapshot-assertions": "^4.0|^5.0" }, "type": "library", "extra": { @@ -8050,7 +8050,7 @@ ], "support": { "issues": "https://github.com/spatie/flare-client-php/issues", - "source": "https://github.com/spatie/flare-client-php/tree/1.4.2" + "source": "https://github.com/spatie/flare-client-php/tree/1.4.3" }, "funding": [ { @@ -8058,20 +8058,20 @@ "type": "github" } ], - "time": "2023-07-28T08:07:24+00:00" + "time": "2023-10-17T15:54:07+00:00" }, { "name": "spatie/ignition", - "version": "1.11.2", + "version": "1.11.3", "source": { "type": "git", "url": "https://github.com/spatie/ignition.git", - "reference": "48b23411ca4bfbc75c75dfc638b6b36159c375aa" + "reference": "3d886de644ff7a5b42e4d27c1e1f67c8b5f00044" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/ignition/zipball/48b23411ca4bfbc75c75dfc638b6b36159c375aa", - "reference": "48b23411ca4bfbc75c75dfc638b6b36159c375aa", + "url": "https://api.github.com/repos/spatie/ignition/zipball/3d886de644ff7a5b42e4d27c1e1f67c8b5f00044", + "reference": "3d886de644ff7a5b42e4d27c1e1f67c8b5f00044", "shasum": "" }, "require": { @@ -8080,19 +8080,19 @@ "php": "^8.0", "spatie/backtrace": "^1.5.3", "spatie/flare-client-php": "^1.4.0", - "symfony/console": "^5.4|^6.0", - "symfony/var-dumper": "^5.4|^6.0" + "symfony/console": "^5.4|^6.0|^7.0", + "symfony/var-dumper": "^5.4|^6.0|^7.0" }, "require-dev": { - "illuminate/cache": "^9.52", + "illuminate/cache": "^9.52|^10.0|^11.0", "mockery/mockery": "^1.4", - "pestphp/pest": "^1.20", + "pestphp/pest": "^1.20|^2.0", "phpstan/extension-installer": "^1.1", "phpstan/phpstan-deprecation-rules": "^1.0", "phpstan/phpstan-phpunit": "^1.0", "psr/simple-cache-implementation": "*", - "symfony/cache": "^6.0", - "symfony/process": "^5.4|^6.0", + "symfony/cache": "^5.4|^6.0|^7.0", + "symfony/process": "^5.4|^6.0|^7.0", "vlucas/phpdotenv": "^5.5" }, "suggest": { @@ -8141,7 +8141,7 @@ "type": "github" } ], - "time": "2023-09-19T15:29:52+00:00" + "time": "2023-10-18T14:09:40+00:00" }, { "name": "spatie/laravel-ignition", diff --git a/database/factories/UserFactory.php b/database/factories/UserFactory.php index 5624f5c..908a624 100644 --- a/database/factories/UserFactory.php +++ b/database/factories/UserFactory.php @@ -2,7 +2,9 @@ namespace Database\Factories; +use Carbon\Carbon; use Illuminate\Database\Eloquent\Factories\Factory; +use Illuminate\Support\Facades\Date; use Illuminate\Support\Str; /** @@ -19,7 +21,8 @@ public function definition() { return [ 'full_name' => fake()->name(), - 'email_verified_at' => null, + 'email' => fake()->email(), + 'email_verified_at' => Carbon::now(), 'points' => 0, 'role' => 'participant', 'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password diff --git a/routes/api.php b/routes/api.php index 30ef843..776b232 100644 --- a/routes/api.php +++ b/routes/api.php @@ -62,7 +62,7 @@ }); }); - Route::prefix('participant')->middleware(['auth:sanctum', 'hasRole:participant']) + Route::prefix('participant')->middleware(['auth:sanctum', 'hasRole:participant', 'verified']) ->controller(ParticipantController::class)->group(function() { Route::prefix('track')->group(function () { Route::get('/', 'get_tracks'); diff --git a/tests/Feature/Admin/UserManagment/CreateJudgeTest.php b/tests/Feature/Admin/UserManagment/CreateJudgeTest.php index 7d17e73..382b1d3 100644 --- a/tests/Feature/Admin/UserManagment/CreateJudgeTest.php +++ b/tests/Feature/Admin/UserManagment/CreateJudgeTest.php @@ -20,6 +20,7 @@ public function test_success_creation_of_judge() $track = Track::factory()->create(); $payload = [ 'full_name' => $this->faker->name(), + 'email' => $this->faker->email(), 'password' => $this->faker->password(), 'track' => $track->type ]; @@ -30,6 +31,7 @@ public function test_success_creation_of_judge() 'success' => true, 'data' => [ 'full_name' => $payload["full_name"], + 'email' => $payload["email"], 'role' => 'judge', 'track' => $track->type, ], @@ -38,6 +40,7 @@ public function test_success_creation_of_judge() $this->assertDatabaseHas('users', [ 'full_name' => $payload['full_name'], + 'email' => $payload['email'], 'role' => 'judge', ]); } @@ -58,6 +61,7 @@ public function test_unsuccess_creation_because_of_missing_data() 'message' => 'Validation failed!', 'data' => [ 'full_name' => ['The full name field is required.'], + 'email' => ['The email field is required.'], 'password' => ['The password field is required.'], 'track' => ['The track field is required.'] ] @@ -75,6 +79,7 @@ public function test_unsuccess_creation_because_of_unexisting_track() { $payload = [ 'full_name' => $this->faker->name(), + 'email' => $this->faker->email(), 'password' => $this->faker->password(), 'track' => $this->faker->text(6) ]; @@ -93,16 +98,17 @@ public function test_unsuccess_creation_because_of_unexisting_track() } /** - * A feature test for unsuccess creation of a judge because of existing user. + * A feature test for unsuccess creation of a judge because of existing user name. * * @return void */ - public function test_unsuccess_creation_because_of_existing_user() + public function test_unsuccess_creation_because_of_existing_user_name() { $user = User::factory()->create(); $track = Track::factory()->create(); $payload = [ 'full_name' => $user->full_name, + 'email' => $this->faker->email(), 'password' => $this->faker->password(), 'track' => $track->type ]; @@ -119,4 +125,33 @@ public function test_unsuccess_creation_because_of_existing_user() $this->assertDatabaseCount('users', 2); } + + /** + * A feature test for unsuccess creation of a judge because of existing user email. + * + * @return void + */ + public function test_unsuccess_creation_because_of_existing_user_email() + { + $user = User::factory()->create(); + $track = Track::factory()->create(); + $payload = [ + 'full_name' => $this->faker->name(), + 'email' => $user->email, + 'password' => $this->faker->password(), + 'track' => $track->type + ]; + + $response = $this->postJson($this->endpoint, $payload); + + $response->assertStatus(400)->assertExactJson([ + 'success' => false, + 'message' => 'Validation failed!', + 'data' => [ + 'email' => ['The email has already been taken.'] + ] + ]); + + $this->assertDatabaseCount('users', 2); + } } diff --git a/tests/Feature/Admin/UserManagment/CreateParticipantTest.php b/tests/Feature/Admin/UserManagment/CreateParticipantTest.php index f9cdde0..78ddd36 100644 --- a/tests/Feature/Admin/UserManagment/CreateParticipantTest.php +++ b/tests/Feature/Admin/UserManagment/CreateParticipantTest.php @@ -20,6 +20,7 @@ public function test_success_creation_of_participant() $track = Track::factory()->create(); $payload = [ 'full_name' => $this->faker->name(), + 'email' => $this->faker->email(), 'password' => $this->faker->password(), 'track' => $track->type ]; @@ -30,6 +31,7 @@ public function test_success_creation_of_participant() 'success' => true, 'data' => [ 'full_name' => $payload["full_name"], + 'email' => $payload["email"], 'points' => 0, 'role' => 'participant', 'email_verified' => false, @@ -63,7 +65,8 @@ public function test_unsuccess_creation_because_of_missing_data() 'data' => [ 'full_name' => ['The full name field is required.'], 'password' => ['The password field is required.'], - 'track' => ['The track field is required.'] + 'track' => ['The track field is required.'], + 'email' => ['The email field is required.'] ] ]); @@ -79,6 +82,7 @@ public function test_unsuccess_creation_because_of_unexisting_track() { $payload = [ 'full_name' => $this->faker->name(), + 'email' => $this->faker->email(), 'password' => $this->faker->password(), 'track' => $this->faker->text(6) ]; @@ -97,16 +101,17 @@ public function test_unsuccess_creation_because_of_unexisting_track() } /** - * A feature test for unsuccess creation of a participant because of existing participant. + * A feature test for unsuccess creation of a participant because of existing participant name. * * @return void */ - public function test_unsuccess_creation_because_of_existing_participant() + public function test_unsuccess_creation_because_of_existing_participant_name() { $user = User::factory()->create(); $track = Track::factory()->create(); $payload = [ 'full_name' => $user->full_name, + 'email' => $this->faker->email(), 'password' => $this->faker->password(), 'track' => $track->type ]; @@ -123,4 +128,33 @@ public function test_unsuccess_creation_because_of_existing_participant() $this->assertDatabaseCount('users', 2); } + + /** + * A feature test for unsuccess creation of a participant because of existing participant email. + * + * @return void + */ + public function test_unsuccess_creation_because_of_existing_participant_email() + { + $user = User::factory()->create(); + $track = Track::factory()->create(); + $payload = [ + 'full_name' => $this->faker->name(), + 'email' => $user->email, + 'password' => $this->faker->password(), + 'track' => $track->type + ]; + + $response = $this->postJson($this->endpoint, $payload); + + $response->assertStatus(400)->assertExactJson([ + 'success' => false, + 'message' => 'Validation failed!', + 'data' => [ + 'email' => ['The email has already been taken.'] + ] + ]); + + $this->assertDatabaseCount('users', 2); + } } diff --git a/tests/Feature/Auth/RegisterUserTest.php b/tests/Feature/Auth/RegisterUserTest.php index 1e7d1ec..76cdca6 100644 --- a/tests/Feature/Auth/RegisterUserTest.php +++ b/tests/Feature/Auth/RegisterUserTest.php @@ -18,6 +18,7 @@ public function test_user_registers_successfully() $track = Track::factory()->create(); $payload = [ 'full_name' => $this->faker->name(), + 'email' => $this->faker->email(), 'password' => $this->faker->password(), 'track' => $track->type, ]; @@ -31,6 +32,7 @@ public function test_user_registers_successfully() 'data' => [ 'user' => [ 'full_name', + 'email', 'points', 'role', 'email_verified', @@ -46,6 +48,7 @@ public function test_user_registers_successfully() $this->assertDatabaseCount('users', 1); $this->assertDatabaseHas('users', [ 'full_name' => $payload["full_name"], + 'email' => $payload["email"], 'points' => 0, 'role' => 'participant', 'track_id' => $track->id, @@ -64,6 +67,7 @@ public function test_unsuccessfull_register_because_of_missing_data() { 'message' => 'Validation of data failed', 'data' => [ 'full_name' => ['The full name field is required.'], + 'email' => ['The email field is required.'], 'password' => ['The password field is required.'], 'track' => ['The track field is required.'], ] @@ -76,6 +80,7 @@ public function test_unsuccessfull_register_because_of_existing_full_name() { $track = Track::factory()->create(); $payload = [ 'full_name' => $user->full_name, + 'email' => $this->faker->email(), 'password' => $this->faker->password(), 'track' => $track->type, ]; @@ -95,10 +100,36 @@ public function test_unsuccessfull_register_because_of_existing_full_name() { $this->assertDatabaseCount('users', 1); } + public function test_unsuccessfull_register_because_of_existing_email() { + $user = User::factory()->create(); + $track = Track::factory()->create(); + $payload = [ + 'full_name' => $this->faker->name(), + 'email' => $user->email, + 'password' => $this->faker->password(), + 'track' => $track->type, + ]; + + $response = $this->postJson('/api/register', $payload); + + $response + ->assertStatus(400) + ->assertExactJson([ + 'success' => false, + 'message' => 'Validation of data failed', + 'data' => [ + 'email' => ['The email has already been taken.'] + ] + ] + ); + $this->assertDatabaseCount('users', 1); + } + public function test_unsuccessfull_register_because_of_unexisting_track() { $payload = [ 'full_name' => $this->faker->name(), + 'email' => $this->faker->email(), 'password' => $this->faker->password(), 'track' => $this->faker->text(8), ];