From cda4c8528e3a2af69eec01a3139eb58e0222a635 Mon Sep 17 00:00:00 2001 From: Ariful Alam Date: Mon, 19 Sep 2022 22:07:24 +0600 Subject: [PATCH] Refactor admin auth --- app/Http/Controllers/Admin/AuthController.php | 105 ++++++++++++++ .../Controllers/Admin/ProfileController.php | 6 +- app/Http/Controllers/AuthController.php | 80 ----------- app/Providers/RouteServiceProvider.php | 55 ++++++-- public/openapi.yaml | 94 ++++++------- routes/api/admin/auth.php | 7 + routes/api/auth.php | 5 - tests/Feature/Admin/AuthTest.php | 128 ++++++++++++++++++ tests/Feature/AuthTest.php | 96 ------------- 9 files changed, 331 insertions(+), 245 deletions(-) create mode 100644 app/Http/Controllers/Admin/AuthController.php create mode 100644 routes/api/admin/auth.php create mode 100644 tests/Feature/Admin/AuthTest.php diff --git a/app/Http/Controllers/Admin/AuthController.php b/app/Http/Controllers/Admin/AuthController.php new file mode 100644 index 0000000..8d73b07 --- /dev/null +++ b/app/Http/Controllers/Admin/AuthController.php @@ -0,0 +1,105 @@ +authService->loginAdmin($request); + + return Response::json(new LoggedInAdminResource($admin)); + } + + /** + * Logout an admin. + * + * @param Request $request + * @return JsonResponse + */ + #[OAT\Post( + tags: ['adminAuth'], + path: '/api/admin/logout', + summary: 'Logout an admin', + operationId: 'Admin.AuthController.logout', + security: [['BearerToken' => []]], + responses: [ + new OAT\Response( + response: HttpResponse::HTTP_NO_CONTENT, + description: 'No content' + ), + ] + )] + public function logout(Request $request): JsonResponse + { + $this->authService->logoutAdmin($request->user()); + + return Response::json(null, HttpResponse::HTTP_NO_CONTENT); + } +} diff --git a/app/Http/Controllers/Admin/ProfileController.php b/app/Http/Controllers/Admin/ProfileController.php index b4a9fd8..efd9f1e 100644 --- a/app/Http/Controllers/Admin/ProfileController.php +++ b/app/Http/Controllers/Admin/ProfileController.php @@ -5,11 +5,11 @@ use App\Http\Controllers\Controller; use App\Http\Resources\AdminResource; use App\Services\AdminService; -use OpenApi\Attributes as OAT; -use Illuminate\Http\Response as HttpResponse; use Illuminate\Http\JsonResponse; -use Illuminate\Support\Facades\Response; use Illuminate\Http\Request; +use Illuminate\Http\Response as HttpResponse; +use Illuminate\Support\Facades\Response; +use OpenApi\Attributes as OAT; class ProfileController extends Controller { diff --git a/app/Http/Controllers/AuthController.php b/app/Http/Controllers/AuthController.php index 4a33a81..6685b63 100644 --- a/app/Http/Controllers/AuthController.php +++ b/app/Http/Controllers/AuthController.php @@ -3,10 +3,8 @@ namespace App\Http\Controllers; use App\Events\UserSignedUp; -use App\Http\Requests\Auth\AdminLoginRequest; use App\Http\Requests\Auth\LoginRequest; use App\Http\Requests\Auth\SignupRequest; -use App\Http\Resources\LoggedInAdminResource; use App\Http\Resources\LoggedInUserResource; use App\Services\AuthService; use Illuminate\Http\JsonResponse; @@ -145,82 +143,4 @@ public function logout(Request $request): JsonResponse return Response::json(null, HttpResponse::HTTP_NO_CONTENT); } - - /** - * Login an admin. - * - * @param AdminLoginRequest $request - * @return JsonResponse - * - * @throws HttpException - * @throws NotFoundHttpException - */ - #[OAT\Post( - tags: ['adminAuth'], - path: '/api/admin/login', - summary: 'Login an admin', - operationId: 'AuthController.adminLogin', - requestBody: new OAT\RequestBody( - required: true, - content: new OAT\JsonContent(ref: '#/components/schemas/AdminLoginRequest') - - ), - responses: [ - new OAT\Response( - response: HttpResponse::HTTP_OK, - description: 'Ok', - content: new OAT\JsonContent(ref: '#/components/schemas/LoggedInAdminResource') - ), - new OAT\Response( - response: HttpResponse::HTTP_UNPROCESSABLE_ENTITY, - description: 'Unprocessable entity', - content: new OAT\JsonContent(ref: '#/components/schemas/ValidationError') - ), - new OAT\Response( - response: HttpResponse::HTTP_UNAUTHORIZED, - description: 'Unauthorized', - content: new OAT\JsonContent( - properties: [ - new OAT\Property( - property: 'message', - type: 'string', - example: 'Invalid credentials.' - ), - ] - ) - ), - ] - )] - public function adminLogin(AdminLoginRequest $request): JsonResponse - { - $admin = $this->authService->loginAdmin($request); - - return Response::json(new LoggedInAdminResource($admin)); - } - - /** - * Logout an admin. - * - * @param Request $request - * @return JsonResponse - */ - #[OAT\Post( - tags: ['adminAuth'], - path: '/api/admin/logout', - summary: 'Logout an admin', - operationId: 'AuthController.adminLogout', - security: [['BearerToken' => []]], - responses: [ - new OAT\Response( - response: HttpResponse::HTTP_NO_CONTENT, - description: 'No content' - ), - ] - )] - public function adminLogout(Request $request): JsonResponse - { - $this->authService->logoutAdmin($request->user()); - - return Response::json(null, HttpResponse::HTTP_NO_CONTENT); - } } diff --git a/app/Providers/RouteServiceProvider.php b/app/Providers/RouteServiceProvider.php index 1fe8846..6494d44 100644 --- a/app/Providers/RouteServiceProvider.php +++ b/app/Providers/RouteServiceProvider.php @@ -3,6 +3,7 @@ namespace App\Providers; use Illuminate\Cache\RateLimiting\Limit; +use Illuminate\Contracts\Container\BindingResolutionException; use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider; use Illuminate\Http\Request; use Illuminate\Support\Facades\RateLimiter; @@ -32,20 +33,8 @@ public function boot() Route::middleware('api') ->prefix('api') ->group(function () { - require base_path('routes/api/auth.php'); - - Route::group([ - 'middleware' => ['auth:user'], - ], function () { - require base_path('routes/api/profile.php'); - }); - - Route::group([ - 'middleware' => ['auth:admin'], - 'prefix' => 'admin' - ], function () { - require base_path('routes/api/admin/profile.php'); - }); + $this->getUserRoutes(); + $this->getAdminRoutes(); }); Route::middleware('web') @@ -53,6 +42,44 @@ public function boot() }); } + /** + * Get the user routes. + * + * @return void + * + * @throws BindingResolutionException + */ + private function getUserRoutes() + { + require base_path('routes/api/auth.php'); + + Route::group([ + 'middleware' => ['auth:user'], + ], function () { + require base_path('routes/api/profile.php'); + }); + } + + /** + * Get the admin routes. + * + * @return void + */ + private function getAdminRoutes() + { + Route::group([ + 'prefix' => 'admin', + ], function () { + require base_path('routes/api/admin/auth.php'); + + Route::group([ + 'middleware' => ['auth:admin'], + ], function () { + require base_path('routes/api/admin/profile.php'); + }); + }); + } + /** * Configure the rate limiters for the application. * diff --git a/public/openapi.yaml b/public/openapi.yaml index 1fb402e..4aaff51 100644 --- a/public/openapi.yaml +++ b/public/openapi.yaml @@ -8,6 +8,53 @@ servers: url: 'http://localhost' description: 'Local API server' paths: + /api/admin/login: + post: + tags: + - adminAuth + summary: 'Login an admin' + description: 'Login an admin.' + operationId: Admin.AuthController.login + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/AdminLoginRequest' + responses: + '200': + description: Ok + content: + application/json: + schema: + $ref: '#/components/schemas/LoggedInAdminResource' + '422': + description: 'Unprocessable entity' + content: + application/json: + schema: + $ref: '#/components/schemas/ValidationError' + '401': + description: Unauthorized + content: + application/json: + schema: + properties: + message: { type: string, example: 'Invalid credentials.' } + type: object + /api/admin/logout: + post: + tags: + - adminAuth + summary: 'Logout an admin' + description: 'Logout an admin.' + operationId: Admin.AuthController.logout + responses: + '204': + description: 'No content' + security: + - + BearerToken: [] /api/admin/profile: get: tags: @@ -98,53 +145,6 @@ paths: security: - BearerToken: [] - /api/admin/login: - post: - tags: - - adminAuth - summary: 'Login an admin' - description: 'Login an admin.' - operationId: AuthController.adminLogin - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/AdminLoginRequest' - responses: - '200': - description: Ok - content: - application/json: - schema: - $ref: '#/components/schemas/LoggedInAdminResource' - '422': - description: 'Unprocessable entity' - content: - application/json: - schema: - $ref: '#/components/schemas/ValidationError' - '401': - description: Unauthorized - content: - application/json: - schema: - properties: - message: { type: string, example: 'Invalid credentials.' } - type: object - /api/admin/logout: - post: - tags: - - adminAuth - summary: 'Logout an admin' - description: 'Logout an admin.' - operationId: AuthController.adminLogout - responses: - '204': - description: 'No content' - security: - - - BearerToken: [] /api/profile: get: tags: diff --git a/routes/api/admin/auth.php b/routes/api/admin/auth.php new file mode 100644 index 0000000..a0288ae --- /dev/null +++ b/routes/api/admin/auth.php @@ -0,0 +1,7 @@ +middleware('auth:admin'); diff --git a/routes/api/auth.php b/routes/api/auth.php index 620bc5c..dc540b4 100644 --- a/routes/api/auth.php +++ b/routes/api/auth.php @@ -6,8 +6,3 @@ Route::post('/signup', [AuthController::class, 'signup']); Route::post('/login', [AuthController::class, 'login']); Route::post('/logout', [AuthController::class, 'logout'])->middleware('auth:user'); - -Route::group(['prefix' => 'admin'], function () { - Route::post('/login', [AuthController::class, 'adminLogin']); - Route::post('/logout', [AuthController::class, 'adminLogout'])->middleware('auth:admin'); -}); diff --git a/tests/Feature/Admin/AuthTest.php b/tests/Feature/Admin/AuthTest.php new file mode 100644 index 0000000..c85a106 --- /dev/null +++ b/tests/Feature/Admin/AuthTest.php @@ -0,0 +1,128 @@ +routes = [ + 'adminLogin' => '/api/admin/login', + 'adminLogout' => '/api/admin/logout', + ]; + } + + /** + * An admin can login successfully. + * + * @return void + */ + public function testAnAdminCanLoginSuccessfully() + { + $admin = Admin::factory(['password' => Hash::make('123456')])->create(); + + $request = [ + 'email' => $admin->email, + 'password' => '123456', + ]; + + $response = $this + ->json('POST', $this->routes['adminLogin'], $request) + ->assertStatus(Response::HTTP_OK) + ->assertJsonStructure([ + 'admin' => [ + 'id', + 'name', + 'email', + 'avatar_url', + 'created_at', + ], + 'token' => [ + 'access_token', + 'type', + ], + ]) + ->assertJson([ + 'admin' => [ + 'id' => $admin->id, + 'email' => $admin->email, + 'name' => $admin->name, + 'avatar_url' => null, + ], + + ]); + + $this->assertInstanceOf(LoggedInAdminResource::class, $response->getOriginalContent()); + } + + /** + * An admin can not login without required input. + * + * @return void + */ + public function testAnAdminCanNotLoginWithoutRequiredInput() + { + $this + ->json('POST', $this->routes['adminLogin'], []) + ->assertStatus(Response::HTTP_UNPROCESSABLE_ENTITY) + ->assertJsonValidationErrorFor('email') + ->assertJsonValidationErrorFor('password'); + } + + /** + * An admin can not login with invalid credentials. + * + * @return void + */ + public function testAnAdminCanNotLoginWithInvalidCredentials() + { + $admin = Admin::factory(['password' => Hash::make('123456')])->create(); + + $request = [ + 'email' => $admin->email, + 'password' => $this->faker->password(), + ]; + + $this + ->json('POST', $this->routes['adminLogin'], $request) + ->assertStatus(Response::HTTP_UNAUTHORIZED) + ->assertJson([ + 'message' => 'Invalid credentials.', + ]); + } + + /** + * An admin can logout successfully. + * + * @return void + */ + public function testAnAdminCanLogoutSuccessfully() + { + Sanctum::actingAs(Admin::factory()->create(), [], 'admin'); + + $this->json('POST', $this->routes['adminLogout']) + ->assertStatus(Response::HTTP_NO_CONTENT); + } +} diff --git a/tests/Feature/AuthTest.php b/tests/Feature/AuthTest.php index 888401a..a6c26c8 100644 --- a/tests/Feature/AuthTest.php +++ b/tests/Feature/AuthTest.php @@ -3,9 +3,7 @@ namespace Tests\Feature; use App\Events\UserSignedUp; -use App\Http\Resources\LoggedInAdminResource; use App\Http\Resources\LoggedInUserResource; -use App\Models\Admin; use App\Models\User; use Illuminate\Foundation\Testing\WithFaker; use Illuminate\Http\Response; @@ -36,8 +34,6 @@ public function setUp(): void 'signup' => '/api/signup', 'login' => '/api/login', 'logout' => '/api/logout', - 'adminLogin' => '/api/admin/login', - 'adminLogout' => '/api/admin/logout', ]; } @@ -222,96 +218,4 @@ public function testAUserCanLogoutSuccessfully() $this->json('POST', $this->routes['logout']) ->assertStatus(Response::HTTP_NO_CONTENT); } - - /** - * An admin can login successfully. - * - * @return void - */ - public function testAnAdminCanLoginSuccessfully() - { - $admin = Admin::factory(['password' => Hash::make('123456')])->create(); - - $request = [ - 'email' => $admin->email, - 'password' => '123456', - ]; - - $response = $this - ->json('POST', $this->routes['adminLogin'], $request) - ->assertStatus(Response::HTTP_OK) - ->assertJsonStructure([ - 'admin' => [ - 'id', - 'name', - 'email', - 'avatar_url', - 'created_at', - ], - 'token' => [ - 'access_token', - 'type', - ], - ]) - ->assertJson([ - 'admin' => [ - 'id' => $admin->id, - 'email' => $admin->email, - 'name' => $admin->name, - 'avatar_url' => null, - ], - - ]); - - $this->assertInstanceOf(LoggedInAdminResource::class, $response->getOriginalContent()); - } - - /** - * An admin can not login without required input. - * - * @return void - */ - public function testAnAdminCanNotLoginWithoutRequiredInput() - { - $this - ->json('POST', $this->routes['adminLogin'], []) - ->assertStatus(Response::HTTP_UNPROCESSABLE_ENTITY) - ->assertJsonValidationErrorFor('email') - ->assertJsonValidationErrorFor('password'); - } - - /** - * An admin can not login with invalid credentials. - * - * @return void - */ - public function testAnAdminCanNotLoginWithInvalidCredentials() - { - $admin = Admin::factory(['password' => Hash::make('123456')])->create(); - - $request = [ - 'email' => $admin->email, - 'password' => $this->faker->password(), - ]; - - $this - ->json('POST', $this->routes['adminLogin'], $request) - ->assertStatus(Response::HTTP_UNAUTHORIZED) - ->assertJson([ - 'message' => 'Invalid credentials.', - ]); - } - - /** - * An admin can logout successfully. - * - * @return void - */ - public function testAnAdminCanLogoutSuccessfully() - { - Sanctum::actingAs(Admin::factory()->create(), [], 'admin'); - - $this->json('POST', $this->routes['adminLogout']) - ->assertStatus(Response::HTTP_NO_CONTENT); - } }