From 1158fa9ea8f257b85b02461f354e7720860f6c3c Mon Sep 17 00:00:00 2001 From: snipe Date: Tue, 28 Jun 2022 21:29:15 -0700 Subject: [PATCH 01/13] Added personal access tokens to api Signed-off-by: snipe --- .../Controllers/Api/ProfileController.php | 95 ++++++++++++++++++- routes/api.php | 21 ++++ 2 files changed, 115 insertions(+), 1 deletion(-) diff --git a/app/Http/Controllers/Api/ProfileController.php b/app/Http/Controllers/Api/ProfileController.php index 8a06a268dd6f..4794fc86a8f0 100644 --- a/app/Http/Controllers/Api/ProfileController.php +++ b/app/Http/Controllers/Api/ProfileController.php @@ -5,10 +5,35 @@ use App\Helpers\Helper; use App\Http\Controllers\Controller; use App\Models\CheckoutRequest; -use Auth; +use Illuminate\Http\Response; +use Illuminate\Support\Facades\Auth; +use Illuminate\Http\Request; +use Laravel\Passport\TokenRepository; +use Illuminate\Contracts\Validation\Factory as ValidationFactory; class ProfileController extends Controller { + + /** + * The token repository implementation. + * + * @var \Laravel\Passport\TokenRepository + */ + protected $tokenRepository; + + /** + * Create a controller instance. + * + * @param \Laravel\Passport\TokenRepository $tokenRepository + * @param \Illuminate\Contracts\Validation\Factory $validation + * @return void + */ + public function __construct(TokenRepository $tokenRepository, ValidationFactory $validation) + { + $this->validation = $validation; + $this->tokenRepository = $tokenRepository; + } + /** * Display a listing of requested assets. * @@ -42,4 +67,72 @@ public function requestedAssets() return $results; } + + + /** + * Delete an API token + * + * @author [A. Gianotto] [] + * @since [v6.0.5] + * + * @return \Illuminate\Http\Response + */ + public function createApiToken(Request $request) { + + $accessTokenName = $request->input('name', 'Auth Token'); + + if ($accessToken = Auth::user()->createToken($accessTokenName)->accessToken) { + return response()->json(Helper::formatStandardApiResponse('success', $accessToken, 'Personal access token '.$accessTokenName.' created successfully')); + } + + return response()->json(Helper::formatStandardApiResponse('error', null, 'Token could not be created.')); + + } + + + /** + * Delete an API token + * + * @author [A. Gianotto] [] + * @since [v6.0.5] + * + * @return \Illuminate\Http\Response + */ + public function deleteApiToken($tokenId) { + + $token = $this->tokenRepository->findForUser( + $tokenId, Auth::user()->getAuthIdentifier() + ); + + if (is_null($token)) { + return new Response('', 404); + } + + $token->revoke(); + + return new Response('', Response::HTTP_NO_CONTENT); + + } + + + /** + * Show user's API tokens + * + * @author [A. Gianotto] [] + * @since [v6.0.5] + * + * @return \Illuminate\Http\Response + */ + public function showTokens(Request $request) { + + $tokens = $this->tokenRepository->forUser(Auth::user()->getAuthIdentifier()); + + return $tokens->load('client')->filter(function ($token) { + return $token->client->personal_access_client && ! $token->revoked; + })->values(); + + } + + + } diff --git a/routes/api.php b/routes/api.php index 29af435687d4..0a3ab2b0a59f 100644 --- a/routes/api.php +++ b/routes/api.php @@ -48,6 +48,27 @@ ] )->name('api.assets.requestable'); + Route::post('personal-access-token', + [ + Api\ProfileController::class, + 'createApiToken' + ] + )->name('api.personal-access-token.create'); + + Route::get('personal-access-tokens', + [ + Api\ProfileController::class, + 'showTokens' + ] + )->name('api.personal-access-token.index'); + + Route::delete('personal-access-token/{tokenId}', + [ + Api\ProfileController::class, + 'deleteApiToken' + ] + )->name('api.personal-access-token.delete'); + }); // end account group From 413487de807e5e27368c1288fa507285edaf1ab6 Mon Sep 17 00:00:00 2001 From: snipe Date: Tue, 28 Jun 2022 21:32:12 -0700 Subject: [PATCH 02/13] Made method naming consistent Signed-off-by: snipe --- app/Http/Controllers/Api/ProfileController.php | 2 +- routes/api.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/Http/Controllers/Api/ProfileController.php b/app/Http/Controllers/Api/ProfileController.php index 4794fc86a8f0..a2902b145e5d 100644 --- a/app/Http/Controllers/Api/ProfileController.php +++ b/app/Http/Controllers/Api/ProfileController.php @@ -123,7 +123,7 @@ public function deleteApiToken($tokenId) { * * @return \Illuminate\Http\Response */ - public function showTokens(Request $request) { + public function showApiTokens(Request $request) { $tokens = $this->tokenRepository->forUser(Auth::user()->getAuthIdentifier()); diff --git a/routes/api.php b/routes/api.php index 0a3ab2b0a59f..85110a8b75ee 100644 --- a/routes/api.php +++ b/routes/api.php @@ -58,7 +58,7 @@ Route::get('personal-access-tokens', [ Api\ProfileController::class, - 'showTokens' + 'showApiTokens' ] )->name('api.personal-access-token.index'); From 112f14759639733e0737ab1af5f41337187d2da1 Mon Sep 17 00:00:00 2001 From: snipe Date: Tue, 28 Jun 2022 22:04:39 -0700 Subject: [PATCH 03/13] Console script to generate API tokens Signed-off-by: snipe --- .../Commands/GeneratePersonalAccessToken.php | 85 +++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 app/Console/Commands/GeneratePersonalAccessToken.php diff --git a/app/Console/Commands/GeneratePersonalAccessToken.php b/app/Console/Commands/GeneratePersonalAccessToken.php new file mode 100644 index 000000000000..ae620642152a --- /dev/null +++ b/app/Console/Commands/GeneratePersonalAccessToken.php @@ -0,0 +1,85 @@ +validation = $validation; + $this->tokenRepository = $tokenRepository; + parent::__construct(); + } + + /** + * Execute the console command. + * + * @return int + */ + public function handle() + { + + if ($this->option('name')=='') { + $accessTokenName = 'CLI Auth Token'; + } + + if ($this->argument('user')=='') { + return false; + } + + if ($user = User::find($this->argument('user'))) { + + if ($this->option('key-only')=='true') { + $this->info($user->createToken($accessTokenName)->accessToken); + } else { + $this->warn('Your API Token has been created. Be sure to copy this token now, as it will not be accessible again.'); + $this->info('API Token Name: '.$accessTokenName); + $this->info('API Token: '.$user->createToken($accessTokenName)->accessToken); + } + } else { + return $this->error('ERROR: Invalid user. API key was not created.'); + } + + + + + } +} From e7de7d1716169d510cd8b81c75fa5479dd995e7f Mon Sep 17 00:00:00 2001 From: snipe Date: Tue, 28 Jun 2022 22:06:46 -0700 Subject: [PATCH 04/13] Show user info as well Signed-off-by: snipe --- app/Console/Commands/GeneratePersonalAccessToken.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/Console/Commands/GeneratePersonalAccessToken.php b/app/Console/Commands/GeneratePersonalAccessToken.php index ae620642152a..fd0d77a46a09 100644 --- a/app/Console/Commands/GeneratePersonalAccessToken.php +++ b/app/Console/Commands/GeneratePersonalAccessToken.php @@ -71,6 +71,7 @@ public function handle() $this->info($user->createToken($accessTokenName)->accessToken); } else { $this->warn('Your API Token has been created. Be sure to copy this token now, as it will not be accessible again.'); + $this->info('API Token User: '.$user->present()->fullName.' ('.$user->username.')'); $this->info('API Token Name: '.$accessTokenName); $this->info('API Token: '.$user->createToken($accessTokenName)->accessToken); } From 9680b02bceae7b1dbaa7ac6afab77348d93c5b49 Mon Sep 17 00:00:00 2001 From: snipe Date: Tue, 28 Jun 2022 22:09:18 -0700 Subject: [PATCH 05/13] Check that the user has permission to create their own API keys Signed-off-by: snipe --- app/Http/Controllers/Api/ProfileController.php | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/app/Http/Controllers/Api/ProfileController.php b/app/Http/Controllers/Api/ProfileController.php index a2902b145e5d..bc5f164128d9 100644 --- a/app/Http/Controllers/Api/ProfileController.php +++ b/app/Http/Controllers/Api/ProfileController.php @@ -10,6 +10,7 @@ use Illuminate\Http\Request; use Laravel\Passport\TokenRepository; use Illuminate\Contracts\Validation\Factory as ValidationFactory; +use Gate; class ProfileController extends Controller { @@ -79,6 +80,10 @@ public function requestedAssets() */ public function createApiToken(Request $request) { + if (!Gate::allows('self.api')) { + abort(403); + } + $accessTokenName = $request->input('name', 'Auth Token'); if ($accessToken = Auth::user()->createToken($accessTokenName)->accessToken) { @@ -100,6 +105,10 @@ public function createApiToken(Request $request) { */ public function deleteApiToken($tokenId) { + if (!Gate::allows('self.api')) { + abort(403); + } + $token = $this->tokenRepository->findForUser( $tokenId, Auth::user()->getAuthIdentifier() ); @@ -125,6 +134,10 @@ public function deleteApiToken($tokenId) { */ public function showApiTokens(Request $request) { + if (!Gate::allows('self.api')) { + abort(403); + } + $tokens = $this->tokenRepository->forUser(Auth::user()->getAuthIdentifier()); return $tokens->load('client')->filter(function ($token) { From 9b6fd7e19ac961b2d3b23664e66306b93cede1d1 Mon Sep 17 00:00:00 2001 From: snipe Date: Tue, 28 Jun 2022 22:11:46 -0700 Subject: [PATCH 06/13] Set $accessTokenName Signed-off-by: snipe --- app/Console/Commands/GeneratePersonalAccessToken.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/Console/Commands/GeneratePersonalAccessToken.php b/app/Console/Commands/GeneratePersonalAccessToken.php index fd0d77a46a09..33f9026d0c13 100644 --- a/app/Console/Commands/GeneratePersonalAccessToken.php +++ b/app/Console/Commands/GeneratePersonalAccessToken.php @@ -57,7 +57,8 @@ public function __construct(TokenRepository $tokenRepository, ValidationFactory public function handle() { - if ($this->option('name')=='') { + $accessTokenName = $this->option('name'); + if ($accessTokenName=='') { $accessTokenName = 'CLI Auth Token'; } From 2f7c04362e56fa78918a8ae2edb75e24a5302aab Mon Sep 17 00:00:00 2001 From: snipe Date: Tue, 28 Jun 2022 22:20:55 -0700 Subject: [PATCH 07/13] Make -kkey-only a flag Signed-off-by: snipe --- app/Console/Commands/GeneratePersonalAccessToken.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/Console/Commands/GeneratePersonalAccessToken.php b/app/Console/Commands/GeneratePersonalAccessToken.php index 33f9026d0c13..d5c94d29ee1c 100644 --- a/app/Console/Commands/GeneratePersonalAccessToken.php +++ b/app/Console/Commands/GeneratePersonalAccessToken.php @@ -19,14 +19,14 @@ class GeneratePersonalAccessToken extends Command protected $signature = 'snipeit:make-api-key {user : The ID of the user to create the token for} {--name= : The name of the new API token} - {--key-only= : Only return the value of the API key}'; + {--key-only : Only return the value of the API key}'; /** * The console command description. * * @var string */ - protected $description = 'Command description'; + protected $description = 'This console command allows you to generate Personal API tokens to be used with the Snipe-IT JSON REST API on behalf of a user.'; /** @@ -68,7 +68,7 @@ public function handle() if ($user = User::find($this->argument('user'))) { - if ($this->option('key-only')=='true') { + if ($this->option('key-only')) { $this->info($user->createToken($accessTokenName)->accessToken); } else { $this->warn('Your API Token has been created. Be sure to copy this token now, as it will not be accessible again.'); From a711e608c9abd8f0718f2a2561f8bef626f2722d Mon Sep 17 00:00:00 2001 From: snipe Date: Tue, 28 Jun 2022 22:26:37 -0700 Subject: [PATCH 08/13] Changed siganture to be clearer Signed-off-by: snipe --- app/Console/Commands/GeneratePersonalAccessToken.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/Console/Commands/GeneratePersonalAccessToken.php b/app/Console/Commands/GeneratePersonalAccessToken.php index d5c94d29ee1c..7179cfeb89f8 100644 --- a/app/Console/Commands/GeneratePersonalAccessToken.php +++ b/app/Console/Commands/GeneratePersonalAccessToken.php @@ -17,7 +17,7 @@ class GeneratePersonalAccessToken extends Command * @var string */ protected $signature = 'snipeit:make-api-key - {user : The ID of the user to create the token for} + {--user_id= : The ID of the user to create the token for.} {--name= : The name of the new API token} {--key-only : Only return the value of the API key}'; @@ -62,11 +62,11 @@ public function handle() $accessTokenName = 'CLI Auth Token'; } - if ($this->argument('user')=='') { - return false; + if ($this->option('user_id')=='') { + return $this->error('ERROR: user_id cannot be blank.'); } - if ($user = User::find($this->argument('user'))) { + if ($user = User::find($this->option('user_id'))) { if ($this->option('key-only')) { $this->info($user->createToken($accessTokenName)->accessToken); From dc27d3bec99874ca51ecab1d80cd8ebfc78e9261 Mon Sep 17 00:00:00 2001 From: snipe Date: Tue, 28 Jun 2022 23:10:40 -0700 Subject: [PATCH 09/13] Change to plural endpoints Signed-off-by: snipe --- routes/api.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/routes/api.php b/routes/api.php index 85110a8b75ee..0b16f84addfe 100644 --- a/routes/api.php +++ b/routes/api.php @@ -48,7 +48,7 @@ ] )->name('api.assets.requestable'); - Route::post('personal-access-token', + Route::post('personal-access-tokens', [ Api\ProfileController::class, 'createApiToken' @@ -62,7 +62,7 @@ ] )->name('api.personal-access-token.index'); - Route::delete('personal-access-token/{tokenId}', + Route::delete('personal-access-tokens/{tokenId}', [ Api\ProfileController::class, 'deleteApiToken' From 52332bc9edebf52a1c88ad97eb715891b66ad13d Mon Sep 17 00:00:00 2001 From: snipe Date: Tue, 28 Jun 2022 23:10:56 -0700 Subject: [PATCH 10/13] Include token ID in payload Signed-off-by: snipe --- app/Http/Controllers/Api/ProfileController.php | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/app/Http/Controllers/Api/ProfileController.php b/app/Http/Controllers/Api/ProfileController.php index bc5f164128d9..ec9b3de3eed8 100644 --- a/app/Http/Controllers/Api/ProfileController.php +++ b/app/Http/Controllers/Api/ProfileController.php @@ -11,6 +11,7 @@ use Laravel\Passport\TokenRepository; use Illuminate\Contracts\Validation\Factory as ValidationFactory; use Gate; +use DB; class ProfileController extends Controller { @@ -87,9 +88,14 @@ public function createApiToken(Request $request) { $accessTokenName = $request->input('name', 'Auth Token'); if ($accessToken = Auth::user()->createToken($accessTokenName)->accessToken) { - return response()->json(Helper::formatStandardApiResponse('success', $accessToken, 'Personal access token '.$accessTokenName.' created successfully')); - } + // Get the ID so we can return that with the payload + $token = DB::table('oauth_access_tokens')->where('user_id', '=', Auth::user()->id)->where('name','=',$accessTokenName)->orderBy('created_at', 'desc')->first(); + $accessTokenData['id'] = $token->id; + $accessTokenData['token'] = $accessToken; + $accessTokenData['name'] = $accessTokenName; + return response()->json(Helper::formatStandardApiResponse('success', $accessTokenData, 'Personal access token '.$accessTokenName.' created successfully')); + } return response()->json(Helper::formatStandardApiResponse('error', null, 'Token could not be created.')); } From 67ad24af084c031a70875f75ebe1e0ca7bc3fde5 Mon Sep 17 00:00:00 2001 From: snipe Date: Tue, 28 Jun 2022 23:13:25 -0700 Subject: [PATCH 11/13] Return token ID in cli Signed-off-by: snipe --- app/Console/Commands/GeneratePersonalAccessToken.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/Console/Commands/GeneratePersonalAccessToken.php b/app/Console/Commands/GeneratePersonalAccessToken.php index 7179cfeb89f8..66fc03e27b84 100644 --- a/app/Console/Commands/GeneratePersonalAccessToken.php +++ b/app/Console/Commands/GeneratePersonalAccessToken.php @@ -7,6 +7,7 @@ use App\Models\User; use Laravel\Passport\TokenRepository; use Illuminate\Contracts\Validation\Factory as ValidationFactory; +use DB; class GeneratePersonalAccessToken extends Command { @@ -71,7 +72,10 @@ public function handle() if ($this->option('key-only')) { $this->info($user->createToken($accessTokenName)->accessToken); } else { + $token = DB::table('oauth_access_tokens')->where('user_id', '=', $user->id)->where('name','=',$accessTokenName)->orderBy('created_at', 'desc')->first(); + $this->warn('Your API Token has been created. Be sure to copy this token now, as it will not be accessible again.'); + $this->info('API Token ID: '.$token->id); $this->info('API Token User: '.$user->present()->fullName.' ('.$user->username.')'); $this->info('API Token Name: '.$accessTokenName); $this->info('API Token: '.$user->createToken($accessTokenName)->accessToken); From 90fe7af863c14a98ff0c4ad2f240d883b74c852e Mon Sep 17 00:00:00 2001 From: snipe Date: Tue, 28 Jun 2022 23:18:16 -0700 Subject: [PATCH 12/13] Small refactor Signed-off-by: snipe --- .../Commands/GeneratePersonalAccessToken.php | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/app/Console/Commands/GeneratePersonalAccessToken.php b/app/Console/Commands/GeneratePersonalAccessToken.php index 66fc03e27b84..098d9678ad56 100644 --- a/app/Console/Commands/GeneratePersonalAccessToken.php +++ b/app/Console/Commands/GeneratePersonalAccessToken.php @@ -69,16 +69,22 @@ public function handle() if ($user = User::find($this->option('user_id'))) { + $createAccessToken = $user->createToken($accessTokenName)->accessToken; + if ($this->option('key-only')) { - $this->info($user->createToken($accessTokenName)->accessToken); + $this->info($createAccessToken); + } else { - $token = DB::table('oauth_access_tokens')->where('user_id', '=', $user->id)->where('name','=',$accessTokenName)->orderBy('created_at', 'desc')->first(); $this->warn('Your API Token has been created. Be sure to copy this token now, as it will not be accessible again.'); - $this->info('API Token ID: '.$token->id); + + if ($token = DB::table('oauth_access_tokens')->where('user_id', '=', $user->id)->where('name','=',$accessTokenName)->orderBy('created_at', 'desc')->first()) { + $this->info('API Token ID: '.$token->id); + } + $this->info('API Token User: '.$user->present()->fullName.' ('.$user->username.')'); $this->info('API Token Name: '.$accessTokenName); - $this->info('API Token: '.$user->createToken($accessTokenName)->accessToken); + $this->info('API Token: '.$createAccessToken); } } else { return $this->error('ERROR: Invalid user. API key was not created.'); From 53bc15900ba293beac08af2268e606358ffd1f75 Mon Sep 17 00:00:00 2001 From: snipe Date: Tue, 28 Jun 2022 23:23:55 -0700 Subject: [PATCH 13/13] Formatted show api Signed-off-by: snipe --- app/Http/Controllers/Api/ProfileController.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/Http/Controllers/Api/ProfileController.php b/app/Http/Controllers/Api/ProfileController.php index ec9b3de3eed8..691efda981ce 100644 --- a/app/Http/Controllers/Api/ProfileController.php +++ b/app/Http/Controllers/Api/ProfileController.php @@ -145,11 +145,12 @@ public function showApiTokens(Request $request) { } $tokens = $this->tokenRepository->forUser(Auth::user()->getAuthIdentifier()); - - return $tokens->load('client')->filter(function ($token) { + $token_values = $tokens->load('client')->filter(function ($token) { return $token->client->personal_access_client && ! $token->revoked; })->values(); + return response()->json(Helper::formatStandardApiResponse('success', $token_values, null)); + }