From 71c8050883af4a4d640aa172620132f524a96767 Mon Sep 17 00:00:00 2001 From: snipe Date: Tue, 28 Jun 2022 19:07:11 -0700 Subject: [PATCH 01/17] Added additional search filters for location API Signed-off-by: snipe --- .../Controllers/Api/LocationsController.php | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/app/Http/Controllers/Api/LocationsController.php b/app/Http/Controllers/Api/LocationsController.php index fa16f8dbb..176033453 100644 --- a/app/Http/Controllers/Api/LocationsController.php +++ b/app/Http/Controllers/Api/LocationsController.php @@ -53,6 +53,30 @@ class LocationsController extends Controller $locations = $locations->TextSearch($request->input('search')); } + if ($request->filled('name')) { + $locations->where('locations.name', '=', $request->input('name')); + } + + if ($request->filled('address')) { + $locations->where('locations.address', '=', $request->input('address')); + } + + if ($request->filled('address2')) { + $locations->where('locations.address2', '=', $request->input('address2')); + } + + if ($request->filled('city')) { + $locations->where('locations.city', '=', $request->input('city')); + } + + if ($request->filled('zip')) { + $locations->where('locations.zip', '=', $request->input('zip')); + } + + if ($request->filled('country')) { + $locations->where('locations.country', '=', $request->input('country')); + } + $offset = (($locations) && (request('offset') > $locations->count())) ? $locations->count() : request('offset', 0); // Check to make sure the limit is not higher than the max allowed From 3df9260ca8c0db639a53059784398b104b3096dd Mon Sep 17 00:00:00 2001 From: snipe Date: Tue, 28 Jun 2022 19:59:45 -0700 Subject: [PATCH 02/17] Added additional filters for api indexes Signed-off-by: snipe --- .../Controllers/Api/CategoriesController.php | 22 ++++++++++ .../Controllers/Api/CompaniesController.php | 5 +++ .../Controllers/Api/ComponentsController.php | 4 ++ .../Controllers/Api/ConsumablesController.php | 4 ++ .../Controllers/Api/DepartmentsController.php | 16 ++++++++ app/Http/Controllers/Api/GroupsController.php | 4 ++ .../Api/ManufacturersController.php | 20 ++++++++++ .../Api/StatuslabelsController.php | 4 ++ .../Controllers/Api/SuppliersController.php | 40 +++++++++++++++++++ 9 files changed, 119 insertions(+) diff --git a/app/Http/Controllers/Api/CategoriesController.php b/app/Http/Controllers/Api/CategoriesController.php index 931056286..164928aab 100644 --- a/app/Http/Controllers/Api/CategoriesController.php +++ b/app/Http/Controllers/Api/CategoriesController.php @@ -32,6 +32,28 @@ class CategoriesController extends Controller $categories = $categories->TextSearch($request->input('search')); } + if ($request->filled('name')) { + $categories->where('name', '=', $request->input('name')); + } + + if ($request->filled('category_type')) { + $categories->where('category_type', '=', $request->input('category_type')); + } + + if ($request->filled('use_default_eula')) { + $categories->where('use_default_eula', '=', $request->input('use_default_eula')); + } + + if ($request->filled('require_acceptance')) { + $categories->where('require_acceptance', '=', $request->input('require_acceptance')); + } + + if ($request->filled('checkin_email')) { + $categories->where('checkin_email', '=', $request->input('checkin_email')); + } + + + // Set the offset to the API call's offset, unless the offset is higher than the actual count of items in which // case we override with the actual count, so we should return 0 items. $offset = (($categories) && ($request->get('offset') > $categories->count())) ? $categories->count() : $request->get('offset', 0); diff --git a/app/Http/Controllers/Api/CompaniesController.php b/app/Http/Controllers/Api/CompaniesController.php index 8efe9f1c7..11f584567 100644 --- a/app/Http/Controllers/Api/CompaniesController.php +++ b/app/Http/Controllers/Api/CompaniesController.php @@ -43,6 +43,11 @@ class CompaniesController extends Controller $companies->TextSearch($request->input('search')); } + if ($request->filled('name')) { + $companies->where('name', '=', $request->input('name')); + } + + // Set the offset to the API call's offset, unless the offset is higher than the actual count of items in which // case we override with the actual count, so we should return 0 items. $offset = (($companies) && ($request->get('offset') > $companies->count())) ? $companies->count() : $request->get('offset', 0); diff --git a/app/Http/Controllers/Api/ComponentsController.php b/app/Http/Controllers/Api/ComponentsController.php index a3938bd39..e72e17eb2 100644 --- a/app/Http/Controllers/Api/ComponentsController.php +++ b/app/Http/Controllers/Api/ComponentsController.php @@ -51,6 +51,10 @@ class ComponentsController extends Controller $components = $components->TextSearch($request->input('search')); } + if ($request->filled('name')) { + $components->where('name', '=', $request->input('name')); + } + if ($request->filled('company_id')) { $components->where('company_id', '=', $request->input('company_id')); } diff --git a/app/Http/Controllers/Api/ConsumablesController.php b/app/Http/Controllers/Api/ConsumablesController.php index 7792663ef..a3aa09736 100644 --- a/app/Http/Controllers/Api/ConsumablesController.php +++ b/app/Http/Controllers/Api/ConsumablesController.php @@ -55,6 +55,10 @@ class ConsumablesController extends Controller $consumables = $consumables->TextSearch(e($request->input('search'))); } + if ($request->filled('name')) { + $consumables->where('name', '=', $request->input('name')); + } + if ($request->filled('company_id')) { $consumables->where('company_id', '=', $request->input('company_id')); } diff --git a/app/Http/Controllers/Api/DepartmentsController.php b/app/Http/Controllers/Api/DepartmentsController.php index 314de2aa5..09e367736 100644 --- a/app/Http/Controllers/Api/DepartmentsController.php +++ b/app/Http/Controllers/Api/DepartmentsController.php @@ -42,6 +42,22 @@ class DepartmentsController extends Controller $departments = $departments->TextSearch($request->input('search')); } + if ($request->filled('name')) { + $departments->where('name', '=', $request->input('name')); + } + + if ($request->filled('company_id')) { + $departments->where('company_id', '=', $request->input('company_id')); + } + + if ($request->filled('manager_id')) { + $departments->where('manager_id', '=', $request->input('manager_id')); + } + + if ($request->filled('location_id')) { + $departments->where('location_id', '=', $request->input('location_id')); + } + // Set the offset to the API call's offset, unless the offset is higher than the actual count of items in which // case we override with the actual count, so we should return 0 items. $offset = (($departments) && ($request->get('offset') > $departments->count())) ? $departments->count() : $request->get('offset', 0); diff --git a/app/Http/Controllers/Api/GroupsController.php b/app/Http/Controllers/Api/GroupsController.php index 5a3fe0642..2c3776062 100644 --- a/app/Http/Controllers/Api/GroupsController.php +++ b/app/Http/Controllers/Api/GroupsController.php @@ -28,6 +28,10 @@ class GroupsController extends Controller $groups = $groups->TextSearch($request->input('search')); } + if ($request->filled('name')) { + $groups->where('name', '=', $request->input('name')); + } + // Set the offset to the API call's offset, unless the offset is higher than the actual count of items in which // case we override with the actual count, so we should return 0 items. $offset = (($groups) && ($request->get('offset') > $groups->count())) ? $groups->count() : $request->get('offset', 0); diff --git a/app/Http/Controllers/Api/ManufacturersController.php b/app/Http/Controllers/Api/ManufacturersController.php index b1a411fc5..e88ef5fed 100644 --- a/app/Http/Controllers/Api/ManufacturersController.php +++ b/app/Http/Controllers/Api/ManufacturersController.php @@ -37,6 +37,26 @@ class ManufacturersController extends Controller $manufacturers = $manufacturers->TextSearch($request->input('search')); } + if ($request->filled('name')) { + $manufacturers->where('name', '=', $request->input('name')); + } + + if ($request->filled('url')) { + $manufacturers->where('url', '=', $request->input('url')); + } + + if ($request->filled('support_url')) { + $manufacturers->where('support_url', '=', $request->input('support_url')); + } + + if ($request->filled('support_phone')) { + $manufacturers->where('support_phone', '=', $request->input('support_phone')); + } + + if ($request->filled('support_email')) { + $manufacturers->where('support_email', '=', $request->input('support_email')); + } + // Set the offset to the API call's offset, unless the offset is higher than the actual count of items in which // case we override with the actual count, so we should return 0 items. $offset = (($manufacturers) && ($request->get('offset') > $manufacturers->count())) ? $manufacturers->count() : $request->get('offset', 0); diff --git a/app/Http/Controllers/Api/StatuslabelsController.php b/app/Http/Controllers/Api/StatuslabelsController.php index f94600e1d..b2522a8a5 100644 --- a/app/Http/Controllers/Api/StatuslabelsController.php +++ b/app/Http/Controllers/Api/StatuslabelsController.php @@ -30,6 +30,10 @@ class StatuslabelsController extends Controller $statuslabels = $statuslabels->TextSearch($request->input('search')); } + if ($request->filled('name')) { + $statuslabels->where('name', '=', $request->input('name')); + } + // if a status_type is passed, filter by that if ($request->filled('status_type')) { diff --git a/app/Http/Controllers/Api/SuppliersController.php b/app/Http/Controllers/Api/SuppliersController.php index 40ff9e8bd..f73f15049 100644 --- a/app/Http/Controllers/Api/SuppliersController.php +++ b/app/Http/Controllers/Api/SuppliersController.php @@ -34,6 +34,46 @@ class SuppliersController extends Controller $suppliers = $suppliers->TextSearch($request->input('search')); } + if ($request->filled('name')) { + $suppliers->where('name', '=', $request->input('name')); + } + + if ($request->filled('address')) { + $suppliers->where('address', '=', $request->input('address')); + } + + if ($request->filled('address2')) { + $suppliers->where('address2', '=', $request->input('address2')); + } + + if ($request->filled('city')) { + $suppliers->where('city', '=', $request->input('city')); + } + + if ($request->filled('zip')) { + $suppliers->where('zip', '=', $request->input('zip')); + } + + if ($request->filled('country')) { + $suppliers->where('country', '=', $request->input('country')); + } + + if ($request->filled('fax')) { + $suppliers->where('fax', '=', $request->input('fax')); + } + + if ($request->filled('email')) { + $suppliers->where('email', '=', $request->input('email')); + } + + if ($request->filled('url')) { + $suppliers->where('url', '=', $request->input('url')); + } + + if ($request->filled('notes')) { + $suppliers->where('notes', '=', $request->input('notes')); + } + // Set the offset to the API call's offset, unless the offset is higher than the actual count of items in which // case we override with the actual count, so we should return 0 items. $offset = (($suppliers) && ($request->get('offset') > $suppliers->count())) ? $suppliers->count() : $request->get('offset', 0); From 1158fa9ea8f257b85b02461f354e7720860f6c3c Mon Sep 17 00:00:00 2001 From: snipe Date: Tue, 28 Jun 2022 21:29:15 -0700 Subject: [PATCH 03/17] 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 8a06a268d..4794fc86a 100644 --- a/app/Http/Controllers/Api/ProfileController.php +++ b/app/Http/Controllers/Api/ProfileController.php @@ -5,10 +5,35 @@ namespace App\Http\Controllers\Api; 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 @@ class ProfileController extends Controller 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 29af43568..0a3ab2b0a 100644 --- a/routes/api.php +++ b/routes/api.php @@ -48,6 +48,27 @@ Route::group(['prefix' => 'v1', 'middleware' => ['api', 'throttle:api']], functi ] )->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 04/17] 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 4794fc86a..a2902b145 100644 --- a/app/Http/Controllers/Api/ProfileController.php +++ b/app/Http/Controllers/Api/ProfileController.php @@ -123,7 +123,7 @@ class ProfileController extends Controller * * @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 0a3ab2b0a..85110a8b7 100644 --- a/routes/api.php +++ b/routes/api.php @@ -58,7 +58,7 @@ Route::group(['prefix' => 'v1', 'middleware' => ['api', 'throttle:api']], functi 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 05/17] 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 000000000..ae6206421 --- /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 06/17] 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 ae6206421..fd0d77a46 100644 --- a/app/Console/Commands/GeneratePersonalAccessToken.php +++ b/app/Console/Commands/GeneratePersonalAccessToken.php @@ -71,6 +71,7 @@ class GeneratePersonalAccessToken extends Command $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 07/17] 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 a2902b145..bc5f16412 100644 --- a/app/Http/Controllers/Api/ProfileController.php +++ b/app/Http/Controllers/Api/ProfileController.php @@ -10,6 +10,7 @@ use Illuminate\Support\Facades\Auth; 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 @@ class ProfileController extends Controller */ 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 @@ class ProfileController extends Controller */ public function deleteApiToken($tokenId) { + if (!Gate::allows('self.api')) { + abort(403); + } + $token = $this->tokenRepository->findForUser( $tokenId, Auth::user()->getAuthIdentifier() ); @@ -125,6 +134,10 @@ class ProfileController extends Controller */ 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 08/17] 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 fd0d77a46..33f9026d0 100644 --- a/app/Console/Commands/GeneratePersonalAccessToken.php +++ b/app/Console/Commands/GeneratePersonalAccessToken.php @@ -57,7 +57,8 @@ class GeneratePersonalAccessToken extends Command 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 09/17] 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 33f9026d0..d5c94d29e 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 @@ class GeneratePersonalAccessToken extends Command 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 10/17] 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 d5c94d29e..7179cfeb8 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 @@ class GeneratePersonalAccessToken extends Command $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 11/17] 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 85110a8b7..0b16f84ad 100644 --- a/routes/api.php +++ b/routes/api.php @@ -48,7 +48,7 @@ Route::group(['prefix' => 'v1', 'middleware' => ['api', 'throttle:api']], functi ] )->name('api.assets.requestable'); - Route::post('personal-access-token', + Route::post('personal-access-tokens', [ Api\ProfileController::class, 'createApiToken' @@ -62,7 +62,7 @@ Route::group(['prefix' => 'v1', 'middleware' => ['api', 'throttle:api']], functi ] )->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 12/17] 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 bc5f16412..ec9b3de3e 100644 --- a/app/Http/Controllers/Api/ProfileController.php +++ b/app/Http/Controllers/Api/ProfileController.php @@ -11,6 +11,7 @@ use Illuminate\Http\Request; use Laravel\Passport\TokenRepository; use Illuminate\Contracts\Validation\Factory as ValidationFactory; use Gate; +use DB; class ProfileController extends Controller { @@ -87,9 +88,14 @@ class ProfileController extends Controller $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 13/17] 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 7179cfeb8..66fc03e27 100644 --- a/app/Console/Commands/GeneratePersonalAccessToken.php +++ b/app/Console/Commands/GeneratePersonalAccessToken.php @@ -7,6 +7,7 @@ use Illuminate\Console\Command; 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 @@ class GeneratePersonalAccessToken extends Command 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 14/17] 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 66fc03e27..098d9678a 100644 --- a/app/Console/Commands/GeneratePersonalAccessToken.php +++ b/app/Console/Commands/GeneratePersonalAccessToken.php @@ -69,16 +69,22 @@ class GeneratePersonalAccessToken extends Command 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 15/17] 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 ec9b3de3e..691efda98 100644 --- a/app/Http/Controllers/Api/ProfileController.php +++ b/app/Http/Controllers/Api/ProfileController.php @@ -145,11 +145,12 @@ class ProfileController extends Controller } $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)); + } From 1debdc47cf6eff2c55b4286f131d629d137f65e6 Mon Sep 17 00:00:00 2001 From: snipe Date: Tue, 28 Jun 2022 22:56:18 -0700 Subject: [PATCH 16/17] Backups endpoint Signed-off-by: snipe --- .../Controllers/Api/SettingsController.php | 42 ++++++++++++++ app/Http/Transformers/BackupsTransformer.php | 55 +++++++++++++++++++ routes/api.php | 14 +++++ 3 files changed, 111 insertions(+) create mode 100644 app/Http/Transformers/BackupsTransformer.php diff --git a/app/Http/Controllers/Api/SettingsController.php b/app/Http/Controllers/Api/SettingsController.php index 9444b5980..d6ac8b826 100644 --- a/app/Http/Controllers/Api/SettingsController.php +++ b/app/Http/Controllers/Api/SettingsController.php @@ -2,6 +2,7 @@ namespace App\Http\Controllers\Api; +use App\Http\Transformers\BackupsTransformer; use Illuminate\Http\Request; use App\Http\Controllers\Controller; use App\Models\Ldap; @@ -265,4 +266,45 @@ class SettingsController extends Controller return (new LoginAttemptsTransformer)->transformLoginAttempts($login_attempt_results, $total); } + + + public function listBackups() { + $settings = Setting::getSettings(); + $path = 'app/backups'; + $backup_files = Storage::files($path); + $files_raw = []; + $count = 0; + + if (count($backup_files) > 0) { + + for ($f = 0; $f < count($backup_files); $f++) { + + // Skip dotfiles like .gitignore and .DS_STORE + if ((substr(basename($backup_files[$f]), 0, 1) != '.')) { + $file_timestamp = Storage::lastModified($backup_files[$f]); + + $files_raw[] = [ + 'filename' => basename($backup_files[$f]), + 'filesize' => Setting::fileSizeConvert(Storage::size($backup_files[$f])), + 'modified_value' => $file_timestamp, + 'modified_display' => date($settings->date_display_format.' '.$settings->time_display_format, $file_timestamp), + + ]; + $count++; + } + + + } + } + + $files = array_reverse($files_raw); + return (new BackupsTransformer())->transformBackups($files, $count); + + } + + + public function downloadBackup($file) { + $path = ''; + return response()->download($path, $file->name, $headers); + } } \ No newline at end of file diff --git a/app/Http/Transformers/BackupsTransformer.php b/app/Http/Transformers/BackupsTransformer.php new file mode 100644 index 000000000..707172489 --- /dev/null +++ b/app/Http/Transformers/BackupsTransformer.php @@ -0,0 +1,55 @@ +transformDatatables($array, $total); + } + + public function transformCategory(Array $file = null) + { + if ($file) { + $array = [ + 'id' => (int) $file->id, + 'name' => e($category->name), + 'image' => ($category->image) ? Storage::disk('public')->url('categories/'.e($category->image)) : null, + 'category_type' => ucwords(e($category->category_type)), + 'has_eula' => ($category->getEula() ? true : false), + 'use_default_eula' => ($category->use_default_eula=='1' ? true : false), + 'eula' => ($category->getEula()), + 'checkin_email' => ($category->checkin_email == '1'), + 'require_acceptance' => ($category->require_acceptance == '1'), + 'item_count' => (int) $category->itemCount(), + 'assets_count' => (int) $category->assets_count, + 'accessories_count' => (int) $category->accessories_count, + 'consumables_count' => (int) $category->consumables_count, + 'components_count' => (int) $category->components_count, + 'licenses_count' => (int) $category->licenses_count, + 'created_at' => Helper::getFormattedDateObject($category->created_at, 'datetime'), + 'updated_at' => Helper::getFormattedDateObject($category->updated_at, 'datetime'), + ]; + + $permissions_array['available_actions'] = [ + 'update' => Gate::allows('update', Category::class), + 'delete' => $category->isDeletable(), + ]; + + $array += $permissions_array; + + return $array; + } + } +} diff --git a/routes/api.php b/routes/api.php index 0b16f84ad..3c0ab53c7 100644 --- a/routes/api.php +++ b/routes/api.php @@ -786,6 +786,20 @@ Route::group(['prefix' => 'v1', 'middleware' => ['api', 'throttle:api']], functi ] )->name('api.settings.mailtest'); + Route::get('backups', + [ + Api\SettingsController::class, + 'listBackups' + ] + )->name('api.settings.backups.index'); + + Route::get('backups/download/{file}', + [ + Api\SettingsController::class, + 'downloadBackup' + ] + )->name('api.settings.backups.download'); + }); Route::resource('settings', From b590f29f33fe7b2d298a7a7a457cc25cd4d23b2e Mon Sep 17 00:00:00 2001 From: snipe Date: Tue, 28 Jun 2022 23:59:13 -0700 Subject: [PATCH 17/17] Attempt a download via API Signed-off-by: snipe --- .../Controllers/Api/SettingsController.php | 17 ++++-- app/Http/Transformers/BackupsTransformer.php | 55 ------------------- 2 files changed, 13 insertions(+), 59 deletions(-) delete mode 100644 app/Http/Transformers/BackupsTransformer.php diff --git a/app/Http/Controllers/Api/SettingsController.php b/app/Http/Controllers/Api/SettingsController.php index d6ac8b826..62380b221 100644 --- a/app/Http/Controllers/Api/SettingsController.php +++ b/app/Http/Controllers/Api/SettingsController.php @@ -2,7 +2,9 @@ namespace App\Http\Controllers\Api; -use App\Http\Transformers\BackupsTransformer; +use App\Helpers\Helper; +use App\Helpers\StorageHelper; +use App\Http\Transformers\DatatablesTransformer; use Illuminate\Http\Request; use App\Http\Controllers\Controller; use App\Models\Ldap; @@ -298,13 +300,20 @@ class SettingsController extends Controller } $files = array_reverse($files_raw); - return (new BackupsTransformer())->transformBackups($files, $count); + return (new DatatablesTransformer)->transformDatatables($files, $count); } public function downloadBackup($file) { - $path = ''; - return response()->download($path, $file->name, $headers); + + $path = 'app/backups'; + if (Storage::exists($path.'/'.$file)) { + $headers = ['ContentType' => 'application/zip']; + return Storage::download($path.'/'.$file, $file, $headers); + } else { + return response()->json(Helper::formatStandardApiResponse('error', null, 'File not found')); + } + } } \ No newline at end of file diff --git a/app/Http/Transformers/BackupsTransformer.php b/app/Http/Transformers/BackupsTransformer.php deleted file mode 100644 index 707172489..000000000 --- a/app/Http/Transformers/BackupsTransformer.php +++ /dev/null @@ -1,55 +0,0 @@ -transformDatatables($array, $total); - } - - public function transformCategory(Array $file = null) - { - if ($file) { - $array = [ - 'id' => (int) $file->id, - 'name' => e($category->name), - 'image' => ($category->image) ? Storage::disk('public')->url('categories/'.e($category->image)) : null, - 'category_type' => ucwords(e($category->category_type)), - 'has_eula' => ($category->getEula() ? true : false), - 'use_default_eula' => ($category->use_default_eula=='1' ? true : false), - 'eula' => ($category->getEula()), - 'checkin_email' => ($category->checkin_email == '1'), - 'require_acceptance' => ($category->require_acceptance == '1'), - 'item_count' => (int) $category->itemCount(), - 'assets_count' => (int) $category->assets_count, - 'accessories_count' => (int) $category->accessories_count, - 'consumables_count' => (int) $category->consumables_count, - 'components_count' => (int) $category->components_count, - 'licenses_count' => (int) $category->licenses_count, - 'created_at' => Helper::getFormattedDateObject($category->created_at, 'datetime'), - 'updated_at' => Helper::getFormattedDateObject($category->updated_at, 'datetime'), - ]; - - $permissions_array['available_actions'] = [ - 'update' => Gate::allows('update', Category::class), - 'delete' => $category->isDeletable(), - ]; - - $array += $permissions_array; - - return $array; - } - } -}