From fdcc17ca2c33d38a7af505c99d9547e014f5f783 Mon Sep 17 00:00:00 2001 From: Tobias Regnery Date: Wed, 16 Oct 2024 11:18:24 +0200 Subject: [PATCH 1/9] Fix user creation with FullMultipleCompanySupport enabled over API It is currently possible as a non-superuser to create a new user or patch an existing user with arbitrary company over the API if FullMultipleCompanySupport is enabled. Altough a highly unlikely scenario as the user needs permission to create API keys and new users, it is a bug that should get fixed. Add a call to getIdForCurrentUser() to normalize the company_id if FullMultipleCompanySupport is enabled. --- app/Http/Controllers/Api/UsersController.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/Http/Controllers/Api/UsersController.php b/app/Http/Controllers/Api/UsersController.php index 4714b29ea3..a9c8c26f14 100644 --- a/app/Http/Controllers/Api/UsersController.php +++ b/app/Http/Controllers/Api/UsersController.php @@ -14,6 +14,7 @@ use App\Http\Transformers\UsersTransformer; use App\Models\Actionlog; use App\Models\Asset; use App\Models\Accessory; +use App\Models\Company; use App\Models\Consumable; use App\Models\License; use App\Models\User; @@ -371,6 +372,7 @@ class UsersController extends Controller $user = new User; $user->fill($request->all()); + $user->company_id = Company::getIdForCurrentUser($request->input('company_id')); $user->created_by = auth()->id(); if ($request->has('permissions')) { @@ -452,6 +454,10 @@ class UsersController extends Controller $user->fill($request->all()); + if ($request->filled('company_id')) { + $user->company_id = Company::getIdForCurrentUser($request->input('company_id')); + } + if ($user->id == $request->input('manager_id')) { return response()->json(Helper::formatStandardApiResponse('error', null, 'You cannot be your own manager')); } From 6b7af802af41c92a36e77605415869c9e72ec192 Mon Sep 17 00:00:00 2001 From: Brady Wetherington Date: Thu, 10 Oct 2024 13:28:23 +0100 Subject: [PATCH 2/9] Add 'bulk checkout' as one of the bulk actions in the bulk actions toolbar --- .../Assets/BulkAssetsController.php | 13 +++++++---- app/Models/Asset.php | 4 ++++ .../lang/en-US/admin/hardware/message.php | 5 ++++ .../partials/asset-bulk-actions.blade.php | 23 +++++++++++-------- 4 files changed, 31 insertions(+), 14 deletions(-) diff --git a/app/Http/Controllers/Assets/BulkAssetsController.php b/app/Http/Controllers/Assets/BulkAssetsController.php index 1ce08e65e9..b11c36469e 100644 --- a/app/Http/Controllers/Assets/BulkAssetsController.php +++ b/app/Http/Controllers/Assets/BulkAssetsController.php @@ -52,6 +52,10 @@ class BulkAssetsController extends Controller } $asset_ids = $request->input('ids'); + if ($request->input('bulk_actions') === 'checkout') { + $request->session()->flashInput(['selected_assets' => $asset_ids]); + return redirect()->route('hardware.bulkcheckout.show'); + } // Figure out where we need to send the user after the update is complete, and store that in the session $bulk_back_url = request()->headers->get('referer'); @@ -580,8 +584,9 @@ class BulkAssetsController extends Controller if ($target->location_id != '') { $asset->location_id = $target->location_id; - $asset->unsetEventDispatcher(); - $asset->save(); + $asset::withoutEvents(function () use ($asset) { // TODO - I don't know why this is being saved without events + $asset->save(); + }); } if ($error) { @@ -592,10 +597,10 @@ class BulkAssetsController extends Controller if (! $errors) { // Redirect to the new asset page - return redirect()->to('hardware')->with('success', trans('admin/hardware/message.checkout.success')); + return redirect()->to('hardware')->with('success', trans_choice('admin/hardware/message.multi-checkout.success', $asset_ids)); } // Redirect to the asset management page with error - return redirect()->route('hardware.bulkcheckout.show')->with('error', trans('admin/hardware/message.checkout.error'))->withErrors($errors); + return redirect()->route('hardware.bulkcheckout.show')->with('error', trans_choice('admin/hardware/message.multi-checkout.error', $asset_ids))->withErrors($errors); } catch (ModelNotFoundException $e) { return redirect()->route('hardware.bulkcheckout.show')->with('error', $e->getErrors()); } diff --git a/app/Models/Asset.php b/app/Models/Asset.php index ce8b870eb2..54257ed240 100644 --- a/app/Models/Asset.php +++ b/app/Models/Asset.php @@ -2,6 +2,7 @@ namespace App\Models; +use Watson\Validating\ValidationException; use App\Events\CheckoutableCheckedOut; use App\Exceptions\CheckoutNotAllowed; use App\Helpers\Helper; @@ -379,6 +380,9 @@ class Asset extends Depreciable return true; } + $validator = $this->makeValidator($this->getRules()); + + throw new ValidationException($validator, $this); return false; } diff --git a/resources/lang/en-US/admin/hardware/message.php b/resources/lang/en-US/admin/hardware/message.php index 041d32f56c..27877e02f9 100644 --- a/resources/lang/en-US/admin/hardware/message.php +++ b/resources/lang/en-US/admin/hardware/message.php @@ -79,6 +79,11 @@ return [ 'no_assets_selected' => 'You must select at least one asset from the list', ], + 'multi-checkout' => [ + 'error' => 'Asset was not checked out, please try again|Assets were not checked out, please try again', + 'success' => 'Asset checked out successfully.|Assets checked out successfully.', + ], + 'checkin' => [ 'error' => 'Asset was not checked in, please try again', 'success' => 'Asset checked in successfully.', diff --git a/resources/views/partials/asset-bulk-actions.blade.php b/resources/views/partials/asset-bulk-actions.blade.php index b597ad647f..992fb52bba 100644 --- a/resources/views/partials/asset-bulk-actions.blade.php +++ b/resources/views/partials/asset-bulk-actions.blade.php @@ -16,17 +16,20 @@ From 3cf746d7df83ef3e7cfa45c602fc182ebe8f11e3 Mon Sep 17 00:00:00 2001 From: Brady Wetherington Date: Wed, 16 Oct 2024 23:13:32 +0100 Subject: [PATCH 3/9] Rework the bulk checkout to not change how all checkouts work --- .../Controllers/Assets/BulkAssetsController.php | 14 ++++++++------ app/Models/Asset.php | 4 ---- resources/views/hardware/bulk-checkout.blade.php | 7 +++++++ 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/app/Http/Controllers/Assets/BulkAssetsController.php b/app/Http/Controllers/Assets/BulkAssetsController.php index b11c36469e..c27cfe3e0c 100644 --- a/app/Http/Controllers/Assets/BulkAssetsController.php +++ b/app/Http/Controllers/Assets/BulkAssetsController.php @@ -575,22 +575,24 @@ class BulkAssetsController extends Controller } $errors = []; - DB::transaction(function () use ($target, $admin, $checkout_at, $expected_checkin, $errors, $asset_ids, $request) { + DB::transaction(function () use ($target, $admin, $checkout_at, $expected_checkin, &$errors, $asset_ids, $request) { //NOTE: $errors is passsed by reference! foreach ($asset_ids as $asset_id) { $asset = Asset::findOrFail($asset_id); $this->authorize('checkout', $asset); - $error = $asset->checkOut($target, $admin, $checkout_at, $expected_checkin, e($request->get('note')), $asset->name, null); + $checkout_success = $asset->checkOut($target, $admin, $checkout_at, $expected_checkin, e($request->get('note')), $asset->name, null); + //TODO - I think this logic is duplicated in the checkOut method? if ($target->location_id != '') { $asset->location_id = $target->location_id; - $asset::withoutEvents(function () use ($asset) { // TODO - I don't know why this is being saved without events + // TODO - I don't know why this is being saved without events + $asset::withoutEvents(function () use ($asset) { $asset->save(); }); } - if ($error) { - array_merge_recursive($errors, $asset->getErrors()->toArray()); + if (!$checkout_success) { + $errors = array_merge_recursive($errors, $asset->getErrors()->toArray()); } } }); @@ -600,7 +602,7 @@ class BulkAssetsController extends Controller return redirect()->to('hardware')->with('success', trans_choice('admin/hardware/message.multi-checkout.success', $asset_ids)); } // Redirect to the asset management page with error - return redirect()->route('hardware.bulkcheckout.show')->with('error', trans_choice('admin/hardware/message.multi-checkout.error', $asset_ids))->withErrors($errors); + return redirect()->route('hardware.bulkcheckout.show')->withInput()->with('error', trans_choice('admin/hardware/message.multi-checkout.error', $asset_ids))->withErrors($errors); } catch (ModelNotFoundException $e) { return redirect()->route('hardware.bulkcheckout.show')->with('error', $e->getErrors()); } diff --git a/app/Models/Asset.php b/app/Models/Asset.php index 54257ed240..ce8b870eb2 100644 --- a/app/Models/Asset.php +++ b/app/Models/Asset.php @@ -2,7 +2,6 @@ namespace App\Models; -use Watson\Validating\ValidationException; use App\Events\CheckoutableCheckedOut; use App\Exceptions\CheckoutNotAllowed; use App\Helpers\Helper; @@ -380,9 +379,6 @@ class Asset extends Depreciable return true; } - $validator = $this->makeValidator($this->getRules()); - - throw new ValidationException($validator, $this); return false; } diff --git a/resources/views/hardware/bulk-checkout.blade.php b/resources/views/hardware/bulk-checkout.blade.php index 405e5e47cd..39e2cdf10c 100644 --- a/resources/views/hardware/bulk-checkout.blade.php +++ b/resources/views/hardware/bulk-checkout.blade.php @@ -115,5 +115,12 @@ @section('moar_scripts') @include('partials/assets-assigned') + @stop From f50ccbcc492db6c98cabf6dc6752dd99ab82bce7 Mon Sep 17 00:00:00 2001 From: Tobias Regnery Date: Thu, 17 Oct 2024 11:07:28 +0200 Subject: [PATCH 4/9] Fix outdated comment in CompanyableTrait As of commit 5800e8d the user model uses CompanyableTrait so remove this clearly outdated comment --- app/Models/CompanyableTrait.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/app/Models/CompanyableTrait.php b/app/Models/CompanyableTrait.php index df67f2be4f..04a620d8e3 100644 --- a/app/Models/CompanyableTrait.php +++ b/app/Models/CompanyableTrait.php @@ -8,9 +8,6 @@ trait CompanyableTrait * This trait is used to scope models to the current company. To use this scope on companyable models, * we use the "use Companyable;" statement at the top of the mode. * - * We CANNOT USE THIS ON USERS, as it causes an infinite loop and prevents users from logging in, since this scope will be - * applied to the currently logged in (or logging in) user in addition to the user model for viewing lists of users. - * * @see \App\Models\Company\Company::scopeCompanyables() * @return void */ From 0933a2d4ea6d5babd6ef3e0e2f8350c2e088648d Mon Sep 17 00:00:00 2001 From: Marcus Moore Date: Thu, 17 Oct 2024 18:01:48 -0700 Subject: [PATCH 5/9] Remove --parallel flag --- .github/workflows/tests-mysql.yml | 2 +- .github/workflows/tests-postgres.yml | 2 +- .github/workflows/tests-sqlite.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/tests-mysql.yml b/.github/workflows/tests-mysql.yml index 737a86dca3..310414cda6 100644 --- a/.github/workflows/tests-mysql.yml +++ b/.github/workflows/tests-mysql.yml @@ -76,4 +76,4 @@ jobs: DB_DATABASE: snipeit DB_PORT: ${{ job.services.mysql.ports[3306] }} DB_USERNAME: root - run: php artisan test --parallel + run: php artisan test diff --git a/.github/workflows/tests-postgres.yml b/.github/workflows/tests-postgres.yml index 0c361511b8..ae48277be3 100644 --- a/.github/workflows/tests-postgres.yml +++ b/.github/workflows/tests-postgres.yml @@ -74,4 +74,4 @@ jobs: DB_PORT: ${{ job.services.postgresql.ports[5432] }} DB_USERNAME: snipeit DB_PASSWORD: password - run: php artisan test --parallel + run: php artisan test diff --git a/.github/workflows/tests-sqlite.yml b/.github/workflows/tests-sqlite.yml index 49c7c92d8e..8bf0115169 100644 --- a/.github/workflows/tests-sqlite.yml +++ b/.github/workflows/tests-sqlite.yml @@ -58,4 +58,4 @@ jobs: - name: Execute tests (Unit and Feature tests) via PHPUnit env: DB_CONNECTION: sqlite_testing - run: php artisan test --parallel + run: php artisan test From 867fa2f36e02bdfe0ee74ae98b590fd013f6fc7a Mon Sep 17 00:00:00 2001 From: Marcus Moore Date: Mon, 21 Oct 2024 12:40:24 -0700 Subject: [PATCH 6/9] Display file in activity report for accessories --- app/Http/Transformers/ActionlogsTransformer.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/Http/Transformers/ActionlogsTransformer.php b/app/Http/Transformers/ActionlogsTransformer.php index d0605c747b..3fe05c687b 100644 --- a/app/Http/Transformers/ActionlogsTransformer.php +++ b/app/Http/Transformers/ActionlogsTransformer.php @@ -141,6 +141,8 @@ class ActionlogsTransformer if ($actionlog->item) { if ($actionlog->itemType() == 'asset') { $file_url = route('show/assetfile', ['assetId' => $actionlog->item->id, 'fileId' => $actionlog->id]); + } elseif ($actionlog->itemType() == 'accessory') { + $file_url = route('show.accessoryfile', ['accessoryId' => $actionlog->item->id, 'fileId' => $actionlog->id]); } elseif ($actionlog->itemType() == 'license') { $file_url = route('show.licensefile', ['licenseId' => $actionlog->item->id, 'fileId' => $actionlog->id]); } elseif ($actionlog->itemType() == 'user') { @@ -346,4 +348,4 @@ class ActionlogsTransformer -} \ No newline at end of file +} From d7f70146f4a886795ddb118cc2f71bbadded72dc Mon Sep 17 00:00:00 2001 From: Marcus Moore Date: Mon, 21 Oct 2024 13:48:25 -0700 Subject: [PATCH 7/9] Remove extra icon in accessory file upload list --- resources/views/accessories/view.blade.php | 1 - 1 file changed, 1 deletion(-) diff --git a/resources/views/accessories/view.blade.php b/resources/views/accessories/view.blade.php index 1f7e5f0b9a..5e68d98d05 100644 --- a/resources/views/accessories/view.blade.php +++ b/resources/views/accessories/view.blade.php @@ -194,7 +194,6 @@ @foreach ($accessory->uploads as $file) - {{ Helper::filetype_icon($file->filename) }} From ce30863177e499a29f395c9c88ce9c67bd669a74 Mon Sep 17 00:00:00 2001 From: Marcus Moore Date: Mon, 21 Oct 2024 13:57:04 -0700 Subject: [PATCH 8/9] Remove brianium/paratest dependency --- composer.json | 1 - composer.lock | 156 +------------------------------------------------- 2 files changed, 1 insertion(+), 156 deletions(-) diff --git a/composer.json b/composer.json index 6d89312578..d3637c3a4b 100644 --- a/composer.json +++ b/composer.json @@ -74,7 +74,6 @@ "ext-exif": "*" }, "require-dev": { - "brianium/paratest": "^7.0", "fakerphp/faker": "^1.16", "larastan/larastan": "^2.9", "mockery/mockery": "^1.4", diff --git a/composer.lock b/composer.lock index 3f79921b26..0631fc275e 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "3819ab4ef72eb77fabe494c0e746b83b", + "content-hash": "5341bc5be02b3c33e28e46e06dd99f29", "packages": [ { "name": "alek13/slack", @@ -11790,101 +11790,6 @@ ], "time": "2024-04-13T18:00:56+00:00" }, - { - "name": "brianium/paratest", - "version": "v7.3.1", - "source": { - "type": "git", - "url": "https://github.com/paratestphp/paratest.git", - "reference": "551f46f52a93177d873f3be08a1649ae886b4a30" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/paratestphp/paratest/zipball/551f46f52a93177d873f3be08a1649ae886b4a30", - "reference": "551f46f52a93177d873f3be08a1649ae886b4a30", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-pcre": "*", - "ext-reflection": "*", - "ext-simplexml": "*", - "fidry/cpu-core-counter": "^0.5.1 || ^1.0.0", - "jean85/pretty-package-versions": "^2.0.5", - "php": "~8.1.0 || ~8.2.0 || ~8.3.0", - "phpunit/php-code-coverage": "^10.1.7", - "phpunit/php-file-iterator": "^4.1.0", - "phpunit/php-timer": "^6.0", - "phpunit/phpunit": "^10.4.2", - "sebastian/environment": "^6.0.1", - "symfony/console": "^6.3.4 || ^7.0.0", - "symfony/process": "^6.3.4 || ^7.0.0" - }, - "require-dev": { - "doctrine/coding-standard": "^12.0.0", - "ext-pcov": "*", - "ext-posix": "*", - "infection/infection": "^0.27.6", - "phpstan/phpstan": "^1.10.40", - "phpstan/phpstan-deprecation-rules": "^1.1.4", - "phpstan/phpstan-phpunit": "^1.3.15", - "phpstan/phpstan-strict-rules": "^1.5.2", - "squizlabs/php_codesniffer": "^3.7.2", - "symfony/filesystem": "^6.3.1 || ^7.0.0" - }, - "bin": [ - "bin/paratest", - "bin/paratest.bat", - "bin/paratest_for_phpstorm" - ], - "type": "library", - "autoload": { - "psr-4": { - "ParaTest\\": [ - "src/" - ] - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Brian Scaturro", - "email": "scaturrob@gmail.com", - "role": "Developer" - }, - { - "name": "Filippo Tessarotto", - "email": "zoeslam@gmail.com", - "role": "Developer" - } - ], - "description": "Parallel testing for PHP", - "homepage": "https://github.com/paratestphp/paratest", - "keywords": [ - "concurrent", - "parallel", - "phpunit", - "testing" - ], - "support": { - "issues": "https://github.com/paratestphp/paratest/issues", - "source": "https://github.com/paratestphp/paratest/tree/v7.3.1" - }, - "funding": [ - { - "url": "https://github.com/sponsors/Slamdunk", - "type": "github" - }, - { - "url": "https://paypal.me/filippotessarotto", - "type": "paypal" - } - ], - "time": "2023-10-31T09:24:17+00:00" - }, { "name": "clue/ndjson-react", "version": "v1.3.0", @@ -12781,65 +12686,6 @@ }, "time": "2020-07-09T08:09:16+00:00" }, - { - "name": "jean85/pretty-package-versions", - "version": "2.0.6", - "source": { - "type": "git", - "url": "https://github.com/Jean85/pretty-package-versions.git", - "reference": "f9fdd29ad8e6d024f52678b570e5593759b550b4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/f9fdd29ad8e6d024f52678b570e5593759b550b4", - "reference": "f9fdd29ad8e6d024f52678b570e5593759b550b4", - "shasum": "" - }, - "require": { - "composer-runtime-api": "^2.0.0", - "php": "^7.1|^8.0" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": "^3.2", - "jean85/composer-provided-replaced-stub-package": "^1.0", - "phpstan/phpstan": "^1.4", - "phpunit/phpunit": "^7.5|^8.5|^9.4", - "vimeo/psalm": "^4.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Jean85\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Alessandro Lai", - "email": "alessandro.lai85@gmail.com" - } - ], - "description": "A library to get pretty versions strings of installed dependencies", - "keywords": [ - "composer", - "package", - "release", - "versions" - ], - "support": { - "issues": "https://github.com/Jean85/pretty-package-versions/issues", - "source": "https://github.com/Jean85/pretty-package-versions/tree/2.0.6" - }, - "time": "2024-03-08T09:58:59+00:00" - }, { "name": "justinrainbow/json-schema", "version": "5.3.0", From 3f79fd7ea744bd18134785c37b87a2f4bcff0347 Mon Sep 17 00:00:00 2001 From: Marcus Moore Date: Mon, 21 Oct 2024 17:07:40 -0700 Subject: [PATCH 9/9] Add test to ensure icon component does not end in newline --- .../BladeComponents/IconComponentTest.php | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 tests/Unit/BladeComponents/IconComponentTest.php diff --git a/tests/Unit/BladeComponents/IconComponentTest.php b/tests/Unit/BladeComponents/IconComponentTest.php new file mode 100644 index 0000000000..b418b38384 --- /dev/null +++ b/tests/Unit/BladeComponents/IconComponentTest.php @@ -0,0 +1,20 @@ + 'checkout'])->render(); + + $this->assertFalse( + Str::endsWith($renderedTemplateString, PHP_EOL), + 'Newline found at end of icon component. Bootstrap tables will not render if there is a newline at the end of the file.' + ); + } +}