From 78cc47a859310845b0ec92051fbe66aaaf8b0d9b Mon Sep 17 00:00:00 2001 From: Raell Dottin Date: Wed, 24 Mar 2021 17:08:37 -0400 Subject: [PATCH 1/5] Added sanity check to determine if a bind user account is set. (#9340) --- app/Services/LdapAd.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/app/Services/LdapAd.php b/app/Services/LdapAd.php index 01e6f77d5..925927d1f 100644 --- a/app/Services/LdapAd.php +++ b/app/Services/LdapAd.php @@ -129,7 +129,13 @@ class LdapAd extends LdapAdConfiguration $login_username = $username; } - if ($this->ldap->auth()->attempt($login_username, $password, true) === false) { + if ($this->ldapConfig['username'] && $this->ldapConfig['password']) { + $bind_as_user = false; + } else { + $bind_as_user = true; + } + + if ($this->ldap->auth()->attempt($login_username, $password, $bind_as_user) === false) { throw new Exception('Unable to validate user credentials!'); } From 3e934a1b96e7bcd1e09d0a9ffae135e36709f324 Mon Sep 17 00:00:00 2001 From: NMC Date: Mon, 29 Mar 2021 22:09:23 -0400 Subject: [PATCH 2/5] Add a way for a user to override the site skin setting + fix mislabeled comment. (#6891) * Add a way for a user to override the skin setting. * Add site setting to allow user to change the skin. * Fix skin list. Co-authored-by: NMC --- app/Http/Controllers/ProfileController.php | 3 +- app/Http/Controllers/SettingsController.php | 1 + ...019_04_06_060145_add_user_skin_setting.php | 34 +++++++++++++++++++ ..._06_205355_add_setting_allow_user_skin.php | 34 +++++++++++++++++++ resources/lang/en/admin/settings/general.php | 2 ++ resources/macros/macros.php | 31 +++++++++++++++++ resources/views/account/profile.blade.php | 15 +++++--- resources/views/layouts/default.blade.php | 12 +++---- resources/views/settings/branding.blade.php | 13 ++++++- 9 files changed, 132 insertions(+), 13 deletions(-) create mode 100644 database/migrations/2019_04_06_060145_add_user_skin_setting.php create mode 100644 database/migrations/2019_04_06_205355_add_setting_allow_user_skin.php diff --git a/app/Http/Controllers/ProfileController.php b/app/Http/Controllers/ProfileController.php index 35ede27cb..c43317fd7 100755 --- a/app/Http/Controllers/ProfileController.php +++ b/app/Http/Controllers/ProfileController.php @@ -48,10 +48,9 @@ class ProfileController extends Controller $user->last_name = $request->input('last_name'); $user->website = $request->input('website'); $user->gravatar = $request->input('gravatar'); + $user->skin = $request->input('skin'); $user->phone = $request->input('phone'); - - if (!config('app.lock_passwords')) { $user->locale = $request->input('locale', 'en'); } diff --git a/app/Http/Controllers/SettingsController.php b/app/Http/Controllers/SettingsController.php index c449df480..e2f15bca8 100755 --- a/app/Http/Controllers/SettingsController.php +++ b/app/Http/Controllers/SettingsController.php @@ -400,6 +400,7 @@ class SettingsController extends Controller $setting->version_footer = $request->input('version_footer'); $setting->footer_text = $request->input('footer_text'); $setting->skin = $request->input('skin'); + $setting->allow_user_skin = $request->input('allow_user_skin'); $setting->show_url_in_emails = $request->input('show_url_in_emails', '0'); $setting->logo_print_assets = $request->input('logo_print_assets', '0'); diff --git a/database/migrations/2019_04_06_060145_add_user_skin_setting.php b/database/migrations/2019_04_06_060145_add_user_skin_setting.php new file mode 100644 index 000000000..efe1f94c4 --- /dev/null +++ b/database/migrations/2019_04_06_060145_add_user_skin_setting.php @@ -0,0 +1,34 @@ +string('skin')->nullable(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + // Update the users table + Schema::table('users', function ($table) { + $table->dropColumn('skin'); + }); + } +} diff --git a/database/migrations/2019_04_06_205355_add_setting_allow_user_skin.php b/database/migrations/2019_04_06_205355_add_setting_allow_user_skin.php new file mode 100644 index 000000000..537c1f1f4 --- /dev/null +++ b/database/migrations/2019_04_06_205355_add_setting_allow_user_skin.php @@ -0,0 +1,34 @@ +boolean('allow_user_skin')->default(0); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + // Update the users table + Schema::table('settings', function ($table) { + $table->dropColumn('allow_user_skin'); + }); + } +} \ No newline at end of file diff --git a/resources/lang/en/admin/settings/general.php b/resources/lang/en/admin/settings/general.php index 547ffc769..37a2d93e7 100644 --- a/resources/lang/en/admin/settings/general.php +++ b/resources/lang/en/admin/settings/general.php @@ -14,6 +14,8 @@ return array( 'alerts_enabled' => 'Email Alerts Enabled', 'alert_interval' => 'Expiring Alerts Threshold (in days)', 'alert_inv_threshold' => 'Inventory Alert Threshold', + 'allow_user_skin' => 'Allow user skin', + 'allow_user_skin_help_text' => 'Checking this box will allow a user to change the site skin for himself.' , 'asset_ids' => 'Asset IDs', 'audit_interval' => 'Audit Interval', 'audit_interval_help' => 'If you are required to regularly physically audit your assets, enter the interval in months.', diff --git a/resources/macros/macros.php b/resources/macros/macros.php index 0cca0864c..f022aaf04 100644 --- a/resources/macros/macros.php +++ b/resources/macros/macros.php @@ -582,3 +582,34 @@ Form::macro('skin', function ($name = "skin", $selected = null, $class = null) { return $select; }); + +Form::macro('user_skin', function ($name = "skin", $selected = null, $class = null) { + + $formats = array( + '' => 'Site Default', + 'blue' => 'Default Blue', + 'blue-dark' => 'Blue (Dark Mode)', + 'green' => 'Green Dark', + 'green-dark' => 'Green (Dark Mode)', + 'red' => 'Red Dark', + 'red-dark' => 'Red (Dark Mode)', + 'orange' => 'Orange Dark', + 'orange-dark' => 'Orange (Dark Mode)', + 'black' => 'Black', + 'black-dark' => 'Black (Dark Mode)', + 'purple' => 'Purple', + 'purple-dark' => 'Purple (Dark Mode)', + 'yellow' => 'Yellow', + 'yellow-dark' => 'Yellow (Dark Mode)', + 'contrast' => 'High Contrast', + ); + + $select = ''; + return $select; + +}); \ No newline at end of file diff --git a/resources/views/account/profile.blade.php b/resources/views/account/profile.blade.php index 37230bf0b..74d085362 100755 --- a/resources/views/account/profile.blade.php +++ b/resources/views/account/profile.blade.php @@ -58,6 +58,17 @@ + @if ($snipeSettings->allow_user_skin=='1') + +
+ +
+ {!! Form::user_skin('skin', Input::old('skin', $user->skin), 'select2') !!} + {!! $errors->first('skin', ':message') !!} +
+
+ @endif +
@@ -67,8 +78,6 @@
- -
@@ -114,8 +123,6 @@ - - @if ($snipeSettings->two_factor_enabled=='1')
diff --git a/resources/views/layouts/default.blade.php b/resources/views/layouts/default.blade.php index 77788b207..7d12de4c7 100644 --- a/resources/views/layouts/default.blade.php +++ b/resources/views/layouts/default.blade.php @@ -28,13 +28,13 @@ {{-- stylesheets --}} - - + + @if (($snipeSettings) && ($snipeSettings->allow_user_skin==1) && Auth::check() && Auth::user()->present()->skin != '') + + @elseif (($snipeSettings) && ($snipeSettings->skin!='')) - - - - {{-- page level css --}} + @endif + {{-- page level css --}} @stack('css') diff --git a/resources/views/settings/branding.blade.php b/resources/views/settings/branding.blade.php index 5081d25fc..92c503cf2 100644 --- a/resources/views/settings/branding.blade.php +++ b/resources/views/settings/branding.blade.php @@ -150,7 +150,7 @@
- +
{{ Form::label('skin', trans('general.skin')) }} @@ -161,6 +161,17 @@
+ +
+
+ {{ Form::label('allow_user_skin', trans('admin/settings/general.allow_user_skin')) }} +
+
+ {{ Form::checkbox('allow_user_skin', '1', Input::old('allow_user_skin', $setting->allow_user_skin),array('class' => 'minimal')) }} + {{ trans('general.yes') }} +

{{ trans('admin/settings/general.allow_user_skin_help_text') }}

+
+
From 90b7d34c69bf49b60c93c6c8ae8c8a2e2a3ddf9f Mon Sep 17 00:00:00 2001 From: Marc Leuser Date: Tue, 30 Mar 2021 04:41:26 +0200 Subject: [PATCH 3/5] Added #6695: add API endpoint for license seats (#8058) * remove miselading comment line * added dedicated API endpoint for license seats * don't display a seat name via API it makes no sense and we don't have any particular sorting order so the numbering would be inconsistent anyway * reduce amount of IFs * add sanity checks to show() * fix goofed logging logic * add tests for action log entries --- .../Api/LicenseSeatsController.php | 138 +++++++++++++ .../Controllers/Api/LicensesController.php | 44 ----- .../Transformers/LicenseSeatsTransformer.php | 7 +- app/Models/LicenseSeat.php | 10 + resources/views/licenses/view.blade.php | 2 +- routes/api.php | 19 +- tests/api/ApiLicenseSeatsCest.php | 185 ++++++++++++++++++ 7 files changed, 351 insertions(+), 54 deletions(-) create mode 100644 app/Http/Controllers/Api/LicenseSeatsController.php create mode 100644 tests/api/ApiLicenseSeatsCest.php diff --git a/app/Http/Controllers/Api/LicenseSeatsController.php b/app/Http/Controllers/Api/LicenseSeatsController.php new file mode 100644 index 000000000..05cdd9895 --- /dev/null +++ b/app/Http/Controllers/Api/LicenseSeatsController.php @@ -0,0 +1,138 @@ +authorize('view', $license); + + $seats = LicenseSeat::with('license', 'user', 'asset', 'user.department') + ->where('license_seats.license_id', $licenseId); + + $order = $request->input('order') === 'asc' ? 'asc' : 'desc'; + + if ($request->input('sort')=='department') { + $seats->OrderDepartments($order); + } else { + $seats->orderBy('id', $order); + } + + $total = $seats->count(); + $offset = (($seats) && (request('offset') > $total)) ? 0 : request('offset', 0); + $limit = request('limit', 50); + + $seats = $seats->skip($offset)->take($limit)->get(); + + if ($seats) { + return (new LicenseSeatsTransformer)->transformLicenseSeats($seats, $total); + } + } + + return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/licenses/message.does_not_exist')), 200); + } + + /** + * Display the specified resource. + * + * @param int $id + * @return \Illuminate\Http\Response + */ + public function show($licenseId, $seatId) + { + // + $this->authorize('view', License::class); + // sanity checks: + // 1. does the license seat exist? + if (!$licenseSeat = LicenseSeat::find($seatId)) { + return response()->json(Helper::formatStandardApiResponse('error', null, 'Seat not found')); + } + // 2. does the seat belong to the specified license? + if (!$license = $licenseSeat->license()->first() || $license->id != intval($licenseId)) { + return response()->json(Helper::formatStandardApiResponse('error', null, 'Seat does not belong to the specified license')); + } + return (new LicenseSeatsTransformer)->transformLicenseSeat($licenseSeat); + } + + /** + * Update the specified resource in storage. + * + * @param \Illuminate\Http\Request $request + * @param int $licenseId + * @param int $seatId + * @return \Illuminate\Http\Response + */ + public function update(Request $request, $licenseId, $seatId) + { + $this->authorize('checkout', License::class); + + // sanity checks: + // 1. does the license seat exist? + if (!$licenseSeat = LicenseSeat::find($seatId)) { + return response()->json(Helper::formatStandardApiResponse('error', null, 'Seat not found')); + } + // 2. does the seat belong to the specified license? + if (!$license = $licenseSeat->license()->first() || $license->id != intval($licenseId)) { + return response()->json(Helper::formatStandardApiResponse('error', null, 'Seat does not belong to the specified license')); + } + + $oldUser = $licenseSeat->user()->first(); + $oldAsset = $licenseSeat->asset()->first(); + + // attempt to update the license seat + $licenseSeat->fill($request->all()); + $licenseSeat->user_id = Auth::user()->id; + + // check if this update is a checkin operation + // 1. are relevant fields touched at all? + $touched = $licenseSeat->isDirty('assigned_to') || $licenseSeat->isDirty('asset_id'); + // 2. are they cleared? if yes then this is a checkin operation + $is_checkin = ($touched && $licenseSeat->assigned_to === null && $licenseSeat->asset_id === null); + + if (!$touched) { + // nothing to update + return response()->json(Helper::formatStandardApiResponse('success', $licenseSeat, trans('admin/licenses/message.update.success'))); + } + + if ($licenseSeat->save()) { + // the logging functions expect only one "target". if both asset and user are present in the request, + // we simply let assets take precedence over users... + $changes = $licenseSeat->getChanges(); + if (array_key_exists('assigned_to', $changes)) { + $target = $is_checkin ? $oldUser : User::find($changes['assigned_to']); + } + if (array_key_exists('asset_id', $changes)) { + $target = $is_checkin ? $oldAsset : Asset::find($changes['asset_id']); + } + + if ($is_checkin) { + $licenseSeat->logCheckin($target, $request->input('note')); + return response()->json(Helper::formatStandardApiResponse('success', $licenseSeat, trans('admin/licenses/message.update.success'))); + } + + // in this case, relevant fields are touched but it's not a checkin operation. so it must be a checkout operation. + $licenseSeat->logCheckout($request->input('note'), $target); + return response()->json(Helper::formatStandardApiResponse('success', $licenseSeat, trans('admin/licenses/message.update.success'))); + } + + return Helper::formatStandardApiResponse('error', null, $licenseSeat->getErrors()); + } +} diff --git a/app/Http/Controllers/Api/LicensesController.php b/app/Http/Controllers/Api/LicensesController.php index 07bacdc4d..268248ab7 100644 --- a/app/Http/Controllers/Api/LicensesController.php +++ b/app/Http/Controllers/Api/LicensesController.php @@ -237,50 +237,6 @@ class LicensesController extends Controller } return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/licenses/message.assoc_users'))); } - - /** - * Get license seat listing - * - * @author [A. Gianotto] [] - * @since [v1.0] - * @param int $licenseId - * @return \Illuminate\Contracts\View\View - */ - public function seats(Request $request, $licenseId) - { - - if ($license = License::find($licenseId)) { - - $this->authorize('view', $license); - - $seats = LicenseSeat::with('license', 'user', 'asset', 'user.department') - ->where('license_seats.license_id', $licenseId); - - $order = $request->input('order') === 'asc' ? 'asc' : 'desc'; - - if ($request->input('sort')=='department') { - $seats->OrderDepartments($order); - } else { - $seats->orderBy('id', $order); - } - - $offset = (($seats) && (request('offset') > $seats->count())) ? 0 : request('offset', 0); - $limit = request('limit', 50); - - $total = $seats->count(); - - $seats = $seats->skip($offset)->take($limit)->get(); - - if ($seats) { - return (new LicenseSeatsTransformer)->transformLicenseSeats($seats, $total); - } - - } - - return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/licenses/message.does_not_exist')), 200); - - } - /** * Gets a paginated collection for the select2 menus diff --git a/app/Http/Transformers/LicenseSeatsTransformer.php b/app/Http/Transformers/LicenseSeatsTransformer.php index f7685db16..c8454d06e 100644 --- a/app/Http/Transformers/LicenseSeatsTransformer.php +++ b/app/Http/Transformers/LicenseSeatsTransformer.php @@ -20,12 +20,11 @@ class LicenseSeatsTransformer return (new DatatablesTransformer)->transformDatatables($array, $total); } - public function transformLicenseSeat (LicenseSeat $seat, $seat_count) + public function transformLicenseSeat (LicenseSeat $seat, $seat_count=0) { $array = [ 'id' => (int) $seat->id, 'license_id' => (int) $seat->license->id, - 'name' => 'Seat '.$seat_count, 'assigned_user' => ($seat->user) ? [ 'id' => (int) $seat->user->id, 'name'=> e($seat->user->present()->fullName), @@ -49,6 +48,10 @@ class LicenseSeatsTransformer 'user_can_checkout' => (($seat->assigned_to=='') && ($seat->asset_id=='')), ]; + if($seat_count != 0) { + $array['name'] = 'Seat '.$seat_count; + } + $permissions_array['available_actions'] = [ 'checkout' => Gate::allows('checkout', License::class), 'checkin' => Gate::allows('checkin', License::class), diff --git a/app/Models/LicenseSeat.php b/app/Models/LicenseSeat.php index 590409f77..40a53adf8 100755 --- a/app/Models/LicenseSeat.php +++ b/app/Models/LicenseSeat.php @@ -20,6 +20,16 @@ class LicenseSeat extends SnipeModel implements ICompanyableChild protected $guarded = 'id'; protected $table = 'license_seats'; + /** + * The attributes that are mass assignable. + * + * @var array + */ + protected $fillable = [ + 'assigned_to', + 'asset_id' + ]; + use Acceptable; public function getCompanyableParents() diff --git a/resources/views/licenses/view.blade.php b/resources/views/licenses/view.blade.php index a578efc0a..80de54d95 100755 --- a/resources/views/licenses/view.blade.php +++ b/resources/views/licenses/view.blade.php @@ -350,7 +350,7 @@ data-sort-order="asc" data-sort-name="name" class="table table-striped snipe-table" - data-url="{{ route('api.license.seats', $license->id) }}" + data-url="{{ route('api.licenses.seats.index', $license->id) }}" data-export-options='{ "fileName": "export-seats-{{ str_slug($license->name) }}-{{ date('Y-m-d') }}", "ignoreColumn": ["actions","image","change","checkbox","checkincheckout","icon"] diff --git a/routes/api.php b/routes/api.php index 2c6a6ab26..c8f05d2f1 100644 --- a/routes/api.php +++ b/routes/api.php @@ -166,7 +166,6 @@ Route::group(['prefix' => 'v1','namespace' => 'Api', 'middleware' => 'auth:api'] /*--- Departments API ---*/ - /*--- Suppliers API ---*/ Route::group(['prefix' => 'departments'], function () { @@ -496,11 +495,6 @@ Route::group(['prefix' => 'v1','namespace' => 'Api', 'middleware' => 'auth:api'] /*--- Licenses API ---*/ Route::group(['prefix' => 'licenses'], function () { - Route::get('{licenseId}/seats', [ - 'as' => 'api.license.seats', - 'uses' => 'LicensesController@seats' - ]); - Route::get('selectlist', [ 'as' => 'api.licenses.selectlist', @@ -525,7 +519,18 @@ Route::group(['prefix' => 'v1','namespace' => 'Api', 'middleware' => 'auth:api'] ] ); // Licenses resource - + Route::resource('licenses.seats', 'LicenseSeatsController', + [ + 'names' => + [ + 'index' => 'api.licenses.seats.index', + 'show' => 'api.licenses.seats.show', + 'update' => 'api.licenses.seats.update' + ], + 'except' => ['create', 'edit', 'destroy', 'store'], + 'parameters' => ['licenseseat' => 'licenseseat_id'] + ] + ); // Licenseseats resource /*--- Locations API ---*/ diff --git a/tests/api/ApiLicenseSeatsCest.php b/tests/api/ApiLicenseSeatsCest.php new file mode 100644 index 000000000..30c9ccaa9 --- /dev/null +++ b/tests/api/ApiLicenseSeatsCest.php @@ -0,0 +1,185 @@ +user = \App\Models\User::find(1); + $I->haveHttpHeader('Accept', 'application/json'); + $I->amBearerAuthenticated($I->getToken($this->user)); + } + + /** @test */ + public function indexLicenseSeats(ApiTester $I) + { + $I->wantTo('Get a list of license seats for a specific license'); + + // call + $I->sendGET('/licenses/1/seats?limit=10&order=desc'); + $I->seeResponseIsJson(); + $I->seeResponseCodeIs(200); + + // sample verify + $licenseSeats = App\Models\LicenseSeat::where('license_id', 1) + ->orderBy('id','desc')->take(10)->get(); + // pick a random seat + $licenseSeat = $licenseSeats->random(); + // need the index in the original list so that the "name" field is determined correctly + $licenseSeatNumber = 0; + foreach($licenseSeats as $index=>$seat) { + if ($licenseSeat === $seat) { + $licenseSeatNumber = $index+1; + } + } + $I->seeResponseContainsJson($I->removeTimestamps((new LicenseSeatsTransformer)->transformLicenseSeat($licenseSeat, $licenseSeatNumber))); + } + + /** @test */ + public function showLicenseSeat(ApiTester $I) + { + $I->wantTo('Get a license seat'); + + // call + $I->sendGET('/licenses/1/seats/10'); + $I->seeResponseIsJson(); + $I->seeResponseCodeIs(200); + + // sample verify + $licenseSeat = App\Models\LicenseSeat::findOrFail(10); + $I->seeResponseContainsJson($I->removeTimestamps((new LicenseSeatsTransformer)->transformLicenseSeat($licenseSeat))); + } + + /** @test */ + public function checkoutLicenseSeatToUser(ApiTester $I) + { + $I->wantTo('Checkout a license seat to a user'); + + $user = App\Models\User::all()->random(); + $licenseSeat = App\Models\LicenseSeat::all()->random(); + $endpoint = '/licenses/'.$licenseSeat->license_id.'/seats/'.$licenseSeat->id; + + $data = [ + 'assigned_to' => $user->id, + 'note' => 'Test Checkout to User via API' + ]; + + // update + $I->sendPATCH($endpoint, $data); + $I->seeResponseIsJson(); + $I->seeResponseCodeIs(200); + + $response = json_decode($I->grabResponse()); + $I->assertEquals('success', $response->status); + $I->assertEquals(trans('admin/licenses/message.update.success'), $response->messages); + $I->assertEquals($licenseSeat->license_id, $response->payload->license_id); // license id does not change + $I->assertEquals($licenseSeat->id, $response->payload->id); // license seat id does not change + + // verify + $licenseSeat = $licenseSeat->fresh(); + $I->sendGET($endpoint); + $I->seeResponseIsJson(); + $I->seeResponseCodeIs(200); + $I->seeResponseContainsJson($I->removeTimestamps((new LicenseSeatsTransformer)->transformLicenseSeat($licenseSeat))); + + // verify that the last logged action is a checkout + $I->sendGET('/reports/activity?item_type=license&limit=1&item_id='.$licenseSeat->license_id); + $I->seeResponseIsJson(); + $I->seeResponseCodeIs(200); + $I->seeResponseContainsJson([ + "action_type" => "checkout" + ]); + } + + /** @test */ + public function checkoutLicenseSeatToAsset(ApiTester $I) + { + $I->wantTo('Checkout a license seat to an asset'); + + $asset = App\Models\Asset::all()->random(); + $licenseSeat = App\Models\LicenseSeat::all()->random(); + $endpoint = '/licenses/'.$licenseSeat->license_id.'/seats/'.$licenseSeat->id; + + $data = [ + 'asset_id' => $asset->id, + 'note' => 'Test Checkout to Asset via API' + ]; + + // update + $I->sendPATCH($endpoint, $data); + $I->seeResponseIsJson(); + $I->seeResponseCodeIs(200); + + $response = json_decode($I->grabResponse()); + $I->assertEquals('success', $response->status); + $I->assertEquals(trans('admin/licenses/message.update.success'), $response->messages); + $I->assertEquals($licenseSeat->license_id, $response->payload->license_id); // license id does not change + $I->assertEquals($licenseSeat->id, $response->payload->id); // license seat id does not change + + // verify + $licenseSeat = $licenseSeat->fresh(); + $I->sendGET($endpoint); + $I->seeResponseIsJson(); + $I->seeResponseCodeIs(200); + $I->seeResponseContainsJson($I->removeTimestamps((new LicenseSeatsTransformer)->transformLicenseSeat($licenseSeat))); + + // verify that the last logged action is a checkout + $I->sendGET('/reports/activity?item_type=license&limit=1&item_id='.$licenseSeat->license_id); + $I->seeResponseIsJson(); + $I->seeResponseCodeIs(200); + $I->seeResponseContainsJson([ + "action_type" => "checkout" + ]); + } + + /** @test */ + public function checkoutLicenseSeatToUserAndAsset(ApiTester $I) + { + $I->wantTo('Checkout a license seat to a user AND an asset'); + + $asset = App\Models\Asset::all()->random(); + $user = App\Models\User::all()->random(); + $licenseSeat = App\Models\LicenseSeat::all()->random(); + $endpoint = '/licenses/'.$licenseSeat->license_id.'/seats/'.$licenseSeat->id; + + $data = [ + 'asset_id' => $asset->id, + 'assigned_to' => $user->id, + 'note' => 'Test Checkout to User and Asset via API' + ]; + + // update + $I->sendPATCH($endpoint, $data); + $I->seeResponseIsJson(); + $I->seeResponseCodeIs(200); + + $response = json_decode($I->grabResponse()); + $I->assertEquals('success', $response->status); + $I->assertEquals(trans('admin/licenses/message.update.success'), $response->messages); + $I->assertEquals($licenseSeat->license_id, $response->payload->license_id); // license id does not change + $I->assertEquals($licenseSeat->id, $response->payload->id); // license seat id does not change + + // verify + $licenseSeat = $licenseSeat->fresh(); + $I->sendGET($endpoint); + $I->seeResponseIsJson(); + $I->seeResponseCodeIs(200); + $I->seeResponseContainsJson($I->removeTimestamps((new LicenseSeatsTransformer)->transformLicenseSeat($licenseSeat))); + + // verify that the last logged action is a checkout + $I->sendGET('/reports/activity?item_type=license&limit=1&item_id='.$licenseSeat->license_id); + $I->seeResponseIsJson(); + $I->seeResponseCodeIs(200); + $I->seeResponseContainsJson([ + "action_type" => "checkout" + ]); + } +} From c88813bbb82d50b41e8633451288923eae8cf679 Mon Sep 17 00:00:00 2001 From: Shaun McPeck Date: Mon, 29 Mar 2021 21:44:42 -0500 Subject: [PATCH 4/5] set docker user as owner of key symbolic links (#7924) Co-authored-by: Shaun McPeck --- Dockerfile | 1 + Dockerfile.alpine | 1 + 2 files changed, 2 insertions(+) diff --git a/Dockerfile b/Dockerfile index 130c901ea..6665c6df7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -97,6 +97,7 @@ RUN \ && mkdir -p "/var/lib/snipeit/keys" && ln -fs "/var/lib/snipeit/keys/oauth-private.key" "/var/www/html/storage/oauth-private.key" \ && ln -fs "/var/lib/snipeit/keys/oauth-public.key" "/var/www/html/storage/oauth-public.key" \ && chown docker "/var/lib/snipeit/keys/" \ + && chown -h docker "/var/www/html/storage/*.key" \ && chmod +x /var/www/html/artisan \ && echo "Finished setting up application in /var/www/html" diff --git a/Dockerfile.alpine b/Dockerfile.alpine index f70b86fed..24a7c3d1f 100644 --- a/Dockerfile.alpine +++ b/Dockerfile.alpine @@ -57,6 +57,7 @@ RUN \ && mkdir -p "/var/lib/snipeit/dumps" && rm -r "/var/www/html/storage/app/backups" && ln -fs "/var/lib/snipeit/dumps" "/var/www/html/storage/app/backups" \ && mkdir -p "/var/lib/snipeit/keys" && ln -fs "/var/lib/snipeit/keys/oauth-private.key" "/var/www/html/storage/oauth-private.key" \ && ln -fs "/var/lib/snipeit/keys/oauth-public.key" "/var/www/html/storage/oauth-public.key" \ + && chown -h "/var/www/html/storage/*.key" && chown -R apache "/var/lib/snipeit" # Install composer From 06e641b782d13604eff2a41ec6e72d726538c6ba Mon Sep 17 00:00:00 2001 From: Nikolay Didenko <706439+Scorcher@users.noreply.github.com> Date: Tue, 30 Mar 2021 06:05:24 +0300 Subject: [PATCH 5/5] Do not override per table data-id-cookie-table attribute by current route name globally (#7835) --- resources/views/partials/bootstrap-table.blade.php | 1 - 1 file changed, 1 deletion(-) diff --git a/resources/views/partials/bootstrap-table.blade.php b/resources/views/partials/bootstrap-table.blade.php index 2faf968da..a458d3c8a 100644 --- a/resources/views/partials/bootstrap-table.blade.php +++ b/resources/views/partials/bootstrap-table.blade.php @@ -41,7 +41,6 @@ iconsPrefix: 'fa', cookie: true, cookieExpire: '2y', - cookieIdTable: '{{ Route::currentRouteName() }}', mobileResponsive: true, maintainSelected: true, trimOnSearch: false,