Compare commits

..

177 Commits

Author SHA1 Message Date
snipe b2056816c9 Use the same string for both radio buttons in bulk user edit
Signed-off-by: snipe <snipe@snipe.net>
2022-03-29 16:45:55 +01:00
snipe 67b0e731e5 Merge pull request #10879 from snipe/rcs/merge_master_into_develop
Merge master back down into develop
2022-03-29 16:40:31 +01:00
snipe a937bd34f6 Merge master back down into develop
Signed-off-by: snipe <snipe@snipe.net>
2022-03-29 16:28:43 +01:00
snipe 7941a88623 Merge pull request #10817 from dampfklon/fix-10810-unaccepted-assets
Fix #10810 unaccepted assets
2022-03-29 12:10:37 +01:00
snipe 80dff41c00 Merge pull request #10843 from ahpaleus/features/add_codeql_to_sdlc
[Feature] Adds CodeQL to the SDLC process
2022-03-29 12:07:44 +01:00
snipe b5f3a357e2 Merge pull request #10850 from Godmartinz/feature/sc-18937/add-manager-to-ldap-sync
This looks good, thanks!
2022-03-29 12:06:01 +01:00
snipe e207a5043e Merge pull request #10737 from Godmartinz/feature/sc-15014/asset-acceptance-and-signed-eula-as-pdf
Fixed #7891, #3019 and #8260 [sc-15014] - added asset acceptance and signed eula as pdf
2022-03-24 20:52:41 +00:00
Godfrey M a1429ce86b removed manager_name from user files 2022-03-24 11:47:13 -07:00
Godfrey M c1f8252388 adds manager field to LDAP sync 2022-03-24 11:24:39 -07:00
snipe edcf109b0f Merge pull request #10861 from andreaci/develop
next_audit_date error on saving asset
2022-03-23 18:44:21 +00:00
andreaci ff5a95a6a4 next_audit_date error on saving asset
Solved error 500 when saving new Asset and no next_audit_date  is defined
2022-03-23 17:23:53 +01:00
Godfrey M 55680762de Merge branch 'develop' into feature/sc-18937/add-manager-to-ldap-sync 2022-03-22 16:33:58 -07:00
Godfrey Martinez fe6a6740db Merge branch 'develop' into feature/sc-15014/asset-acceptance-and-signed-eula-as-pdf 2022-03-22 10:10:56 -07:00
Godfrey M 3e01981576 clean up 2022-03-22 10:07:08 -07:00
Godfrey M bc30eef23e clean up 2022-03-22 10:04:54 -07:00
Godfrey M 62352ef1a1 completes accessory eula agreement blade 2022-03-22 10:04:22 -07:00
snipe e9a59f55ab Merge pull request #10855 from inietov/fixes/exception_boolean_field_check_no_customfields
Validate custom fieldset only if the asset have one
2022-03-22 15:58:01 +00:00
Ivan Nieto Vivanco 98b78837e1 Validate custom fieldset only if the asset have one 2022-03-22 09:38:57 -06:00
snipe 26cababc43 Merge pull request #10851 from inietov/fixes/licenses_assigned_to_assets_dont_follow_users
Fixes #10753 Licenses assigned to assets don't follow users
2022-03-21 21:22:41 +00:00
Ivan Nieto Vivanco 319d816002 Update in the license seat the user that the asset with that license have assigned 2022-03-21 15:06:43 -06:00
Godfrey M 4956eb0410 WIP: needs to be tested. adds ldap_manager field to ldap sync 2022-03-21 11:15:39 -07:00
Godfrey M 220df9710e adds most of the fields for an accessory eula agreement pdf 2022-03-21 09:18:29 -07:00
snipe 820f0a7f04 Merge pull request #10849 from snipe/fixes/10769_return_to_due_for_audit
Fixes #10769 - return to Due for Audit screen after auditing
2022-03-21 14:28:13 +00:00
snipe df60729140 Fixed #10769 - redirect user back to due for audit page
This could probably take a little reworking, since if you are auditing from the Overdue for Audit page, we’d want to take you back *there* instead of the Due for Audit page.

Signed-off-by: snipe <snipe@snipe.net>
2022-03-21 14:27:18 +00:00
snipe c01cc48482 Merge pull request #10848 from snipe/fixes/10819_use_full_url_for_barcodes_in_label_view
Fixed #10819 - use full url for label barcodes
2022-03-21 14:00:56 +00:00
snipe 760b78b584 Fixed #10819 - use full url for label barcodes
I don’t know when/why this would really be necessary, since it would be weird to call /hardware/123/label, but this fixes the broken barcodes

Signed-off-by: snipe <snipe@snipe.net>
2022-03-21 13:57:18 +00:00
Maciej Domanski d53750d389 LdapSync 2022-03-21 10:37:14 +01:00
Maciej Domanski 565f4eb6a1 LdapSync 2022-03-21 10:36:15 +01:00
Maciej Domanski 4a9ff86941 revert LdapSync 2022-03-21 10:31:21 +01:00
Maciej Domanski 70b22784fe Delete LdapSync.php 2022-03-21 10:04:42 +01:00
Maciej Domanski 374bed3f0c info update codeql 2022-03-21 09:51:29 +01:00
snipe ada0932e80 Merge pull request #10841 from snipe/fixes/default_id_suppliers
Fixed #10840 - defaulting to 0 on supplier ID if no value provided
2022-03-19 23:20:36 +00:00
snipe e04ae63d02 Apply the fix to API controller too :(
Signed-off-by: snipe <snipe@snipe.net>
2022-03-19 23:12:48 +00:00
snipe 7db252ade3 Fixed #10840 - defaulting to 0 on supplier ID if no value provided
Signed-off-by: snipe <snipe@snipe.net>
2022-03-19 23:11:42 +00:00
snipe 42220cc566 Merge pull request #10809 from inietov/fixes/customfields_boolean_format_not_saving
Fixes Customfields with boolean format not saving [ch-9973]
2022-03-19 22:10:54 +00:00
Maciej Domanski 41e89123e1 revert from develop 2022-03-17 12:22:37 +01:00
Maciej Domanski e09516d69b codeql sa 2022-03-17 12:10:44 +01:00
Ivan Nieto Vivanco 6ed9ed0e79 Add field type boolean to custom fields list all 2022-03-16 17:21:05 -06:00
Godfrey M b2e78e3382 formats most of the pdf blade 2022-03-16 15:53:32 -07:00
Dampfklon 9818d16834 fix showDeletedFunction by reverting #9508 2022-03-16 20:20:46 +01:00
Godfrey M 5fcc3c39f3 routes eula agreements for download correctly to the asset history 2022-03-16 11:56:56 -07:00
snipe d55c176199 Merge pull request #10831 from snipe/merges/master_down_to_develop_march_16
v6.0.0-RC-5 - Merges master down to develop
2022-03-16 18:35:42 +00:00
snipe c0451fe17a Bumped version to v6.0.0-RC-5
Signed-off-by: snipe <snipe@snipe.net>
2022-03-16 18:10:17 +00:00
snipe 0f95802699 Fixed mis-automerge
Signed-off-by: snipe <snipe@snipe.net>
2022-03-16 18:05:47 +00:00
snipe 9db8bd782d Merging master down into develop
Signed-off-by: snipe <snipe@snipe.net>
2022-03-16 18:02:07 +00:00
snipe 93ff9524d6 Merge pull request #10829 from snipe/features/add_statuslabel_filter_by_type_in_api
Added filter by status_type in StatusLabels API index endpoint
2022-03-16 10:04:58 -07:00
Godfrey M 6386fa1c5e clean up 2022-03-16 09:54:10 -07:00
snipe 89ddbddada Fixed comment
Signed-off-by: snipe <snipe@snipe.net>
2022-03-16 16:53:18 +00:00
snipe 7498fe36e9 Removed extra space because pedantry
Signed-off-by: snipe <snipe@snipe.net>
2022-03-16 16:45:03 +00:00
snipe babf7c064b Added ability to filter status label index endpoint by status type
Signed-off-by: snipe <snipe@snipe.net>
2022-03-16 16:38:45 +00:00
snipe 40a9470770 Merge pull request #10828 from snipe/fixes/10826_parse_error_on_print_all
Fixed #10826 - parse error on translation string for print all assets
2022-03-16 08:54:10 -07:00
snipe 781b426018 Fixed #10826 - parse error on translation string for print all assets
Signed-off-by: snipe <snipe@snipe.net>
2022-03-16 08:50:47 -07:00
snipe f499dcf7c3 Merge pull request #10827 from snipe/fixes/modal_dropdowns_broken
Fixed #10825 - selectlists in modals b0rked
2022-03-16 08:25:29 -07:00
snipe 54d6a4d978 Fixed #10825 - selectlists in modals b0rked
Signed-off-by: snipe <snipe@snipe.net>
2022-03-16 08:23:50 -07:00
Maciej Domanski 44d5b589bb Update phpstan.neon 2022-03-16 15:22:45 +01:00
Maciej Domanski 0a98adb246 Update SA-phpstan.yml 2022-03-16 15:22:27 +01:00
Maciej Domanski 540f783ba2 phpstan-fixes 2022-03-16 15:11:40 +01:00
Maciej Domanski 67ed550743 phpstan update 2022-03-16 12:37:32 +01:00
Maciej Domanski 967ee714a1 sa tooling 2022-03-16 12:14:18 +01:00
Godfrey M b2f7262cd2 adds table structure for asset history 2022-03-14 19:38:36 -07:00
Dampfklon 29e1d657e3 fix missed rename 2022-03-11 21:33:25 +01:00
snipe 84aa26dd50 Merge pull request #10728 from Godmartinz/gh10191-css-dropdownmenu
Fixed #10191 - font color contrast on mobile menu
2022-03-10 13:02:45 -08:00
Godfrey M e1a6a2afc6 adds translation for accept_eula, a bit more logic on the Acceptance Controller that is not working yet 2022-03-10 12:15:50 -08:00
snipe 8984b3a59d Merge pull request #10812 from inietov/fixes/CarbonExceptions_InvalidFormatException_DateTime_develop
Fixes Carbon\Exceptions\InvalidFormatException: DateTime::__construct(): Failed to parse time string develop
2022-03-10 10:40:33 -08:00
Ivan Nieto Vivanco cdc0805fc4 Sanitize dates input in the importer before saving 2022-03-10 12:07:07 -06:00
Ivan Nieto Vivanco 86bd90c564 Show icons in the asset view for boolean customfields 2022-03-09 21:46:19 -06:00
Ivan Nieto Vivanco e104195796 Convert value of the customfield element from string to boolean 2022-03-09 18:49:39 -06:00
snipe d06ef4bdef Bumped lockfile
Signed-off-by: snipe <snipe@snipe.net>
2022-03-08 22:17:46 -08:00
snipe c817daa4da Merge pull request #10767 from uberbrady/fix_double_request_datatable
One attempt to fix the 'datatable double-request' problem.
2022-03-08 20:34:15 -08:00
snipe 1b3631d57d Merge pull request #10806 from snipe/fixes/removed_experimental_content_policies
Removed experimental feature policies
2022-03-08 20:32:39 -08:00
snipe e3d2f7cc96 Missed a few
Signed-off-by: snipe <snipe@snipe.net>
2022-03-08 20:05:17 -08:00
snipe d1358b6249 Removed experimental feature policies
Signed-off-by: snipe <snipe@snipe.net>
2022-03-08 16:58:24 -08:00
Godfrey M 59c583ac74 adds the ability to save eula pdfs to storage, still working on storing them in the DB and exposing them in the UI 2022-03-08 16:42:27 -08:00
snipe 95d6e1a402 Merge pull request #10805 from inietov/fixes/date_localization_not_working_custom_fields_develop
Fixes #8143 Date localization not work in custom fields for develop branch
2022-03-08 16:36:31 -08:00
Ivan Nieto Vivanco 657cfddd3c Add a condition to show customfields as the user wants in localization settings 2022-03-08 18:28:13 -06:00
snipe 9311584f43 Merge pull request #10033 from uberbrady/ldap_troubleshooter_cherrypicked
Fixed [ch14855] - create LDAP troubleshooter
2022-03-07 20:24:11 -08:00
Brady Wetherington ab7bf3c5d4 Get rid of stale commented-out line 2022-03-07 20:22:23 -08:00
Brady Wetherington 0556c7653a Implement a generic timeout function, and run methods that need to run with a timeout using it. 2022-03-07 19:51:21 -08:00
Brady Wetherington b96303cb38 Clean up errors, add new required package to Dockerfile 2022-03-07 19:51:21 -08:00
Brady Wetherington 312a90ce77 Add start of Tracing to LDAP troubleshooter 2022-03-07 19:51:21 -08:00
Brady Wetherington 71b5c0e80f Add new LDAP troubleshoot ldapsearch subcommand, broaden out troubleshooting 2022-03-07 19:51:21 -08:00
Brady Wetherington 519bd00bef WIP - just need to get authed connections to work 2022-03-07 19:51:21 -08:00
Brady Wetherington ffd8f583b4 Got most of the stages working for the LDAP troubleshooter 2022-03-07 19:51:21 -08:00
Brady Wetherington 8cd5ec6799 Most of the host-port-tls discovery stuff is in-place now 2022-03-07 19:51:20 -08:00
Brady Wetherington ef0e9a3c93 WIP: Enhancing stage 2 of LDAP troubleshooter 2022-03-07 19:51:20 -08:00
Brady Wetherington 8aa975e959 First pass at Artisan-based LDAP troubleshooter 2022-03-07 19:51:20 -08:00
Brady Wetherington 8d49d35463 Remove another data-toggle="table" - possibly the last one 2022-03-07 12:52:57 -08:00
Brady Wetherington 6f3e9033c2 One attempt to fix the 'datatable double-request' problem. 2022-03-07 12:52:57 -08:00
snipe 0c520476a3 Removed namespace
Signed-off-by: snipe <snipe@snipe.net>
2022-03-04 12:30:47 -08:00
snipe ba010618f7 Throttle got merged out - re-adding it
Signed-off-by: snipe <snipe@snipe.net>
2022-03-04 12:15:09 -08:00
snipe 2d7a8b5e15 Merge pull request #10776 from snipe/added_trim_to_email_and_username
Added trim to email and username
2022-03-04 11:57:33 -08:00
snipe 1dabd6cc71 Merge pull request #10777 from snipe/added_remote_to_bulk_user_edit
Add remote user setting to bulk edit
2022-03-04 10:35:07 -08:00
snipe 9a358087ec Add remote user to bulk edit
Signed-off-by: snipe <snipe@snipe.net>
2022-03-04 06:47:23 -08:00
snipe b6a1d245e8 Added trim to CC email as well
Signed-off-by: snipe <snipe@snipe.net>
2022-03-04 06:20:34 -08:00
snipe 67134ca387 Do a trim() before inserting/updating
Signed-off-by: snipe <snipe@snipe.net>
2022-03-04 06:18:52 -08:00
snipe 0dfc27f56e Added remote to allowed_columns for sorting
Signed-off-by: snipe <snipe@snipe.net>
2022-03-04 05:44:46 -08:00
snipe eb8b4a2aa5 Merge pull request #10775 from snipe/features/add_remote_checkbox_to_user
Added remote checkbox to user
2022-03-04 05:39:23 -08:00
snipe 06a05e6340 Added remote to view
Signed-off-by: snipe <snipe@snipe.net>
2022-03-04 05:37:28 -08:00
snipe 43c1949092 Add remote option to user
Signed-off-by: snipe <snipe@snipe.net>
2022-03-04 05:35:26 -08:00
snipe a9069e65e5 Don’t show groups in menu if there are none yet
Signed-off-by: snipe <snipe@snipe.net>
2022-03-04 04:39:39 -08:00
snipe 1400c610a1 Use correct button translation
Signed-off-by: snipe <snipe@snipe.net>
2022-03-04 04:22:44 -08:00
snipe 67ed0d91c0 Handle file_get_contents more gracefully
Signed-off-by: snipe <snipe@snipe.net>
2022-03-04 02:54:37 -08:00
snipe 349715576a Bumped RC version
Signed-off-by: snipe <snipe@snipe.net>
2022-03-03 22:23:24 -08:00
snipe 0d787fcc94 Updated language files
Signed-off-by: snipe <snipe@snipe.net>
2022-03-03 22:21:44 -08:00
snipe b876d0abb0 Merge remote-tracking branch 'origin/master' into develop
Signed-off-by: snipe <snipe@snipe.net>

# Conflicts:
#	.env.example
#	app/Http/Controllers/Auth/LoginController.php
#	app/Http/Kernel.php
#	app/Http/Transformers/ActionlogsTransformer.php
#	app/Importer/AssetImporter.php
#	app/Models/Accessory.php
#	app/Models/Consumable.php
#	app/Presenters/AccessoryPresenter.php
#	app/Presenters/ComponentPresenter.php
#	app/Presenters/ConsumablePresenter.php
#	app/Providers/AuthServiceProvider.php
#	composer.json
#	composer.lock
#	config/app.php
#	config/cors.php
#	config/version.php
#	package-lock.json
#	public/js/build/app.js
#	public/js/build/app.js.LICENSE.txt
#	public/js/dist/all.js
#	public/mix-manifest.json
#	resources/views/accessories/view.blade.php
#	resources/views/consumables/view.blade.php
#	resources/views/settings/saml.blade.php
#	routes/api.php
2022-03-03 21:59:38 -08:00
snipe afd83311aa Fixed version number (vV)
Signed-off-by: snipe <snipe@snipe.net>
2022-03-03 20:58:54 -08:00
snipe 29a2e80984 Merge pull request #10771 from uberbrady/retry_fix_bad_metadata_display
Retry fix bad metadata display
2022-03-03 20:49:30 -08:00
snipe fa576bc78f Bumped version
Signed-off-by: snipe <snipe@snipe.net>
2022-03-03 20:45:34 -08:00
snipe 3e22dce117 Merge pull request #10774 from snipe/features/added_notes_to_accessories_etc
Fixed #6918 - added notes to accessories, components, consumables
2022-03-03 20:41:18 -08:00
snipe 9adcf58ae5 Added notes migration
Signed-off-by: snipe <snipe@snipe.net>
2022-03-03 20:30:58 -08:00
snipe 5266b2c71f Added notes field to view blades
Signed-off-by: snipe <snipe@snipe.net>
2022-03-03 20:30:27 -08:00
snipe 94dded3785 Added notes field to presenters for table view show/hide columns
Signed-off-by: snipe <snipe@snipe.net>
2022-03-03 20:30:07 -08:00
snipe ac8a7d0bc9 Made notes field fillable and searchable
Signed-off-by: snipe <snipe@snipe.net>
2022-03-03 20:29:48 -08:00
snipe 6fca8350f9 Added notes field to transformers
Signed-off-by: snipe <snipe@snipe.net>
2022-03-03 20:29:35 -08:00
snipe 9acb5413f6 Added notes field to controllers
Signed-off-by: snipe <snipe@snipe.net>
2022-03-03 20:29:25 -08:00
snipe 08a2fe4edb Merge pull request #10773 from snipe/features/adds_ldap_import_and_assets_count_to_user_api
Features/adds ldap import and assets count to user api
2022-03-03 19:15:14 -08:00
snipe 6abe8f296b We don’t need the users table here
Signed-off-by: snipe <snipe@snipe.net>
2022-03-03 19:12:03 -08:00
snipe a53a67be4a Added consumables, licenses, accessories count filters too
Signed-off-by: snipe <snipe@snipe.net>
2022-03-03 19:08:08 -08:00
snipe b72cac3511 Adds ldap_import and assets_count filter to user API endpoint
Signed-off-by: snipe <snipe@snipe.net>
2022-03-03 19:03:35 -08:00
snipe 698b7301b6 Merge pull request #10772 from snipe/fixes/use_correct_gate_for_maintainances
Use “update” instead of edit in gate
2022-03-03 18:50:33 -08:00
snipe c940d36fff Updated maintenance gate in API
Signed-off-by: snipe <snipe@snipe.net>
2022-03-03 18:49:54 -08:00
snipe cd12028845 Use “update” instead of edit in gate
Signed-off-by: snipe <snipe@snipe.net>
2022-03-03 18:47:20 -08:00
Brady Wetherington 3225605ef3 Handling the old weirdly-formatted data wasn't quite working. 2022-03-03 18:17:29 -08:00
Brady Wetherington 31dde20a2b Actually, re-introduce this code comment as it's still relevant and tricky 2022-03-03 15:48:04 -08:00
Brady Wetherington aa36ebc947 Merge branch 'fix_bad_metadata_display' into retry_fix_bad_metadata_display 2022-03-03 15:46:19 -08:00
Brady Wetherington 92434fa943 Revert "Revert "refactor and clean up attribute-changing logic for assets""
This reverts commit bdc737ce0c.
2022-03-03 15:45:54 -08:00
Godfrey M 14bd07e97d gerge branchevelop' into gh10191-css-dropdownmenu 2022-03-03 10:14:30 -08:00
Godfrey M 4435ea95fd removes changes to mix-manifest 2022-03-03 09:47:11 -08:00
Godfrey M f115385243 C&P mix-manifest to be tracked 2022-03-03 09:32:05 -08:00
Godfrey M 708dc08b4f mixes color contrast on dropdown menu from navbar
on file
2022-03-03 09:27:52 -08:00
Brady Wetherington 0164354463 Fix for the weird error that blew up the demo when my PR got merged 2022-03-02 15:15:16 -08:00
snipe bdc737ce0c Revert "refactor and clean up attribute-changing logic for assets"
This reverts commit 290baf1c8d.

Signed-off-by: snipe <snipe@snipe.net>
2022-03-02 14:45:14 -08:00
snipe 733b3a7550 Merge pull request #10682 from uberbrady/fix_bad_metadata_display
Fixed weird JSON-handling on log_meta updates
2022-03-02 14:36:38 -08:00
snipe 16fd109540 Merge pull request #10417 from inietov/fixes/trying_to_get_property_id_of_nonobject
Fixes trying to get property 'id' of non-object when running develop seeders.
2022-03-02 14:35:53 -08:00
snipe 2b4ee4827f Merge branch 'develop' into fixes/trying_to_get_property_id_of_nonobject 2022-03-02 14:35:35 -08:00
snipe 3339a691e1 Merge pull request #10721 from snipe/features/adds_users_consumables_endpoint
Added consumables endpoint to user API
2022-03-02 14:33:45 -08:00
snipe dc1e0ecdb7 Merge pull request #10739 from insert-waffle/fixes/prevent_double_values_email
Feature: Prevent showing of double values in a checkin or checkout email
2022-03-02 14:28:22 -08:00
snipe 2eef43e8bf Applies develop fix to master for location drop downs 2022-03-01 12:43:35 -08:00
snipe 5d1cd89838 Merge pull request #10751 from aranar-pro/bug_fix/location-drop-down-#10701
Fix for location and model drop down with granular permissions.
2022-02-28 16:33:12 -08:00
Andrew Roth 74248a4314 removing composer.lock from PR 2022-02-28 19:30:32 -05:00
Andrew Roth 2e60420aeb Fix for location and model drop down with granular permissions. 2022-02-28 17:38:38 -05:00
Jens Maes 9b88f650d2 Prevents showing double values
- If the asset name and tag are the same, the tag does not get shown in an email.
- If the model name is the same as the model number (when importing from LanSweeper for example), do not show the model number.
2022-02-25 08:19:46 +01:00
snipe 336d8e6574 Updated version 2022-02-24 18:17:15 -08:00
Godfrey M 533670f3f1 faulty method for converting asset acceptance to pdf 2022-02-24 14:50:16 -08:00
snipe a5c16c6a9c Update @insert-waffle as a contributor 2022-02-24 14:47:48 -08:00
snipe 1ee37212f1 Merge branch 'develop' of https://github.com/snipe/snipe-it into develop 2022-02-24 14:47:14 -08:00
snipe ed43d36895 Updated assets
Signed-off-by: snipe <snipe@snipe.net>
2022-02-24 14:47:09 -08:00
snipe d243b822e7 Merge pull request #10729 from insert-waffle/fix-10708
Fixes 10708: removed line height of manufacturer and changed size of fa-hdd to 2x
2022-02-24 14:46:51 -08:00
snipe dfbff27483 Updated sample CSVs
Signed-off-by: snipe <snipe@snipe.net>
2022-02-24 14:19:35 -08:00
snipe 24ce34c8d7 Updated assets
Signed-off-by: snipe <snipe@snipe.net>
2022-02-24 14:19:27 -08:00
snipe 7abb1f960c Merge branch 'master' of https://github.com/snipe/snipe-it 2022-02-24 14:16:00 -08:00
snipe ec7df11d73 Merge pull request #10736 from inietov/fixes/imported_assets_duplicate_model_notes_from_assets_master
Separate notes on assets and asset models on master branch [sc-18810]
2022-02-24 14:15:48 -08:00
snipe f077d096e2 Merge pull request #10735 from inietov/fixes/imported_assets_duplicate_model_notes_from_assets
Separate notes on assets and asset models [sc-18810]
2022-02-24 14:15:13 -08:00
Ivan Nieto Vivanco 64fa7e23fc Separate notes on assets and asset models 2022-02-24 16:05:03 -06:00
snipe 93ad59466b Fixed typo
Signed-off-by: snipe <snipe@snipe.net>
2022-02-24 13:52:33 -08:00
Ivan Nieto Vivanco 577dc6b02c Separate notes on assets and asset models 2022-02-24 15:50:59 -06:00
snipe 4b7f45a15e Merge pull request #10734 from snipe/fixes/api_throttling
Fixed API throttling
2022-02-24 13:43:49 -08:00
snipe 83f21d0ddf Added a comment about why we use the middleware there
Signed-off-by: snipe <snipe@snipe.net>
2022-02-24 13:41:16 -08:00
snipe 2906a89442 Make the 429 error less stupid
Signed-off-by: snipe <snipe@snipe.net>
2022-02-24 13:10:34 -08:00
snipe 50f55b4308 Fixes broken API throttling
Signed-off-by: snipe <snipe@snipe.net>
2022-02-24 13:10:10 -08:00
Jens Maes 57e93e4e92 Fixes 10708: change size of fa-hdd to 2x 2022-02-24 11:05:40 +01:00
Jens Maes c0893c44a3 Fixes 10708: Removed line heigt from manufacturer list item. 2022-02-24 10:29:39 +01:00
snipe a8028e7dd0 Merge pull request #10727 from uberbrady/master
Fix for negative CORS/SAML interaction
2022-02-23 11:17:17 -08:00
Brady Wetherington 15abc84ab0 Migrate to Fruitcake/laravel-cors which has path-exclusion built-in 2022-02-23 11:06:19 -08:00
snipe 8a09211310 Merge pull request #10722 from snipe/features/add_force_saml_to_master
Fixes Restricted Logins to SAML controlled via environment variable #10436 - applies #10449 to master
2022-02-22 21:45:19 -08:00
snipe 42fcd29200 Fixed #10436 on master, applies #10449
Signed-off-by: snipe <snipe@snipe.net>
2022-02-22 21:06:54 -08:00
snipe dc2e6c2b06 Adds consumables endpoint to user API
Signed-off-by: snipe <snipe@snipe.net>
2022-02-22 20:44:34 -08:00
snipe 0cfc0a4bee Merge pull request #10718 from uberbrady/fix_cors_option_1_master
Fix CORS requests on master (option *ONE*)
2022-02-22 19:24:18 -08:00
snipe 292bf21e7d Merge pull request #10720 from uberbrady/fix_cors_develop
Fix cors develop
2022-02-22 19:23:43 -08:00
Brady Wetherington 294606fb0b Move new CORS library to global middleware, the only place it works 2022-02-22 19:07:23 -08:00
Brady Wetherington cd2c92fd6c Cors should respect the .env var for CORS domains 2022-02-22 19:00:59 -08:00
Brady Wetherington 64f83f9a5f Move HandleCors middleware to be universal 2022-02-22 17:43:40 -08:00
snipe 6ef053bc52 Merge pull request #10713 from snipe/fixes/error_handler
Added use statement
2022-02-22 10:46:58 -08:00
snipe 0a4a548f9c Added use statement
Signed-off-by: snipe <snipe@snipe.net>
2022-02-22 10:46:02 -08:00
Brady Wetherington 290baf1c8d refactor and clean up attribute-changing logic for assets 2022-02-15 18:29:45 -08:00
Ivan Nieto Vivanco 5bda4b79d2 Fixes the Asset Factory to assign example Suppliers and Locations to Assets 2021-12-11 21:36:54 -06:00
Ivan Nieto Vivanco b43a0569b1 Fixes trying to get property 'id' of non-object in develop seeders 2021-12-11 17:26:41 -06:00
354 changed files with 4684 additions and 2980 deletions
+11 -2
View File
@@ -2514,7 +2514,7 @@
"code"
]
},
{
{
"login": "Haxatron",
"name": "Haxatron",
"avatar_url": "https://avatars.githubusercontent.com/u/76475453?v=4",
@@ -2541,7 +2541,7 @@
"code"
]
},
{
{
"login": "UniversalSuperBox",
"name": "Dalton Durst",
"avatar_url": "https://avatars.githubusercontent.com/u/21966173?v=4",
@@ -2585,6 +2585,15 @@
"contributions": [
"code"
]
},
{
"login": "QveenSi",
"name": "Yevhenii Huzii",
"avatar_url": "https://avatars.githubusercontent.com/u/19945501?v=4",
"profile": "https://github.com/QveenSi",
"contributions": [
"code"
]
}
]
}
+1 -1
View File
@@ -157,4 +157,4 @@ IMPORT_TIME_LIMIT=600
IMPORT_MEMORY_LIMIT=500M
REPORT_TIME_LIMIT=12000
REQUIRE_SAML=false
API_THROTTLE_PER_MINUTE=120
+39
View File
@@ -0,0 +1,39 @@
# This workflow checks out code, performs a CodeQL analysis (for JavaScript) and integrates the results
# with the GitHub Advanced Security code scanning feature.
# More information: https://codeql.github.com/
name: CodeQL Security Scan
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
# schedule:
# - cron: '15 17 * * 1'
jobs:
analyze:
name: CodeQL Security Scan
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'javascript' ]
steps:
- name: Checkout repository
uses: actions/checkout@v2
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: ${{ matrix.language }}
- name: Autobuild
uses: github/codeql-action/autobuild@v1
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1
+1
View File
@@ -41,6 +41,7 @@ libmcrypt-dev \
php7.4-dev \
ca-certificates \
unzip \
dnsutils \
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
+6 -2
View File
@@ -1,5 +1,5 @@
![Build Status](https://app.chipperci.com/projects/0e5f8979-31eb-4ee6-9abf-050b76ab0383/status/master) [![Crowdin](https://d322cqt584bo4o.cloudfront.net/snipe-it/localized.svg)](https://crowdin.com/project/snipe-it) [![Docker Pulls](https://img.shields.io/docker/pulls/snipe/snipe-it.svg)](https://hub.docker.com/r/snipe/snipe-it/) [![Twitter Follow](https://img.shields.io/twitter/follow/snipeitapp.svg?style=social)](https://twitter.com/snipeitapp) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/553ce52037fc43ea99149785afcfe641)](https://www.codacy.com/app/snipe/snipe-it?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=snipe/snipe-it&amp;utm_campaign=Badge_Grade)
[![All Contributors](https://img.shields.io/badge/all_contributors-282-orange.svg?style=flat-square)](#contributors) [![Discord](https://badgen.net/badge/icon/discord?icon=discord&label)](https://discord.gg/yZFtShAcKk) [![huntr](https://cdn.huntr.dev/huntr_security_badge_mono.svg)](https://huntr.dev)
[![All Contributors](https://img.shields.io/badge/all_contributors-284-orange.svg?style=flat-square)](#contributors) [![Discord](https://badgen.net/badge/icon/discord?icon=discord&label)](https://discord.gg/yZFtShAcKk) [![huntr](https://cdn.huntr.dev/huntr_security_badge_mono.svg)](https://huntr.dev)
## Snipe-IT - Open Source Asset Management System
@@ -131,7 +131,11 @@ Thanks goes to all of these wonderful people ([emoji key](https://github.com/ken
| [<img src="https://avatars.githubusercontent.com/u/8735148?v=4" width="110px;"/><br /><sub>Petri Asikainen</sub>](https://github.com/PetriAsi)<br />[💻](https://github.com/snipe/snipe-it/commits?author=PetriAsi "Code") | [<img src="https://avatars.githubusercontent.com/u/11424540?v=4" width="110px;"/><br /><sub>derdeagle</sub>](https://github.com/derdeagle)<br />[💻](https://github.com/snipe/snipe-it/commits?author=derdeagle "Code") | [<img src="https://avatars.githubusercontent.com/u/176950?v=4" width="110px;"/><br /><sub>Mike Frysinger</sub>](https://wh0rd.org/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=vapier "Code") | [<img src="https://avatars.githubusercontent.com/u/22044358?v=4" width="110px;"/><br /><sub>ALPHA</sub>](https://github.com/AL4AL)<br />[💻](https://github.com/snipe/snipe-it/commits?author=AL4AL "Code") | [<img src="https://avatars.githubusercontent.com/u/1042587?v=4" width="110px;"/><br /><sub>FliegenKLATSCH</sub>](https://www.ifern.de)<br />[💻](https://github.com/snipe/snipe-it/commits?author=FliegenKLATSCH "Code") | [<img src="https://avatars.githubusercontent.com/u/442138?v=4" width="110px;"/><br /><sub>Jeremy Price</sub>](https://github.com/jerm)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jerm "Code") | [<img src="https://avatars.githubusercontent.com/u/84392209?v=4" width="110px;"/><br /><sub>Toreg87</sub>](https://github.com/Toreg87)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Toreg87 "Code") |
| [<img src="https://avatars.githubusercontent.com/u/67638596?v=4" width="110px;"/><br /><sub>Matthew Nickson</sub>](https://github.com/Computroniks)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Computroniks "Code") | [<img src="https://avatars.githubusercontent.com/u/1646397?v=4" width="110px;"/><br /><sub>Jethro Nederhof</sub>](https://jethron.id.au)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jethron "Code") | [<img src="https://avatars.githubusercontent.com/u/23289826?v=4" width="110px;"/><br /><sub>Oskar Stenberg</sub>](https://github.com/01ste02)<br />[💻](https://github.com/snipe/snipe-it/commits?author=01ste02 "Code") | [<img src="https://avatars.githubusercontent.com/u/82208283?v=4" width="110px;"/><br /><sub>Robert-Azelis</sub>](https://github.com/Robert-Azelis)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Robert-Azelis "Code") | [<img src="https://avatars.githubusercontent.com/u/60648387?v=4" width="110px;"/><br /><sub>Alexander William Smith</sub>](https://github.com/alwism)<br />[💻](https://github.com/snipe/snipe-it/commits?author=alwism "Code") | [<img src="https://avatars.githubusercontent.com/u/24418301?v=4" width="110px;"/><br /><sub>LEITWERK AG</sub>](https://www.leitwerk.de/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=leitwerk-ag "Code") | [<img src="https://avatars.githubusercontent.com/u/1911435?v=4" width="110px;"/><br /><sub>Adam</sub>](http://www.aboutcher.co.uk)<br />[💻](https://github.com/snipe/snipe-it/commits?author=adamboutcher "Code") |
| [<img src="https://avatars.githubusercontent.com/u/16104273?v=4" width="110px;"/><br /><sub>Ian</sub>](https://snksrv.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=sneak-it "Code") | [<img src="https://avatars.githubusercontent.com/u/4023909?v=4" width="110px;"/><br /><sub>Shao Yu-Lung (Allen)</sub>](http://blog.bestlong.idv.tw/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=bestlong "Code") | [<img src="https://avatars.githubusercontent.com/u/76475453?v=4" width="110px;"/><br /><sub>Haxatron</sub>](https://github.com/Haxatron)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Haxatron "Code") | [<img src="https://avatars.githubusercontent.com/u/3842948?v=4" width="110px;"/><br /><sub>Bradley Coudriet</sub>](http://bjcpgd.cias.rit.edu)<br />[💻](https://github.com/snipe/snipe-it/commits?author=exula "Code") | [<img src="https://avatars.githubusercontent.com/u/21966173?v=4" width="110px;"/><br /><sub>Dalton Durst</sub>](https://daltondur.st)<br />[💻](https://github.com/snipe/snipe-it/commits?author=UniversalSuperBox "Code") | [<img src="https://avatars.githubusercontent.com/u/48162670?v=4" width="110px;"/><br /><sub>TenOfTens</sub>](https://github.com/TenOfTens)<br />[💻](https://github.com/snipe/snipe-it/commits?author=TenOfTens "Code") | [<img src="https://avatars.githubusercontent.com/u/917232?v=4" width="110px;"/><br /><sub>Simona Avornicesei</sub>](http://www.avornicesei.com)<br />[⚠️](https://github.com/snipe/snipe-it/commits?author=savornicesei "Tests") |
| [<img src="https://avatars.githubusercontent.com/u/48162670?v=4" width="110px;"/><br /><sub>TenOfTens</sub>](https://github.com/TenOfTens)<br />[💻](https://github.com/snipe/snipe-it/commits?author=TenOfTens "Code") | [<img src="https://avatars.githubusercontent.com/u/9415391?v=4" width="110px;"/><br /><sub>waffle</sub>](https://ditisjens.be/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=insert-waffle "Code") |
| [<img src="https://avatars.githubusercontent.com/u/19945501?v=4" width="110px;"/><br /><sub>Yevhenii Huzii</sub>](https://github.com/QveenSi)<br />[💻](https://github.com/snipe/snipe-it/commits?author=QveenSi "Code") |
| [<img src="https://avatars.githubusercontent.com/u/1975640?v=4" width="110px;"/><br /><sub>Evan Taylor</sub>](https://github.com/Delta5)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Delta5 "Code") | [<img src="https://avatars.githubusercontent.com/u/8735148?v=4" width="110px;"/><br /><sub>Petri Asikainen</sub>](https://github.com/PetriAsi)<br />[💻](https://github.com/snipe/snipe-it/commits?author=PetriAsi "Code") | [<img src="https://avatars.githubusercontent.com/u/11424540?v=4" width="110px;"/><br /><sub>derdeagle</sub>](https://github.com/derdeagle)<br />[💻](https://github.com/snipe/snipe-it/commits?author=derdeagle "Code") | [<img src="https://avatars.githubusercontent.com/u/176950?v=4" width="110px;"/><br /><sub>Mike Frysinger</sub>](https://wh0rd.org/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=vapier "Code") | [<img src="https://avatars.githubusercontent.com/u/22044358?v=4" width="110px;"/><br /><sub>ALPHA</sub>](https://github.com/AL4AL)<br />[💻](https://github.com/snipe/snipe-it/commits?author=AL4AL "Code") | [<img src="https://avatars.githubusercontent.com/u/1042587?v=4" width="110px;"/><br /><sub>FliegenKLATSCH</sub>](https://www.ifern.de)<br />[💻](https://github.com/snipe/snipe-it/commits?author=FliegenKLATSCH "Code") | [<img src="https://avatars.githubusercontent.com/u/442138?v=4" width="110px;"/><br /><sub>Jeremy Price</sub>](https://github.com/jerm)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jerm "Code") |
| [<img src="https://avatars.githubusercontent.com/u/84392209?v=4" width="110px;"/><br /><sub>Toreg87</sub>](https://github.com/Toreg87)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Toreg87 "Code") | [<img src="https://avatars.githubusercontent.com/u/67638596?v=4" width="110px;"/><br /><sub>Matthew Nickson</sub>](https://github.com/Computroniks)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Computroniks "Code") | [<img src="https://avatars.githubusercontent.com/u/1646397?v=4" width="110px;"/><br /><sub>Jethro Nederhof</sub>](https://jethron.id.au)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jethron "Code") | [<img src="https://avatars.githubusercontent.com/u/23289826?v=4" width="110px;"/><br /><sub>Oskar Stenberg</sub>](https://github.com/01ste02)<br />[💻](https://github.com/snipe/snipe-it/commits?author=01ste02 "Code") | [<img src="https://avatars.githubusercontent.com/u/82208283?v=4" width="110px;"/><br /><sub>Robert-Azelis</sub>](https://github.com/Robert-Azelis)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Robert-Azelis "Code") | [<img src="https://avatars.githubusercontent.com/u/60648387?v=4" width="110px;"/><br /><sub>Alexander William Smith</sub>](https://github.com/alwism)<br />[💻](https://github.com/snipe/snipe-it/commits?author=alwism "Code") | [<img src="https://avatars.githubusercontent.com/u/24418301?v=4" width="110px;"/><br /><sub>LEITWERK AG</sub>](https://www.leitwerk.de/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=leitwerk-ag "Code") |
| [<img src="https://avatars.githubusercontent.com/u/1911435?v=4" width="110px;"/><br /><sub>Adam</sub>](http://www.aboutcher.co.uk)<br />[💻](https://github.com/snipe/snipe-it/commits?author=adamboutcher "Code") | [<img src="https://avatars.githubusercontent.com/u/16104273?v=4" width="110px;"/><br /><sub>Ian</sub>](https://snksrv.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=sneak-it "Code") | [<img src="https://avatars.githubusercontent.com/u/4023909?v=4" width="110px;"/><br /><sub>Shao Yu-Lung (Allen)</sub>](http://blog.bestlong.idv.tw/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=bestlong "Code") | [<img src="https://avatars.githubusercontent.com/u/76475453?v=4" width="110px;"/><br /><sub>Haxatron</sub>](https://github.com/Haxatron)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Haxatron "Code") | [<img src="https://avatars.githubusercontent.com/u/88776392?v=4" width="110px;"/><br /><sub>PlaneNuts</sub>](https://github.com/PlaneNuts)<br />[💻](https://github.com/snipe/snipe-it/commits?author=PlaneNuts "Code") | [<img src="https://avatars.githubusercontent.com/u/3842948?v=4" width="110px;"/><br /><sub>Bradley Coudriet</sub>](http://bjcpgd.cias.rit.edu)<br />[💻](https://github.com/snipe/snipe-it/commits?author=exula "Code") | [<img src="https://avatars.githubusercontent.com/u/21966173?v=4" width="110px;"/><br /><sub>Dalton Durst</sub>](https://daltondur.st)<br />[💻](https://github.com/snipe/snipe-it/commits?author=UniversalSuperBox "Code") |
| [<img src="https://avatars.githubusercontent.com/u/38761237?v=4" width="110px;"/><br /><sub>Alex Janes</sub>](https://adagiohealth.org)<br />[💻](https://github.com/snipe/snipe-it/commits?author=adagioajanes "Code") | [<img src="https://avatars.githubusercontent.com/u/32387849?v=4" width="110px;"/><br /><sub>Nuraeil</sub>](https://github.com/nuraeil)<br />[💻](https://github.com/snipe/snipe-it/commits?author=nuraeil "Code") | [<img src="https://avatars.githubusercontent.com/u/48162670?v=4" width="110px;"/><br /><sub>TenOfTens</sub>](https://github.com/TenOfTens)<br />[💻](https://github.com/snipe/snipe-it/commits?author=TenOfTens "Code") | [<img src="https://avatars.githubusercontent.com/u/9415391?v=4" width="110px;"/><br /><sub>waffle</sub>](https://ditisjens.be/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=insert-waffle "Code") |
<!-- ALL-CONTRIBUTORS-LIST:END -->
This project follows the [all-contributors](https://github.com/kentcdodds/all-contributors) specification. Contributions of any kind welcome!
+59
View File
@@ -0,0 +1,59 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
class KillAllSessions extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'snipeit:global-logout {--force : Skip the danger prompt; assuming you enter "y"} ';
/**
* The console command description.
*
* @var string
*/
protected $description = 'This command will destroy all web sessions on disk and will force a re-login for all users.';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
if (!$this->option('force') && !$this->confirm("****************************************************\nTHIS WILL FORCE A LOGIN FOR ALL LOGGED IN USERS.\n\nAre you SURE you wish to continue? ")) {
return $this->error("Session loss not confirmed");
}
$session_files = glob(storage_path("framework/sessions/*"));
$count = 0;
foreach ($session_files as $file) {
if (is_file($file))
unlink($file);
$count++;
}
\DB::table('users')->update(['remember_token' => null]);
$this->info($count. ' sessions cleared!');
}
}
+11 -1
View File
@@ -56,6 +56,7 @@ class LdapSync extends Command
$ldap_result_jobtitle = Setting::getSettings()->ldap_jobtitle;
$ldap_result_country = Setting::getSettings()->ldap_country;
$ldap_result_dept = Setting::getSettings()->ldap_dept;
$ldap_result_manager = Setting::getSettings()->ldap_manager;
try {
$ldapconn = Ldap::connectToLdap();
@@ -184,12 +185,12 @@ class LdapSync extends Command
$item['jobtitle'] = isset($results[$i][$ldap_result_jobtitle][0]) ? $results[$i][$ldap_result_jobtitle][0] : '';
$item['country'] = isset($results[$i][$ldap_result_country][0]) ? $results[$i][$ldap_result_country][0] : '';
$item['department'] = isset($results[$i][$ldap_result_dept][0]) ? $results[$i][$ldap_result_dept][0] : '';
$item['manager'] = isset($results[$i][$ldap_result_manager][0]) ? $results[$i][$ldap_result_manager][0] : '';
$department = Department::firstOrCreate([
'name' => $item['department'],
]);
$user = User::where('username', $item['username'])->first();
if ($user) {
// Updating an existing user.
@@ -212,6 +213,15 @@ class LdapSync extends Command
$user->country = $item['country'];
$user->department_id = $department->id;
if($item['manager']!= null) {
//Captures only the Canonical Name
$item['manager'] = ltrim($item['manager'], "CN=");
$item['manager'] = substr($item['manager'],0, strpos($item['manager'], ','));
$ldap_manager = User::where('username', $item['manager'])->first();
$user->manager_id = $ldap_manager->id;
}
// Sync activated state for Active Directory.
if (array_key_exists('useraccountcontrol', $results[$i])) {
/* The following is _probably_ the correct logic, but we can't use it because
+503
View File
@@ -0,0 +1,503 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use App\Models\Setting;
use Exception;
use Crypt;
/**
* Check if a given ip is in a network
* @param string $ip IP to check in IPV4 format eg. 127.0.0.1
* @param string $range IP/CIDR netmask eg. 127.0.0.0/24, also 127.0.0.1 is accepted and /32 assumed
* @return boolean true if the ip is in this range / false if not.
*/
function ip_in_range( $ip, $range ) {
if ( strpos( $range, '/' ) == false ) {
$range .= '/32';
}
// $range is in IP/CIDR format eg 127.0.0.1/24
list( $range, $netmask ) = explode( '/', $range, 2 );
$range_decimal = ip2long( $range );
$ip_decimal = ip2long( $ip );
$wildcard_decimal = pow( 2, ( 32 - $netmask ) ) - 1;
$netmask_decimal = ~ $wildcard_decimal;
return ( ( $ip_decimal & $netmask_decimal ) == ( $range_decimal & $netmask_decimal ) );
}
// NOTE - this function was shamelessly stolen from this gist: https://gist.github.com/tott/7684443
class LdapTroubleshooter extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'ldap:troubleshoot
{--ldap-search : Output an ldapsearch command-line for testing your LDAP config}
{--force : Skip the interactive yes/no prompt for confirmation}
{--debug : Include debugging output (verbose)}
{--trace : Include extremely verbose LDAP trace output}
{--timeout=15 : Timeout for LDAP Bind operations}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Runs a series of non-destructive LDAP commands to help try and determine correct LDAP settings for your environment.';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Output something *only* if debug is enabled
*
* @return void
*/
public function debugout($string)
{
if($this->option('debug')) {
$this->line($string);
}
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
if($this->option('trace')) {
ldap_set_option(NULL, LDAP_OPT_DEBUG_LEVEL, 7);
}
$settings = Setting::getSettings();
$this->settings = $settings;
if($this->option('ldap-search')) {
if(!$this->option('force')) {
$confirmation = $this->confirm('WARNING: This command will display your LDAP password on your terminal. Are you sure this is ok?');
if(!$confirmation) {
$this->error('ABORTING');
exit(-1);
}
}
$output = [];
if($settings->ldap_server_cert_ignore) {
$this->line("# Ignoring server certificate validity");
$output[] = "LDAPTLS_REQCERT=never";
}
if($settings->ldap_client_tls_cert && $settings->ldap_client_tls_key) {
$this->line("# Adding LDAP Client Certificate and Key");
$output[] = "LDAPTLS_CERT=storage/ldap_client_tls.cert";
$output[] = "LDAPTLS_KEY=storage/ldap_client_tls.key";
}
$output[] = "ldapsearch";
$output[] = $settings->ldap_server;
$output[] = "-x";
$output[] = "-b ".escapeshellarg($settings->ldap_basedn);
$output[] = "-D ".escapeshellarg($settings->ldap_uname);
$output[] = "-w ".escapeshellarg(\Crypt::Decrypt($settings->ldap_pword));
if(substr($settings->ldap_filter,0,1) == "(" ) {
$output[] = escapeshellarg($settings->ldap_filter);
} else {
$output[] = escapeshellarg("(".$settings->ldap_filter.")");
}
if($settings->ldap_tls) {
$this->line("# adding STARTTLS option");
$output[] = "-Z";
}
$output[] = "-v";
$this->line("\n");
$this->line(implode(" \\\n",$output));
exit(0);
}
if(!$this->option('force')) {
$confirmation = $this->confirm('WARNING: This command will make several attempts to connect to your LDAP server. Are you sure this is ok?');
if(!$confirmation) {
$this->error('ABORTING');
exit(-1);
}
}
//$this->line(print_r($settings,true));
$this->info("STAGE 1: Checking settings");
if(!$settings->ldap_enabled) {
$this->error("WARNING: Snipe-IT's LDAP setting is not turned on. (That may be OK if you're still trying to figure out settings)");
}
$ldap_conn = false;
try {
$ldap_conn = ldap_connect($settings->ldap_server);
} catch (Exception $e) {
$this->error("WARNING: Exception caught when executing 'ldap_connect()' - ".$e->getMessage().". We will try to guess.");
}
if(!$ldap_conn) {
$this->error("WARNING: LDAP Server setting of: ".$settings->ldap_server." cannot be parsed. We will try to guess.");
//exit(-1);
}
//since we never use $ldap_conn again, we don't have to ldap_unbind() it (it's not even connected, tbh - that only happens at bind-time)
$parsed = parse_url($settings->ldap_server);
if(@$parsed['scheme'] != 'ldap' && @$parsed['scheme'] != 'ldaps') {
$this->error("WARNING: LDAP URL Scheme of '".@$parsed['scheme']."' is probably incorrect; should usually be ldap or ldaps");
}
if(!@$parsed['host']) {
$this->error("ERROR: Cannot determine hostname or IP from ldap URL: ".$settings->ldap_server.". ABORTING.");
exit(-1);
} else {
$this->info("Determined LDAP hostname to be: ".$parsed['host']);
}
$this->info("Performing DNS lookup of: ".$parsed['host']);
$ips = dns_get_record($parsed['host']);
$raw_ips = [];
//$this->info("Host IP is: ".print_r($ips,true));
if(!$ips || count($ips) == 0) {
$this->error("ERROR: DNS lookup of host: ".$parsed['host']." has failed. ABORTING.");
exit(-1);
}
$this->debugout("IP's? ".print_r($ips,true));
foreach($ips as $ip) {
if(!isset($ip['ip'])) {
continue;
}
$raw_ips[]=$ip['ip'];
if($ip['ip'] == "127.0.0.1") {
$this->error("WARNING: Using the localhost IP as the LDAP server. This is usually wrong");
}
if(ip_in_range($ip['ip'],'10.0.0.0/8') || ip_in_range($ip['ip'],'192.168.0.0/16') || ip_in_range($ip['ip'], '172.16.0.0/12')) {
$this->error("WARNING: Using an RFC1918 Private address for LDAP server. This may be correct, but it can be a problem if your Snipe-IT instance is not hosted on your private network");
}
}
$this->info("STAGE 2: Checking basic network connectivity");
$ports = [389,636];
if(@$parsed['port'] && !in_array($parsed['port'],$ports)) {
$ports[] = $parsed['port'];
}
$open_ports=[];
foreach($ports as $port ) {
$errno = 0;
$errstr = '';
$timeout = 30.0;
$result = '';
$this->info("Attempting to connect to port: ".$port." - may take up to $timeout seconds");
try {
$result = fsockopen($parsed['host'], $port, $errno, $errstr, 30.0);
} catch(Exception $e) {
$this->error("Exception: ".$e->getMessage());
}
if($result) {
$this->info("Success!");
$open_ports[] = $port;
} else {
$this->error("WARNING: Cannot connect to port: $port - $errstr ($errno)");
}
}
if(count($open_ports) == 0) {
$this->error("ERROR - no open ports. ABORTING.");
exit(-1);
}
$this->info("STAGE 3: Determine encryption algorithm, if any");
$ldap_urls = [];
$pretty_ldap_urls = [];
foreach($open_ports as $port) {
$this->line("Trying TLS first for port $port");
$ldap_url = "ldaps://".$parsed['host'].":$port";
if($this->test_anonymous_bind($ldap_url)) {
$this->info("Anonymous bind succesful to $ldap_url!");
$ldap_urls[] = [ $ldap_url, true, false ];
$pretty_ldap_urls[] = [ $ldap_url, "YES", "no" ];
continue; // TODO - lots of copypasta in these if(test_anonymous_bind()) routines...
} else {
$this->error("WARNING: Failed to bind to $ldap_url - trying without certificate checks.");
}
if($this->test_anonymous_bind($ldap_url, false)) {
$this->info("Anonymous bind succesful to $ldap_url with certifcate-checks disabled");
$ldap_urls[] = [ $ldap_url, false, false ];
$pretty_ldap_urls[] = [ $ldap_url, "no", "no" ];
continue;
} else {
$this->error("WARNING: Failed to bind to $ldap_url with certificate checks disabled. Trying unencrypted with STARTTLS");
}
$ldap_url = "ldap://".$parsed['host'].":$port";
if($this->test_anonymous_bind($ldap_url, true, true)) {
$this->info("Plain connection to $ldap_url with STARTTLS succesful!");
$ldap_urls[] = [ $ldap_url, true, true ];
$pretty_ldap_urls[] = [ $ldap_url, "YES", "YES" ];
continue;
} else {
$this->error("WARNING: Failed to bind to $ldap_url with STARTTLS enabled. Trying without STARTTLS");
}
if($this->test_anonymous_bind($ldap_url)) {
$this->info("Plain connection to $ldap_url succesful!");
$ldap_urls[] = [ $ldap_url, true, false ];
$pretty_ldap_urls[] = [ $ldap_url, "YES", "no" ];
continue;
} else {
$this->error("WARNING: Failed to bind to $ldap_url. Giving up on port $port");
}
}
$this->debugout(print_r($ldap_urls,true));
if(count($ldap_urls) > 0 ) {
$this->info("Found working LDAP URL's: ");
foreach($ldap_urls as $ldap_url) { // TODO maybe do this as a $this->table() instead?
$this->info("LDAP URL: ".$ldap_url[0]);
$this->info($ldap_url[0]. ($ldap_url[1] ? " certificate checks enabled" : " certificate checks disabled"). ($ldap_url[2] ? " STARTTLS Enabled ": " STARTTLS Disabled"));
}
$this->table(["URL", "Cert Checks Enabled?", "STARTTLS Enabled?"],$pretty_ldap_urls);
} else {
$this->error("ERROR - no valid LDAP URL's available - ABORTING");
exit(1);
}
$this->info("STAGE 4: Test Administrative Bind for LDAP Sync");
foreach($ldap_urls AS $ldap_url) {
$this->test_authed_bind($ldap_url[0], $ldap_url[1], $ldap_url[2], $settings->ldap_uname, Crypt::decrypt($settings->ldap_pword));
}
$this->info("STAGE 5: Test BaseDN");
//grab all LDAP_ constants and fill up a reversed array mapping from weird LDAP dotted-strings to (Constant Name)
$all_defined_constants = get_defined_constants();
$ldap_constants = [];
foreach($all_defined_constants AS $key => $val) {
if(starts_with($key,"LDAP_") && is_string($val)) {
$ldap_constants[$val] = $key; // INVERT the meaning here!
}
}
$this->debugout("LDAP constants are: ".print_r($ldap_constants,true));
// recursive function that 'cleans' the returned array from ldap_get_entries which are formatted awfully
$cleaner = function ($array) {
$cleaned = [];
for($i = 0; $i < $array['count']; $i++) {
$row = $array[$i];
$clean_row = [];
foreach($row AS $key => $val ) {
$this->debugout("Key is: ".$key);
if($key == "count" || is_int($key) || $key == "dn") {
$this->debugout(" and we're gonna skip it\n");
continue;
}
$this->debugout(" And that seems fine.\n");
if(array_key_exists('count',$val)) {
if($val['count'] == 1) {
$clean_row[$key] = $val[0];
} else {
unset($val['count']); //these counts are annoying
$elements = [];
foreach($val as $entry) {
if(isset($ldap_constants[$entry])) {
$elements[] = $ldap_constants[$entry];
} else {
$elements[] = $entry;
}
}
$clean_row[$key] = $elements;
}
} else {
$clean_row[$key] = $val;
}
}
$cleaned[$i] = $clean_row;
}
return $cleaned;
};
foreach($ldap_urls AS $ldap_url) {
if($this->test_informational_bind($ldap_url[0],$ldap_url[1],$ldap_url[2],$settings->ldap_uname,Crypt::decrypt($settings->ldap_pword))) {
$this->info("Success getting informational bind!");
} else {
$this->error("Unable to get information from bind.");
}
}
$this->info("STAGE 6: Test LDAP Login to Snipe-IT");
foreach($ldap_urls AS $ldap_url) {
$this->info("Starting auth to ".$ldap_url[0]);
while(true) {
$with_tls = $ldap_url[1] ? "with": "without";
$with_startssl = $ldap_url[2] ? "using": "not using";
if(!$this->confirm('Do you wish to try to authenticate to this directory: '.$ldap_url[0]." $with_tls TLS and $with_startssl STARTSSL?")) {
break;
}
$username = $this->ask("Username");
$password = $this->secret("Password");
$this->test_authed_bind($ldap_url[0], $ldap_url[1], $ldap_url[2], $username, $password); // FIXME - should do some other stuff here, maybe with the concatenating or something? maybe? and/or should put up some results?
}
}
$this->info("LDAP TROUBLESHOOTING COMPLETE!");
}
public function connect_to_ldap($ldap_url, $check_cert, $start_tls)
{
$lconn = ldap_connect($ldap_url);
ldap_set_option($lconn, LDAP_OPT_PROTOCOL_VERSION, 3); // should we 'test' different protocol versions here? Does anyone even use anything other than LDAPv3?
// no - it's formally deprecated: https://tools.ietf.org/html/rfc3494
if(!$check_cert) {
putenv('LDAPTLS_REQCERT=never'); // This is horrible; is this *really* the only way to do it?
} else {
putenv('LDAPTLS_REQCERT'); // have to very explicitly and manually *UN* set the env var here to ensure it works
}
if($this->settings->ldap_client_tls_cert && $this->settings->ldap_client_tls_key) {
// client-side TLS certificate support for LDAP (Google Secure LDAP)
putenv('LDAPTLS_CERT=storage/ldap_client_tls.cert');
putenv('LDAPTLS_KEY=storage/ldap_client_tls.key');
}
if($start_tls) {
if(!ldap_start_tls($lconn)) {
$this->error("WARNING: Unable to start TLS");
return false;
}
}
if(!$lconn) {
$this->error("WARNING: Failed to generate connection string - using: ".$ldap_url);
return false;
}
$net = ldap_set_option($lconn, LDAP_OPT_NETWORK_TIMEOUT, $this->option('timeout'));
$time = ldap_set_option($lconn, LDAP_OPT_TIMELIMIT, $this->option('timeout'));
if(!$net || !$time) {
$this->error("Unable to set timeouts!");
}
return $lconn;
}
public function test_anonymous_bind($ldap_url, $check_cert = true, $start_tls = false)
{
return $this->timed_boolean_execute(function () use ($ldap_url, $check_cert , $start_tls) {
try {
$lconn = $this->connect_to_ldap($ldap_url, $check_cert, $start_tls);
$this->info("gonna try to bind now, this can take a while if we mess it up");
$bind_results = ldap_bind($lconn);
$this->info("Bind results are: ".$bind_results." which translate into boolean: ".(bool)$bind_results);
return (bool)$bind_results;
} catch (Exception $e) {
$this->error("WARNING: Exception caught during bind - ".$e->getMessage());
return false;
}
});
}
public function test_authed_bind($ldap_url, $check_cert, $start_tls, $username, $password)
{
return $this->timed_boolean_execute(function () use ($ldap_url, $check_cert, $start_tls, $username, $password) {
try {
$lconn = $this->connect_to_ldap($ldap_url, $check_cert, $start_tls);
$bind_results = ldap_bind($lconn, $username, $password);
if(!$bind_results) {
$this->error("WARNING: Failed to bind to $ldap_url as $username");
return false;
} else {
$this->info("SUCCESS - Able to bind to $ldap_url as $username");
return (bool)$lconn;
}
} catch (Exception $e) {
$this->error("WARNING: Exception caught during Authed bind to $username - ".$e->getMessage());
return false;
}
});
}
public function test_informational_bind($ldap_url, $check_cert, $start_tls, $username, $password)
{
return $this->timed_boolean_execute(function () use ($ldap_url, $check_cert, $start_tls, $username, $password) {
try { // TODO - copypasta'ed from test_authed_bind
$conn = $this->connect_to_ldap($ldap_url, $check_cert, $start_tls);
$bind_results = ldap_bind($conn, $username, $password);
if(!$bind_results) {
$this->error("WARNING: Failed to bind to $ldap_url as $username");
return false;
}
$this->info("SUCCESS - Able to bind to $ldap_url as $username");
$result = ldap_read($conn, '', '(objectClass=*)'/* , ['supportedControl']*/);
$results = ldap_get_entries($conn, $result);
$cleaned_results = $cleaner($results);
$this->line(print_r($cleaned_results,true));
//okay, great - now how do we display those results? I have no idea.
// I don't see why this throws an Exception for Google LDAP, but I guess we ought to try and catch it?
$this->comment("I guess we're trying to do the ldap search here, but sometimes it takes too long?");
$search_results = ldap_search($conn, $settings->base_dn, $settings->filter);
$this->info("Printing first 10 results: ");
for($i=0;$i<10;$i++) {
$this->info($search_results[$i]);
}
} catch (\Exception $e) {
$this->error("WARNING: Exception caught during Authed bind to $username - ".$e->getMessage());
return false;
}
});
}
/***********************************************
*
* This function executes $function - which is expected to be some kind of executable function -
* with a timeout set. It respects the timeout by forking execution and setting a strict timer
* for which to get back a SIGUSR1 or SIGUSR2 signal from the forked process.
*
***********************************************/
private function timed_boolean_execute($function)
{
if(!(function_exists('pcntl_sigtimedwait') && function_exists('posix_getpid') && function_exists('pcntl_fork') && function_exists('posix_kill') && function_exists('pcntl_wifsignaled'))) {
// POSIX functions needed for forking aren't present, just run the function inline (ignoring timeout)
$this->info('WARNING: Unable to execute POSIX fork() commands, timeout may not be respected');
return $function();
} else {
$parent_pid = posix_getpid();
$pid = pcntl_fork();
switch($pid) {
case 0:
//we're the 'child'
if($function()) {
//SUCCESS = SIGUSR1
posix_kill($parent_pid, SIGUSR1);
} else {
//FAILURE = SIGUSR2
posix_kill($parent_pid, SIGUSR2);
}
exit();
break; //yes I know we don't need it.
case -1:
//couldn't fork
$this->error("COULD NOT FORK - assuming failure");
return false;
break; //I still know that we don't need it
default:
//we remain the 'parent', $pid is the PID of the forked process.
$siginfo = [];
$exit_status = pcntl_sigtimedwait ([SIGUSR1, SIGUSR2], $siginfo, $this->option('timeout'));
if ($exit_status == SIGUSR1) {
return true;
} else {
posix_kill($pid, SIGKILL); //make sure we don't have processes hanging around that might try and send signals during later executions, confusing us
return false;
}
break; //Yeah I get it already, shush.
}
}
}
}
+73 -69
View File
@@ -46,32 +46,33 @@ class MoveUploadsToNewDisk extends Command
}
$delete_local = $this->argument('delete_local');
$public_uploads['accessories'] = glob('public/accessories'.'/*.*');
$public_uploads['assets'] = glob('public/assets'.'/*.*');
$public_uploads['avatars'] = glob('public/avatars'.'/*.*');
$public_uploads['categories'] = glob('public/categories'.'/*.*');
$public_uploads['companies'] = glob('public/companies'.'/*.*');
$public_uploads['components'] = glob('public/components'.'/*.*');
$public_uploads['consumables'] = glob('public/consumables'.'/*.*');
$public_uploads['departments'] = glob('public/departments'.'/*.*');
$public_uploads['locations'] = glob('public/locations'.'/*.*');
$public_uploads['manufacturers'] = glob('public/manufacturers'.'/*.*');
$public_uploads['suppliers'] = glob('public/suppliers'.'/*.*');
$public_uploads['assetmodels'] = glob('public/models'.'/*.*');
$public_uploads['accessories'] = glob('public/uploads/accessories' . "/*.*");
$public_uploads['assets'] = glob('public/uploads/assets' . "/*.*");
$public_uploads['avatars'] = glob('public/uploads/avatars' . "/*.*");
$public_uploads['categories'] = glob('public/uploads/categories' . "/*.*");
$public_uploads['companies'] = glob('public/uploads/companies' . "/*.*");
$public_uploads['components'] = glob('public/uploads/components' . "/*.*");
$public_uploads['consumables'] = glob('public/uploads/consumables' . "/*.*");
$public_uploads['departments'] = glob('public/uploads/departments' . "/*.*");
$public_uploads['locations'] = glob('public/uploads/locations' . "/*.*");
$public_uploads['manufacturers'] = glob('public/uploads/manufacturers' . "/*.*");
$public_uploads['suppliers'] = glob('public/uploads/suppliers' . "/*.*");
$public_uploads['assetmodels'] = glob('public/uploads/models' . "/*.*");
// iterate files
foreach ($public_uploads as $public_type => $public_upload) {
$type_count = 0;
$this->info('- There are '.count($public_upload).' PUBLIC '.$public_type.' files.');
$this->info('- There are ' . count($public_upload) . ' PUBLIC ' . $public_type . ' files.');
for ($i = 0; $i < count($public_upload); $i++) {
$type_count++;
$filename = basename($public_upload[$i]);
try {
Storage::disk('public')->put('uploads/'.public_type.'/'.$filename, file_get_contents($public_upload[$i]));
$new_url = Storage::disk('public')->url('uploads/'.$public_type.'/'.$filename, $filename);
$this->info($type_count.'. PUBLIC: '.$filename.' was copied to '.$new_url);
Storage::disk('public')->put('uploads/' . $public_type . '/' . $filename, file_get_contents($public_upload[$i]));
$new_url = Storage::disk('public')->url('uploads/' . $public_type . '/' . $filename, $filename);
$this->info($type_count . '. PUBLIC: ' . $filename . ' was copied to ' . $new_url);
} catch (\Exception $e) {
\Log::debug($e);
$this->error($e);
@@ -80,82 +81,85 @@ class MoveUploadsToNewDisk extends Command
}
$logos = glob('public/uploads/setting*.*');
$this->info('- There are '.count($logos).' files that might be logos.');
$this->info('- There are ' . count($logos) . ' files that might be logos.');
$type_count = 0;
foreach ($logos as $logo) {
$this->info($logo);
$type_count++;
$filename = basename($logo);
Storage::disk('public')->put('uploads/'.$filename, file_get_contents($logo));
$this->info($type_count.'. LOGO: '.$filename.' was copied to '.env('PUBLIC_AWS_URL').'/uploads/'.$filename);
Storage::disk('public')->put('uploads/' . $filename, file_get_contents($logo));
$this->info($type_count . '. LOGO: ' . $filename . ' was copied to ' . env('PUBLIC_AWS_URL') . '/uploads/' . $filename);
}
$private_uploads['assets'] = glob('storage/private_uploads/assets'.'/*.*');
$private_uploads['signatures'] = glob('storage/private_uploads/signatures'.'/*.*');
$private_uploads['audits'] = glob('storage/private_uploads/audits'.'/*.*');
$private_uploads['assetmodels'] = glob('storage/private_uploads/assetmodels'.'/*.*');
$private_uploads['imports'] = glob('storage/private_uploads/imports'.'/*.*');
$private_uploads['licenses'] = glob('storage/private_uploads/licenses'.'/*.*');
$private_uploads['users'] = glob('storage/private_uploads/users'.'/*.*');
$private_uploads['backups'] = glob('storage/private_uploads/users'.'/*.*');
$private_uploads['assets'] = glob('storage/private_uploads/assets' . '/*.*');
$private_uploads['signatures'] = glob('storage/private_uploads/signatures' . '/*.*');
$private_uploads['audits'] = glob('storage/private_uploads/audits' . '/*.*');
$private_uploads['assetmodels'] = glob('storage/private_uploads/assetmodels' . '/*.*');
$private_uploads['imports'] = glob('storage/private_uploads/imports' . '/*.*');
$private_uploads['licenses'] = glob('storage/private_uploads/licenses' . '/*.*');
$private_uploads['users'] = glob('storage/private_uploads/users' . '/*.*');
$private_uploads['backups'] = glob('storage/private_uploads/users' . '/*.*');
foreach ($private_uploads as $private_type => $private_upload) {
$this->info('- There are '.count($private_upload).' PRIVATE '.$private_type.' files.');
{
$this->info('- There are ' . count($private_upload) . ' PRIVATE ' . $private_type . ' files.');
$type_count = 0;
for ($x = 0; $x < count($private_upload); $x++) {
$type_count++;
$filename = basename($private_upload[$x]);
$type_count = 0;
for ($x = 0; $x < count($private_upload); $x++) {
$type_count++;
$filename = basename($private_upload[$x]);
try {
Storage::put($private_type.'/'.$filename, file_get_contents($private_upload[$i]));
$new_url = Storage::url($private_type.'/'.$filename, $filename);
$this->info($type_count.'. PRIVATE: '.$filename.' was copied to '.$new_url);
} catch (\Exception $e) {
\Log::debug($e);
$this->error($e);
try {
Storage::put($private_type . '/' . $filename, file_get_contents($private_upload[$i]));
$new_url = Storage::url($private_type . '/' . $filename, $filename);
$this->info($type_count . '. PRIVATE: ' . $filename . ' was copied to ' . $new_url);
} catch (\Exception $e) {
\Log::debug($e);
$this->error($e);
}
}
}
}
if ($delete_local == 'true') {
$public_delete_count = 0;
$private_delete_count = 0;
$this->info("\n\n");
$this->error('!!!!!!!!!!!!!!!!!!!!!!!!!!!!! WARNING!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!');
$this->warn("\nTHIS WILL DELETE ALL OF YOUR LOCAL UPLOADED FILES. \n\nThis cannot be undone, so you should take a backup of your system before you proceed.\n");
$this->error('!!!!!!!!!!!!!!!!!!!!!!!!!!!!! WARNING!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!');
if ($delete_local == 'true') {
$public_delete_count = 0;
$private_delete_count = 0;
if ($this->confirm('Do you wish to continue?')) {
foreach ($public_uploads as $public_type => $public_upload) {
for ($i = 0; $i < count($public_upload); $i++) {
$filename = $public_upload[$i];
try {
unlink($filename);
$public_delete_count++;
} catch (\Exception $e) {
\Log::debug($e);
$this->error($e);
$this->info("\n\n");
$this->error('!!!!!!!!!!!!!!!!!!!!!!!!!!!!! WARNING!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!');
$this->warn("\nTHIS WILL DELETE ALL OF YOUR LOCAL UPLOADED FILES. \n\nThis cannot be undone, so you should take a backup of your system before you proceed.\n");
$this->error('!!!!!!!!!!!!!!!!!!!!!!!!!!!!! WARNING!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!');
if ($this->confirm('Do you wish to continue?')) {
foreach ($public_uploads as $public_type => $public_upload) {
for ($i = 0; $i < count($public_upload); $i++) {
$filename = $public_upload[$i];
try {
unlink($filename);
$public_delete_count++;
} catch (\Exception $e) {
\Log::debug($e);
$this->error($e);
}
}
}
}
foreach ($private_uploads as $private_type => $private_upload) {
for ($i = 0; $i < count($private_upload); $i++) {
$filename = $private_upload[$i];
try {
unlink($filename);
$private_delete_count++;
} catch (\Exception $e) {
\Log::debug($e);
$this->error($e);
foreach ($private_uploads as $private_type => $private_upload) {
for ($i = 0; $i < count($private_upload); $i++) {
$filename = $private_upload[$i];
try {
unlink($filename);
$private_delete_count++;
} catch (\Exception $e) {
\Log::debug($e);
$this->error($e);
}
}
}
}
$this->info($public_delete_count.' PUBLIC local files and '.$private_delete_count.' PRIVATE local files were deleted from your filesystem.');
$this->info($public_delete_count . ' PUBLIC local files and ' . $private_delete_count . ' PRIVATE local files were deleted from your filesystem.');
}
}
}
}
+6 -3
View File
@@ -13,8 +13,8 @@ class RestoreFromBackup extends Command
* @var string
*/
protected $signature = 'snipeit:restore
{--force : Skip the danger prompt; assuming you hit "y"}
{filename : The full path of the .zip file to be migrated}
{--force : Skip the danger prompt; assuming you enter "y"}
{filename : The zip file to be migrated}
{--no-progress : Don\'t show a progress bar}';
/**
@@ -82,6 +82,7 @@ class RestoreFromBackup extends Command
return $this->error('Could not access file: '.$filename.' - '.array_key_exists($errcode, $errors) ? $errors[$errcode] : " Unknown reason: $errcode");
}
$private_dirs = [
'storage/private_uploads/assets', // these are asset _files_, not the pictures.
'storage/private_uploads/audits',
@@ -245,19 +246,21 @@ class RestoreFromBackup extends Command
return false;
}
$bytes_read = 0;
while (($buffer = fgets($sql_contents, self::$buffer_size)) !== false) {
$bytes_read += strlen($buffer);
// \Log::debug("Buffer is: '$buffer'");
$bytes_written = fwrite($pipes[0], $buffer);
if ($bytes_written === false) {
$stdout = fgets($pipes[1]);
$this->info($stdout);
$stderr = fgets($pipes[2]);
$this->info($stderr);
return false;
}
}
if (!feof($sql_contents) || $bytes_read == 0) {
return $this->error("Not at end of file for sql file, or zero bytes read. aborting!");
}
+6 -3
View File
@@ -5,6 +5,7 @@ namespace App\Exceptions;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use App\Helpers\Helper;
use Illuminate\Validation\ValidationException;
use Illuminate\Auth\AuthenticationException;
use Log;
use Throwable;
use JsonException;
@@ -83,10 +84,12 @@ class Handler extends ExceptionHandler
switch ($e->getStatusCode()) {
case '404':
return response()->json(Helper::formatStandardApiResponse('error', null, $statusCode . ' endpoint not found'), 404);
case '405':
case '429':
return response()->json(Helper::formatStandardApiResponse('error', null, 'Too many requests'), 429);
case '405':
return response()->json(Helper::formatStandardApiResponse('error', null, 'Method not allowed'), 405);
default:
return response()->json(Helper::formatStandardApiResponse('error', null, $statusCode), 405);
return response()->json(Helper::formatStandardApiResponse('error', null, $statusCode), $statusCode);
}
}
@@ -142,4 +145,4 @@ class Handler extends ExceptionHandler
//
});
}
}
}
@@ -79,6 +79,8 @@ class AccessoriesController extends Controller
$accessory->qty = request('qty');
$accessory->user_id = Auth::user()->id;
$accessory->supplier_id = request('supplier_id');
$accessory->notes = request('notes');
$accessory = $request->handleImages($accessory);
@@ -143,6 +145,7 @@ class AccessoriesController extends Controller
$accessory->purchase_cost = Helper::ParseCurrency(request('purchase_cost'));
$accessory->qty = request('qty');
$accessory->supplier_id = request('supplier_id');
$accessory->notes = request('notes');
$accessory = $request->handleImages($accessory);
@@ -7,13 +7,22 @@ use App\Events\CheckoutDeclined;
use App\Events\ItemAccepted;
use App\Events\ItemDeclined;
use App\Http\Controllers\Controller;
use App\Models\Actionlog;
use App\Models\Asset;
use App\Models\CheckoutAcceptance;
use App\Models\Company;
use App\Models\Contracts\Acceptable;
use App\Models\User;
use App\Models\AssetModel;
use App\Models\Accessory;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use App\Http\Controllers\SettingsController;
use Barryvdh\DomPDF\Facade\Pdf;
use Carbon\Carbon;
class AcceptanceController extends Controller
{
@@ -39,6 +48,7 @@ class AcceptanceController extends Controller
{
$acceptance = CheckoutAcceptance::find($id);
if (is_null($acceptance)) {
return redirect()->route('account.accept')->with('error', trans('admin/hardware/message.does_not_exist'));
}
@@ -102,7 +112,8 @@ class AcceptanceController extends Controller
$data_uri = e($request->input('signature_output'));
$encoded_image = explode(',', $data_uri);
$decoded_image = base64_decode($encoded_image[1]);
Storage::put('private_uploads/signatures/'.$sig_filename, (string) $decoded_image);
$acceptance->stored_eula_file = 'accepted-eula-'.date('Y-m-d-h-i-s').'.pdf';
$path = Storage::put('private_uploads/signatures/'.$sig_filename, (string) $decoded_image);
}
if ($request->input('asset_acceptance') == 'accepted') {
@@ -111,6 +122,8 @@ class AcceptanceController extends Controller
event(new CheckoutAccepted($acceptance));
$return_msg = trans('admin/users/message.accepted');
} else {
$acceptance->decline($sig_filename);
@@ -119,6 +132,61 @@ class AcceptanceController extends Controller
$return_msg = trans('admin/users/message.declined');
}
$item = $acceptance->checkoutable_type::find($acceptance->checkoutable_id);
if ($acceptance->checkoutable_type== 'App\Models\Asset') {
$assigned_to = User::find($item->assigned_to);
$asset_model = AssetModel::find($item->model_id);
$branding_settings = SettingsController::getPDFBranding();
$data = [
'item_tag' => $item->asset_tag,
'item_model' => $asset_model->name,
'item_serial' => $item->serial,
'eula' => $item->getEula(),
'check_out_date' => Carbon::parse($acceptance->created_at)->format($branding_settings->date_display_format),
'accepted_date' => Carbon::parse($acceptance->accepted_at)->format($branding_settings->date_display_format),
'assigned_to' => $assigned_to->first_name . ' ' . $assigned_to->last_name,
'company_name' => $branding_settings->site_name,
'signature' => storage_path() . '/private_uploads/signatures/' . $sig_filename,
'logo' => public_path() . '/uploads/' . $branding_settings->logo,
'date_settings' => $branding_settings->date_display_format,
];
$pdf = Pdf::loadView('account.accept.accept-asset-eula', $data);
Storage::put('private_uploads/eula-pdfs/' . $acceptance->stored_eula_file, $pdf->output());
$a = new Actionlog();
$a->stored_eula = $item->getEula();
$a->stored_eula_file = $acceptance->stored_eula_file;
$a->save();
return redirect()->to('account/accept')->with('success', $return_msg);
}
//
$accessory_user= DB::table('checkout_acceptances')->find($acceptance->assigned_to_id);
$assigned_to = User::find($accessory_user->assigned_to_id);
$accessory_model = Accessory::find($item->id);
$branding_settings = SettingsController::getPDFBranding();
$data = [
'item_tag' => $item->model_number,
'item_model' => $accessory_model->name,
'eula' => $item->getEula(),
'check_out_date' => Carbon::parse($acceptance->created_at)->format($branding_settings->date_display_format),
'accepted_date' => Carbon::parse($acceptance->accepted_at)->format($branding_settings->date_display_format),
// 'assigned_by' => self
'assigned_to' => $assigned_to->first_name . ' ' . $assigned_to->last_name,
'company_name' => $branding_settings->site_name,
'signature' => storage_path() . '/private_uploads/signatures/' . $sig_filename,
'logo' => public_path() . '/uploads/' . $branding_settings->logo,
'date_settings' => $branding_settings->date_display_format,
];
$pdf = Pdf::loadView('account.accept.accept-accessory-eula', $data);
Storage::put('private_uploads/eula-pdfs/' . $acceptance->stored_eula_file, $pdf->output());
$a = new Actionlog();
$a->stored_eula = $item->getEula();
$a->stored_eula_file = $acceptance->stored_eula_file;
$a->save();
return redirect()->to('account/accept')->with('success', $return_msg);
}
}
}
+19 -2
View File
@@ -3,17 +3,34 @@
namespace App\Http\Controllers;
use App\Helpers\Helper;
use App\Models\Actionlog;
use Response;
class ActionlogController extends Controller
{
public function displaySig($filename)
{
// PHP doesn't let you handle file not found errors well with
// file_get_contents, so we set the error reporting for just this class
error_reporting(0);
$this->authorize('view', \App\Models\Asset::class);
$file = config('app.private_uploads').'/signatures/'.$filename;
$filetype = Helper::checkUploadIsImage($file);
$contents = file_get_contents($file);
return Response::make($contents)->header('Content-Type', $filetype);
$contents = file_get_contents($file, false, stream_context_create(['http' => ['ignore_errors' => true]]));
if ($contents === false) {
\Log::warn('File '.$file.' not found');
return false;
} else {
return Response::make($contents)->header('Content-Type', $filetype);
}
}
public function getStoredEula($filename){
$this->authorize('view', \App\Models\Asset::class);
$file = config('app.private_uploads').'/eula-pdfs/'.$filename;
return Response::download($file);
}
}
@@ -40,7 +40,8 @@ class AccessoriesController extends Controller
'notes',
'created_at',
'min_amt',
'company_id'
'company_id',
'notes',
];
@@ -70,6 +71,10 @@ class AccessoriesController extends Controller
$accessories->where('location_id','=',$request->input('location_id'));
}
if ($request->filled('notes')) {
$accessories->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 = (($accessories) && ($request->get('offset') > $accessories->count())) ? $accessories->count() : $request->get('offset', 0);
@@ -102,7 +102,7 @@ class AssetMaintenancesController extends Controller
*/
public function store(Request $request)
{
$this->authorize('edit', Asset::class);
$this->authorize('update', Asset::class);
// create a new model instance
$assetMaintenance = new AssetMaintenance();
$assetMaintenance->supplier_id = $request->input('supplier_id');
@@ -154,7 +154,7 @@ class AssetMaintenancesController extends Controller
*/
public function update(Request $request, $assetMaintenanceId = null)
{
$this->authorize('edit', Asset::class);
$this->authorize('update', Asset::class);
// Check if the asset maintenance exists
$assetMaintenance = AssetMaintenance::findOrFail($assetMaintenanceId);
@@ -218,7 +218,7 @@ class AssetMaintenancesController extends Controller
*/
public function destroy($assetMaintenanceId)
{
$this->authorize('edit', Asset::class);
$this->authorize('update', Asset::class);
// Check if the asset maintenance exists
$assetMaintenance = AssetMaintenance::findOrFail($assetMaintenanceId);
@@ -521,7 +521,7 @@ class AssetsController extends Controller
$asset->purchase_cost = Helper::ParseCurrency($request->get('purchase_cost')); // this is the API's store method, so I don't know that I want to do this? Confusing. FIXME (or not?!)
$asset->purchase_date = $request->get('purchase_date', null);
$asset->assigned_to = $request->get('assigned_to', null);
$asset->supplier_id = $request->get('supplier_id', 0);
$asset->supplier_id = $request->get('supplier_id');
$asset->requestable = $request->get('requestable', 0);
$asset->rtd_location_id = $request->get('rtd_location_id', null);
$asset->location_id = $request->get('rtd_location_id', null);
@@ -40,6 +40,7 @@ class ComponentsController extends Controller
'purchase_cost',
'qty',
'image',
'notes',
];
@@ -62,6 +63,10 @@ class ComponentsController extends Controller
$components->where('location_id', '=', $request->input('location_id'));
}
if ($request->filled('notes')) {
$components->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 = (($components) && ($request->get('offset') > $components->count())) ? $components->count() : $request->get('offset', 0);
@@ -42,6 +42,7 @@ class ConsumablesController extends Controller
'item_no',
'qty',
'image',
'notes',
];
@@ -74,6 +75,10 @@ class ConsumablesController extends Controller
$consumables->where('location_id','=',$request->input('location_id'));
}
if ($request->filled('notes')) {
$consumables->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.
@@ -52,6 +52,7 @@ class ReportsController extends Controller
'accept_signature',
'action_type',
'note',
'stored_eula_file',
];
$sort = in_array($request->input('sort'), $allowed_columns) ? e($request->input('sort')) : 'created_at';
@@ -30,6 +30,20 @@ class StatuslabelsController extends Controller
$statuslabels = $statuslabels->TextSearch($request->input('search'));
}
// if a status_type is passed, filter by that
if ($request->filled('status_type')) {
if (strtolower($request->input('status_type')) == 'pending') {
$statuslabels = $statuslabels->Pending();
} elseif (strtolower($request->input('status_type')) == 'archived') {
$statuslabels = $statuslabels->Archived();
} elseif (strtolower($request->input('status_type')) == 'deployable') {
$statuslabels = $statuslabels->Deployable();
} elseif (strtolower($request->input('status_type')) == 'undeployable') {
$statuslabels = $statuslabels->Undeployable();
}
}
// 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 = (($statuslabels) && ($request->get('offset') > $statuslabels->count())) ? $statuslabels->count() : $request->get('offset', 0);
@@ -80,8 +94,8 @@ class StatuslabelsController extends Controller
if ($statuslabel->save()) {
return response()->json(Helper::formatStandardApiResponse('success', $statuslabel, trans('admin/statuslabels/message.create.success')));
}
return response()->json(Helper::formatStandardApiResponse('error', null, $statuslabel->getErrors()));
}
/**
@@ -100,6 +114,7 @@ class StatuslabelsController extends Controller
return (new StatuslabelsTransformer)->transformStatuslabel($statuslabel);
}
/**
* Update the specified resource in storage.
*
@@ -116,6 +131,7 @@ class StatuslabelsController extends Controller
$request->except('deployable', 'pending', 'archived');
if (! $request->filled('type')) {
return response()->json(Helper::formatStandardApiResponse('error', null, 'Status label type is required.'));
}
@@ -161,6 +177,7 @@ class StatuslabelsController extends Controller
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/statuslabels/message.assoc_assets')));
}
/**
* Show a count of assets by status label for pie chart
*
+45 -1
View File
@@ -7,6 +7,7 @@ use App\Http\Controllers\Controller;
use App\Http\Requests\SaveUserRequest;
use App\Http\Transformers\AccessoriesTransformer;
use App\Http\Transformers\AssetsTransformer;
use App\Http\Transformers\ConsumablesTransformer;
use App\Http\Transformers\LicensesTransformer;
use App\Http\Transformers\SelectlistTransformer;
use App\Http\Transformers\UsersTransformer;
@@ -62,6 +63,7 @@ class UsersController extends Controller
'users.updated_at',
'users.username',
'users.zip',
'users.remote',
'users.ldap_import',
])->with('manager', 'groups', 'userloc', 'company', 'department', 'assets', 'licenses', 'accessories', 'consumables')
@@ -131,6 +133,30 @@ class UsersController extends Controller
$users = $users->where('users.manager_id','=',$request->input('manager_id'));
}
if ($request->filled('ldap_import')) {
$users = $users->where('ldap_import', '=', $request->input('ldap_import'));
}
if ($request->filled('remote')) {
$users = $users->where('remote', '=', $request->input('remote'));
}
if ($request->filled('assets_count')) {
$users->has('assets', '=', $request->input('assets_count'));
}
if ($request->filled('consumables_count')) {
$users->has('consumables', '=', $request->input('consumables_count'));
}
if ($request->filled('licenses_count')) {
$users->has('licenses', '=', $request->input('licenses_count'));
}
if ($request->filled('accessories_count')) {
$users->has('accessories', '=', $request->input('accessories_count'));
}
if ($request->filled('search')) {
$users = $users->TextSearch($request->input('search'));
}
@@ -166,7 +192,7 @@ class UsersController extends Controller
'assets', 'accessories', 'consumables', 'licenses', 'groups', 'activated', 'created_at',
'two_factor_enrolled', 'two_factor_optin', 'last_login', 'assets_count', 'licenses_count',
'consumables_count', 'accessories_count', 'phone', 'address', 'city', 'state',
'country', 'zip', 'id', 'ldap_import',
'country', 'zip', 'id', 'ldap_import', 'remote',
];
$sort = in_array($request->get('sort'), $allowed_columns) ? $request->get('sort') : 'first_name';
@@ -445,6 +471,24 @@ class UsersController extends Controller
return (new AssetsTransformer)->transformAssets($assets, $assets->count(), $request);
}
/**
* Return JSON containing a list of consumables assigned to a user.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v3.0]
* @param $userId
* @return string JSON
*/
public function consumables(Request $request, $id)
{
$this->authorize('view', User::class);
$this->authorize('view', Consumable::class);
$user = User::findOrFail($id);
$consumables = $user->consumables;
return (new ConsumablesTransformer)->transformConsumables($consumables, $consumables->count(), $request);
}
/**
* Return JSON containing a list of accessories assigned to a user.
*
@@ -65,7 +65,7 @@ class AssetMaintenancesController extends Controller
*/
public function create()
{
$this->authorize('edit', Asset::class);
$this->authorize('update', Asset::class);
$asset = null;
if ($asset = Asset::find(request('asset_id'))) {
@@ -96,7 +96,7 @@ class AssetMaintenancesController extends Controller
*/
public function store(Request $request)
{
$this->authorize('edit', Asset::class);
$this->authorize('update', Asset::class);
// create a new model instance
$assetMaintenance = new AssetMaintenance();
$assetMaintenance->supplier_id = $request->input('supplier_id');
@@ -148,7 +148,7 @@ class AssetMaintenancesController extends Controller
*/
public function edit($assetMaintenanceId = null)
{
$this->authorize('edit', Asset::class);
$this->authorize('update', Asset::class);
// Check if the asset maintenance exists
if (is_null($assetMaintenance = AssetMaintenance::find($assetMaintenanceId))) {
// Redirect to the improvement management page
@@ -199,7 +199,7 @@ class AssetMaintenancesController extends Controller
*/
public function update(Request $request, $assetMaintenanceId = null)
{
$this->authorize('edit', Asset::class);
$this->authorize('update', Asset::class);
// Check if the asset maintenance exists
if (is_null($assetMaintenance = AssetMaintenance::find($assetMaintenanceId))) {
// Redirect to the asset maintenance management page
@@ -267,7 +267,7 @@ class AssetMaintenancesController extends Controller
*/
public function destroy($assetMaintenanceId)
{
$this->authorize('edit', Asset::class);
$this->authorize('update', Asset::class);
// Check if the asset maintenance exists
if (is_null($assetMaintenance = AssetMaintenance::find($assetMaintenanceId))) {
// Redirect to the asset maintenance management page
@@ -111,6 +111,13 @@ class AssetCheckinController extends Controller
$checkin_at = $request->input('checkin_at');
}
if(!empty($asset->licenseseats->all())){
foreach ($asset->licenseseats as $seat){
$seat->assigned_to = null;
$seat->save();
}
}
// Get all pending Acceptances for this asset and delete them
$acceptances = CheckoutAcceptance::pending()->whereHasMorph('checkoutable',
[Asset::class],
@@ -80,6 +80,15 @@ class AssetCheckoutController extends Controller
$asset->status_id = $request->get('status_id');
}
if(!empty($asset->licenseseats->all())){
if(request('checkout_to_type') == 'user') {
foreach ($asset->licenseseats as $seat){
$seat->assigned_to = $target->id;
$seat->save();
}
}
}
if ($asset->checkOut($target, $admin, $checkout_at, $expected_checkin, e($request->get('note')), $request->get('name'))) {
return redirect()->route('hardware.index')->with('success', trans('admin/hardware/message.checkout.success'));
}
@@ -137,12 +137,12 @@ class AssetsController extends Controller
$asset->archived = '0';
$asset->physical = '1';
$asset->depreciate = '0';
$asset->status_id = request('status_id', 0);
$asset->status_id = request('status_id');
$asset->warranty_months = request('warranty_months', null);
$asset->purchase_cost = Helper::ParseCurrency($request->get('purchase_cost'));
$asset->purchase_date = request('purchase_date', null);
$asset->assigned_to = request('assigned_to', null);
$asset->supplier_id = request('supplier_id', 0);
$asset->supplier_id = request('supplier_id', null);
$asset->requestable = request('requestable', 0);
$asset->rtd_location_id = request('rtd_location_id', null);
@@ -235,6 +235,7 @@ class AssetsController extends Controller
->with('statuslabel_types', Helper::statusTypeList());
}
/**
* Returns a view that presents information about an asset for detail view.
*
@@ -309,6 +310,7 @@ class AssetsController extends Controller
$asset->location_id = $request->input('rtd_location_id', null);
}
if ($request->filled('image_delete')) {
try {
unlink(public_path().'/uploads/assets/'.$asset->image);
@@ -401,6 +403,24 @@ class AssetsController extends Controller
return redirect()->route('hardware.index')->with('success', trans('admin/hardware/message.delete.success'));
}
/**
* Searches the assets table by serial, and redirects if it finds one
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v3.0]
* @return Redirect
*/
public function getAssetBySerial(Request $request)
{
$topsearch = ($request->get('topsearch')=="true");
if (!$asset = Asset::where('serial', '=', $request->get('serial'))->first()) {
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.does_not_exist'));
}
$this->authorize('view', $asset);
return redirect()->route('hardware.show', $asset->id)->with('topsearch', $topsearch);
}
/**
* Searches the assets table by asset tag, and redirects if it finds one
*
@@ -420,6 +440,7 @@ class AssetsController extends Controller
return redirect()->route('hardware.show', $asset->id)->with('topsearch', $topsearch);
}
/**
* Return a QR code for the asset
*
@@ -792,6 +813,7 @@ class AssetsController extends Controller
return view('hardware/audit-overdue');
}
public function auditStore(Request $request, $id)
{
$this->authorize('audit', Asset::class);
@@ -822,6 +844,7 @@ class AssetsController extends Controller
$asset->location_id = $request->input('location_id');
}
if ($asset->save()) {
$file_name = '';
// Upload an image, if attached
@@ -838,7 +861,7 @@ class AssetsController extends Controller
$asset->logAudit($request->input('note'), $request->input('location_id'), $file_name);
return redirect()->to('hardware')->with('success', trans('admin/hardware/message.audit.success'));
return redirect()->route('assets.audit.due')->with('success', trans('admin/hardware/message.audit.success'));
}
}
@@ -68,12 +68,13 @@ class LoginController extends Controller
return redirect()->intended('/');
}
//If the environment is set to ALWAYS require SAML, go straight to the SAML route.
//We don't need to check other settings, as this should override those.
if(config('app.require_saml')) {
// If the environment is set to ALWAYS require SAML, go straight to the SAML route.
// We don't need to check other settings, as this should override those.
if (config('app.require_saml')) {
return redirect()->route('saml.login');
}
if ($this->saml->isEnabled() && Setting::getSettings()->saml_forcelogin == '1' && ! ($request->has('nosaml') || $request->session()->has('error'))) {
return redirect()->route('saml.login');
}
@@ -235,8 +236,9 @@ class LoginController extends Controller
*/
public function login(Request $request)
{
//If the environment is set to ALWAYS require SAML, return access denied
if(config('app.require_saml')) {
if (config('app.require_saml')) {
return view('errors.403');
}
@@ -80,6 +80,7 @@ class ComponentsController extends Controller
$component->purchase_cost = Helper::ParseCurrency($request->input('purchase_cost', null));
$component->qty = $request->input('qty');
$component->user_id = Auth::id();
$component->notes = $request->input('notes');
$component = $request->handleImages($component);
@@ -152,6 +153,7 @@ class ComponentsController extends Controller
$component->purchase_date = $request->input('purchase_date');
$component->purchase_cost = Helper::ParseCurrency(request('purchase_cost'));
$component->qty = $request->input('qty');
$component->notes = $request->input('notes');
$component = $request->handleImages($component);
@@ -78,6 +78,8 @@ class ConsumablesController extends Controller
$consumable->purchase_cost = Helper::ParseCurrency($request->input('purchase_cost'));
$consumable->qty = $request->input('qty');
$consumable->user_id = Auth::id();
$consumable->notes = $request->input('notes');
$consumable = $request->handleImages($consumable);
@@ -140,6 +142,7 @@ class ConsumablesController extends Controller
$consumable->purchase_date = $request->input('purchase_date');
$consumable->purchase_cost = Helper::ParseCurrency($request->input('purchase_cost'));
$consumable->qty = Helper::ParseFloat($request->input('qty'));
$consumable->notes = $request->input('notes');
$consumable = $request->handleImages($consumable);
+9 -4
View File
@@ -933,12 +933,17 @@ class ReportsController extends Controller
/**
* Get all assets with pending checkout acceptances
*/
$acceptances = CheckoutAcceptance::pending()->with('assignedTo')->get();
if($showDeleted) {
$acceptances = CheckoutAcceptance::pending()->withTrashed()->with(['assignedTo' , 'checkoutable.assignedTo', 'checkoutable.model'])->get();
} else {
$acceptances = CheckoutAcceptance::pending()->with(['assignedTo' => function ($query) {
$query->withTrashed();
}, 'checkoutable.assignedTo', 'checkoutable.model'])->get();
}
$assetsForReport = $acceptances
->filter(function($acceptance) {
return $acceptance->checkoutable_type == 'App\Models\Asset' && !is_null($acceptance->assignedTo);
->filter(function ($acceptance) {
return $acceptance->checkoutable_type == 'App\Models\Asset';
})
->map(function($acceptance) {
return ['assetItem' => $acceptance->checkoutable, 'acceptance' => $acceptance];
@@ -946,6 +946,7 @@ class SettingsController extends Controller
$setting->ldap_active_flag = $request->input('ldap_active_flag');
$setting->ldap_emp_num = $request->input('ldap_emp_num');
$setting->ldap_email = $request->input('ldap_email');
$setting->ldap_manager = $request->input('ldap_manager');
$setting->ad_domain = $request->input('ad_domain');
$setting->is_ad = $request->input('is_ad', '0');
$setting->ad_append_domain = $request->input('ad_append_domain', '0');
@@ -1025,6 +1026,12 @@ class SettingsController extends Controller
return redirect()->back()->withInput()->withErrors($setting->getErrors());
}
public static function getPDFBranding()
{
$pdf_branding= Setting::getSettings();
return $pdf_branding;
}
/**
* Show the listing of backups.
@@ -90,7 +90,9 @@ class BulkUsersController extends Controller
->conditionallyAddItem('department_id')
->conditionallyAddItem('company_id')
->conditionallyAddItem('locale')
->conditionallyAddItem('remote')
->conditionallyAddItem('activated');
// If the manager_id is one of the users being updated, generate a warning.
if (array_search($request->input('manager_id'), $user_raw_array)) {
$manager_conflict = true;
@@ -93,8 +93,8 @@ class UsersController extends Controller
$this->authorize('create', User::class);
$user = new User;
//Username, email, and password need to be handled specially because the need to respect config values on an edit.
$user->email = e($request->input('email'));
$user->username = e($request->input('username'));
$user->email = trim($request->input('email'));
$user->username = trim($request->input('username'));
if ($request->filled('password')) {
$user->password = bcrypt($request->input('password'));
}
@@ -115,6 +115,7 @@ class UsersController extends Controller
$user->state = $request->input('state', null);
$user->country = $request->input('country', null);
$user->zip = $request->input('zip', null);
$user->remote = $request->input('remote', 0);
// Strip out the superuser permission if the user isn't a superadmin
$permissions_array = $request->input('permission');
@@ -179,7 +180,6 @@ class UsersController extends Controller
if ($user = User::find($id)) {
$this->authorize('update', $user);
$permissions = config('permissions');
$groups = Group::pluck('name', 'id');
$userGroups = $user->groups()->pluck('name', 'id');
@@ -190,9 +190,7 @@ class UsersController extends Controller
return view('users/edit', compact('user', 'groups', 'userGroups', 'permissions', 'userPermissions'))->with('item', $user);
}
$error = trans('admin/users/message.user_not_found', compact('id'));
return redirect()->route('users.index')->with('error', $error);
return redirect()->route('users.index')->with('error', trans('admin/users/message.user_not_found', compact('id')));
}
/**
@@ -245,9 +243,9 @@ class UsersController extends Controller
// Update the user
if ($request->filled('username')) {
$user->username = $request->input('username');
$user->username = trim($request->input('username'));
}
$user->email = $request->input('email');
$user->email = trim($request->input('email'));
$user->first_name = $request->input('first_name');
$user->last_name = $request->input('last_name');
$user->two_factor_optin = $request->input('two_factor_optin') ?: 0;
@@ -267,6 +265,7 @@ class UsersController extends Controller
$user->country = $request->input('country', null);
$user->activated = $request->input('activated', 0);
$user->zip = $request->input('zip', null);
$user->remote = $request->input('remote', 0);
// Update the location of any assets checked out to this user
Asset::where('assigned_type', User::class)
@@ -623,7 +622,7 @@ class UsersController extends Controller
public function sendPasswordReset($id)
{
if (($user = User::find($id)) && ($user->activated == '1') && ($user->email != '') && ($user->ldap_import == '0')) {
$credentials = ['email' => $user->email];
$credentials = ['email' => trim($user->email)];
try {
\Password::sendResetLink($credentials, function (Message $message) use ($user) {
+1 -2
View File
@@ -24,6 +24,7 @@ class Kernel extends HttpKernel
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
\App\Http\Middleware\SecurityHeaders::class,
\App\Http\Middleware\PreventBackHistory::class,
\Fruitcake\Cors\HandleCors::class,
];
@@ -44,8 +45,6 @@ class Kernel extends HttpKernel
],
'api' => [
\Fruitcake\Cors\HandleCors::class,
'throttle:120,1',
'auth:api',
],
];
-15
View File
@@ -42,30 +42,15 @@ class SecurityHeaders
// - https://github.com/w3c/webappsec-feature-policy/issues/189
$feature_policy[] = "accelerometer 'none'";
$feature_policy[] = "ambient-light-sensor 'none'";
$feature_policy[] = "animations 'none'";
$feature_policy[] = "autoplay 'none'";
$feature_policy[] = "battery 'none'";
$feature_policy[] = "camera 'none'";
$feature_policy[] = "display-capture 'none'";
$feature_policy[] = "document-domain 'none'";
$feature_policy[] = "encrypted-media 'none'";
$feature_policy[] = "fullscreen 'none'";
$feature_policy[] = "geolocation 'none'";
$feature_policy[] = "gyroscope 'none'";
$feature_policy[] = "legacy-image-formats 'none'";
$feature_policy[] = "magnetometer 'none'";
$feature_policy[] = "microphone 'none'";
$feature_policy[] = "midi 'none'";
$feature_policy[] = "oversized-images 'none'";
$feature_policy[] = "payment 'none'";
$feature_policy[] = "picture-in-picture 'none'";
$feature_policy[] = "publickey-credentials 'none'";
$feature_policy[] = "sync-xhr 'none'";
$feature_policy[] = "unsized-media 'none'";
$feature_policy[] = "usb 'none'";
$feature_policy[] = "vibrate 'none'";
$feature_policy[] = "wake-lock 'none'";
$feature_policy[] = "xr-spatial-tracking 'none'";
$feature_policy = implode(';', $feature_policy);
+44 -54
View File
@@ -1,5 +1,4 @@
<?php
namespace App\Http\Transformers;
use App\Helpers\Helper;
@@ -9,73 +8,58 @@ use Illuminate\Database\Eloquent\Collection;
class ActionlogsTransformer
{
public function transformActionlogs(Collection $actionlogs, $total)
public function transformActionlogs (Collection $actionlogs, $total)
{
$array = [];
$array = array();
$settings = Setting::getSettings();
foreach ($actionlogs as $actionlog) {
$array[] = self::transformActionlog($actionlog, $settings);
}
return (new DatatablesTransformer)->transformDatatables($array, $total);
}
public function transformActionlog(Actionlog $actionlog, $settings = null)
private function clean_field($value)
{
// This object stuff is weird, and is used to make up for the fact that
// older data can get strangely formatted if an asset existed,
// then a new custom field is added, and the asset is saved again.
// It can result in funnily-formatted strings like:
//
// {"_snipeit_right_sized_fault_tolerant_localareanetwo_1":
// {"old":null,"new":{"value":"1579490695972","_snipeit_new_field_2":2,"_snipeit_new_field_3":"Monday, 20 January 2020 2:24:55 PM"}}
// so we have to walk down that next level
if(is_object($value) && isset($value->value)) {
return $this->clean_field($value->value);
}
return is_scalar($value) || is_null($value) ? e($value) : e(json_encode($value));
}
public function transformActionlog (Actionlog $actionlog, $settings = null)
{
$icon = $actionlog->present()->icon();
if ($actionlog->filename != '') {
$icon = e(Helper::filetype_icon($actionlog->filename));
if ($actionlog->filename!='') {
$icon = e(\App\Helpers\Helper::filetype_icon($actionlog->filename));
}
// This is necessary since we can't escape special characters within a JSON object
if (($actionlog->log_meta) && ($actionlog->log_meta != '')) {
if (($actionlog->log_meta) && ($actionlog->log_meta!='')) {
$meta_array = json_decode($actionlog->log_meta);
if ($meta_array) {
foreach ($meta_array as $key => $value) {
foreach ($value as $meta_key => $meta_value) {
if (is_array($meta_value)) {
foreach ($meta_value as $meta_value_key => $meta_value_value) {
if (is_scalar($meta_value_value)) {
$clean_meta[$key][$meta_value_key] = e($meta_value_value);
} else {
$clean_meta[$key][$meta_value_key] = 'invalid scalar: '.print_r($meta_value_value, true);
}
}
} else {
// This object stuff is weird, and is used to make up for the fact that
// older data can get strangely formatted if an asset existed,
// then a new custom field is added, and the asset is saved again.
// It can result in funnily-formatted strings like:
//
// {"_snipeit_right_sized_fault_tolerant_localareanetwo_1":
// {"old":null,"new":{"value":"1579490695972","_snipeit_new_field_2":2,"_snipeit_new_field_3":"Monday, 20 January 2020 2:24:55 PM"}}
// so we have to walk down that next level
if (is_object($meta_value)) {
foreach ($meta_value as $meta_value_key => $meta_value_value) {
if ($meta_value_key == 'value') {
$clean_meta[$key]['old'] = null;
$clean_meta[$key]['new'] = e($meta_value->value);
} else {
$clean_meta[$meta_value_key]['old'] = null;
$clean_meta[$meta_value_key]['new'] = e($meta_value_value);
}
}
} else {
$clean_meta[$key][$meta_key] = e($meta_value);
}
}
}
foreach ($meta_array as $fieldname => $fieldata) {
$clean_meta[$fieldname]['old'] = $this->clean_field($fieldata->old);
$clean_meta[$fieldname]['new'] = $this->clean_field($fieldata->new);
}
}
}
$array = [
'id' => (int) $actionlog->id,
'icon' => $icon,
'file' => ($actionlog->filename != '') ?
'file' => ($actionlog->filename!='') ?
[
'url' => route('show/assetfile', ['assetId' => $actionlog->item->id, 'fileId' => $actionlog->id]),
'filename' => $actionlog->filename,
@@ -84,7 +68,7 @@ class ActionlogsTransformer
'item' => ($actionlog->item) ? [
'id' => (int) $actionlog->item->id,
'name' => ($actionlog->itemType() == 'user') ? $actionlog->filename : e($actionlog->item->getDisplayNameAttribute()),
'name' => ($actionlog->itemType()=='user') ? $actionlog->filename : e($actionlog->item->getDisplayNameAttribute()),
'type' => e($actionlog->itemType()),
] : null,
'location' => ($actionlog->location) ? [
@@ -93,18 +77,18 @@ class ActionlogsTransformer
] : null,
'created_at' => Helper::getFormattedDateObject($actionlog->created_at, 'datetime'),
'updated_at' => Helper::getFormattedDateObject($actionlog->updated_at, 'datetime'),
'next_audit_date' => ($actionlog->itemType() == 'asset') ? Helper::getFormattedDateObject($actionlog->calcNextAuditDate(null, $actionlog->item), 'date') : null,
'next_audit_date' => ($actionlog->itemType()=='asset') ? Helper::getFormattedDateObject($actionlog->calcNextAuditDate(null, $actionlog->item), 'date'): null,
'days_to_next_audit' => $actionlog->daysUntilNextAudit($settings->audit_interval, $actionlog->item),
'action_type' => $actionlog->present()->actionType(),
'admin' => ($actionlog->user) ? [
'id' => (int) $actionlog->user->id,
'name' => e($actionlog->user->getFullNameAttribute()),
'first_name'=> e($actionlog->user->first_name),
'last_name'=> e($actionlog->user->last_name),
'last_name'=> e($actionlog->user->last_name)
] : null,
'target' => ($actionlog->target) ? [
'id' => (int) $actionlog->target->id,
'name' => ($actionlog->targetType() == 'user') ? e($actionlog->target->getFullNameAttribute()) : e($actionlog->target->getDisplayNameAttribute()),
'name' => ($actionlog->targetType()=='user') ? e($actionlog->target->getFullNameAttribute()) : e($actionlog->target->getDisplayNameAttribute()),
'type' => e($actionlog->targetType()),
] : null,
@@ -112,19 +96,25 @@ class ActionlogsTransformer
'signature_file' => ($actionlog->accept_signature) ? route('log.signature.view', ['filename' => $actionlog->accept_signature ]) : null,
'log_meta' => ((isset($clean_meta)) && (is_array($clean_meta))) ? $clean_meta: null,
'action_date' => ($actionlog->action_date) ? Helper::getFormattedDateObject($actionlog->action_date, 'datetime'): Helper::getFormattedDateObject($actionlog->created_at, 'datetime'),
'stored_eula_file' => ($actionlog->stored_eula_file) ? route('log.storedeula.download', ['filename' => $actionlog->stored_eula_file]) : null,
];
//\Log::info("Clean Meta is: ".print_r($clean_meta,true));
return $array;
}
public function transformCheckedoutActionlog(Collection $accessories_users, $total)
public function transformCheckedoutActionlog (Collection $accessories_users, $total)
{
$array = [];
$array = array();
foreach ($accessories_users as $user) {
$array[] = (new UsersTransformer)->transformUser($user);
}
return (new DatatablesTransformer)->transformDatatables($array, $total);
}
}
}
@@ -4,6 +4,7 @@ namespace App\Http\Transformers;
use App\Helpers\Helper;
use App\Models\Asset;
use App\Models\Setting;
use Gate;
use Illuminate\Database\Eloquent\Collection;
@@ -21,6 +22,9 @@ class AssetsTransformer
public function transformAsset(Asset $asset)
{
// This uses the getSettings() method so we're pulling from the cache versus querying the settings on single asset
$setting = Setting::getSettings();
$array = [
'id' => (int) $asset->id,
'name' => e($asset->name),
@@ -65,6 +69,8 @@ class AssetsTransformer
'name'=> e($asset->defaultLoc->name),
] : null,
'image' => ($asset->getImageUrl()) ? $asset->getImageUrl() : null,
'qr' => ($setting->qr_code=='1') ? config('app.url').'/uploads/barcodes/qr-'.str_slug($asset->asset_tag).'-'.str_slug($asset->id).'.png' : null,
'alt_barcode' => ($setting->alt_barcode_enabled=='1') ? config('app.url').'/uploads/barcodes/'.str_slug($setting->alt_barcode).'-'.str_slug($asset->asset_tag).'.png' : null,
'assigned_to' => $this->transformAssignedTo($asset),
'warranty_months' => ($asset->warranty_months > 0) ? e($asset->warranty_months.' '.trans('admin/hardware/form.months')) : null,
'warranty_expires' => ($asset->warranty_months > 0) ? Helper::getFormattedDateObject($asset->warranty_expires, 'date') : null,
@@ -45,6 +45,7 @@ class ComponentsTransformer
'id' => (int) $component->company->id,
'name' => e($component->company->name),
] : null,
'notes' => ($component->notes) ? e($component->notes) : null,
'created_at' => Helper::getFormattedDateObject($component->created_at, 'datetime'),
'updated_at' => Helper::getFormattedDateObject($component->updated_at, 'datetime'),
'user_can_checkout' => ($component->numRemaining() > 0) ? 1 : 0,
@@ -38,6 +38,7 @@ class ConsumablesTransformer
'purchase_cost' => Helper::formatCurrencyOutput($consumable->purchase_cost),
'purchase_date' => Helper::getFormattedDateObject($consumable->purchase_date, 'date'),
'qty' => (int) $consumable->qty,
'notes' => ($consumable->notes) ? e($consumable->notes) : null,
'created_at' => Helper::getFormattedDateObject($consumable->created_at, 'datetime'),
'updated_at' => Helper::getFormattedDateObject($consumable->updated_at, 'datetime'),
];
@@ -28,6 +28,7 @@ class UsersTransformer
'first_name' => e($user->first_name),
'last_name' => e($user->last_name),
'username' => e($user->username),
'remote' => ($user->remote == '1') ? true : false,
'locale' => ($user->locale) ? e($user->locale) : null,
'employee_num' => e($user->employee_num),
'manager' => ($user->manager) ? [
+4 -1
View File
@@ -39,6 +39,7 @@ class AssetImporter extends ItemImporter
}
}
$this->createAssetIfNotExists($row);
}
@@ -68,7 +69,7 @@ class AssetImporter extends ItemImporter
$this->log('No Matching Asset, Creating a new one');
$asset = new Asset;
}
$this->item['notes'] = $this->findCsvMatch($row, 'asset_notes');
$this->item['image'] = $this->findCsvMatch($row, 'image');
$this->item['requestable'] = $this->fetchHumanBoolean($this->findCsvMatch($row, 'requestable'));
$asset->requestable = $this->fetchHumanBoolean($this->findCsvMatch($row, 'requestable'));
@@ -96,10 +97,12 @@ class AssetImporter extends ItemImporter
$item['rtd_location_id'] = $this->item['location_id'];
}
$item['last_audit_date'] = null;
if (isset($this->item['last_audit_date'])) {
$item['last_audit_date'] = $this->item['last_audit_date'];
}
$item['next_audit_date'] = null;
if (isset($this->item['next_audit_date'])) {
$item['next_audit_date'] = $this->item['next_audit_date'];
}
+2
View File
@@ -221,6 +221,7 @@ class ItemImporter extends Importer
$this->log('Matching Model found, updating it.');
$item = $this->sanitizeItemForStoring($asset_model, $editingModel);
$item['name'] = $asset_model_name;
$item['notes'] = $this->findCsvMatch($row, 'model_notes');
if(!empty($asset_modelNumber)){
$item['model_number'] = $asset_modelNumber;
@@ -238,6 +239,7 @@ class ItemImporter extends Importer
$item = $this->sanitizeItemForStoring($asset_model, $editingModel);
$item['name'] = $asset_model_name;
$item['model_number'] = $asset_modelNumber;
$item['notes'] = $this->findCsvMatch($row, 'model_notes');
$asset_model->fill($item);
$item = null;
+11 -3
View File
@@ -35,7 +35,7 @@ class LicenseImporter extends ItemImporter
->first();
if ($license) {
if (! $this->updating) {
$this->log('A matching License '.$this->item['name'].'with serial '.$this->item['serial'].' already exists');
$this->log('A matching License '.$this->item['name'].' with serial '.$this->item['serial'].' already exists');
return;
}
@@ -47,14 +47,22 @@ class LicenseImporter extends ItemImporter
$license = new License;
}
$asset_tag = $this->item['asset_tag'] = $this->findCsvMatch($row, 'asset_tag'); // used for checkout out to an asset.
$this->item['expiration_date'] = $this->findCsvMatch($row, 'expiration_date');
$this->item["expiration_date"] = null;
if ($this->findCsvMatch($row, "expiration_date")!='') {
$this->item["expiration_date"] = date("Y-m-d 00:00:01", strtotime($this->findCsvMatch($row, "expiration_date")));
}
$this->item['license_email'] = $this->findCsvMatch($row, 'license_email');
$this->item['license_name'] = $this->findCsvMatch($row, 'license_name');
$this->item['maintained'] = $this->findCsvMatch($row, 'maintained');
$this->item['purchase_order'] = $this->findCsvMatch($row, 'purchase_order');
$this->item['reassignable'] = $this->findCsvMatch($row, 'reassignable');
$this->item['seats'] = $this->findCsvMatch($row, 'seats');
$this->item['termination_date'] = $this->findCsvMatch($row, 'termination_date');
$this->item["termination_date"] = null;
if ($this->findCsvMatch($row, "termination_date")!='') {
$this->item["termination_date"] = date("Y-m-d 00:00:01", strtotime($this->findCsvMatch($row, "termination_date")));
}
if ($editingLicense) {
$license->update($this->sanitizeItemForUpdating($license));
+2 -1
View File
@@ -34,11 +34,12 @@ class LogListener
public function onCheckoutAccepted(CheckoutAccepted $event)
{
$logaction = new Actionlog();
$logaction = new Actionlog();
$logaction->item()->associate($event->acceptance->checkoutable);
$logaction->target()->associate($event->acceptance->assignedTo);
$logaction->accept_signature = $event->acceptance->signature_filename;
$logaction->stored_eula_file = $event->acceptance->stored_eula_file;
$logaction->action_type = 'accepted';
// TODO: log the actual license seat that was checked out
+5 -2
View File
@@ -37,7 +37,7 @@ class Accessory extends SnipeModel
*
* @var array
*/
protected $searchableAttributes = ['name', 'model_number', 'order_number', 'purchase_date'];
protected $searchableAttributes = ['name', 'model_number', 'order_number', 'purchase_date', 'notes'];
/**
* The relations and their attributes that should be included when searching the model.
@@ -64,6 +64,7 @@ class Accessory extends SnipeModel
'purchase_cost' => 'numeric|nullable',
];
/**
* Whether the model should inject it's identifier to the unique
* validation rules before attempting validation. If this property
@@ -94,6 +95,7 @@ class Accessory extends SnipeModel
'qty',
'min_amt',
'requestable',
'notes',
];
@@ -110,6 +112,7 @@ class Accessory extends SnipeModel
return $this->belongsTo(\App\Models\Supplier::class, 'supplier_id');
}
/**
* Sets the requestable attribute on the accessory
*
@@ -220,8 +223,8 @@ class Accessory extends SnipeModel
if ($this->image) {
return Storage::disk('public')->url(app('accessories_upload_path').$this->image);
}
return false;
}
/**
+1 -1
View File
@@ -25,7 +25,7 @@ class Actionlog extends SnipeModel
protected $table = 'action_logs';
public $timestamps = true;
protected $fillable = ['created_at', 'item_type', 'user_id', 'item_id', 'action_type', 'note', 'target_id', 'target_type'];
protected $fillable = ['created_at', 'item_type', 'user_id', 'item_id', 'action_type', 'note', 'target_id', 'target_type', 'stored_eula', 'stored_eula_file'];
use Searchable;
+15
View File
@@ -195,10 +195,25 @@ class Asset extends Depreciable
$model = AssetModel::find($this->model_id);
if (($model) && ($model->fieldset)) {
foreach ($model->fieldset->fields as $field){
if($field->format == 'BOOLEAN'){
$this->{$field->db_column} = filter_var($this->{$field->db_column}, FILTER_VALIDATE_BOOLEAN);
}
}
$this->rules += $model->fieldset->validation_rules();
foreach ($this->model->fieldset->fields as $field){
if($field->format == 'BOOLEAN'){
$this->{$field->db_column} = filter_var($this->{$field->db_column}, FILTER_VALIDATE_BOOLEAN);
}
}
}
}
return parent::save($params);
}
+2 -1
View File
@@ -65,6 +65,7 @@ class Component extends SnipeModel
'order_number',
'qty',
'serial',
'notes',
];
use Searchable;
@@ -74,7 +75,7 @@ class Component extends SnipeModel
*
* @var array
*/
protected $searchableAttributes = ['name', 'order_number', 'serial', 'purchase_cost', 'purchase_date'];
protected $searchableAttributes = ['name', 'order_number', 'serial', 'purchase_cost', 'purchase_date', 'notes'];
/**
* The relations and their attributes that should be included when searching the model.
+4 -2
View File
@@ -27,7 +27,8 @@ class Consumable extends SnipeModel
'category_id' => 'integer',
'company_id' => 'integer',
'qty' => 'integer',
'min_amt' => 'integer', ];
'min_amt' => 'integer',
];
/**
* Category validation rules
@@ -70,6 +71,7 @@ class Consumable extends SnipeModel
'qty',
'min_amt',
'requestable',
'notes',
];
use Searchable;
@@ -79,7 +81,7 @@ class Consumable extends SnipeModel
*
* @var array
*/
protected $searchableAttributes = ['name', 'order_number', 'purchase_cost', 'purchase_date', 'item_no', 'model_number'];
protected $searchableAttributes = ['name', 'order_number', 'purchase_cost', 'purchase_date', 'item_no', 'model_number', 'notes'];
/**
* The relations and their attributes that should be included when searching the model.
+2
View File
@@ -208,6 +208,7 @@ class Ldap extends Model
$ldap_result_jobtitle = Setting::getSettings()->ldap_jobtitle;
$ldap_result_country = Setting::getSettings()->ldap_country;
$ldap_result_dept = Setting::getSettings()->ldap_dept;
$ldap_result_manager = Setting::getSettings()->ldap_manager;
// Get LDAP user data
$item = [];
$item['username'] = isset($ldapattributes[$ldap_result_username][0]) ? $ldapattributes[$ldap_result_username][0] : '';
@@ -219,6 +220,7 @@ class Ldap extends Model
$item['jobtitle'] = isset($ldapattributes[$ldap_result_jobtitle][0]) ? $ldapattributes[$ldap_result_jobtitle][0] : '';
$item['country'] = isset($ldapattributes[$ldap_result_country][0]) ? $ldapattributes[$ldap_result_country][0] : '';
$item['department'] = isset($ldapattributes[$ldap_result_dept][0]) ? $ldapattributes[$ldap_result_dept][0] : '';
$item['manager'] = isset($ldapattributes[$ldap_result_manager][0]) ? $ldapattributes[$ldap_result_manager][0] : '';
return $item;
}
+1 -1
View File
@@ -9,6 +9,6 @@ class AdminRecipient extends Recipient
public function __construct()
{
$settings = Setting::getSettings();
$this->email = $settings->admin_cc_email;
$this->email = trim($settings->admin_cc_email);
}
}
+12
View File
@@ -120,6 +120,18 @@ class Statuslabel extends SnipeModel
->where('deployable', '=', 1);
}
/**
* Query builder scope for undeployable status types
*
* @return \Illuminate\Database\Query\Builder Modified query builder
*/
public function scopeUndeployable()
{
return $this->where('pending', '=', 0)
->where('archived', '=', 0)
->where('deployable', '=', 0);
}
/**
* Helper function to determine type attributes
*
+1 -1
View File
@@ -58,6 +58,7 @@ class User extends SnipeModel implements AuthenticatableContract, AuthorizableCo
'state',
'username',
'zip',
'remote',
];
protected $casts = [
@@ -73,7 +74,6 @@ class User extends SnipeModel implements AuthenticatableContract, AuthorizableCo
* @var array
*/
// 'username' => 'required|string|min:1|unique:users,username,NULL,id,deleted_at,NULL',
protected $rules = [
'first_name' => 'required|string|min:1',
'username' => 'required|string|min:1|unique_undeleted',
+7 -4
View File
@@ -17,12 +17,15 @@ class AssetObserver
*/
public function updating(Asset $asset)
{
$attributes = $asset->getAttributes();
$attributesOriginal = $asset->getAttributes();
// If the asset isn't being checked out or audited, log the update.
// (Those other actions already create log entries.)
if (($asset->getAttributes()['assigned_to'] == $asset->getOriginal()['assigned_to'])
&& ($asset->getAttributes()['next_audit_date'] == $asset->getOriginal()['next_audit_date'])
&& ($asset->getAttributes()['last_checkout'] == $asset->getOriginal()['last_checkout'])) {
if (($attributes['assigned_to'] == $attributesOriginal['assigned_to'])
&& ((isset( $attributes['next_audit_date']) ? $attributes['next_audit_date'] : null) == (isset($attributesOriginal['next_audit_date']) ? $attributesOriginal['next_audit_date']: null))
&& ($attributes['last_checkout'] == $attributesOriginal['last_checkout'])) {
$changed = [];
foreach ($asset->getOriginal() as $key => $value) {
+10
View File
@@ -59,7 +59,9 @@ class AccessoryPresenter extends Presenter
'field' => 'manufacturer',
'searchable' => true,
'sortable' => true,
'switchable' => true,
'title' => trans('general.manufacturer'),
'visible' => false,
'formatter' => 'manufacturersLinkObjFormatter',
], [
'field' => 'supplier',
@@ -89,6 +91,7 @@ class AccessoryPresenter extends Presenter
'field' => 'remaining_qty',
'searchable' => false,
'sortable' => false,
'visible' => false,
'title' => trans('admin/accessories/general.remaining'),
], [
'field' => 'purchase_date',
@@ -110,6 +113,13 @@ class AccessoryPresenter extends Presenter
'sortable' => true,
'visible' => false,
'title' => trans('general.order_number'),
],[
'field' => 'notes',
'searchable' => true,
'sortable' => true,
'visible' => false,
'title' => trans('general.notes'),
'formatter' => 'notesFormatter'
], [
'field' => 'change',
'searchable' => false,
+7
View File
@@ -103,6 +103,13 @@ class ComponentPresenter extends Presenter
'visible' => true,
'footerFormatter' => 'sumFormatterQuantity',
'class' => 'text-right',
], [
'field' => 'notes',
'searchable' => true,
'sortable' => true,
'visible' => false,
'title' => trans('general.notes'),
'formatter' => 'notesFormatter',
],
];
+7
View File
@@ -115,6 +115,13 @@ class ConsumablePresenter extends Presenter
'visible' => true,
'footerFormatter' => 'sumFormatterQuantity',
'class' => 'text-right',
], [
'field' => 'notes',
'searchable' => true,
'sortable' => true,
'visible' => false,
'title' => trans('general.notes'),
'formatter' => 'notesFormatter',
], [
'field' => 'change',
'searchable' => false,
+9
View File
@@ -85,6 +85,15 @@ class UserPresenter extends Presenter
'visible' => true,
'formatter' => 'usersLinkFormatter',
],
[
'field' => 'remote',
'searchable' => false,
'sortable' => true,
'switchable' => true,
'title' => trans('admin/users/general.remote'),
'visible' => false,
'formatter' => 'trueFalseFormatter',
],
[
'field' => 'email',
'searchable' => true,
+16 -6
View File
@@ -121,6 +121,7 @@ class AuthServiceProvider extends ServiceProvider
}
});
// -----------------------------------------
// Reports
// -----------------------------------------
@@ -174,12 +175,21 @@ class AuthServiceProvider extends ServiceProvider
// to the logged in API user, but creating assets, licenses, etc won't work
// if the user can't view and interact with the select lists.
Gate::define('view.selectlists', function ($user) {
return $user->can(['create','update'], Asset::class)
|| $user->can(['create','update'], License::class)
|| $user->can(['create','update'], Component::class)
|| $user->can(['create','update'], Consumable::class)
|| $user->can(['create','update'], Accessory::class)
|| $user->can(['create','update'], User::class);
return $user->can('update', Asset::class)
|| $user->can('create', Asset::class)
|| $user->can('checkout', Asset::class)
|| $user->can('checkin', Asset::class)
|| $user->can('audit', Asset::class)
|| $user->can('update', License::class)
|| $user->can('create', License::class)
|| $user->can('update', Component::class)
|| $user->can('create', Component::class)
|| $user->can('update', Consumable::class)
|| $user->can('create', Consumable::class)
|| $user->can('update', Accessory::class)
|| $user->can('create', Accessory::class)
|| $user->can('update', User::class)
|| $user->can('create', User::class);
});
}
}
+2 -1
View File
@@ -20,6 +20,7 @@
"alek13/slack": "^2.0",
"bacon/bacon-qr-code": "^2.0",
"barryvdh/laravel-debugbar": "^3.6",
"barryvdh/laravel-dompdf": "^1.0",
"doctrine/cache": "^1.10",
"doctrine/common": "^2.12",
"doctrine/dbal": "^3.1",
@@ -30,7 +31,7 @@
"erusev/parsedown": "^1.7",
"facade/ignition": "^2.10",
"fideloper/proxy": "^4.3",
"fruitcake/laravel-cors": "^2.0",
"fruitcake/laravel-cors": "^2.2",
"guzzlehttp/guzzle": "^7.0.1",
"intervention/image": "^2.5",
"javiereguiluz/easyslugger": "^1.0",
Generated
+1067 -765
View File
File diff suppressed because it is too large Load Diff
+14 -1
View File
@@ -250,6 +250,7 @@ return [
'enable_csp' => env('ENABLE_CSP', false),
/*
|--------------------------------------------------------------------------
| Require SAML Login
@@ -265,7 +266,6 @@ return [
'require_saml' => env('REQUIRE_SAML', false),
/*
|--------------------------------------------------------------------------
| Demo Mode Lockdown
@@ -328,6 +328,7 @@ return [
Illuminate\Translation\TranslationServiceProvider::class,
Illuminate\Validation\ValidationServiceProvider::class,
Illuminate\View\ViewServiceProvider::class,
Barryvdh\DomPDF\ServiceProvider::class,
/*
* Package Service Providers...
@@ -396,6 +397,7 @@ return [
'Mail' => Illuminate\Support\Facades\Mail::class,
'Notification' => Illuminate\Support\Facades\Notification::class,
'Password' => Illuminate\Support\Facades\Password::class,
'PDF' => Barryvdh\DomPDF\Facade::class,
'Queue' => Illuminate\Support\Facades\Queue::class,
'Redirect' => Illuminate\Support\Facades\Redirect::class,
'Redis' => Illuminate\Support\Facades\Redis::class,
@@ -418,4 +420,15 @@ return [
],
/*
|--------------------------------------------------------------------------
| API Throttling
|--------------------------------------------------------------------------
|
| This value determines the number of API requests permitted per minute
|
*/
'api_throttle_per_minute' => env('API_THROTTLE_PER_MINUTE', 120),
];
+36 -31
View File
@@ -1,44 +1,49 @@
<?php
/**
* ---------------------------------------------------------------------
* THIS IS $allowed_origins code IS NOT PART OF THE ORIGINAL CORS PACKAGE.
* IT IS A MODIFICATION BY SNIPE-IT TO ALLOW ADDING ALLOWED ORIGINS VIA THE ENV.
* ---------------------------------------------------------------------
*
* Since we don't really want people editing config files (lest they get
* overwritten later), this enables the person managing the Snipe-IT
* installation to modify these values without modifying the code.
*
* If APP_CORS_ALLOWED_ORIGINS is not set in the .env (for example if no one added it
* after an upgrade from a previous version that didn't include it in the .env.example) or is null,
* set it to * to allow all. If there is a value, either a single url or a comma-delimited
* list of urls, explode that out into an array to whitelist just those urls.
*/
$allowed_origins = env('CORS_ALLOWED_ORIGINS') !== null ?
explode(',', env('CORS_ALLOWED_ORIGINS')) : [];
/**
* Original Laravel CORS package config file modifications end here
*
*/
return [
'supportsCredentials' => false,
'allowedOrigins' => ['*'],
'allowedHeaders' => ['*'],
'allowedMethods' => ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'],
'maxAge' => 0,
/*
|--------------------------------------------------------------------------
| Cross-Origin Resource Sharing (CORS) Configuration
| Laravel CORS
|--------------------------------------------------------------------------
|
| Here you may configure your settings for cross-origin resource sharing
| or "CORS". This determines what cross-origin operations may execute
| in web browsers. You are free to adjust these settings as needed.
|
| To learn more: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
| allowedOrigins, allowedHeaders and allowedMethods can be set to array('*')
| to accept any value.
|
*/
'supports_credentials' => false,
'allowed_origins' => $allowed_origins,
'allowed_origins_patterns' => [],
'allowed_headers' => ['*'],
'allowed_methods' => ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'],
'exposed_headers' => [],
'max_age' => 0,
'paths' => ['api/*', 'sanctum/csrf-cookie'],
'allowed_methods' => ['*'],
'allowed_origins' => ['*'],
'allowed_origins_patterns' => [],
'allowed_headers' => ['*'],
'exposed_headers' => [],
'max_age' => 0,
'supports_credentials' => false,
];
+5 -5
View File
@@ -1,10 +1,10 @@
<?php
return array (
'app_version' => 'v6.0.0-RC3',
'full_app_version' => 'v6.0.0-RC3 - build 6627-g2815e0d36',
'build_version' => '6627',
'app_version' => 'v6.0.0-RC-6',
'full_app_version' => 'v6.0.0-RC-6 - build 6785-gab18ceb2f',
'build_version' => '6785',
'prerelease_version' => '',
'hash_version' => 'g2815e0d36',
'full_hash' => 'v6.0.0-RC3-6-g2815e0d36',
'hash_version' => 'gab18ceb2f',
'full_hash' => 'v6.0.0-RC-6-gab18ceb2f',
'branch' => 'develop',
);
+3 -3
View File
@@ -8,7 +8,6 @@ use App\Models\Category;
use App\Models\Location;
use App\Models\Supplier;
use Illuminate\Database\Eloquent\Factories\Factory;
use App\Models\StatusLabel;
/*
|--------------------------------------------------------------------------
@@ -39,15 +38,16 @@ class AssetFactory extends Factory
{
return [
'name' => null,
'rtd_location_id' => Location::factory()->create(),
'rtd_location_id' => Location::all()->random()->id,
'serial' => $this->faker->uuid,
'status_id' => $this->faker->numberBetween(1,5),
'status_id' => 1,
'user_id' => 1,
'asset_tag' => $this->faker->unixTime('now'),
'notes' => 'Created by DB seeder',
'purchase_date' => $this->faker->dateTimeBetween('-1 years', 'now', date_default_timezone_get()),
'purchase_cost' => $this->faker->randomFloat(2, '299.99', '2999.99'),
'order_number' => $this->faker->numberBetween(1000000, 50000000),
'supplier_id' => Supplier::all()->random()->id,
'requestable' => $this->faker->boolean(),
'assigned_to' => null,
'assigned_type' => null,
@@ -13,20 +13,22 @@ class CreateCheckoutAcceptancesTable extends Migration
*/
public function up()
{
Schema::create('checkout_acceptances', function (Blueprint $table) {
$table->increments('id');
if (!Schema::hasTable('checkout_acceptances')) {
Schema::create('checkout_acceptances', function (Blueprint $table) {
$table->increments('id');
$table->morphs('checkoutable');
$table->integer('assigned_to_id')->nullable();
$table->morphs('checkoutable');
$table->integer('assigned_to_id')->nullable();
$table->string('signature_filename')->nullable();
$table->string('signature_filename')->nullable();
$table->timestamp('accepted_at')->nullable();
$table->timestamp('declined_at')->nullable();
$table->timestamp('accepted_at')->nullable();
$table->timestamp('declined_at')->nullable();
$table->timestamps();
$table->softDeletes();
});
$table->timestamps();
$table->softDeletes();
});
}
}
/**
@@ -36,6 +38,8 @@ class CreateCheckoutAcceptancesTable extends Migration
*/
public function down()
{
Schema::dropIfExists('checkout_acceptances');
if (Schema::hasTable('checkout_acceptances')) {
Schema::dropIfExists('checkout_acceptances');
}
}
}
@@ -1,7 +1,9 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Support\Facades\Schema;
class AddKitsLicensesTable extends Migration
{
@@ -12,15 +14,16 @@ class AddKitsLicensesTable extends Migration
*/
public function up()
{
//
Schema::create('kits_licenses', function ($table) {
$table->increments('id');
$table->integer('kit_id')->nullable()->default(null);
$table->integer('license_id')->nullable()->default(null);
$table->integer('quantity')->default(1);
$table->timestamps();
});
}
if (!Schema::hasTable('kits_licenses')) {
Schema::create('kits_licenses', function ($table) {
$table->increments('id');
$table->integer('kit_id')->nullable()->default(NULL);
$table->integer('license_id')->nullable()->default(NULL);
$table->integer('quantity')->default(1);
$table->timestamps();
});
}
}
/**
* Reverse the migrations.
@@ -29,7 +32,9 @@ class AddKitsLicensesTable extends Migration
*/
public function down()
{
//
Schema::drop('kits_licenses');
}
if (Schema::hasTable('kits_licenses')) {
Schema::drop('kits_licenses');
}
}
}
@@ -1,7 +1,8 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Support\Facades\Schema;
class AddKitsTable extends Migration
{
@@ -12,14 +13,16 @@ class AddKitsTable extends Migration
*/
public function up()
{
//
Schema::create('kits', function ($table) {
$table->increments('id');
$table->string('name')->nullable()->default(null);
$table->timestamps();
$table->engine = 'InnoDB';
});
}
if (!Schema::hasTable('kits')) {
Schema::create('kits', function ($table) {
$table->increments('id');
$table->string('name')->nullable()->default(NULL);
$table->timestamps();
$table->engine = 'InnoDB';
});
}
}
/**
* Reverse the migrations.
@@ -28,7 +31,10 @@ class AddKitsTable extends Migration
*/
public function down()
{
//
Schema::drop('kits');
}
if (Schema::hasTable('kits')) {
Schema::drop('kits');
}
}
}
@@ -1,7 +1,9 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Support\Facades\Schema;
class AddKitsModelsTable extends Migration
{
@@ -12,15 +14,16 @@ class AddKitsModelsTable extends Migration
*/
public function up()
{
//
Schema::create('kits_models', function ($table) {
$table->increments('id');
$table->integer('kit_id')->nullable()->default(null);
$table->integer('model_id')->nullable()->default(null);
$table->integer('quantity')->default(1);
$table->timestamps();
});
}
if (!Schema::hasTable('kits_models')) {
Schema::create('kits_models', function ($table) {
$table->increments('id');
$table->integer('kit_id')->nullable()->default(NULL);
$table->integer('model_id')->nullable()->default(NULL);
$table->integer('quantity')->default(1);
$table->timestamps();
});
}
}
/**
* Reverse the migrations.
@@ -29,7 +32,9 @@ class AddKitsModelsTable extends Migration
*/
public function down()
{
//
Schema::drop('kits_models');
if (Schema::hasTable('kits_models')) {
Schema::drop('kits_models');
}
}
}
@@ -1,8 +1,8 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class AddKitsConsumablesTable extends Migration
{
@@ -13,14 +13,15 @@ class AddKitsConsumablesTable extends Migration
*/
public function up()
{
//
Schema::create('kits_consumables', function ($table) {
$table->increments('id');
$table->integer('kit_id')->nullable()->default(null);
$table->integer('consumable_id')->nullable()->default(null);
$table->integer('quantity')->default(1);
$table->timestamps();
});
if (!Schema::hasTable('kits_consumables')) {
Schema::create('kits_consumables', function ($table) {
$table->increments('id');
$table->integer('kit_id')->nullable()->default(NULL);
$table->integer('consumable_id')->nullable()->default(NULL);
$table->integer('quantity')->default(1);
$table->timestamps();
});
}
}
/**
@@ -30,7 +31,8 @@ class AddKitsConsumablesTable extends Migration
*/
public function down()
{
//
Schema::drop('kits_consumables');
if (Schema::hasTable('kits_consumables')) {
Schema::drop('kits_consumables');
}
}
}
@@ -1,8 +1,8 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class AddKitsAccessoriesTable extends Migration
{
@@ -13,14 +13,15 @@ class AddKitsAccessoriesTable extends Migration
*/
public function up()
{
//
Schema::create('kits_accessories', function ($table) {
$table->increments('id');
$table->integer('kit_id')->nullable()->default(null);
$table->integer('accessory_id')->nullable()->default(null);
$table->integer('quantity')->default(1);
$table->timestamps();
});
if (!Schema::hasTable('kits_accessories')) {
Schema::create('kits_accessories', function ($table) {
$table->increments('id');
$table->integer('kit_id')->nullable()->default(NULL);
$table->integer('accessory_id')->nullable()->default(NULL);
$table->integer('quantity')->default(1);
$table->timestamps();
});
}
}
/**
@@ -30,7 +31,8 @@ class AddKitsAccessoriesTable extends Migration
*/
public function down()
{
//
Schema::drop('kits_accessories');
if (Schema::hasTable('kits_accessories')) {
Schema::drop('kits_accessories');
}
}
}
@@ -0,0 +1,34 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddNotesToAccessories extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('accessories', function (Blueprint $table) {
$table->text("notes")->nullable()->default(null);
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('accessories', function (Blueprint $table) {
if (Schema::hasColumn('accessories', 'notes')) {
$table->dropColumn('notes');
}
});
}
}
@@ -0,0 +1,34 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddNotesToComponents extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('components', function (Blueprint $table) {
$table->text("notes")->nullable()->default(null);
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('components', function (Blueprint $table) {
if (Schema::hasColumn('components', 'notes')) {
$table->dropColumn('notes');
}
});
}
}
@@ -0,0 +1,34 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddNotesToConsumables extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('consumables', function (Blueprint $table) {
$table->text("notes")->nullable()->default(null);
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('consumables', function (Blueprint $table) {
if (Schema::hasColumn('consumables', 'notes')) {
$table->dropColumn('notes');
}
});
}
}
@@ -0,0 +1,34 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddRemoteToUser extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('users', function (Blueprint $table) {
$table->boolean('remote')->nullable()->default(0);
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('users', function (Blueprint $table) {
if (Schema::hasColumn('users', 'remote')) {
$table->dropColumn('remote');
}
});
}
}
@@ -0,0 +1,38 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddEulaToCheckoutAcceptance extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('checkout_acceptances', function (Blueprint $table) {
$table->text('stored_eula')->nullable()->default(null);
$table->string('stored_eula_file')->nullable()->default(null);
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('checkout_acceptances', function (Blueprint $table) {
if (Schema::hasColumn('checkout_acceptances', 'stored_eula')) {
$table->dropColumn('stored_eula');
}
if (Schema::hasColumn('checkout_acceptances', 'stored_eula_file')) {
$table->dropColumn('stored_eula_file');
}
});
}
}
@@ -0,0 +1,34 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddEulaToActionLogs extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('action_logs', function (Blueprint $table) {
$table->text('stored_eula')->nullable()->default(null);
$table->string('stored_eula_file')->nullable()->default(null);
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('action_logs', function (Blueprint $table) {
$table->dropColumn('stored_eula');
$table->dropColumn('stored_eula_file');
});
}
}
@@ -0,0 +1,34 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddsLdapManager extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
//Updates the Settings Table
Schema::table('settings', function (Blueprint $table) {
$table->string('ldap_manager')->after('ldap_jobtitle')->nullable();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
//
Schema::table('settings', function (Blueprint $table) {
$table->dropColumn('ldap_manager');
});
}
}
+1 -1
View File
@@ -15662,7 +15662,7 @@
"jquery": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/jquery/-/jquery-3.6.0.tgz",
"integrity": "sha1-xyoJ8Vwb3OFC9J2/EXC9+K2sJHA="
"integrity": "sha512-JVzAR/AjBvVt2BmYhxRCSYysDsPcssdmTFnzyLEts9qNwmjmu4JTAMYubEfwVOSwpQ1I1sKKFcxhZCI2buerfw=="
},
"jquery-form-validator": {
"version": "2.3.79",
+6 -6
View File
@@ -37,13 +37,18 @@
color: #fff;
}
.skin-black-dark .main-header .navbar .dropdown-menu li a {
color: #fff;
color: #333;
}
.skin-black-dark .main-header .navbar .dropdown-menu li a:hover {
background: #040404;
color: #fff;
}
}
@media (max-width: 991px) {
.skin-black-dark .main-header .navbar-custom-menu > .navbar-nav > li > .dropdown-menu {
background-color: var(--back-sub);
}
}
.skin-black-dark .main-header li.user-header {
background-color: #111;
}
@@ -315,11 +320,6 @@ body {
#details > div > div > div > table {
background-color: transparent;
}
.dropdown-menu,
.dropdown-menu > li > a {
background-color: var(--back-sub);
color: var(--text-main) !important;
}
.dropdown-menu > li > a:hover {
background-color: var(--back-main);
color: var(--nav-link);
+6 -6
View File
@@ -37,13 +37,18 @@
color: #fff;
}
.skin-black-dark .main-header .navbar .dropdown-menu li a {
color: #fff;
color: #333;
}
.skin-black-dark .main-header .navbar .dropdown-menu li a:hover {
background: #040404;
color: #fff;
}
}
@media (max-width: 991px) {
.skin-black-dark .main-header .navbar-custom-menu > .navbar-nav > li > .dropdown-menu {
background-color: var(--back-sub);
}
}
.skin-black-dark .main-header li.user-header {
background-color: #111;
}
@@ -315,11 +320,6 @@ body {
#details > div > div > div > table {
background-color: transparent;
}
.dropdown-menu,
.dropdown-menu > li > a {
background-color: var(--back-sub);
color: var(--text-main) !important;
}
.dropdown-menu > li > a:hover {
background-color: var(--back-main);
color: var(--nav-link);
+1 -1
View File
@@ -36,7 +36,7 @@
background-color: rgba(255, 255, 255, 0.1);
}
.skin-black .main-header .navbar .dropdown-menu li a {
color: #fff;
color: #333;
}
.skin-black .main-header .navbar .dropdown-menu li a:hover {
background: #040404;
+1 -1
View File
@@ -36,7 +36,7 @@
background-color: rgba(255, 255, 255, 0.1);
}
.skin-black .main-header .navbar .dropdown-menu li a {
color: #fff;
color: #333;
}
.skin-black .main-header .navbar .dropdown-menu li a:hover {
background: #040404;
-5
View File
@@ -301,11 +301,6 @@ body {
#details > div > div > div > table {
background-color: transparent;
}
.dropdown-menu,
.dropdown-menu > li > a {
background-color: var(--back-sub);
color: var(--text-main) !important;
}
.dropdown-menu > li > a:hover {
background-color: var(--back-main);
color: var(--nav-link);
-5
View File
@@ -301,11 +301,6 @@ body {
#details > div > div > div > table {
background-color: transparent;
}
.dropdown-menu,
.dropdown-menu > li > a {
background-color: var(--back-sub);
color: var(--text-main) !important;
}
.dropdown-menu > li > a:hover {
background-color: var(--back-main);
color: var(--nav-link);
+1 -1
View File
@@ -36,7 +36,7 @@
background-color: rgba(255, 255, 255, 0.1);
}
.skin-blue .main-header .navbar .dropdown-menu li a {
color: #fff;
color: #333;
}
.skin-blue .main-header .navbar .dropdown-menu li a:hover {
background: #367fa9;
+1 -1
View File
@@ -36,7 +36,7 @@
background-color: rgba(255, 255, 255, 0.1);
}
.skin-blue .main-header .navbar .dropdown-menu li a {
color: #fff;
color: #333;
}
.skin-blue .main-header .navbar .dropdown-menu li a:hover {
background: #367fa9;
-5
View File
@@ -301,11 +301,6 @@ body {
#details > div > div > div > table {
background-color: transparent;
}
.dropdown-menu,
.dropdown-menu > li > a {
background-color: var(--back-sub);
color: var(--text-main) !important;
}
.dropdown-menu > li > a:hover {
background-color: var(--back-main);
color: var(--nav-link);
-5
View File
@@ -301,11 +301,6 @@ body {
#details > div > div > div > table {
background-color: transparent;
}
.dropdown-menu,
.dropdown-menu > li > a {
background-color: var(--back-sub);
color: var(--text-main) !important;
}
.dropdown-menu > li > a:hover {
background-color: var(--back-main);
color: var(--nav-link);
+1 -1
View File
@@ -36,7 +36,7 @@
background-color: rgba(255, 255, 255, 0.1);
}
.skin-green .main-header .navbar .dropdown-menu li a {
color: #fff;
color: #333;
}
.skin-green .main-header .navbar .dropdown-menu li a:hover {
background: #008d4c;
+1 -1
View File
@@ -36,7 +36,7 @@
background-color: rgba(255, 255, 255, 0.1);
}
.skin-green .main-header .navbar .dropdown-menu li a {
color: #fff;
color: #333;
}
.skin-green .main-header .navbar .dropdown-menu li a:hover {
background: #008d4c;
-5
View File
@@ -304,11 +304,6 @@ a:link.btn-default {
#details > div > div > div > table {
background-color: transparent;
}
.dropdown-menu,
.dropdown-menu > li > a {
background-color: var(--back-sub);
color: var(--text-main) !important;
}
.dropdown-menu > li > a:hover {
background-color: var(--back-main);
color: var(--nav-link);
-5
View File
@@ -304,11 +304,6 @@ a:link.btn-default {
#details > div > div > div > table {
background-color: transparent;
}
.dropdown-menu,
.dropdown-menu > li > a {
background-color: var(--back-sub);
color: var(--text-main) !important;
}
.dropdown-menu > li > a:hover {
background-color: var(--back-main);
color: var(--nav-link);
+1 -1
View File
@@ -36,7 +36,7 @@
background-color: rgba(255, 255, 255, 0.1);
}
.skin-orange .main-header .navbar .dropdown-menu li a {
color: #fff;
color: #333;
}
.skin-orange .main-header .navbar .dropdown-menu li a:hover {
background: #e67e00;
+1 -1
View File
@@ -36,7 +36,7 @@
background-color: rgba(255, 255, 255, 0.1);
}
.skin-orange .main-header .navbar .dropdown-menu li a {
color: #fff;
color: #333;
}
.skin-orange .main-header .navbar .dropdown-menu li a:hover {
background: #e67e00;
-5
View File
@@ -304,11 +304,6 @@ body {
#details > div > div > div > table {
background-color: transparent;
}
.dropdown-menu,
.dropdown-menu > li > a {
background-color: var(--back-sub);
color: var(--text-main) !important;
}
.dropdown-menu > li > a:hover {
background-color: var(--back-main);
color: var(--nav-link);
-5
View File
@@ -304,11 +304,6 @@ body {
#details > div > div > div > table {
background-color: transparent;
}
.dropdown-menu,
.dropdown-menu > li > a {
background-color: var(--back-sub);
color: var(--text-main) !important;
}
.dropdown-menu > li > a:hover {
background-color: var(--back-main);
color: var(--nav-link);
+1 -1
View File
@@ -36,7 +36,7 @@
background-color: rgba(255, 255, 255, 0.1);
}
.skin-purple .main-header .navbar .dropdown-menu li a {
color: #fff;
color: #333;
}
.skin-purple .main-header .navbar .dropdown-menu li a:hover {
background: #555299;
+1 -1
View File
@@ -36,7 +36,7 @@
background-color: rgba(255, 255, 255, 0.1);
}
.skin-purple .main-header .navbar .dropdown-menu li a {
color: #fff;
color: #333;
}
.skin-purple .main-header .navbar .dropdown-menu li a:hover {
background: #555299;
-5
View File
@@ -302,11 +302,6 @@ body {
#details > div > div > div > table {
background-color: transparent;
}
.dropdown-menu,
.dropdown-menu > li > a {
background-color: var(--back-sub);
color: var(--text-main) !important;
}
.dropdown-menu > li > a:hover {
background-color: var(--back-main);
color: var(--nav-link);

Some files were not shown because too many files have changed in this diff Show More