Compare commits

..

716 Commits

Author SHA1 Message Date
snipe
8cd78b2790 Merge remote-tracking branch 'origin/develop'
Signed-off-by: snipe <snipe@snipe.net>

# Conflicts:
#	config/version.php
2021-06-10 17:44:17 -07:00
snipe
aae6a8fc6c Bumped version
Signed-off-by: snipe <snipe@snipe.net>
2021-06-10 17:42:29 -07:00
snipe
ace7abc1ad Merge remote-tracking branch 'origin/develop' 2021-06-10 15:18:24 -07:00
snipe
c567ad0617 Add @travismiller as a contributor 2021-06-10 15:18:02 -07:00
snipe
4c007ae085 Merge remote-tracking branch 'origin/develop' 2021-06-10 15:17:19 -07:00
snipe
54cb6c050a Merge pull request #9679 from tulsaschoolsdata/serial-number-indexes
Fixed #8486: Add index for asset serial number
2021-06-09 17:36:06 -07:00
snipe
851e5ca96e Merge pull request #9683 from tulsaschoolsdata/9682-company-id-indexes
Thanks!
2021-06-09 17:35:37 -07:00
Travis Miller
103c4325ce Fixed #9682: Add indexes for company_id 2021-06-09 14:29:00 -05:00
Travis Miller
ebe7c2da87 Fixed #8486: Add index for asset serial number 2021-06-09 14:12:35 -05:00
snipe
6c1dd81e0a Merge pull request #9640 from inietov/fixes/errorexception_invalid_argument_supplied
Fixes an issue when tried to upload a file to an user without actually selecting a file. [ch16471]
2021-05-27 14:03:25 -07:00
Ivan Nieto Vivanco
9f944ad497 Added the 'required' attribute to the input file n the upload file form modal. Added a validation for the UserFilesController if the user doesn't select any file to upload [ch16471]. 2021-05-27 15:48:13 -05:00
snipe
01e3296ff3 Updated hash
Signed-off-by: snipe <snipe@snipe.net>
2021-05-26 15:58:24 -07:00
snipe
7d5a9180e6 Updated hash
Signed-off-by: snipe <snipe@snipe.net>
2021-05-26 15:57:56 -07:00
snipe
9f2b4c721d Allow password reset from user profile
Signed-off-by: snipe <snipe@snipe.net>
2021-05-26 15:32:23 -07:00
snipe
3d008079c9 Merge branch 'develop' of https://github.com/snipe/snipe-it into develop 2021-05-26 14:32:33 -07:00
snipe
b8d413a6b8 Merge pull request #9632 from inietov/fixes/api_allow_duplicate_asset_tags
Fixes an issue that allows duplicate asset tags when the Asset is created via API.
2021-05-26 13:34:59 -07:00
snipe
30a193502f Merge branch 'develop' of https://github.com/snipe/snipe-it into develop 2021-05-26 13:27:20 -07:00
snipe
11f1b29db9 Add @kcoyo as a contributor 2021-05-26 13:04:51 -07:00
snipe
200d0804ec Merge pull request #9630 from kcoyo/develop
Fixed #9607: Sessions expire for SAML/RemoteUser/LDAP
2021-05-26 13:04:13 -07:00
Ivan Nieto Vivanco
d9f5f1182a Delete a couple lines that rewrites validation rules for asset tags in the model Asset.php:save() method. 2021-05-25 23:09:27 -05:00
snipe
3f09d17389 Fixed typo “synchronization”
Signed-off-by: snipe <snipe@snipe.net>
2021-05-25 18:45:06 -07:00
kcoyo
55555ee233 Merge pull request #1 from kcoyo/Fixed-#9607---Sessions-expire-for-SAML/RemoteUser/LDAP
Fixed #9607 - Sessions expire for SAML/RemoteUser/LDAP
2021-05-25 16:44:08 -07:00
kcoyo
cee6f0d579 Update LoginController.php
Fixed #9607 - Sessions expire for SAML/RemoteUser/LDAP
2021-05-25 16:37:34 -07:00
snipe
5b4550a6a8 Merge branch 'develop' of https://github.com/snipe/snipe-it into develop 2021-05-21 16:56:15 -07:00
snipe
1a7edb3411 Accept webp in uploads (not just image files)
Signed-off-by: snipe <snipe@snipe.net>
2021-05-21 16:56:11 -07:00
snipe
796c7c8431 Merge pull request #9619 from uberbrady/migrator_misses_logos
Several improvements to the snipeit:restore Artisan command
2021-05-21 16:02:55 -07:00
Brady Wetherington
94c1d36e08 The legacy 'logo.png' might not be a PNG, so switched to wildcard. 2021-05-21 15:55:37 -07:00
Brady Wetherington
e71bba441e Several improvements to the snipeit:restore Artisan command
The output now focuses on files that were skipped. Wildcard support
was added for individual files. A progress bar fills as the files
are transferred from the ZIPball to the filesystem. A new command-line
switch can be used to disable the progress bar. Barcode restores are
now skipped (we'd probably prefer to regenerate them). A few missed
directories have been added in. Some logic to skip macOS resource-fork
files has been put in. Some bugs with array operations were fixed.
There's now a concept of 'valid' and 'invalid' files for wildcard
operations.
2021-05-21 15:23:23 -07:00
Sabir Manandhar
c3d75d3be3 feat[docker]: create session table migration if session driver is set to database (#9587) 2021-05-20 14:31:22 -07:00
snipe
9eea46adef Fixed parse error in user print view
Signed-off-by: snipe <snipe@snipe.net>
2021-05-20 13:53:43 -07:00
sabir-dzangolab
19766a0a72 Fix/startup-sh (#9586)
* fix: update docker/startup.sh broken after merging hotfixs/2fa_qr

* fix: Typo in Dockerfile.alpine
2021-05-17 15:39:05 -07:00
Marc Leuser
4030789786 fix LicenseSeatsController method documentation (#9584)
in an attempt to get the automatic API reference generation to work
2021-05-14 16:14:32 -07:00
Jonathon Reinhart
fd082addff Fix incorrect case of ldap sync status string (#9563)
Fixes #9562
2021-05-12 19:15:39 -07:00
snipe
d636566012 Merge remote-tracking branch 'origin/master' into develop 2021-05-12 17:45:06 -07:00
Brady Wetherington
6066005aeb [WIP] Initial rough stabs at the Backup Migrator. It kinda-sorta works? (#9457)
* Initial rough stabs at the Backup Migrator. It kinda-sorta works?

* Fix hardcoded mysql path var
2021-05-12 17:44:39 -07:00
snipe
76897f3a3a Make label printing from asset page more consistent with normal UI
Signed-off-by: snipe <snipe@snipe.net>
2021-05-12 17:08:53 -07:00
snipe
c1babdc9b0 Merge remote-tracking branch 'origin/develop' 2021-05-12 16:51:57 -07:00
snipe
1a95337db1 Merge branch 'develop' of https://github.com/snipe/snipe-it into develop 2021-05-12 16:51:39 -07:00
Ivan Nieto Vivanco
6ed5dff1a5 Fix the target path to copy the demo logos in the database\seeds\SettingsSeeder.php. Also added the code that copies the demo logos to app\Console\Commands\ResetDemoSettings.php (#9571) 2021-05-12 16:41:49 -07:00
snipe
3f559c4a50 Add @tbrconnect as a contributor 2021-05-12 13:38:56 -07:00
tbrconnect
a29ef73346 Feature #3088 Print labels from asset detail page (#9559)
This PR adds a "Generate Label" button to the detail asset page as
described in #3088.

Fixes #3088

Co-authored-by: tilmann.bitterberg <tilmann@tbrglobal.com>
2021-05-12 13:38:26 -07:00
snipe
015ca1fcdc Merge remote-tracking branch 'origin/develop' 2021-05-07 18:13:22 -07:00
snipe
ded61614d1 Attempt to fix transient LDAP bug
Signed-off-by: snipe <snipe@snipe.net>
2021-05-07 18:13:04 -07:00
Brady Wetherington
62199f6255 Manually re-add support for Label Logos in Labels (#9552) 2021-05-07 17:01:32 -07:00
snipe
75b89b5a97 Merge remote-tracking branch 'origin/develop' 2021-05-07 16:42:41 -07:00
snipe
a704614397 Bumped max for country on model
Signed-off-by: snipe <snipe@snipe.net>
2021-05-07 16:42:26 -07:00
snipe
2dcb50d28e Merge branch 'develop' of https://github.com/snipe/snipe-it into develop 2021-05-07 16:36:12 -07:00
snipe
9aac3ae628 Fixed HTML causing two backup headers to appear
Signed-off-by: snipe <snipe@snipe.net>
2021-05-07 16:36:07 -07:00
Tom Misilo
397e2df3ea Upgrade the Alpine docker img to v3.13 for PHP 7.4 (#9550)
v3.13 of the alpine image has php7.4 instead of php7.3
2021-05-07 13:50:36 -07:00
Tom Misilo
4e408cbc42 Fix CSP Always being Enabled unless in debug mode. (#9543) 2021-05-05 10:51:47 -07:00
snipe
570a31a1c4 Merge remote-tracking branch 'origin/develop' 2021-05-05 10:29:10 -07:00
snipe
ece627b3a3 Small fixes for location printing when relationships are missing/invalid, per #9521
Signed-off-by: snipe <snipe@snipe.net>
2021-05-05 10:23:46 -07:00
snipe
d8d3fa2293 Only show department if it’s valid
Signed-off-by: snipe <snipe@snipe.net>
2021-05-05 10:13:19 -07:00
snipe
ab694347b0 Added favicon to demo dir
Signed-off-by: snipe <snipe@snipe.net>
2021-05-05 01:18:49 -07:00
snipe
aa98e4ea7a Bumped hash
Signed-off-by: snipe <snipe@snipe.net>

# Conflicts:
#	config/version.php
2021-05-05 00:04:17 -07:00
snipe
6935d94184 Bumped hash
Signed-off-by: snipe <snipe@snipe.net>
2021-05-05 00:03:33 -07:00
snipe
d9d5b4d730 Merge remote-tracking branch 'origin/develop'
Signed-off-by: snipe <snipe@snipe.net>

# Conflicts:
#	.all-contributorsrc
#	README.md
2021-05-05 00:01:09 -07:00
Ivan Nieto Vivanco
36a43642d8 Changed the orderBy clause inside the custom reports function that forms the CSV to be 'id' so making it a unique value and don't causes repeated items. [ch14587] (#9535) 2021-05-04 23:50:02 -07:00
snipe
ed98683496 Add @mikecmpbll as a contributor 2021-05-04 22:40:04 -07:00
snipe
c2dc7ac182 Merge branch 'develop' of https://github.com/snipe/snipe-it into develop 2021-05-04 22:39:24 -07:00
Dampfklon
37f7768e7a Fix Dockerfile.alpine build error and snipeit runtime permission error (#9520) 2021-05-04 22:29:24 -07:00
snipe
c1a8b609ea Add @markbrule as a contributor 2021-05-04 22:27:35 -07:00
markbrule
6c1553167d issue #9422 - pivot ID was being used as a user_id (#9512) 2021-05-04 22:27:01 -07:00
snipe
c572c98361 Merge branch 'develop' of https://github.com/snipe/snipe-it into develop 2021-05-04 22:21:35 -07:00
snipe
6c0af3c6e1 Add @JohnnyPicnic as a contributor 2021-05-04 22:21:18 -07:00
snipe
715b1a0fb2 Fixed #9157 Update .env to API_TOKEN_EXPIRATION_YEARS=15 (reapplies #9524)
Signed-off-by: snipe <snipe@snipe.net>
2021-05-04 22:20:54 -07:00
Tom Misilo
a6bbe1fec3 Delete file from assets folder, not just the ref. (#9525) 2021-05-04 22:18:22 -07:00
snipe
67ac3631df Production assets
Signed-off-by: snipe <snipe@snipe.net>
2021-05-04 22:05:26 -07:00
snipe
5760b76c4e Merge remote-tracking branch 'origin/develop'
Signed-off-by: snipe <snipe@snipe.net>

# Conflicts:
#	public/css/dist/skins/skin-black-dark.css
#	public/css/dist/skins/skin-black-dark.min.css
#	public/css/dist/skins/skin-black.css
#	public/css/dist/skins/skin-black.min.css
#	public/css/dist/skins/skin-blue-dark.css
#	public/css/dist/skins/skin-blue-dark.min.css
#	public/css/dist/skins/skin-blue.css
#	public/css/dist/skins/skin-blue.min.css
#	public/css/dist/skins/skin-contrast.css
#	public/css/dist/skins/skin-contrast.min.css
#	public/css/dist/skins/skin-green-dark.css
#	public/css/dist/skins/skin-green-dark.min.css
#	public/css/dist/skins/skin-green.css
#	public/css/dist/skins/skin-green.min.css
#	public/css/dist/skins/skin-orange-dark.css
#	public/css/dist/skins/skin-orange-dark.min.css
#	public/css/dist/skins/skin-orange.css
#	public/css/dist/skins/skin-orange.min.css
#	public/css/dist/skins/skin-purple-dark.css
#	public/css/dist/skins/skin-purple-dark.min.css
#	public/css/dist/skins/skin-purple.css
#	public/css/dist/skins/skin-purple.min.css
#	public/css/dist/skins/skin-red-dark.css
#	public/css/dist/skins/skin-red-dark.min.css
#	public/css/dist/skins/skin-red.css
#	public/css/dist/skins/skin-red.min.css
#	public/css/dist/skins/skin-yellow-dark.css
#	public/css/dist/skins/skin-yellow-dark.min.css
#	public/css/dist/skins/skin-yellow.css
#	public/css/dist/skins/skin-yellow.min.css
#	public/mix-manifest.json
2021-05-04 22:03:56 -07:00
snipe
57b9b571dc Fixed #9531 - highlight search box when filled, add clear button (#9534) 2021-05-04 22:00:34 -07:00
snipe
62f091e769 Merge remote-tracking branch 'origin/develop' 2021-04-30 15:38:02 -07:00
snipe
cee5eea121 Use 24-hour date format for audit
Signed-off-by: snipe <snipe@snipe.net>
2021-04-30 15:37:39 -07:00
snipe
c6726015f7 Added missing use statement
Signed-off-by: snipe <snipe@snipe.net>
2021-04-30 14:52:08 -07:00
snipe
961268172b Merge remote-tracking branch 'origin/develop' 2021-04-26 18:12:10 -07:00
snipe
b01baec7a8 Updated languages
Signed-off-by: snipe <snipe@snipe.net>
2021-04-26 18:11:48 -07:00
snipe
a1bc984d17 Generate production assets, bumped version
Signed-off-by: snipe <snipe@snipe.net>
2021-04-26 17:34:02 -07:00
snipe
31944cd4d4 Bumped version
Signed-off-by: snipe <snipe@snipe.net>
2021-04-26 17:28:38 -07:00
snipe
d5894a4d64 Generated dev assets
Signed-off-by: snipe <snipe@snipe.net>
2021-04-26 17:27:28 -07:00
snipe
32f043c5df Set the body skin style if one was overridden by the user profile
Signed-off-by: snipe <snipe@snipe.net>
2021-04-26 17:25:39 -07:00
snipe
23376c317e Use correct URL for css for subdirs
Signed-off-by: snipe <snipe@snipe.net>
2021-04-26 17:22:02 -07:00
snipe
b3b02933a5 Removed duplicate code
Signed-off-by: snipe <snipe@snipe.net>
2021-04-26 17:21:48 -07:00
snipe
882732b2de WTF kind of gendered language was this mess?
Signed-off-by: snipe <snipe@snipe.net>
2021-04-26 17:18:17 -07:00
snipe
b8c3564434 Removed min from personal CSS
Signed-off-by: snipe <snipe@snipe.net>
2021-04-26 14:05:18 -07:00
snipe
138ddfec1c Merge remote-tracking branch 'origin/develop' 2021-04-23 14:27:12 -07:00
snipe
315bcb6b38 Added use statement
Signed-off-by: snipe <snipe@snipe.net>
2021-04-23 14:26:57 -07:00
snipe
8016807268 Add @Nevets82 as a contributor 2021-04-23 12:09:34 -07:00
Steven
82f73eb9e2 Added user locale to REST API GET /api/users response; (#9486)
* Clearer reporting on import

Signed-off-by: snipe <snipe@snipe.net>

* Try adding  text/x-Algol68 to import

Signed-off-by: snipe <snipe@snipe.net>

* Added user locale to REST API GET /api/users response;

Co-authored-by: snipe <snipe@snipe.net>
2021-04-23 12:09:00 -07:00
snipe
b865a8aeea Merge remote-tracking branch 'origin/develop' 2021-04-21 20:19:17 -07:00
snipe
1d43eda21f Features: improved UI for importer (#9467)
* Small UI improvements for importer

Signed-off-by: snipe <snipe@snipe.net>

* And UI backup improvements

Signed-off-by: snipe <snipe@snipe.net>
2021-04-21 20:16:17 -07:00
snipe
ea0d0df1af Try adding text/x-Algol68 to import
Signed-off-by: snipe <snipe@snipe.net>
2021-04-21 13:48:51 -07:00
snipe
54ef469d98 Clearer reporting on import
Signed-off-by: snipe <snipe@snipe.net>
2021-04-21 13:27:23 -07:00
snipe
cef689a679 Merge branch 'develop' of https://github.com/snipe/snipe-it into develop 2021-04-21 12:29:06 -07:00
snipe
36c2edb278 Add @nepella as a contributor 2021-04-21 11:40:38 -07:00
Renee Margaret McConahy
e6c3d7fe57 Restore LOG_CHANNEL environment variable. (#9464)
This allows selecting the Monolog channel with the LOG_CHANNEL variable.
2021-04-21 11:40:23 -07:00
snipe
3855b74161 Merge remote-tracking branch 'origin/develop' 2021-04-21 10:24:04 -07:00
snipe
90f79eaf83 Merge branch 'develop' of https://github.com/snipe/snipe-it into develop 2021-04-21 10:23:46 -07:00
Brady Wetherington
72a813f23d This fixes the controller signature error people are getting with LDAP logins (#9466) 2021-04-21 10:23:32 -07:00
snipe
d90abdf86f Snipe codeacy workflow (#9460)
* Removed printerClass="NunoMaduro\Collision\Adapters\Phpunit\Printer"

Signed-off-by: snipe <snipe@snipe.net>

* fix ldap ad authentication filter query mechanism (#7441)

* Create SECURITY.md

* Create codacy-analysis.yml

Co-authored-by: Istvan Basa <basipottom@gmail.com>
2021-04-20 22:17:37 -07:00
snipe
1886841ec5 Create SECURITY.md 2021-04-20 22:15:41 -07:00
Istvan Basa
b037d0efdd fix ldap ad authentication filter query mechanism (#7441) 2021-04-20 21:25:45 -07:00
aranar-pro
5127727730 Fixed #9424: import history adds asset model to assigned user and respects checkin date (#9350)
* Fixed #9294: Assets import history. Behaviour based on Checkin Date added, including assigning checked out items to users.

* Fixed #9294: Fixed asset import history to respect checkin and update user with checked out items.

* Fixed #9294: whitespace and comment cleanup for merge

* Fixed #9294: Fixed asset import history to respect checkin and update user with checked out items.
2021-04-20 21:25:17 -07:00
Ross Crawford-d'Heureuse
2a058c3ce1 Added a docker-compose with helper services (#9105)
* Updates

* BAse

* pdated instructions round app_key

Co-authored-by: Ross Crawford-d'Heureuse <ross.crawford@mindcurv.com>
2021-04-20 21:17:34 -07:00
Mateus Villar
fdcb63f251 Added #9313: Add new fpm-alpine docker image and docker secrets support (#9331)
* Add docker secret support

* Add docker secret support to selected environment variables below:

- APP_KEY_FILE        -> APP_KEY;

- DB_HOST_FILE        -> DB_HOST;
- DB_PORT_FILE        -> DB_PORT;
- DB_DATABASE_FILE    -> DB_DATABASE;
- DB_USERNAME_FILE    -> DB_USERNAME;
- DB_PASSWORD_FILE    -> DB_PASSWORD;

- REDIS_HOST_FILE     -> REDIS_HOST;
- REDIS_PASSWORD_FILE -> REDIS_PASSWORD;
- REDIS_PORT_FILE     -> REDIS_PORT;

- MAIL_HOST_FILE      -> MAIL_HOST;
- MAIL_PORT_FILE      -> MAIL_PORT;
- MAIL_USERNAME_FILE  -> MAIL_USERNAME;
- MAIL_PASSWORD_FILE  -> MAIL_PASSWORD;

* Add env file for docker secrets

* Added #9313: add new fpm-image using docker secrets

* Fix broken symlinks

* Add docker secrets support using shell script

* Remove old docker config php files
2021-04-20 21:14:47 -07:00
dependabot[bot]
da79a16284 Bump ssri from 6.0.1 to 6.0.2 (#9459)
* Removed printerClass="NunoMaduro\Collision\Adapters\Phpunit\Printer"

Signed-off-by: snipe <snipe@snipe.net>

* Bump ssri from 6.0.1 to 6.0.2

Bumps [ssri](https://github.com/npm/ssri) from 6.0.1 to 6.0.2.
- [Release notes](https://github.com/npm/ssri/releases)
- [Changelog](https://github.com/npm/ssri/blob/v6.0.2/CHANGELOG.md)
- [Commits](https://github.com/npm/ssri/compare/v6.0.1...v6.0.2)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: snipe <snipe@snipe.net>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-20 21:13:34 -07:00
snipe
d10d090d33 Moved status label help text to headline
Signed-off-by: snipe <snipe@snipe.net>
2021-04-20 15:31:07 -07:00
snipe
aa6b1456b2 Added help text to custom fields headline
Signed-off-by: snipe <snipe@snipe.net>
2021-04-20 15:29:43 -07:00
snipe
b2de0d4ade Changed “Asset Categories” to “Categories” since there are more than asset categories now
Signed-off-by: snipe <snipe@snipe.net>
2021-04-20 15:29:22 -07:00
snipe
c17eaaad69 Check that the field is valid before checking to see count()
This mostly affects the demo, since the seeder updates info often

Signed-off-by: snipe <snipe@snipe.net>
2021-04-20 15:10:28 -07:00
snipe
e286ff0be3 Added show() redirect for CustomFields::show()
Signed-off-by: snipe <snipe@snipe.net>
2021-04-20 15:09:23 -07:00
snipe
83200d3cbd Merge remote-tracking branch 'origin/develop' 2021-04-20 14:55:48 -07:00
snipe
d40fe1b683 Merge branch 'develop' of https://github.com/snipe/snipe-it into develop 2021-04-20 14:54:29 -07:00
Brady Wetherington
2a28f5e66c (Maybe?) Fixes the problem where we always need LDAP enabled (#9321)
* I *think* this fixes the problem where we need LDAP even if we aren't using it?

* Pull the LdapAd dependency out of the AuthController constructor
2021-04-20 14:53:47 -07:00
snipe
dd1a5681da Add @psarossy as a contributor 2021-04-20 14:53:10 -07:00
Peter Sarossy
cacb707a7f Added #9355: include Accessories and Consumables on the location reports (#9356) 2021-04-20 14:53:04 -07:00
snipe
e92f69dcda Add @zybersup as a contributor 2021-04-20 14:52:29 -07:00
Supapong Areeprasertkul
b28b245acc Update labels.blade.php (#8441)
- Make the qr_size bigger when no barcode printed.
- BUG: Missing unit of font size for .qr-text class.
- Reduce padding-right to remove the additional space above text when print from Google Chrome. (I do not know the cause. I tried "box-sizing: border-box" without success.)
2021-04-20 14:52:17 -07:00
Michael Pietsch
15e729f4b8 fix css when not runnin in webroot directory (#9445)
Co-authored-by: Michael Pietsch <skywalker-11@mi-pietsch.de>
2021-04-20 14:49:57 -07:00
snipe
3138f45e8c Merge branch 'develop' of https://github.com/snipe/snipe-it into develop 2021-04-20 14:49:38 -07:00
snipe
3e471a6587 Add @sh1hab as a contributor 2021-04-20 14:49:31 -07:00
Masudul Haque Shihab
f3831fe010 Fixed #9447 change route parameter name in locations view (#9456) 2021-04-20 14:49:05 -07:00
Ivan Nieto Vivanco
36bc47c61c Handle regex format when assigned from API (#9443) 2021-04-20 14:44:47 -07:00
snipe
e4acf8d795 Merge remote-tracking branch 'origin/master' into develop 2021-04-20 14:44:01 -07:00
snipe
b562f38729 Removed printerClass="NunoMaduro\Collision\Adapters\Phpunit\Printer"
Signed-off-by: snipe <snipe@snipe.net>
2021-04-19 10:51:29 -07:00
snipe
e8adf8d44c One more category fix
Signed-off-by: snipe <snipe@snipe.net>
2021-04-14 16:57:55 -07:00
snipe
b693e5202b Check for category name
Signed-off-by: snipe <snipe@snipe.net>
2021-04-14 16:54:36 -07:00
snipe
415ae5854f Add @Skywalker-11 as a contributor 2021-04-14 15:15:57 -07:00
snipe
e1c6d4ced7 Merge branch 'develop' of https://github.com/Skywalker-11/snipe-it into Skywalker-11-develop
Signed-off-by: snipe <snipe@snipe.net>

# Conflicts:
#	app/Http/Requests/SettingsSamlRequest.php
2021-04-14 15:15:07 -07:00
snipe
252b2ee1b4 Add @Jelle-S as a contributor 2021-04-14 15:07:40 -07:00
snipe
39b0f464d2 Adds ID columns to tables that don’t have them - reproduces #9363
This reproduces #9363 without the PK in custom fields

Signed-off-by: snipe <snipe@snipe.net>
2021-04-14 15:07:11 -07:00
snipe
2c0438bdcb Add @andreaci as a contributor 2021-04-14 11:18:13 -07:00
snipe
2986765a68 Implements #9373
Signed-off-by: snipe <snipe@snipe.net>
2021-04-14 11:17:59 -07:00
snipe
f19595f525 Add @deivishome as a contributor 2021-04-14 10:27:08 -07:00
snipe
465ec054d3 Add @limeless as a contributor 2021-04-14 10:22:58 -07:00
theburger
785b1ad5a6 Update snipeit.sh php version from 7.1 to 7.4 (#9407)
Snipe-it 5.2.x is required PHP 7.4 or above,
update script to install PHP 7.4 instead of 7.1

## Changed
- PHP to install in script is changed to 7.4 from 7.1
- Add `''`at DB_PASSWORD when configuring `.env` file
2021-04-14 10:22:31 -07:00
snipe
ab8f7e7b84 Add @Joly0 as a contributor 2021-04-14 10:21:18 -07:00
Joly0
31b2287a57 Update print.blade.php (#9426)
Fixes #9418
2021-04-14 10:20:36 -07:00
Godfrey Martinez
5fab1d6f0d FIxes Sum of purchase cost of components not taking quantity into account in Asset View > Components tab (#9424)
* git commit -m

* added text
2021-04-14 10:19:40 -07:00
Godfrey Martinez
246cc0eaa8 Feature/ch16172/adding other fields to ldap sync settings (#9416)
* new branch, added manager as an additional field. Currently having DB issues

* WIP DB Issue with eloquent

* added department to LDAP sync

* removed unused variables
2021-04-14 10:17:57 -07:00
snipe
ce1d3284b0 Buld assets
Signed-off-by: snipe <snipe@snipe.net>
2021-04-14 10:12:14 -07:00
Brady Wetherington
bf344fd707 Merge branch 'develop'
mix-manifest I just picked one.
version.php will get bumped by hand
bootstrap table seems to just be another copy? Added it back.
2021-04-14 09:58:21 -07:00
Ivan Nieto Vivanco
9cf5fbd675 Logic added to handle when a cloned user is imported via LDAP. (#9429) 2021-04-14 09:19:25 -07:00
Ivan Nieto Vivanco
3824a50e8b Fixes an issue with CSS when the 'skin' value in settings table is empty. (#9423) 2021-04-12 20:37:02 -07:00
snipe
b6006769c3 Bumped version
Signed-off-by: snipe <snipe@snipe.net>
2021-04-06 21:02:05 -07:00
snipe
bf2479c5d9 Fixed url to user-ridden style
Signed-off-by: snipe <snipe@snipe.net>
2021-04-06 20:36:13 -07:00
snipe
7ddcc97e79 Generated dev assets
Signed-off-by: snipe <snipe@snipe.net>
2021-04-06 20:35:58 -07:00
snipe
ba92d751a3 Removed depreciated Input:: facade
Signed-off-by: snipe <snipe@snipe.net>
2021-04-06 20:28:05 -07:00
Brady Wetherington
6b86e2a58f Rebuilt all assets after all commits had landed 2021-04-06 20:16:59 -07:00
Brady Wetherington
792a31cc7f Merge branch 'develop' 2021-04-06 20:10:22 -07:00
snipe
e47e2e3754 Fixed #9404 - include note in accessory API
Signed-off-by: snipe <snipe@snipe.net>
2021-04-06 10:57:30 -07:00
snipe
105f57e059 Possible fix for Docker failing on master
Signed-off-by: snipe <snipe@snipe.net>
2021-04-06 06:09:04 -07:00
snipe
390403ddb7 Fixed #9370 - listbox custom fields not decryoted on edit
Signed-off-by: snipe <snipe@snipe.net>
2021-04-06 05:59:31 -07:00
snipe
7da32443ff Move develop fixes to master
Signed-off-by: snipe <snipe@snipe.net>
2021-04-06 05:05:16 -07:00
Brady Wetherington
72806cf8db Fix up docker build on develop branch (#9401) 2021-04-05 22:13:31 -07:00
Serkan
0e34e43abb The return early pattern applied to improve readability. (#8894) 2021-04-05 22:03:15 -07:00
snipe
981b503653 Merge branch 'develop' of https://github.com/snipe/snipe-it into develop 2021-04-05 22:01:35 -07:00
Joel Pittet
af8509c4d0 Allow NPM packages to build for production (#9171)
* add back changes before compiling assets

* new css location and mix manifest

* update papaparse, most vunerabilities attached to it

* update axios and papaparse for security releases

* removing security fixes that still allow npm run production to work

* Update lodash
2021-04-05 22:01:11 -07:00
snipe
abddda2ab8 Add @kajes as a contributor 2021-04-05 21:56:45 -07:00
Lars Kajes
49532e1cd6 Add option to force TLS connection (#9327)
Co-authored-by: Lars Kajes <lars.kajes@iusinnovation.se>
2021-04-05 21:56:25 -07:00
snipe
09887bdabd Removed stray opening tag
Signed-off-by: snipe <snipe@snipe.net>
2021-04-05 21:54:31 -07:00
snipe
ff0e526021 Merge branch 'develop' of https://github.com/snipe/snipe-it into develop 2021-04-05 21:53:47 -07:00
Godfrey Martinez
f9f8ce6df6 Auditing feature improvement Better mobile [ch14328] (#9181) 2021-04-05 21:53:19 -07:00
snipe
55692bfe98 Add @Tetrachloromethane250 as a contributor 2021-04-05 21:52:14 -07:00
snipe
5c5fe2bd87 Merge branch 'develop' of https://github.com/snipe/snipe-it into develop 2021-04-05 21:52:01 -07:00
Tetrachloromethane250
157d9e4ebb Added #5977: Add permission to view files attached to licenses (#9264)
* Add permission to view and modify files for licenses

* Actually use the permission
2021-04-05 21:51:47 -07:00
snipe
20a3028386 Merge branch 'develop' of https://github.com/snipe/snipe-it into develop 2021-04-05 21:48:01 -07:00
snipe
3ffa3534a0 Use environmental variables in memory limits
Signed-off-by: snipe <snipe@snipe.net>
2021-04-05 21:47:57 -07:00
Jan Kiesewetter
d61d189328 Change owner while copy (#7552)
* Change owner while copy

Copy all files and chown them in a 2nd layer leads to a larger image.
See layer 22 and 26 of https://hub.docker.com/layers/snipe/snipe-it/v4.7.8/images/sha256-67c865d91df1b90cef1112f12bbc9c64402dfeafde0bdb160c4f07e785ee0bcc

* Copy docker.env as user docker
2021-04-05 21:15:54 -07:00
Earl Ramirez
6a8d5282ef Add support for Debian 10 (#7414) 2021-04-05 21:06:53 -07:00
snipe
f147c43dd4 Add @t3easy as a contributor 2021-04-05 21:04:39 -07:00
snipe
c9ec15101c Add @ajsy as a contributor 2021-04-05 20:56:39 -07:00
snipe
b9bab05ac3 Merge branch 'master' of https://github.com/ajsy/snipe-it into ajsy-master
Signed-off-by: snipe <snipe@snipe.net>

# Conflicts:
#	resources/views/users/print.blade.php
2021-04-05 20:55:49 -07:00
snipe
298bfa73c8 Add @ocelotsloth as a contributor 2021-04-05 20:35:28 -07:00
Mark Stenglein
dd1b9ab926 VAGRANT: Fix Ubuntu Development VMs (#6909)
* GITIGNORE: Add vagrant log and ansible retry files

This patch adds the following to the project's .gitignore file:
  - Vagrant's log file
  - Ansible's retry files

Signed-off-by: Mark Stenglein <mark@stengle.in>

* ANSIBLE: Add Ubuntu Ansible playbook for dev env

This patch adds an Ansible playbook to provision an Ubuntu based
development environment. This playbook is fully idempotent and
can be run multiple times without negative consequences.

Signed-off-by: Mark Stenglein <mark@stengle.in>

* VAGRANTFILE: Clean Vagrantfile

There was a lot of extra VMs being provisioned by the existing
Vagrantfile. Some of this may need to come back but I am removing
it with this commit to demonstrate that this is all that is
needed.

Signed-off-by: Mark Stenglein <mark@stengle.in>

* ANSIBLE: UBUNTU: PLAYBOOK: Add .env configuration

I missed the configuration of the .env file in the initial commit.
This patch adds these configuration steps as well as the needed
Cron job for the Artisan scheduler.

Signed-off-by: Mark Stenglein <mark@stengle.in>

* Revert "VAGRANTFILE: Clean Vagrantfile"

This reverts commit a608a30d6b.

* VAGRANTFILE: Fix Ubuntu Vagrant Development Boxes

This patch addresses problems with the existing Vagrant deployment
configurations for the Ubuntu VMs.
  - The vagrant file is configuring the VMs for an incorrect bridge
    interface. Fixed by changing the config to use the default
    networking as well as a port-forward.
  - Moves provisioning over to the Ansible-Local playbook I wrote.

Signed-off-by: Mark Stenglein <mark@stengle.in>
2021-04-05 20:35:04 -07:00
Sxderp
f01c93e162 Extend #6229 to include superuser permission check (#6772) 2021-04-05 20:26:06 -07:00
snipe
26b97d2b0b Add @JuustoMestari as a contributor 2021-04-05 20:16:35 -07:00
snipe
df72f92bc0 Brings PR #6744 up to parity with previous changes - Set custom field's default value when creating a new asset using the API
Signed-off-by: snipe <snipe@snipe.net>
2021-04-05 20:16:06 -07:00
AlexanderWPapyrus
a1f9642a18 Increases DPI of barcode for small lables (#9344)
solves issue #9293
2021-04-05 19:33:10 -07:00
Ivan Nieto Vivanco
90a24539b0 Fixes/cli importer issue (#9199)
* Added logic to handle the CLI importer.

* Fix bug introduced with the commit previous to the regression.

* Adds a validation for  variable when is null, add comments to clarify where the  class variable came from.

* Add support for when  variable is an instance of User class.
2021-04-05 19:28:31 -07:00
Godfrey Martinez
5ea759f615 Fixed #7211, #9197, #7864, [ch15504] - Added additional LDAP fields to sync (#9318)
* LDAP and Active Directory has plenty of other [ch15504]

* removed department id from ldap sync

* removed department id from ldap sync

* Update 2021_03_18_184102_adds_several_ldap_fields.php
2021-04-05 19:26:04 -07:00
Ivan Nieto Vivanco
eb0ae74ef8 Fixes typo that doesn't accepts 'textarea' as custom field type element. (#9387) 2021-04-01 18:23:05 -07:00
snipe
6f4215cfac Fixed label name for field_values
Signed-off-by: snipe <snipe@snipe.net>
2021-03-31 14:07:30 -07:00
snipe
a199c75f5c Added textarea and checkbox to validation rules
Signed-off-by: snipe <snipe@snipe.net>
2021-03-31 14:04:01 -07:00
snipe
b5f7cb534e Fixed element name
Signed-off-by: snipe <snipe@snipe.net>
2021-03-31 14:02:25 -07:00
snipe
618e4439e2 Fixed validation to include textarea
Signed-off-by: snipe <snipe@snipe.net>
2021-03-31 14:02:10 -07:00
snipe
6a8e761c5e Added created_at and updated_at to presenter
Signed-off-by: snipe <snipe@snipe.net>
2021-03-30 08:57:20 -07:00
snipe
70a7a8f20b WTF. This shit is haunted.
Signed-off-by: snipe <snipe@snipe.net>
2021-03-30 08:00:27 -07:00
snipe
a5b67965f2 Fixed CSS path
(Fixes RB #14746)

Signed-off-by: snipe <snipe@snipe.net>
2021-03-30 07:49:30 -07:00
snipe
2354e37504 Merge branch 'develop' of https://github.com/snipe/snipe-it into develop 2021-03-30 07:40:14 -07:00
Nikolay Didenko
06e641b782 Do not override per table data-id-cookie-table attribute by current route name globally (#7835) 2021-03-29 20:05:24 -07:00
Shaun McPeck
c88813bbb8 set docker user as owner of key symbolic links (#7924)
Co-authored-by: Shaun McPeck <shaun@shaunmcpeck.com>
2021-03-29 19:44:42 -07:00
Marc Leuser
90b7d34c69 Added #6695: add API endpoint for license seats (#8058)
* remove miselading comment line

* added dedicated API endpoint for license seats

* don't display a seat name via API
it makes no sense and we don't have any particular sorting order
so the numbering would be inconsistent anyway

* reduce amount of IFs

* add sanity checks to show()

* fix goofed logging logic

* add tests for action log entries
2021-03-29 19:41:26 -07:00
R. Christian McDonald
cb1a95a530 Apache site configurations shouldn't be written to sites-enabled (#6982)
It is better (best) practice to write configurations to sites-available and then let Apache copy the configuration to sites-enabled via 'a2ensite' command
2021-03-29 19:21:30 -07:00
NMC
3e934a1b96 Add a way for a user to override the site skin setting + fix mislabeled comment. (#6891)
* Add a way for a user to override the skin setting.

* Add site setting to allow user to change the skin.

* Fix skin list.

Co-authored-by: NMC <info@nmc-lab.com>
2021-03-29 19:09:23 -07:00
Nuno Maduro
8b6b95a05b Makes nunomaduro/collision versioning consistent with other dependencies (#9316) 2021-03-29 18:15:20 -07:00
snipe
53651ba3df Add @misilot as a contributor 2021-03-24 14:09:02 -07:00
snipe
d527f23ec8 Add @raelldottin as a contributor 2021-03-24 14:08:56 -07:00
Raell Dottin
78cc47a859 Added sanity check to determine if a bind user account is set. (#9340) 2021-03-24 14:08:37 -07:00
snipe
b00413e8aa Added missing .env vars to example from #8389
Signed-off-by: snipe <snipe@snipe.net>
2021-03-24 11:46:07 -07:00
Tom Misilo
7557879d4a Add support for overriding the Time and Memory Limits (#8389)
This is similar to what exists for the LDAP Import, and adds support for
setting th  Import and Report max execution time and memory limits

Co-authored-by: snipe <snipe@snipe.net>
2021-03-24 11:40:43 -07:00
snipe
0b41f9182a Merge remote-tracking branch 'origin/develop' 2021-03-24 11:35:39 -07:00
Jo Drexl
0114373468 Fixing #9224 (#9328) 2021-03-22 12:57:26 -07:00
snipe
86fef3f40a Set SAML errors to warning instead of error
Signed-off-by: snipe <snipe@snipe.net>
2021-03-17 22:30:26 -07:00
snipe
c90604b5ae Merge remote-tracking branch 'origin/develop' 2021-03-17 22:19:24 -07:00
snipe
069e9e52fe Try/catch barcodes so they don’t shit up the logs
Signed-off-by: snipe <snipe@snipe.net>
2021-03-17 22:19:10 -07:00
Peter Dave Hello
ca8b152549 Remove unnecessary apt-get clean in Dockerfile (#9201)
This image is built from the official Debian image as upstream, so there
is no need to do apt-get clean manually.

Ref:
- docs.docker.com/develop/develop-images/dockerfile_best-practices/#apt-get

> Official Debian and Ubuntu images automatically run apt-get clean, so
> explicit invocation is not required.
2021-03-17 20:33:54 -07:00
snipe
b2a3a80f96 Bumped hash
Signed-off-by: snipe <snipe@snipe.net>

# Conflicts:
#	composer.lock
#	config/version.php
2021-03-17 20:04:43 -07:00
snipe
c8e172ec6b Downgrade packages
Signed-off-by: snipe <snipe@snipe.net>
2021-03-17 20:03:09 -07:00
snipe
afb7fcfa3e Bumped hash
Signed-off-by: snipe <snipe@snipe.net>
2021-03-17 20:02:54 -07:00
snipe
9f3a8a43cc Merge remote-tracking branch 'origin/develop'
Signed-off-by: snipe <snipe@snipe.net>

# Conflicts:
#	composer.lock
2021-03-17 19:41:02 -07:00
snipe
8fd8e716ac Changed debug level on bad LDAP connection (#9314)
* Changed debug level on bad LDAP connection

TODO:

Unfuck all of this. It’s a mess and it really doesn’t work the way we think it does. AdLdap library strikes again. :(

Signed-off-by: snipe <snipe@snipe.net>

* Improved phrasing

Signed-off-by: snipe <snipe@snipe.net>
2021-03-17 19:24:28 -07:00
snipe
72f7baf5ee Removed unused class references 2021-03-17 16:56:32 -07:00
snipe
1b890ffcc5 Added collision 2021-03-17 15:45:00 -07:00
snipe
ca882e2b3d Add @elyscape as a contributor 2021-03-15 16:42:52 -07:00
Eli Young
97fa9663b1 Fixed #9299: Use correct SVG MIME type for uploads (#9300)
The correct MIME type of SVG is image/svg+xml. Out of an abundance of
caution, I am leaving in image/svg to avoid potentially causing issues
on very old browsers, but this can likely be removed without issue.
2021-03-15 16:42:11 -07:00
snipe
ab092fd209 Merge remote-tracking branch 'origin/develop'
# Conflicts:
#	app/Providers/AppServiceProvider.php
#	config/logging.php
#	config/services.php
2021-03-15 12:29:29 -07:00
Brady Wetherington
c7626f8387 Add new StorageHelper and use it where it makes sense (#9276) 2021-03-15 12:26:39 -07:00
snipe
3fc24b4e61 Set spatie encryption to null 2021-03-09 21:07:41 -08:00
snipe
f164f0ea60 Fixed #9266 - set a colors_array variable even if other conditions are not met 2021-03-09 21:04:32 -08:00
snipe
0dd38c4a9b One more try for logo copying
Again, this might not work, but I cannot find snipe-logo-lg.png anywhere in the repo
2021-03-09 20:41:47 -08:00
snipe
6e8aaddb40 One more changes for settings seeder (this may not work) 2021-03-09 17:40:22 -08:00
snipe
104912cdf3 Added default logos to setting seeder 2021-03-09 17:39:10 -08:00
snipe
b103f724b5 Added discard changes in composer (for those weird cases where it says something changed in a vendor directory) 2021-03-09 17:38:06 -08:00
snipe
0fa07a4bca Bumped point version 2021-03-09 16:19:21 -08:00
snipe
c3871c98df Updated rollbar package 2021-03-09 16:16:57 -08:00
snipe
cf4e97f103 Log namespacing 2021-03-09 13:39:53 -08:00
snipe
f05a8d782c Added spatie encryption line 2021-03-09 13:38:53 -08:00
snipe
89ab4bb86f Revert "Added PHP8 compatibility [experimental]"
This reverts commit 3873f14971.

# Conflicts:
#	composer.lock
2021-03-09 13:38:38 -08:00
snipe
707a68fc54 Rollback :( 2021-03-09 13:15:00 -08:00
snipe
4bd9706693 Merge branch 'develop' of https://github.com/snipe/snipe-it into develop 2021-03-09 12:56:17 -08:00
snipe
0d91ebfed8 One more try at Spatie 2021-03-09 12:56:13 -08:00
snipe
2d6dcb6b3b Applying fix for Undefined class constant 'EM_AES_256' in spatie
https://github.com/spatie/laravel-backup/discussions/1247
2021-03-09 12:52:26 -08:00
snipe
e9ee9ea2e9 Fixed rollbar integration 2021-03-09 12:40:00 -08:00
snipe
3873f14971 Added PHP8 compatibility [experimental] 2021-03-09 12:39:49 -08:00
snipe
7e56fc5e0d Updated mix manifest 2021-03-02 12:33:39 -08:00
snipe
3f01b02fd9 Updated BS tables 2021-03-02 12:33:29 -08:00
snipe
77ec64aded Merge remote-tracking branch 'origin/develop'
# Conflicts:
#	public/css/dist/skins/skin-black-dark.css
#	public/css/dist/skins/skin-black-dark.min.css
#	public/css/dist/skins/skin-blue-dark.css
#	public/css/dist/skins/skin-blue-dark.min.css
#	public/css/dist/skins/skin-green-dark.css
#	public/css/dist/skins/skin-green-dark.min.css
#	public/css/dist/skins/skin-orange-dark.css
#	public/css/dist/skins/skin-orange-dark.min.css
#	public/css/dist/skins/skin-purple-dark.css
#	public/css/dist/skins/skin-purple-dark.min.css
#	public/css/dist/skins/skin-red-dark.css
#	public/css/dist/skins/skin-red-dark.min.css
#	public/css/dist/skins/skin-yellow-dark.css
#	public/css/dist/skins/skin-yellow-dark.min.css
#	public/mix-manifest.json
2021-03-02 12:33:18 -08:00
Ivan Nieto Vivanco
9ed226a0af Fixes links in Models Actions, and to view the fieldset assigned to that Model. (#9232) 2021-03-01 13:08:29 -08:00
Ivan Nieto Vivanco
d64b35c348 Added a condition to ensure that only assets checked out to an user that is being deleted are updating their status (#9233) 2021-03-01 13:07:23 -08:00
snipe
5aa960603a Merge branch 'develop' of https://github.com/snipe/snipe-it into develop 2021-02-26 15:59:53 -08:00
Joel Pittet
c979779249 Fixes a couple blade templates for relative URLs (#9170)
* Switched branch name to master

* Production assets for better contrast in dark mode skins

* Fixed #9115: Duplicate column name 'provider' (#9137)

* Sigh. Real assets

* fix blade templates with missing url() function wrapper for relative URLs

* Develop to master (#9195)

* Added saml custom setting retrieveParametersFromServer to enable fixing SLO issues with Azure AD (#9187)

* [FIX] Lite test email won't use the mail component (#9092)

* [FIX] Lite test email won't use the mail component

* Revert "[FIX] Lite test email won't use the mail component"

This reverts commit 6dab9aa1a8.

* Fix check for snipeSettings

* Remove random text from message template

* Revert "Fix check for snipeSettings"

This reverts commit 887dcc7bbc.

* Fix test notification with setupCompleted

Co-authored-by: johnson-yi <63399474+johnson-yi@users.noreply.github.com>
Co-authored-by: Oliver Walerys <owalerys@users.noreply.github.com>

* Changed branch from develop to master

* fix blade templates with missing url() function wrapper for relative URLs

* Re-apply patch against develop

* Rebase against develop

Co-authored-by: snipe <snipe@snipe.net>
Co-authored-by: Kevin Köllmann <mail@kevinkoellmann.de>
Co-authored-by: johnson-yi <63399474+johnson-yi@users.noreply.github.com>
Co-authored-by: Oliver Walerys <owalerys@users.noreply.github.com>
2021-02-26 15:59:36 -08:00
snipe
52bf050c4f Add @joelpittet as a contributor 2021-02-26 15:59:17 -08:00
Ivan Nieto Vivanco
ab7dd90602 Fixes ternary that sets the offset in 0 when the offset passed to the API for the user is greater than total locations. (#9210) 2021-02-26 12:56:04 -08:00
snipe
2e298893b6 Updated mix manifest 2021-02-25 19:43:58 -08:00
Godfrey Martinez
b0078ff64d Audit dark mode theme skins and check for [ch15847] (#9102)
Co-authored-by: snipe <snipe@snipe.net>
2021-02-25 19:41:31 -08:00
snipe
fcd805638e Changed branch from develop to master 2021-02-25 16:54:23 -08:00
snipe
63629abb93 Merge remote-tracking branch 'origin/develop' 2021-02-25 16:53:50 -08:00
snipe
6373ef3283 Bumped version 2021-02-25 16:53:37 -08:00
snipe
00a7c1e9e2 Merge remote-tracking branch 'origin/develop' 2021-02-25 16:44:55 -08:00
snipe
f37d5d3d03 Fixed #9085 - asset models not showing proper pagination
Huge thanks to @inietov for catching this one
2021-02-25 16:43:38 -08:00
snipe
57e52f0ba4 Fixed extraneous closing </i> 2021-02-25 13:35:05 -08:00
snipe
dc8e06fc65 Merge remote-tracking branch 'origin/develop' 2021-02-24 09:12:27 -08:00
snipe
db3f80bb9b Fixed #9198 - added stdout as a log driver 2021-02-24 09:10:03 -08:00
snipe
f5dda06c55 Develop to master (#9195)
* Added saml custom setting retrieveParametersFromServer to enable fixing SLO issues with Azure AD (#9187)

* [FIX] Lite test email won't use the mail component (#9092)

* [FIX] Lite test email won't use the mail component

* Revert "[FIX] Lite test email won't use the mail component"

This reverts commit 6dab9aa1a8.

* Fix check for snipeSettings

* Remove random text from message template

* Revert "Fix check for snipeSettings"

This reverts commit 887dcc7bbc.

* Fix test notification with setupCompleted

Co-authored-by: johnson-yi <63399474+johnson-yi@users.noreply.github.com>
Co-authored-by: Oliver Walerys <owalerys@users.noreply.github.com>
2021-02-23 14:53:55 -08:00
Oliver Walerys
c3166d491a [FIX] Lite test email won't use the mail component (#9092)
* [FIX] Lite test email won't use the mail component

* Revert "[FIX] Lite test email won't use the mail component"

This reverts commit 6dab9aa1a8.

* Fix check for snipeSettings

* Remove random text from message template

* Revert "Fix check for snipeSettings"

This reverts commit 887dcc7bbc.

* Fix test notification with setupCompleted
2021-02-23 14:49:40 -08:00
johnson-yi
763e17f491 Added saml custom setting retrieveParametersFromServer to enable fixing SLO issues with Azure AD (#9187) 2021-02-23 11:05:22 -08:00
snipe
47b2fe571e Merge remote-tracking branch 'origin/develop' 2021-02-19 10:52:33 -08:00
snipe
c8f6318aba Fixed funky layout in asset model modal window (broken HTML) 2021-02-19 10:51:56 -08:00
snipe
adbb3a8f31 Merge remote-tracking branch 'origin/develop' 2021-02-19 10:20:07 -08:00
snipe
1f142fde8a Merge branch 'develop' of https://github.com/snipe/snipe-it into develop 2021-02-19 10:19:37 -08:00
Brady Wetherington
44eee019d9 Add variable declaration for $result (#9168) 2021-02-19 10:19:24 -08:00
snipe
f636aac2dd Add @sw-mreyes as a contributor 2021-02-19 10:11:52 -08:00
snipe
54fd1b993b Merge remote-tracking branch 'origin/develop'
# Conflicts:
#	config/version.php
2021-02-19 10:11:23 -08:00
snipe
a5731a3088 Updated hash 2021-02-19 10:09:22 -08:00
snipe
6449d0aaf9 Sigh. Real assets 2021-02-19 10:06:06 -08:00
snipe
931e2df3bd Updated production assets
# Conflicts:
#	public/js/build/app.js
#	public/js/dist/all.js
2021-02-19 10:05:29 -08:00
snipe
7f8eddede6 Updated compiled assets 2021-02-19 10:02:30 -08:00
snipe
4ddab03792 Merge branch 'develop' of https://github.com/sw-mreyes/snipe-it into sw-mreyes-develop
# Conflicts:
#	public/mix-manifest.json
2021-02-19 10:00:35 -08:00
snipe
ff341caf34 Merge remote-tracking branch 'origin/develop' 2021-02-18 14:21:34 -08:00
snipe
5754f0aa3f Revert "Added logic to handle the CLI importer. (#9088)"
This reverts commit 2e2d087639.
2021-02-18 14:21:19 -08:00
snipe
785bc40d9d Merge remote-tracking branch 'origin/develop'
# Conflicts:
#	public/css/dist/skins/skin-black.css
#	public/css/dist/skins/skin-blue.css
#	public/css/dist/skins/skin-green.css
#	public/css/dist/skins/skin-orange.css
#	public/css/dist/skins/skin-purple.css
#	public/css/dist/skins/skin-red.css
#	public/css/dist/skins/skin-yellow.css
#	public/mix-manifest.json
2021-02-17 13:50:14 -08:00
snipe
9d50e0e8d0 Fixed weird table formatting in expected checkin report 2021-02-17 13:44:35 -08:00
snipe
9deb4204c8 Fixed #9116 - incorrect parameter name sent to hardware.show in expected assets report 2021-02-17 13:41:08 -08:00
snipe
1ab349a63d Merge branch 'develop' of https://github.com/snipe/snipe-it into develop 2021-02-17 13:34:59 -08:00
Ivan Nieto
2e2d087639 Added logic to handle the CLI importer. (#9088) 2021-02-17 13:13:17 -08:00
snipe
bfadb2cea6 Add @koelle25 as a contributor 2021-02-17 12:58:57 -08:00
Kevin Köllmann
44eb67440a Fixed #9115: Duplicate column name 'provider' (#9137) 2021-02-17 12:57:08 -08:00
Godfrey Martinez
8fb97da314 Color of asset selection in bulk checkout [ch15525] (#9145) 2021-02-17 12:56:26 -08:00
Ivan Nieto
12ff465cdb Added feature: Checkbox Custom Fields as list of values. (#9112)
* Added functionality for checkboxes in custom fields. Similar to how radio buttons work.

* Added the same functionality when the custom fieldset is encrypted.

* Added missing bits, so the edit custom assets view shows what values have been already stored in database
2021-02-16 17:20:29 -08:00
Ivan Nieto
f89d789832 Fixes #8918 The validation rules on Manufacturer Model 'name' attribute are malformed. (#9133)
* Fixes the validation rules on Manufacturer Model

* Fixes a little issue; if the manufacturer is active soft-deletes it, if is already deleted permanently deletes it
2021-02-16 12:52:55 -08:00
Marcelo Reyes
4c3b46ea88 Fixed #9129: add missing function formatDatalistSafe to snipeit_modals.js 2021-02-16 13:50:00 +01:00
snipe
834e0a9dd5 Merge remote-tracking branch 'origin/develop'
# Conflicts:
#	config/version.php
2021-02-05 14:29:13 -08:00
snipe
681c41bd18 Bumped version 2021-02-05 14:28:31 -08:00
snipe
74488ddceb Added provider column to oauth_clients table for passport upgrade 2021-02-05 14:27:56 -08:00
snipe
19820f1b42 Merge remote-tracking branch 'origin/develop' 2021-02-03 16:08:36 -08:00
snipe
2a88781cd5 Add @Kurokat as a contributor 2021-02-03 16:07:25 -08:00
snipe
f96c867bd3 Update @ghost as a contributor 2021-02-03 16:07:06 -08:00
snipe
06f8e8620a Add @EDVLeer as a contributor 2021-02-03 16:06:28 -08:00
snipe
95d907c9e9 Add @sean-borg as a contributor 2021-02-03 16:06:14 -08:00
snipe
d990152856 Add @andres-baller as a contributor 2021-02-03 16:05:45 -08:00
snipe
05609230b2 Add @ncareau as a contributor 2021-02-03 16:05:21 -08:00
snipe
220c254093 Add @benwa as a contributor 2021-02-03 16:05:06 -08:00
snipe
02313ce361 Add @fashberg as a contributor 2021-02-03 16:04:47 -08:00
snipe
e70f7c610a Add @winstan as a contributor 2021-02-03 16:03:20 -08:00
snipe
32f77c3285 Add @alek13 as a contributor 2021-02-03 16:02:49 -08:00
snipe
0a639f4fcc Add @ThoBur as a contributor 2021-02-03 16:02:38 -08:00
snipe
f2b55fb641 Add @PauloLuna as a contributor 2021-02-03 16:02:27 -08:00
snipe
0e443356f0 Add @iansltx as a contributor 2021-02-03 16:02:00 -08:00
snipe
76f7f01398 Add @derlucas as a contributor 2021-02-03 16:01:42 -08:00
snipe
992de0156b Add @phenixdotnet as a contributor 2021-02-03 16:01:27 -08:00
snipe
c96b5f5a85 Add @sigmoidal as a contributor 2021-02-03 16:01:12 -08:00
snipe
8d2685f0f0 Add @PeterDaveHello as a contributor 2021-02-03 16:01:00 -08:00
snipe
eb14cc7f43 Add @giannello as a contributor 2021-02-03 15:59:48 -08:00
snipe
3dc67cdba6 Updated dark skins with compiled assets for prod 2021-02-03 15:20:26 -08:00
snipe
0bbe0c85d7 Small improvement to topnav dropdown text colors in dark skins 2021-02-03 15:17:31 -08:00
snipe
49415806e1 Nicer formatting of the page if custom logout [ch15660] 2021-02-03 14:56:26 -08:00
Giuseppe Iannello
5edbb4b229 Support Google Cloud IAP (#8768)
Following up on 7c2da81700,
this extends the logic, adding support for Google Cloud IAP.
2021-02-03 11:59:55 -08:00
ThoBur
c40b8334fc Update edit.blade.php (#8876)
Fix issue #8518
2021-02-03 11:59:05 -08:00
snipe
0a37c9564a Merge remote-tracking branch 'origin/develop' 2021-02-03 01:30:11 -08:00
snipe
985193ffff Fixed #9082 - allow deployable status type on checkout 2021-02-03 01:29:54 -08:00
snipe
721add5bc1 Merge remote-tracking branch 'origin/develop'
# Conflicts:
#	public/css/dist/skins/skin-blue-dark.css
#	public/css/dist/skins/skin-blue-dark.min.css
#	public/css/dist/skins/skin-green-dark.css
#	public/css/dist/skins/skin-green-dark.min.css
#	public/css/dist/skins/skin-orange-dark.css
#	public/css/dist/skins/skin-orange-dark.min.css
#	public/css/dist/skins/skin-purple-dark.css
#	public/css/dist/skins/skin-purple-dark.min.css
#	public/css/dist/skins/skin-red-dark.css
#	public/css/dist/skins/skin-red-dark.min.css
#	public/css/dist/skins/skin-yellow-dark.css
#	public/css/dist/skins/skin-yellow-dark.min.css
#	public/mix-manifest.json
2021-02-03 00:57:58 -08:00
snipe
ff8fa6ec77 Production assets for better contrast in dark mode skins 2021-02-03 00:52:23 -08:00
snipe
e0a6f22489 Small fixes to dark mode skins 2021-02-03 00:49:09 -08:00
snipe
030fdd60ff Switched branch name to master 2021-02-02 21:49:21 -08:00
snipe
fdde844ce5 Bumped version to 5.1.0 2021-02-02 21:48:42 -08:00
snipe
d263990401 Updated min requirements in composer.json 2021-02-02 21:29:09 -08:00
snipe
bf7a856fa6 Update minimum PHP requirements to 7.2.5 2021-02-02 21:05:37 -08:00
snipe
1e062d4fc8 Re-applying laravel update with PHP min 7.2.5 2021-02-02 20:58:15 -08:00
snipe
ca37de5e45 Ugh. Rolling back composer.lock
Everything sucks
2021-02-02 20:44:07 -08:00
snipe
9ba2fd93c1 Reverted Laravel upgrade :( 2021-02-02 20:34:23 -08:00
snipe
a2e177e754 Merge branch 'develop' of https://github.com/snipe/snipe-it into develop 2021-02-02 20:30:36 -08:00
snipe
5e6db0b219 Removed platform req 2021-02-02 20:30:33 -08:00
Brady Wetherington
b09ded2a3b Upgrade laravel (#9081)
* Hotfix to develop to yank extraneous e()

* Upgrade Laravel release only to a later 6.18.x version

* Upgrade Laravel to 6.x and Passport 9.x, the recommended versions
2021-02-02 20:22:36 -08:00
snipe
124343911f Merge remote-tracking branch 'origin/develop' 2021-02-02 16:57:18 -08:00
Brady Wetherington
462f8c791f Hotfix to develop to yank extraneous e() (#9080) 2021-02-02 16:44:54 -08:00
Brady Wetherington
9a224a07ba Modified how we do Select2 dynamic drop-down menus to be more secure (#9079)
* Modified how we do Select2 dynamic drop-down menus to be more secure

As noted by the author of select2, the more-secure way of creating
rich Select-dropdowns is to use jquery to create HTML snippets and
carefully modify text attributes within there. This prevents any
XSS from being brought to the page. As a side-effect, the extra
escaping that we had to do in all of the internal selectlist calls
is now no longer necessary, and has been removed. Rebased and
squashed from the original.

* Rebuilt all assets, but this still feels like it's too much stuff in here.

* Whoops, need to run that in dev, not prod
2021-02-02 15:55:21 -08:00
Ivan Nieto
df4686bc96 Added a clause that lets the function that updates the department to fail gracefully if no dept. name is provided in the CSV passed to the Importer. (#9078) 2021-02-02 12:23:36 -08:00
snipe
b6c432a596 One more line of logging for purges 2021-02-02 02:20:30 -08:00
snipe
de9f487664 Merge remote-tracking branch 'origin/develop' 2021-02-02 01:59:13 -08:00
snipe
ef668317a9 Log user initiating a purge 2021-02-02 01:58:50 -08:00
Godfrey Martinez
cf368a4577 fixed an issue with links colors (#9073)
* fixed an issue with links colors

* corrected btn-default's color
2021-02-01 13:50:37 -08:00
snipe
2e71968c04 Merge remote-tracking branch 'origin/develop' 2021-01-27 17:44:36 -08:00
snipe
fdb5b3baf1 Depreciation detail view fixes [ch15776] (#9059)
* Allow sorting by months for depreciation list view

* Added dataTableLayout to standardize the list display table

* Implement the dataTableLayout() on the list view blade

* Split the view into tabs so we can combine asset depreciations and license depreciations

* Updated depreciation view to use tabbed interface for assets and licenses

* Added asset models to depreciation details page

* Make asset model category sortable

* Added cateory as allowed to be sorted on

* Added category sort scope

* Removed offset variable

* Small fixes to asset modes display in depreciation to bulk edit models
2021-01-27 17:44:05 -08:00
snipe
c745fa095b Merge remote-tracking branch 'origin/develop' 2021-01-27 15:38:15 -08:00
Brady Wetherington
70e6a6ced6 Fix issue where users with edit permission cannot invoke LDAP sync (#9058)
* Fix issue where users with edit permission cannot invoke LDAP sync

* Make User::class consistent with usage elsewhere in the same directory
2021-01-27 15:36:43 -08:00
Ivan Nieto
6772835efc Added support for radio buttons in Custom Fields. (#9053) 2021-01-27 14:41:58 -08:00
snipe
fb482b0dd6 Fixed translation string in Depreciation view [ch15776] 2021-01-27 14:09:01 -08:00
snipe
9f43d3345f Merge remote-tracking branch 'origin/develop' 2021-01-27 12:49:33 -08:00
Ivan Nieto
6e83679528 Instead of return a JSON response, redirect back to the previous screen (#9055) 2021-01-27 12:01:42 -08:00
snipe
a050aba72f Merge remote-tracking branch 'origin/develop' 2021-01-27 01:34:57 -08:00
snipe
0031fab0fe Added termination date, depreciation in license column selection [ch14505] (#9052) 2021-01-27 01:34:32 -08:00
snipe
585bdff364 Added other logo for demo 2021-01-27 00:35:32 -08:00
snipe
1d9741a49e Merge remote-tracking branch 'origin/develop' 2021-01-26 22:12:39 -08:00
snipe
9f7f1460e9 Simplified availableForCheckout() on asset 2021-01-26 22:12:24 -08:00
snipe
f871759753 Bumped version to 5.0.13-pre 2021-01-26 20:08:44 -08:00
snipe
8e17818f1e Bumped version to 5.0.13-pre 2021-01-26 20:08:08 -08:00
snipe
d19c6ab8e7 Use fadeIn/fadeOut for password reset page for nicer look 2021-01-26 20:05:01 -08:00
snipe
d14b1e3825 Added better visual cues for login (#9051)
* Added better visual cues for login

* Changed box header class to box-header with-border to match other screens

* Since we have all.js now, added class=“minimal” to use icheck
2021-01-26 19:57:39 -08:00
snipe
ba12ee9954 Specifically call out username in forgotten password 2021-01-26 18:00:50 -08:00
Evgeny
d8bb69533c To eliminate 12/24hours ambiguity fix. This fix prevents loss of time accuracy. (#8887)
Its a tiny fix to eliminate 12/24hours ambiguity. Fix prevents time accuracy loss in field "last_audit_date" for assets.
2021-01-26 12:22:59 -08:00
snipe
01d3606c42 Merge remote-tracking branch 'origin/develop' 2021-01-26 12:20:57 -08:00
sigmoidal
208f1db3b2 Update .htaccess (#9001)
.htaccess will work on both apache v2.2 and v2.4 (without this change, on upgrade, an internal error on the server occurs)
2021-01-26 12:20:03 -08:00
Peter Dave Hello
e5b02da54b Fix apk add usage in Dockerfile.alpine (#9032)
There is no need to use `--update` when `--no-cache` is already
specified. Use `--no-cache` only will make `apk` leave no local cache
and make the image smaller.
2021-01-26 12:12:00 -08:00
Vincent Lainé
d6ead5ae17 Added #8931: add health controller without session (#8978)
* Added health controller

* Trying to move session middleware to web and api group to have health controller without session

* Fix health route store the session

Co-authored-by: Vincent Lainé <v.laine@dental-monitoring.com>
2021-01-26 12:10:54 -08:00
Ian Littman
1d7d31b9ae Optimize target type + ID index for more realistic use cases (#8923)
Per https://youtu.be/EOXgHH4-WX4?t=1378 or thereabouts
2021-01-26 12:08:25 -08:00
Brady Wetherington
2a817c2123 Add migration for ldap_server URL's to ensure they at least start with ldap:// or ldaps:// (#8936) 2021-01-26 12:07:32 -08:00
Lucas Pleß
f3a7467235 Added today as default Date for checkout Form. (#8938) 2021-01-26 12:06:58 -08:00
Ivan Nieto
2da6f9136f Add 'Last Audit Date/Next Audit Date' filter to custom report (#8989)
* Add 'Last Audit Date/Next Audit Date' filter to custom report

* Added a constraint: cannot require a report with a Last Audit Date in the future.
2021-01-26 12:05:31 -08:00
Ivan Nieto
79549dbfb9 Use the correct env variables in config/auth.php file. (#9048) 2021-01-26 12:04:41 -08:00
snipe
a48d09f37e Fixed non-superadmin gate permissions for kits (#9029) 2021-01-26 11:56:42 -08:00
Godfrey Martinez
0dc78fdea6 fixed color schema on dark mode skins for links (#9034) 2021-01-21 14:41:42 -08:00
snipe
75a8639a20 Fixed weird checkbox display
TODO: fix the 500 on the save
2021-01-12 18:16:42 -08:00
snipe
380c6171b7 Check if the asset is deployed before prompting for an expected checkin date 2021-01-12 18:02:10 -08:00
snipe
d36d6b8e07 Added expected_checkout as editable field in asset edit 2021-01-12 16:48:49 -08:00
snipe
c00a1fa21b Added new generic datepicker partial 2021-01-12 16:48:32 -08:00
snipe
bbcd215ea4 Suppress OAuth token errors 2021-01-12 08:58:40 -08:00
Godfrey Martinez
444f9a81da Bug/ch15603/read only text field in saml screen dark (#8993)
* fixed dark mode skins read only color schema

* fixed dark mode skins read only color schema
2021-01-11 14:07:55 -08:00
snipe
b4eee5a9b7 Merge remote-tracking branch 'origin/develop' 2021-01-05 19:40:25 -08:00
Ivan Nieto
72f9fe444d Fix for default parameters in JS functions IE (#8973)
* Small fix for IE which doesn't support default parameters in the function definition

* Stylistic changes for better comprehension
2021-01-05 19:25:30 -08:00
snipe
eb423c252a Merge remote-tracking branch 'origin/develop'
# Conflicts:
#	config/version.php
2021-01-04 09:09:39 -08:00
snipe
382fb31670 Updated translations (Finnish, Polish, Japanese, Turkish, Spanish) 2020-12-30 12:51:09 -08:00
snipe
e6ba4a423d Fixed #8721 - duplicate asset tags in select list when asset has name 2020-12-21 13:43:54 -08:00
snipe
13ed6cde67 Migration to confirm password min complies with newer Laravel min 2020-12-21 12:39:19 -08:00
snipe
cac78cdbf3 Use snipe-logo.png as default in settings seeder 2020-12-21 11:36:43 -08:00
snipe
8b67326e95 Merge branch 'develop' of https://github.com/snipe/snipe-it into develop 2020-12-21 11:35:21 -08:00
snipe
f65bc5caee Updated logos 2020-12-21 11:35:17 -08:00
Brady Wetherington
0329028e2c Fixed #8926, #8252 - introduce circular reference check for location parent_id - rebased from #8253 (#8927)
* Fixed #8252 - circular references in location parents

* Remove non-translated translation changes

* Fix typo

* Add loop limit to avoid unforseen infinite loops

* Remove check against parent_id in location controllers

* Remove the Location->id=null piece (no longer needed)

* Fix some formatting and whitespace

* Re-introduce accidentally merged-out language file

Co-authored-by: Travis Miller <milletr@tulsaschools.org>
2020-12-18 17:18:04 -08:00
snipe
d3d96c8285 Reverted composer until we can figure out wtf happened 2020-12-15 17:25:17 -08:00
snipe
5909860c5a Updated exception handler 2020-12-15 17:25:05 -08:00
snipe
1023fa3edd Re-added updated slack library 2020-12-15 14:10:57 -08:00
snipe
65e6d56f1f Display the extension error at the bottom of the extension list to make it more noticable 2020-12-15 14:09:42 -08:00
snipe
bf34385c3e Temp revert to old slack library
Getting a weird error, need to troubleshoot remotely
2020-12-15 13:43:18 -08:00
Godfrey Martinez
55a526a6b3 corrected background color for bulk checkout listable items in all da… (#8916)
* corrected background color for bulk checkout listable items in all dark schemas

* resubmitting
2020-12-15 12:37:22 -08:00
Evgeny
bbf7fbcff4 Localization digit separator feature. (#8915)
Provides an ability to localize the purchase_cost field in front-end hardware index table.
Has two digit separator formats in admin settings with comma and dot.
2020-12-15 11:49:13 -08:00
snipe
4a5cb94d94 Switch version file back to develop 2020-12-14 17:47:02 -08:00
snipe
cb184a9687 Updated composer.lock with newer packages 2020-12-14 17:46:39 -08:00
Alexander Chibrikin
fb37dbed92 use supported package for slack (#8867)
* Updated version.php to master

* use supported package for slack

Co-authored-by: snipe <snipe@snipe.net>
2020-12-14 17:45:00 -08:00
João Paulo
e410696a36 Fixed #8884: Fixed alpine image build (#8885)
* Fixed #8884: update alpine image dependencies, fix permission error in vendor folder and laravel.log file

* Fixed #8884: Removes unnecessary changes to fix alpine image build

* Fixed #8884: Removes unnecessary changes to fix alpine image build

* Fixed #8884: Fix typo
2020-12-14 17:42:43 -08:00
snipe
45bfec5cd3 Match setup admin on Quickstart password with min reqs for Settings model 2020-12-11 13:52:48 -08:00
snipe
055522510b Fixed logo in seeder 2020-12-09 08:48:47 -08:00
snipe
f1d0d1bfe7 Merge remote-tracking branch 'origin/develop' 2020-12-09 08:19:18 -08:00
snipe
c0aa6c153e Better callout for warning on api token page 2020-12-09 08:19:04 -08:00
snipe
da3451bf0d Merge remote-tracking branch 'origin/develop' 2020-12-09 08:15:04 -08:00
snipe
5f76e03616 Clarified API url info in account > api 2020-12-09 08:14:44 -08:00
snipe
84710eac98 Merge remote-tracking branch 'origin/develop' 2020-12-08 21:10:05 -08:00
snipe
81bf41a091 Added an explanation for folks trying to access the API base endpoint with no real endpoint 2020-12-08 21:05:01 -08:00
snipe
134acf3b87 Merge remote-tracking branch 'origin/develop' 2020-12-08 20:04:19 -08:00
snipe
82d8b2ab82 Fixed weird parsing in newer markdown 2020-12-08 20:04:01 -08:00
snipe
adc0d3a6ac Merge remote-tracking branch 'origin/develop'
# Conflicts:
#	config/version.php
2020-12-08 16:53:43 -08:00
snipe
8b15841c4b Bumped version 2020-12-07 19:16:42 -08:00
snipe
fd4ee60276 Merge remote-tracking branch 'origin/develop' 2020-12-07 19:14:00 -08:00
Brady Wetherington
93358b5872 Merge pull request #8863 from johnson-yi/fixes/saml_debug
Fixes #8853 - allow saml to be more easily debugged
2020-12-07 14:50:23 -08:00
Johnson Yi
1c4e20c712 Allow saml to be more easily debugged 2020-12-04 21:54:04 +11:00
snipe
0e1f6a3fd1 Merge remote-tracking branch 'origin/develop' 2020-12-02 12:38:16 -08:00
snipe
71d0e6369e Merge branch 'develop' of https://github.com/snipe/snipe-it into develop 2020-12-02 11:01:10 -08:00
snipe
b27aeb1952 Added LDAP flag in users API for #8741 2020-12-02 11:01:05 -08:00
snipe
bec2b170ec Only overwrite notes if the LDAP user is new 2020-12-02 11:00:18 -08:00
snipe
269d3fe509 Make assigned_to_id nullable 2020-12-02 10:48:34 -08:00
Brady Wetherington
34d5473553 Fixes 8472 (again) - LDAP sync was assigning a bad default location (#8846) 2020-12-01 21:26:52 -08:00
snipe
4ac15daee7 Fixed #8147 - allow webp image format for public file uploads 2020-12-01 19:06:53 -08:00
snipe
5f3a1f6287 Merge remote-tracking branch 'origin/develop' 2020-11-30 20:47:03 -08:00
snipe
bfc60864dd Fixed typo on route for licenses 2020-11-30 20:46:45 -08:00
snipe
ffa2701f89 Fixed typo in demo logo 2020-11-30 18:59:44 -08:00
snipe
60d269afb5 Merge remote-tracking branch 'origin/develop'
# Conflicts:
#	config/version.php
2020-11-30 17:28:46 -08:00
snipe
e2cb7a0242 Bumped version 2020-11-30 17:28:03 -08:00
snipe
f9b1fdc36b Merge remote-tracking branch 'origin/develop' 2020-11-30 17:26:31 -08:00
Brady Wetherington
93cf8d4e0a Forward-port of the old LDAP sync system (#8801)
* Forward-port of the old LDAP sync system

* Need to rename the class to avoid classname conflicts

* Make 'classic' LDAP sync not add surrounding parens to filters that already have them

* Re-work Test LDAP button to return 10 sample users

* Remove useless debugging code
2020-11-30 17:11:44 -08:00
snipe
e83bc03d97 Switch backup files array order to show latest first [ch15486]' 2020-11-30 14:46:10 -08:00
snipe
b0d493ee51 Merge remote-tracking branch 'origin/develop' 2020-11-30 12:54:30 -08:00
snipe
4882b01787 Added purchase order and order number to user > licenses view 2020-11-30 12:54:15 -08:00
snipe
f9dcf0783a Added phantomjs to package.json 2020-11-28 16:58:02 -08:00
snipe
985f3658be Added wider logo 2020-11-27 18:17:09 -08:00
snipe
705dd34f3e Removed older SAML fields 2020-11-27 18:14:32 -08:00
snipe
6cf5426540 Removed unused validation rule 2020-11-25 11:00:28 -08:00
snipe
2105a1ec1d Merge remote-tracking branch 'origin/develop' 2020-11-25 08:55:14 -08:00
snipe
f475bdbb2d Fixed #8797 - use html_entity_decode in fullName presenter for User 2020-11-25 08:54:23 -08:00
snipe
96eb623229 Merge remote-tracking branch 'origin/develop' 2020-11-25 01:53:21 -08:00
snipe
820a39cc90 Fixed #8814 - added App\Models\Recipients\AlertRecipient 2020-11-25 01:52:56 -08:00
snipe
615051cf66 Skip posix_getpwuid in upgrader if posix isn’t installed
We don’t need it for anything else, so no need to require it. Posix not being installed usually means it’s a windows machine.
2020-11-25 01:19:32 -08:00
snipe
bef42eb43c Merge remote-tracking branch 'origin/develop' 2020-11-25 00:47:15 -08:00
snipe
6f99ce2b07 Branding page UI improvements (image previews inline) 2020-11-25 00:45:46 -08:00
snipe
76ee5a679b Fixed #8810 - email logo was not being used in emails 2020-11-25 00:05:02 -08:00
snipe
26e4354433 Merge remote-tracking branch 'origin/develop' 2020-11-24 19:49:59 -08:00
snipe
72fc03aa50 Adds location to searchableRelations for asset model 2020-11-24 19:49:46 -08:00
snipe
bf1f8659cb Merge remote-tracking branch 'origin/develop' 2020-11-24 16:06:01 -08:00
snipe
c0d7564658 Fixed #8794 - Switched to firstOrCreate to create parents on import 2020-11-24 16:05:24 -08:00
snipe
74b26a349c Merge remote-tracking branch 'origin/develop'
# Conflicts:
#	config/version.php
2020-11-24 13:54:02 -08:00
snipe
e9bfb157bb Bumped version. Again. 2020-11-24 13:53:28 -08:00
snipe
ef957399aa Merge remote-tracking branch 'origin/develop' 2020-11-24 13:51:19 -08:00
snipe
973eacf6c3 Small fixes for SAML
The SAML routes are in a service provide (sigh), so they did not have the `web` middleware group assigned to it.

I also added some additional checks so that the setup blade won’t fail (the migrations wouldn’t have been run yet, so outside of a try/catch, it would return an error since those tables don’t exist.)
2020-11-24 13:51:02 -08:00
snipe
02fef7049f Merge remote-tracking branch 'origin/develop'
# Conflicts:
#	config/version.php
2020-11-24 12:17:38 -08:00
snipe
b2660002b9 Fixed #8781 - added asset count by status type (#8806) 2020-11-24 12:15:07 -08:00
snipe
2c0b9f959b Comment with possible alternatve to the custom undeleted validator 2020-11-24 12:01:09 -08:00
snipe
51286d2244 Merge branch 'develop' of https://github.com/snipe/snipe-it into develop 2020-11-24 11:40:35 -08:00
snipe
712363f861 Bumped version 2020-11-23 12:33:07 -08:00
johnson-yi
0cdd83aabf Fixes #8584, #8654, #8727 - fixes and improvements for saml (#8795)
* Let onelogin/php-saml know to use 'X-Forwarded-*' headers if it is from a trusted proxy

* Gracefully handle the case where openssl_csr_new fails when openssl.cnf is invalid/missing

* Improve ui of saml sp metadata by displaying it's url and a download button
2020-11-20 18:54:25 -08:00
snipe
98dfb9d1b5 Merge remote-tracking branch 'origin/develop' 2020-11-20 14:35:12 -08:00
snipe
ae5635ff97 Merge remote-tracking branch 'origin/develop' 2020-11-20 14:34:43 -08:00
snipe
8a38b9d018 Merge branch 'develop' of https://github.com/snipe/snipe-it into develop 2020-11-20 14:34:35 -08:00
Brady Wetherington
734cb941dd Fix for #8772 - initial LDAP import of users are deactivated (#8776) 2020-11-20 11:11:14 -08:00
snipe
c66f8c04c8 For for deleted user API 2020-11-18 14:57:44 -08:00
Brady Wetherington
fa24799d2b Change license serial type to TEXT for longer license files (#8782) 2020-11-18 14:22:51 -08:00
snipe
57d6a7d35e Fixed #8753 - create-asset-from-model did not preselect the model 2020-11-18 13:59:02 -08:00
snipe
9e2d402c3a Merge remote-tracking branch 'origin/develop' 2020-11-18 07:06:33 -08:00
snipe
551e28eec9 Fixed #8749 - added more default colors to prevent missing index in pie chart math 2020-11-18 07:06:14 -08:00
snipe
9e30be8a04 Merge remote-tracking branch 'origin/develop' 2020-11-17 22:31:46 -08:00
Brady Wetherington
8457207c8f Fix for 500-page on deactivated LDAP user trying to log in (#8774) 2020-11-17 22:31:22 -08:00
snipe
cc4f1a1485 Merge remote-tracking branch 'origin/develop' 2020-11-17 22:20:38 -08:00
snipe
2ea805b7ed Added search/sort by Location OU, added to listing 2020-11-17 22:17:07 -08:00
snipe
20ef74db42 Merge remote-tracking branch 'origin/develop' 2020-11-17 19:26:15 -08:00
Brady Wetherington
3dda4c9116 Add a new UAC setting to valid User Accounts for Active Directory (#8775) 2020-11-17 19:25:49 -08:00
snipe
e805feff9e Merge remote-tracking branch 'origin/develop' 2020-11-17 19:22:56 -08:00
snipe
0286cf6d46 Prevent license categories from being deleted if there are licenses in that category 2020-11-17 19:22:10 -08:00
snipe
6ebdfdbabb Merge remote-tracking branch 'origin/develop' 2020-11-17 16:16:11 -08:00
snipe
68487e1200 Added img-responsive class to preview images 2020-11-17 16:15:25 -08:00
snipe
f19b9a44fc Fixed #8773 - wrong path for suppliers image on suppliers edit 2020-11-17 16:10:54 -08:00
snipe
124af6ac6b Merge remote-tracking branch 'origin/develop' 2020-11-17 14:33:35 -08:00
snipe
7f126969d0 Fixed #8601 - Switched to “attachment” from “base64” 2020-11-17 14:33:16 -08:00
snipe
e4b0e9673d Merge remote-tracking branch 'origin/develop' 2020-11-17 13:43:46 -08:00
snipe
7ab44ca963 Ignore php-lint cache 2020-11-17 13:43:24 -08:00
snipe
4898b58bdb Fixed #8765 - UI not indicating model is required 2020-11-17 13:40:15 -08:00
snipe
9b1e7ba8a6 Updated translations with new strings 2020-11-17 13:22:53 -08:00
snipe
e3d2369151 Fixed #8769 - incorrect translation for “DELETE” in purge 2020-11-17 12:01:02 -08:00
snipe
e5f0a12c25 Updated version.php to master 2020-11-17 00:14:28 -08:00
Brady Wetherington
de6f3f866f Attempt to solve LDAP sync overriding administrator choices (#8742) 2020-11-17 00:11:00 -08:00
snipe
8444a60bc9 Merge branch 'develop' of https://github.com/snipe/snipe-it into develop 2020-11-16 23:57:23 -08:00
snipe
3237411b5d Bumped version 2020-11-16 23:57:17 -08:00
snipe
5ba1659563 Removed dupe date 2020-11-16 23:56:48 -08:00
snipe
6e068ec339 Add @chamilton-ccn as a contributor 2020-11-16 22:15:59 -08:00
Charles Hamilton
4fd666716f Fixed #8482 - Ubuntu version (#8598)
* Bash cannot compare floating point numbers natively. This modification addresses issue #8482 and retains the intended function of the previous code.

* Bash cannot compare floating point numbers natively. This modification addresses issue #8482 and retains the intended function of the previous code.

Co-authored-by: Charles Hamilton <chamilton@dyercpa.com>
2020-11-16 22:15:40 -08:00
snipe
6eb860ca24 Swapped travis build badge for chipper build badge 2020-11-16 21:49:57 -08:00
snipe
903698a7b0 Merge remote-tracking branch 'origin/develop' 2020-11-16 21:35:26 -08:00
snipe
513faf2db5 Merge branch 'develop' of https://github.com/snipe/snipe-it into develop 2020-11-16 21:34:59 -08:00
snipe
9e46bc6c28 Fix for spanish translation 2020-11-16 21:34:47 -08:00
NMC
402fecd408 Fixed #7850: allow xlsx file. (#8386)
Thanks!
2020-11-16 21:32:15 -08:00
Brady Wetherington
a97b15ec96 Re-add LDAP "test login" feature to LDAP settings (helps with #8751) (#8757)
* Start work on re-adding back the "Test LDAP logins" feature to develop

* Add back-end method to allow LDAP test users to try to log in.
2020-11-16 21:23:08 -08:00
snipe
8504c9e8b9 Added phplint to dev in composer 2020-11-16 19:08:04 -08:00
snipe
4ebfd6624c Merge remote-tracking branch 'origin/develop' 2020-11-16 17:24:57 -08:00
snipe
fbaf6e2494 Fixed #8747 - upload path for uploaded images 2020-11-16 16:39:02 -08:00
snipe
612d3f9b2a Merge remote-tracking branch 'origin/develop' 2020-11-16 13:32:53 -08:00
Brady Wetherington
6e4ab5cd96 Fix for #8746 - missing comma in a translation file (#8756) 2020-11-16 13:29:57 -08:00
snipe
849966d2c5 Merge remote-tracking branch 'origin/develop' 2020-11-13 16:48:58 -08:00
snipe
1f5bcf2475 Added option to download activity report 2020-11-13 16:25:05 -08:00
snipe
58c476195b Bumped hash 2020-11-13 14:14:27 -08:00
snipe
dcd98a7bf1 Bumped hash 2020-11-13 14:13:44 -08:00
snipe
686ab681e5 Removed duplicate badge 2020-11-13 13:54:27 -08:00
snipe
052f8e2c42 Addresses - not fixes - #8740
More investigation here is needed
2020-11-13 12:34:42 -08:00
snipe
e9578ba8a1 Fixed #8737 - incorrect validation string for components update 2020-11-13 12:09:48 -08:00
snipe
0c0de5e351 Added clarity in language as to what the activated flag does on LDAP screens 2020-11-13 11:52:34 -08:00
snipe
10cadecd14 Moved a few of the user edit fields to make more sense (optional stuff further down) 2020-11-13 11:52:09 -08:00
snipe
644084658a Merge remote-tracking branch 'origin/develop' 2020-11-13 04:30:03 -08:00
snipe
07936ea901 Changed asset name to asset tag in maintenances 2020-11-13 03:38:31 -08:00
snipe
08784f9cc5 Make expected_checkin fillable 2020-11-13 03:22:26 -08:00
snipe
a87e615e7f Fixed #8544 - escaping on maintenance notes 2020-11-12 23:55:16 -08:00
snipe
df5cc7525e Merge remote-tracking branch 'origin/develop' 2020-11-12 23:39:09 -08:00
snipe
75b8c3455c Fixed #8609 - custom fieldsets not draggable anymore (regression) 2020-11-12 23:38:12 -08:00
snipe
81d38a0ded Fixed #8733 - typo in unaccepted assets report 2020-11-12 23:26:05 -08:00
snipe
b2a8af2fa9 Fixed #8647 - Added additional help info on importer page 2020-11-12 22:46:51 -08:00
snipe
9d2363741e Improved 2FA reset layout 2020-11-12 22:25:07 -08:00
snipe
fc6a33ad38 Added some LDAP debugging - related to #8670 2020-11-12 22:19:16 -08:00
snipe
896ce3456e Possible fix for #8670 - LDAP users deactivated on login 2020-11-12 22:18:49 -08:00
snipe
9db191f0b2 Fixes for #8732 - Flysystem paths, migrator script 2020-11-12 21:46:06 -08:00
snipe
c7d752fb65 Added S3 url into CSP 2020-11-12 19:50:01 -08:00
snipe
5026177161 Added link to bricelabelle/snipe-it-bulkedit 2020-11-12 18:55:02 -08:00
snipe
d2805442ad Updated Laravel version 2020-11-12 18:54:49 -08:00
snipe
7765c87387 Fixed #8669 - Allow application/xml in SAML upload in Safari 2020-11-12 18:47:19 -08:00
snipe
6dccf399a5 Fixed offset issue for #8732 2020-11-12 15:51:31 -08:00
snipe
7432e3fb2d Add @dampfklon as a contributor 2020-11-12 15:28:29 -08:00
snipe
caeea9f530 Merge remote-tracking branch 'origin/develop' 2020-11-12 15:27:49 -08:00
snipe
0fdfd013e7 Fixed #8715 - wrong API url for unaccepted assets 2020-11-12 15:26:50 -08:00
snipe
d537fc5c32 Added linebreak to console confirmation 2020-11-12 15:21:00 -08:00
snipe
9164dda64f Added artisan command to purge logins 2020-11-12 15:20:15 -08:00
snipe
5ea9c31eab Removed debugging 2020-11-12 15:14:56 -08:00
snipe
c8572deb5c Merge branch 'develop' of https://github.com/snipe/snipe-it into develop 2020-11-12 15:13:50 -08:00
snipe
57d25ebb20 Fixed #8719 - use same convention for file names on licenses as we do on assets 2020-11-12 15:13:45 -08:00
Dampfklon
be114176a2 Refactor Expected Checkin Notification into markdown and enable translation (#8236)
Co-authored-by: snipe <snipe@snipe.net>
2020-11-12 15:09:40 -08:00
Dampfklon
1f9b04405c Enable translation of accept-asset page (#8237) 2020-11-12 15:07:45 -08:00
Dampfklon
4ef11c463c Enable translations of checkin notifications (#8235) 2020-11-12 15:05:57 -08:00
Dampfklon
5fb31a5a3f Use translated salutation from other notifications (#8234) 2020-11-12 15:05:22 -08:00
snipe
f0e04ab9e4 Fixed #8726 - disallow archived assets to be checked out 2020-11-12 15:01:13 -08:00
snipe
8a65081768 Possible fix for #8732 2020-11-12 14:47:38 -08:00
snipe
c451fde466 Fixed exception namespace 2020-11-12 14:27:59 -08:00
snipe
5098d69c05 Fixed stupid auto-indent 2020-11-12 14:20:54 -08:00
snipe
b12de13041 Fixed markdown in expiring licenses alert 2020-11-12 14:10:45 -08:00
snipe
72126f7d44 Removed duplicate extension checks 2020-11-12 13:24:53 -08:00
snipe
df000ce32f Merge branch 'develop' of https://github.com/snipe/snipe-it into develop 2020-11-12 13:22:54 -08:00
snipe
6a2e21f502 More in depth error reporting on mail test failure 2020-11-12 13:22:49 -08:00
Brady Wetherington
5ad6234584 Merge pull request #8730 from winstan/patch-1
Fixed #8699: exclude blank LDAP attributes from searches
2020-11-12 11:26:19 -08:00
Anthony Winstanley
cc79bb1449 Exclude blank LDAP attributes
Asking for '' attributes is an error for some LDAP servers.
2020-11-12 09:34:28 -08:00
snipe
c8d588871c Merge remote-tracking branch 'origin/develop' 2020-11-11 14:47:30 -08:00
snipe
8890372a69 Production assets with dark skin fixes 2020-11-11 14:47:13 -08:00
Godfrey Martinez
80f2d749a2 Corrected css to the bootstrap popovers for all dark mode skins (#8714)
* added css to the bootstrap popovers in dark blue

* corrects the tool tips css for all dark mode skins other than dark blue
2020-11-11 14:11:39 -08:00
snipe
7f7064c835 Make last_checkout fillable 2020-11-11 13:30:11 -08:00
snipe
31d0b4a46c Merge remote-tracking branch 'origin/develop' 2020-11-10 12:39:55 -08:00
snipe
5759d4819e Improved upgrade.php script to check for PHP extensions (#8712)
* Troubleshooting gd detection :(

* Welp, that didn’t work.

* Improvements to the update script that will make it easier to maintain

* Improved spacing

* Nicer intro

* Sorry for all the commits - I have to push in order to test :(

* More display improvements

* Phrasing!

* More formatting

* Removed extra line break

* Few more formatting changes

* Remove the config caching - it’s too confusing for users

* Added comments

* Temp required extension in the array so I can show output :)

* Added ascii x

* removed extra line break

* Made error message clearer

* Remove extra line

* Removed farts

* Changed phrasing for PHP upgrade warning

* Small tweaks per PR review

* Some spacing fixes

* Added confirmation of the either/ors

* Minor formatting tweaks

* Fixed weird indenting?

* Fixed missing $unused_file variable
2020-11-10 12:39:10 -08:00
snipe
c49788dd9f Fixed weird linebreaks in upgrade.php 2020-11-10 09:08:13 -08:00
snipe
3641a6d451 Additional extension checking 2020-11-10 09:06:44 -08:00
snipe
8c79070cd9 Added some extension checking for upgrade.php 2020-11-10 08:47:35 -08:00
snipe
f8563bec94 Added some extension checking for upgrade.php 2020-11-10 08:46:19 -08:00
snipe
fd7c0bc5fb Use newer blade facade for requests 2020-11-10 08:36:18 -08:00
snipe
7d708572fc Slightly nicer formatting for query 2020-11-10 07:43:54 -08:00
snipe
ea68ff1284 Sigh. Typo 2020-11-10 06:51:40 -08:00
snipe
66ccf4da03 Merge remote-tracking branch 'origin/develop' 2020-11-10 06:44:10 -08:00
snipe
0b4a13156f Added more detail and dryrun to assigned_to vs log checker script 2020-11-10 06:43:36 -08:00
snipe
a8a3962008 Merge remote-tracking branch 'origin/develop' 2020-11-10 06:17:04 -08:00
snipe
e110a7b15e Experimental script to find mismatched IDs based on logs 2020-11-10 06:16:15 -08:00
snipe
30d68309a9 Add ability to checkout an asset if the user it’s assigned_to isn’t valid
This would only happen if a merge-users went wonky
2020-11-10 05:06:47 -08:00
snipe
547d1a5a93 Merge remote-tracking branch 'origin/develop' 2020-11-10 04:43:20 -08:00
snipe
adf64361e8 Updated most recent translations 2020-11-10 04:43:02 -08:00
snipe
a43fb060f4 Throw an error if the asset cannot be updated in the merge 2020-11-10 04:41:40 -08:00
snipe
c607d89817 Merge remote-tracking branch 'origin/develop' 2020-11-10 01:59:18 -08:00
snipe
8c19b11e73 Fixed #8448 - “undefined” when testing email from quickstart 2020-11-10 01:48:18 -08:00
snipe
178ed82dc4 Fixed [ch9336] - Removed time stamp in Depreciation report 2020-11-10 01:13:13 -08:00
snipe
6757df5a2d Added Checkout Date Time to Asset Details Page [ch1418] 2020-11-10 01:02:08 -08:00
snipe
044dfe2620 Fixed [ch945] - Status colors are not displaying correctly in the chart on the dashboard 2020-11-10 00:54:39 -08:00
snipe
9e319e91d6 Merge remote-tracking branch 'origin/develop' 2020-11-09 23:56:33 -08:00
snipe
895a544d4c Fixed [ch15347] - added status in Depreciation Report 2020-11-09 23:56:16 -08:00
snipe
2aa9412565 Bumped version
# Conflicts:
#	config/version.php
2020-11-09 23:09:46 -08:00
snipe
35ab5c7df0 Bumped version 2020-11-09 23:09:10 -08:00
snipe
8a7cd87644 Merge remote-tracking branch 'origin/develop' 2020-11-09 23:05:40 -08:00
snipe
1cdf6f8263 Fixed #8673 - added category to accessories listing on Account > Assigned Assets 2020-11-09 23:05:19 -08:00
snipe
b8ad930690 Merge remote-tracking branch 'origin/develop' 2020-11-09 22:55:42 -08:00
snipe
ec14a117b7 Display on the API token page the expiration in years
TODO: Translate these strings
2020-11-09 22:55:27 -08:00
snipe
b7cc12a466 Merge remote-tracking branch 'origin/develop' 2020-11-09 22:53:23 -08:00
snipe
f4080a7aa9 Fixed created_at and expired_at order in API token screen 2020-11-09 22:53:07 -08:00
snipe
5abfbdd1d2 Allow API token expiration in years to be configured via env 2020-11-09 22:52:55 -08:00
snipe
31bbb2d035 Merge remote-tracking branch 'origin/develop' 2020-11-09 22:43:41 -08:00
snipe
33dca84ec7 Show expiry and created at date on API token page 2020-11-09 22:43:27 -08:00
snipe
460485d843 Make API tokens expire in 20 years
TODO: Make this configurable and report the expiration in the UI
2020-11-09 22:33:43 -08:00
snipe
b59269d5e2 Bumped version 2020-11-09 21:46:42 -08:00
snipe
118931b6fb Merge remote-tracking branch 'origin/develop' 2020-11-09 21:46:26 -08:00
snipe
8bbf8409b1 Bumped hash 2020-11-09 21:46:08 -08:00
snipe
b2101a5188 Merge remote-tracking branch 'origin/develop' 2020-11-09 21:09:05 -08:00
snipe
ede20523f2 Fixed #8680 - location image path 2020-11-09 21:08:19 -08:00
snipe
89d9b830a0 Updated PHP requirement 2020-11-09 21:05:41 -08:00
snipe
d76eea53c4 Bumped hash 2020-11-09 19:45:27 -08:00
snipe
63a8535de1 Merge remote-tracking branch 'origin/develop' 2020-11-09 19:11:37 -08:00
snipe
ef7434b7ac Updated template 2020-11-09 19:11:18 -08:00
snipe
148b375ec2 Make 600 a string for ini_set 2020-11-09 19:11:00 -08:00
snipe
d49bc33b54 Merge branch 'develop' of https://github.com/snipe/snipe-it into develop 2020-11-09 18:21:15 -08:00
snipe
5bff5cb8c0 Updated Crowdin file 2020-11-09 18:21:10 -08:00
snipe
c8634d85f5 Merge pull request #8706 from uberbrady/fix_bootstrap_tables_empty_fields
Potential fix for empty-page problem in Assets view
2020-11-09 18:19:42 -08:00
Brady Wetherington
a5423649f4 This is the smallest set of changes we can use to work around a change in functionality
in Bootstrap Tables. This should remedy some of the issues we've been having with large
GET strings being sent to the back-end by the Assets system.
2020-11-09 17:36:07 -08:00
snipe
b026953190 Merge pull request #8688 from sanjay900/patch-1
Fix AD Accounts with delegation disabled
2020-11-09 16:13:13 -08:00
snipe
3c209b29b1 Merge remote-tracking branch 'origin/develop' 2020-11-09 15:09:05 -08:00
snipe
c0fb87c7f9 Merge pull request #8702 from uberbrady/ldap_time_limit_sync
Restore the LDAP_TIME_LIM .env var
2020-11-09 15:08:17 -08:00
snipe
48c3d0fd19 Merge remote-tracking branch 'origin/develop'
# Conflicts:
#	resources/lang/es-VE/general.php
2020-11-09 14:55:11 -08:00
snipe
67f60127be Updated translations with latest from CrowdIn 2020-11-09 14:52:11 -08:00
snipe
4a8a98a7d3 Fixed user test 2020-11-09 14:17:45 -08:00
snipe
4b8e9a962b Fixed Venezuelan translation file 2020-11-09 14:05:31 -08:00
Brady Wetherington
966179290b Restore the LDAP_TIME_LIM env var for ldap sync of very large directories 2020-11-09 11:11:42 -08:00
snipe
6618e88a9a Fixed spanish translation parse error 2020-11-06 10:19:32 -08:00
Sanjay Govind
ab6d2987a4 Include documentation from @uberbrady
Previously on v4 this was all documented, so i have copied that documentation across to v5
2020-11-06 12:31:33 +13:00
Sanjay Govind
45c140814f Fix AD Accounts with delegation disabled 2020-11-06 12:02:19 +13:00
snipe
467ff045aa Moved the BACKUP_ENV to new BACKUP block 2020-11-03 14:58:19 -08:00
snipe
e0103a7fbd Fixed #8595 - Updated spatie settings 2020-11-03 14:50:02 -08:00
snipe
432263f09f Merge pull request #8671 from mzack5020/patch-1
Fixed #8624 #8312: Re-Added Accessories API
2020-11-03 13:50:38 -08:00
Matthew Zackschewski
911841d188 Re-Added Accessories API
#8312 #8624 Removed unnecessary duplicate licenses endpoint and re-added the user accessories endpoint which was left off.
2020-11-03 16:40:43 -05:00
snipe
e8fd2a7fe8 Merge remote-tracking branch 'origin/develop' 2020-11-03 13:07:31 -08:00
snipe
df5972c4fa Fixed send creds checkbox in create user form 2020-11-03 13:07:15 -08:00
snipe
4d003ac97a Merge remote-tracking branch 'origin/develop'
# Conflicts:
#	app/Http/Controllers/Auth/ResetPasswordController.php
#	config/version.php
2020-11-03 11:49:34 -08:00
snipe
9f4a212b44 Bumped hash 2020-11-03 11:47:58 -08:00
snipe
12667f41b9 bumped hash 2020-11-03 11:47:13 -08:00
snipe
a8390ce4bd Removed debugging 2020-11-03 11:45:19 -08:00
snipe
cfc1e1366c Added new password complexity rules to forgot password 2020-11-03 11:42:42 -08:00
snipe
44e5fec707 Fixed #8603 - Use correct CSS path for signature-pad.min.css 2020-11-03 10:01:55 -08:00
snipe
f138d3b781 Fixed typo 2020-11-03 01:00:37 -08:00
snipe
bbd7a752a0 Minor cleanup 2020-11-02 23:58:49 -08:00
snipe
a66a22ffb2 Updated passwordComplexityRulesSaving() signature so it isn’t nullable 2020-11-02 23:58:37 -08:00
snipe
b29f794b35 password.expire updated for forgotten password token expiration 2020-11-02 23:58:09 -08:00
snipe
410c99da17 Added signature-pad.min.css to the css directory - should possibly fix #8603 2020-11-02 23:57:39 -08:00
snipe
c800662f0c Use username for forgotten password
Since emails are not required to be unique
2020-11-02 23:57:00 -08:00
snipe
b828985151 Fixed strpos to !== FALSE in ProfileController for complexity eval 2020-11-02 22:28:56 -08:00
snipe
e6f53a53bc Updated min requirement for passwords in lang files 2020-11-02 22:15:01 -08:00
snipe
0c2800c7dd Removed debugging line 2020-11-02 21:26:13 -08:00
snipe
a8f9f5239c Merge pull request #8662 from snipe/features/disallow_password_equal_to_username_etc
Fixed #8661 - Added feature to disallow password equal to username, email, etc
2020-11-02 21:09:31 -08:00
snipe
63bb2de4d4 Derp. Check and make sure that setting is actually on 2020-11-02 20:13:54 -08:00
snipe
c4b2ef5660 Removed debugging code 2020-11-02 20:10:18 -08:00
snipe
296655542d Added comments 2020-11-02 20:07:39 -08:00
snipe
a55694da2f Added validation to disallow password to be the same as username, email, etc 2020-11-02 20:03:26 -08:00
snipe
33bda9b6d1 Added blade option for same_pwd_as_user_fields
Disallows password form value to be the same as username, email, first name or last name
2020-11-02 17:47:25 -08:00
snipe
42347d0f0c Added validation language for same_pwd_as_user_fields 2020-11-02 17:46:30 -08:00
snipe
6554903aeb Removed “use slack” debugging code 2020-11-02 17:46:09 -08:00
snipe
502322be4b Fixed v5 500 server error when requesting multiple [ch15345] 2020-11-02 16:08:38 -08:00
snipe
72facffcbe Fixed demo settings value for support_footer 2020-10-26 15:09:02 -07:00
snipe
001bb3cbe2 Bumped version to “pre” 2020-10-26 15:04:22 -07:00
snipe
f42bd3a144 Fixed #8613 - Added deleted_at to user API response, added all=true to include deleted and not-deleted in one call 2020-10-26 14:58:45 -07:00
snipe
b9330cf6b1 Fixed #8613 - 500 error on deleted users API call 2020-10-26 14:55:18 -07:00
snipe
2844800caf Merge remote-tracking branch 'origin/develop'
# Conflicts:
#	config/version.php
2020-10-26 14:26:05 -07:00
snipe
b7e8b9bad7 Merge branch 'develop' of https://github.com/snipe/snipe-it into develop 2020-10-26 14:24:43 -07:00
snipe
a7ecaa6ebc Merge pull request #8619 from uberbrady/fix_ldap_paging
Fixed #8563 - Clean up AdLdap2 integration to better handle paged result-sets
2020-10-26 14:24:34 -07:00
snipe
7937258f6e Bumped version 2020-10-26 14:24:19 -07:00
Brady Wetherington
cce0739bb7 Clean up AdLdap2 integration to better handle paged result-sets 2020-10-26 12:53:45 -07:00
snipe
c93f4ef0d5 Merge remote-tracking branch 'origin/develop' 2020-10-23 19:50:56 -07:00
snipe
89e36dbc42 Merge pull request #8606 from uberbrady/fix_cant_manage_self
Add a new custom validator for Users
2020-10-23 19:16:11 -07:00
snipe
d317052ede Updated CSS assets with green skin edits 2020-10-23 18:55:00 -07:00
snipe
8ee9e5e059 Fixed #8537 - wrong sorting on dashboard 2020-10-23 18:33:10 -07:00
snipe
2602e4d602 Exclude the config directory from backups
Everything there is handled via .env anyway
2020-10-23 18:21:32 -07:00
snipe
f357d9fc90 Include audit settings text in settings overview so they get picked up by the filter box 2020-10-23 18:13:28 -07:00
Brady Wetherington
0eda53c484 Add a new custom validator for Users to prevent someone from managing themselves 2020-10-23 16:55:10 -07:00
snipe
79a4acae1a Merge remote-tracking branch 'origin/develop'
# Conflicts:
#	config/version.php
2020-10-23 14:29:40 -07:00
snipe
91e0b26bbb Bumped version 2020-10-23 14:28:55 -07:00
snipe
5f82214703 Merge remote-tracking branch 'origin/develop' 2020-10-23 14:24:24 -07:00
Brady Wetherington
d517e2fd61 Merge pull request #8594 from uberbrady/try_to_fix_ldap_oom
Possible fix to #8563 - unset $ldapUsers to avoid OOM'ing
2020-10-23 14:23:53 -07:00
snipe
28612d8b61 Merge remote-tracking branch 'origin/develop' 2020-10-23 14:23:46 -07:00
snipe
6ee3d0eb97 Merge pull request #8605 from snipe/fixes/legacy_location_id_equals_zero
Fix for legacy location_id=0 issue that can cause failure to checkout/checkin
2020-10-23 14:22:53 -07:00
snipe
7987a4eca4 Used consistent phrasing in query 2020-10-23 14:22:15 -07:00
snipe
2aa8e1e76b Merge pull request #8596 from snipe/fixes/8462_move_accessory_notes
Fixes #8462 - move accessory notes into pivot table
2020-10-23 14:19:51 -07:00
snipe
be0e327221 Simplified the migration 2020-10-23 14:14:34 -07:00
snipe
9a1acced58 Fix for legacy location_id=0 issue 2020-10-23 14:00:04 -07:00
snipe
d74df93c48 Merge remote-tracking branch 'origin/develop' 2020-10-23 12:10:13 -07:00
snipe
b9a9949570 Use the form partial for avatars 2020-10-23 12:09:20 -07:00
snipe
4ccba5337a Added https://gravatar address to CSP 2020-10-23 12:09:03 -07:00
snipe
8aae2b46cd Merge remote-tracking branch 'origin/develop' 2020-10-23 11:56:10 -07:00
snipe
d03d4deef9 Temp fix for #8561 - manager_id validation error 2020-10-23 11:55:53 -07:00
snipe
21ceea0aed Removed extra debugging 2020-10-23 08:19:04 -07:00
snipe
2219c9ccb5 Merge remote-tracking branch 'origin/develop' 2020-10-23 06:55:39 -07:00
snipe
bf9e53fbe7 Updated markdown templates 2020-10-23 06:55:23 -07:00
snipe
f68580b482 Make the email table wider 2020-10-23 06:55:04 -07:00
snipe
aec2f2a249 Fixed #8576 - switch to HTML from markdown
This is really stupid. A markdown table with over 3 columns doesn’t render correctly. :(
2020-10-23 06:54:45 -07:00
snipe
831da2d6d1 New mail vendor files 2020-10-23 06:17:07 -07:00
snipe
f2aebe5f9a Fixed #8558 - error on asset acceptance when no sig is required 2020-10-23 05:09:03 -07:00
snipe
40c0ba9a95 Merge remote-tracking branch 'origin/develop' 2020-10-23 04:46:44 -07:00
snipe
f5a0726f98 Fixed #8597 - Added leading slash to notifications console commands 2020-10-23 04:46:26 -07:00
snipe
2263dae8f4 Fixed HTML 2020-10-23 04:07:55 -07:00
snipe
f182ce1c89 Merge remote-tracking branch 'origin/develop'
# Conflicts:
#	config/version.php
2020-10-23 01:12:54 -07:00
snipe
c6dde67e86 Bumped version 2020-10-23 01:12:19 -07:00
snipe
88f03e6b55 Added last_checkout and notes from pivot for accessories 2020-10-23 00:44:26 -07:00
snipe
8827d33a43 Fixed query to copy notes 2020-10-23 00:25:20 -07:00
snipe
c3e8f64064 Merge remote-tracking branch 'origin/develop' 2020-10-22 23:24:56 -07:00
snipe
68cfbaab2e Fixed signature issue on accepted assets - related to #8577
I have no idea how this ever worked? We didn’t rename that fieldname recently.
2020-10-22 23:20:55 -07:00
snipe
66ac5d05ad Started migration to normalize note 2020-10-22 23:18:14 -07:00
Brady Wetherington
18012279f9 Possible fix to #8563 - unset $ldapUsers to avoid OOM'ing 2020-10-22 21:51:23 -07:00
Brady Wetherington
2abfd8da74 Merge pull request #8568 from uberbrady/fix_wordy_json_ldap_output
Add a dirtiness check to slim down JSON LDAP sync summaries
2020-10-22 21:18:21 -07:00
snipe
ec7d70cea0 Merge pull request #8551 from Godmartinz/bug/ch10148/error-when-using-dark-green-skin
fixed the Green skin (dark mode) color scheme on the importer page and asset table
2020-10-22 21:16:52 -07:00
snipe
6c285b0273 Merge pull request #8586 from Godmartinz/Confusing_wording_in_LDAP
reworded the ad_append_domain_help string
2020-10-22 21:15:35 -07:00
snipe
e65269e9c7 Merge remote-tracking branch 'origin/develop' 2020-10-22 20:09:16 -07:00
snipe
5d112be49a Fixed #8585 - requested assets page blank 2020-10-22 20:08:33 -07:00
snipe
d3f5fde84a Merge remote-tracking branch 'origin/develop' 2020-10-22 19:46:55 -07:00
snipe
496b176d4e Fixed #8589 - unable to accept asset 2020-10-22 19:46:40 -07:00
snipe
fd66f7aeb4 Merge remote-tracking branch 'origin/develop' 2020-10-22 19:37:21 -07:00
snipe
c71086d2f3 Fixed #8590 - unable to checkin and delete from user view 2020-10-22 19:32:51 -07:00
snipe
153f849ef7 Merge pull request #8581 from fashberg/iss8580
Fixed #8580: ReportsController called method on NULL Object
2020-10-22 13:00:08 -07:00
Godfrey M
fcdb945f5f reworded the ad_append_domain_help string 2020-10-22 09:21:18 -07:00
Folke Ashberg
377c92a290 update 2020-10-22 14:33:24 +02:00
Folke Ashberg
5824ac3b28 Fix for #8580
Checking $asset->depreciation before calling $asset->depreciated_date()
2020-10-22 14:22:42 +02:00
Brady Wetherington
fad0ed6d5b Add a dirtiness check to slim down JSON LDAP sync summaries 2020-10-21 15:13:36 -07:00
snipe
ec9c69323a Merge remote-tracking branch 'origin/develop' 2020-10-21 13:58:58 -07:00
snipe
338106734a Fixed #8562 - manufacturer logo upload failing 2020-10-21 13:57:56 -07:00
Godfrey M
4a3d8f2cc0 fixed the Green skin (dark mode) color scheme on the importer page and assets table 2020-10-20 15:22:38 -07:00
Michael Pietsch
4f5374b2e8 enable use custom file based saml certificate/private key 2020-08-14 12:25:00 +02:00
ajsy
a714cd357f Update web.php 2019-05-03 15:22:05 +03:00
ajsy
0cdc7af611 Update print.blade.php 2019-05-03 15:22:01 +03:00
ajsy
9b3a8c046c Update LocationsController.php 2019-05-03 15:21:57 +03:00
ajsy
878cfee5a2 Revert "Delete print.blade.php"
This reverts commit a0721412fa.
2019-04-20 09:28:48 +03:00
ajsy
a0721412fa Delete print.blade.php 2019-04-19 15:50:05 +03:00
ajsy
52add03e56 Update print.blade.php
replace string in serial number with x except last five character
2019-04-19 15:43:40 +03:00
ajsy
c974968821 Update view.blade.php 2019-04-19 14:03:09 +03:00
ajsy
5dcf9fbb50 Update web.php 2019-04-19 13:20:44 +03:00
ajsy
f2242fa32e Update view.blade.php 2019-04-19 13:20:41 +03:00
ajsy
38e8028300 Update LocationsController.php 2019-04-19 13:20:37 +03:00
ajsy
7a68187466 Update table.php 2019-04-19 13:20:31 +03:00
ajsy
57c0f69286 Update LocationsController.php 2019-03-09 13:45:19 +03:00
ajsy
03c5b8e4ab Create print.blade.php 2019-03-09 11:36:42 +03:00
ajsy
311dd18443 Update LocationsController.php 2019-03-09 11:34:47 +03:00
ajsy
a958d56590 Update view.blade.php 2019-03-09 11:29:06 +03:00
ajsy
c11e93b72f Update web.php 2019-03-09 11:14:24 +03:00
1670 changed files with 34440 additions and 14600 deletions

View File

@@ -135,7 +135,8 @@
"avatar_url": "https://avatars0.githubusercontent.com/u/10137?v=3",
"profile": "https://github.com/ghost",
"contributions": [
"translation"
"translation",
"code"
]
},
{
@@ -1938,6 +1939,427 @@
"contributions": [
"code"
]
},
{
"login": "dampfklon",
"name": "Dampfklon",
"avatar_url": "https://avatars1.githubusercontent.com/u/1733625?v=4",
"profile": "https://github.com/dampfklon",
"contributions": [
"code"
]
},
{
"login": "chamilton-ccn",
"name": "Charles Hamilton",
"avatar_url": "https://avatars2.githubusercontent.com/u/52973156?v=4",
"profile": "https://communityclosing.com",
"contributions": [
"code"
]
},
{
"login": "giannello",
"name": "Giuseppe Iannello",
"avatar_url": "https://avatars.githubusercontent.com/u/551789?v=4",
"profile": "https://github.com/giannello",
"contributions": [
"code"
]
},
{
"login": "PeterDaveHello",
"name": "Peter Dave Hello",
"avatar_url": "https://avatars.githubusercontent.com/u/3691490?v=4",
"profile": "https://www.peterdavehello.org/",
"contributions": [
"code"
]
},
{
"login": "sigmoidal",
"name": "sigmoidal",
"avatar_url": "https://avatars.githubusercontent.com/u/6106332?v=4",
"profile": "https://github.com/sigmoidal",
"contributions": [
"code"
]
},
{
"login": "phenixdotnet",
"name": "Vincent Lainé",
"avatar_url": "https://avatars.githubusercontent.com/u/2082554?v=4",
"profile": "https://github.com/phenixdotnet",
"contributions": [
"code"
]
},
{
"login": "derlucas",
"name": "Lucas Pleß",
"avatar_url": "https://avatars.githubusercontent.com/u/1943040?v=4",
"profile": "http://www.lucas-pless.com",
"contributions": [
"code"
]
},
{
"login": "iansltx",
"name": "Ian Littman",
"avatar_url": "https://avatars.githubusercontent.com/u/472804?v=4",
"profile": "http://twitter.com/iansltx",
"contributions": [
"code"
]
},
{
"login": "PauloLuna",
"name": "João Paulo",
"avatar_url": "https://avatars.githubusercontent.com/u/3519029?v=4",
"profile": "https://github.com/PauloLuna",
"contributions": [
"code"
]
},
{
"login": "ThoBur",
"name": "ThoBur",
"avatar_url": "https://avatars.githubusercontent.com/u/70443365?v=4",
"profile": "https://github.com/ThoBur",
"contributions": [
"code"
]
},
{
"login": "alek13",
"name": "Alexander Chibrikin",
"avatar_url": "https://avatars.githubusercontent.com/u/1972329?v=4",
"profile": "http://phpprofi.ru/",
"contributions": [
"code"
]
},
{
"login": "winstan",
"name": "Anthony Winstanley",
"avatar_url": "https://avatars.githubusercontent.com/u/438332?v=4",
"profile": "https://github.com/winstan",
"contributions": [
"code"
]
},
{
"login": "fashberg",
"name": "Folke",
"avatar_url": "https://avatars.githubusercontent.com/u/3075214?v=4",
"profile": "https://github.com/fashberg",
"contributions": [
"code"
]
},
{
"login": "benwa",
"name": "Bennett Blodinger",
"avatar_url": "https://avatars.githubusercontent.com/u/1351571?v=4",
"profile": "https://github.com/benwa",
"contributions": [
"code"
]
},
{
"login": "ncareau",
"name": "NMC",
"avatar_url": "https://avatars.githubusercontent.com/u/2974631?v=4",
"profile": "https://nmc.dev",
"contributions": [
"code"
]
},
{
"login": "andres-baller",
"name": "andres-baller",
"avatar_url": "https://avatars.githubusercontent.com/u/52182449?v=4",
"profile": "https://github.com/andres-baller",
"contributions": [
"code"
]
},
{
"login": "sean-borg",
"name": "sean-borg",
"avatar_url": "https://avatars.githubusercontent.com/u/67109348?v=4",
"profile": "https://github.com/sean-borg",
"contributions": [
"code"
]
},
{
"login": "EDVLeer",
"name": "EDVLeer",
"avatar_url": "https://avatars.githubusercontent.com/u/32170051?v=4",
"profile": "https://github.com/EDVLeer",
"contributions": [
"code"
]
},
{
"login": "Kurokat",
"name": "Kurokat",
"avatar_url": "https://avatars.githubusercontent.com/u/23075196?v=4",
"profile": "https://github.com/Kurokat",
"contributions": [
"code"
]
},
{
"login": "koelle25",
"name": "Kevin Köllmann",
"avatar_url": "https://avatars.githubusercontent.com/u/915514?v=4",
"profile": "https://www.kevinkoellmann.de",
"contributions": [
"code"
]
},
{
"login": "sw-mreyes",
"name": "sw-mreyes",
"avatar_url": "https://avatars.githubusercontent.com/u/49025941?v=4",
"profile": "https://github.com/sw-mreyes",
"contributions": [
"code"
]
},
{
"login": "joelpittet",
"name": "Joel Pittet",
"avatar_url": "https://avatars.githubusercontent.com/u/70129?v=4",
"profile": "https://pittet.ca",
"contributions": [
"code"
]
},
{
"login": "elyscape",
"name": "Eli Young",
"avatar_url": "https://avatars.githubusercontent.com/u/792695?v=4",
"profile": "https://elyscape.com",
"contributions": [
"code"
]
},
{
"login": "raelldottin",
"name": "Raell Dottin",
"avatar_url": "https://avatars.githubusercontent.com/u/317015?v=4",
"profile": "https://github.com/raelldottin",
"contributions": [
"code"
]
},
{
"login": "misilot",
"name": "Tom Misilo",
"avatar_url": "https://avatars.githubusercontent.com/u/1446856?v=4",
"profile": "https://github.com/misilot",
"contributions": [
"code"
]
},
{
"login": "JuustoMestari",
"name": "David Davenne",
"avatar_url": "https://avatars.githubusercontent.com/u/4496300?v=4",
"profile": "http://david.davenne.be",
"contributions": [
"code"
]
},
{
"login": "ocelotsloth",
"name": "Mark Stenglein",
"avatar_url": "https://avatars.githubusercontent.com/u/9255772?v=4",
"profile": "https://markstenglein.com",
"contributions": [
"code"
]
},
{
"login": "ajsy",
"name": "ajsy",
"avatar_url": "https://avatars.githubusercontent.com/u/35658596?v=4",
"profile": "https://github.com/ajsy",
"contributions": [
"code"
]
},
{
"login": "t3easy",
"name": "Jan Kiesewetter",
"avatar_url": "https://avatars.githubusercontent.com/u/3628035?v=4",
"profile": "https://github.com/t3easy",
"contributions": [
"code"
]
},
{
"login": "Tetrachloromethane250",
"name": "Tetrachloromethane250",
"avatar_url": "https://avatars.githubusercontent.com/u/79449630?v=4",
"profile": "https://github.com/Tetrachloromethane250",
"contributions": [
"code"
]
},
{
"login": "kajes",
"name": "Lars Kajes",
"avatar_url": "https://avatars.githubusercontent.com/u/22004482?v=4",
"profile": "https://www.kajes.se/",
"contributions": [
"code"
]
},
{
"login": "Joly0",
"name": "Joly0",
"avatar_url": "https://avatars.githubusercontent.com/u/13993216?v=4",
"profile": "https://github.com/Joly0",
"contributions": [
"code"
]
},
{
"login": "limeless",
"name": "theburger",
"avatar_url": "https://avatars.githubusercontent.com/u/1501022?v=4",
"profile": "https://github.com/limeless",
"contributions": [
"code"
]
},
{
"login": "deivishome",
"name": "David Valin Alonso",
"avatar_url": "https://avatars.githubusercontent.com/u/36065681?v=4",
"profile": "https://github.com/deivishome",
"contributions": [
"code"
]
},
{
"login": "andreaci",
"name": "andreaci",
"avatar_url": "https://avatars.githubusercontent.com/u/8290389?v=4",
"profile": "https://github.com/andreaci",
"contributions": [
"code"
]
},
{
"login": "Jelle-S",
"name": "Jelle Sebreghts",
"avatar_url": "https://avatars.githubusercontent.com/u/1828542?v=4",
"profile": "http://www.jellesebreghts.be",
"contributions": [
"code"
]
},
{
"login": "Skywalker-11",
"name": "Michael Pietsch",
"avatar_url": "https://avatars.githubusercontent.com/u/11180862?v=4",
"profile": "https://github.com/Skywalker-11",
"contributions": []
},
{
"login": "sh1hab",
"name": "Masudul Haque Shihab",
"avatar_url": "https://avatars.githubusercontent.com/u/22068886?v=4",
"profile": "https://github.com/sh1hab",
"contributions": [
"code"
]
},
{
"login": "zybersup",
"name": "Supapong Areeprasertkul",
"avatar_url": "https://avatars.githubusercontent.com/u/16099942?v=4",
"profile": "http://www.freedomdive.com/",
"contributions": [
"code"
]
},
{
"login": "psarossy",
"name": "Peter Sarossy",
"avatar_url": "https://avatars.githubusercontent.com/u/207358?v=4",
"profile": "https://github.com/psarossy",
"contributions": [
"code"
]
},
{
"login": "nepella",
"name": "Renee Margaret McConahy",
"avatar_url": "https://avatars.githubusercontent.com/u/11823649?v=4",
"profile": "https://github.com/nepella",
"contributions": [
"code"
]
},
{
"login": "JohnnyPicnic",
"name": "JohnnyPicnic",
"avatar_url": "https://avatars.githubusercontent.com/u/5553884?v=4",
"profile": "https://github.com/JohnnyPicnic",
"contributions": [
"code"
]
},
{
"login": "markbrule",
"name": "markbrule",
"avatar_url": "https://avatars.githubusercontent.com/u/8799594?v=4",
"profile": "https://github.com/markbrule",
"contributions": [
"code"
]
},
{
"login": "mikecmpbll",
"name": "Mike Campbell",
"avatar_url": "https://avatars.githubusercontent.com/u/1962801?v=4",
"profile": "https://github.com/mikecmpbll",
"contributions": [
"code"
]
},
{
"login": "tbrconnect",
"name": "tbrconnect",
"avatar_url": "https://avatars.githubusercontent.com/u/11973217?v=4",
"profile": "https://github.com/tbrconnect",
"contributions": [
"code"
]
},
{
"login": "kcoyo",
"name": "kcoyo",
"avatar_url": "https://avatars.githubusercontent.com/u/12447225?v=4",
"profile": "https://github.com/kcoyo",
"contributions": [
"code"
]
},
{
"login": "travismiller",
"name": "Travis Miller",
"avatar_url": "https://avatars.githubusercontent.com/u/494017?v=4",
"profile": "https://travismiller.com/",
"contributions": [
"code"
]
}
]
}

157
.env.docker Normal file
View File

@@ -0,0 +1,157 @@
# --------------------------------------------
# REQUIRED: DB SETUP
# --------------------------------------------
MYSQL_DATABASE=snipeit
MYSQL_USER=snipeit
MYSQL_PASSWORD=changeme1234
MYSQL_ROOT_PASSWORD=changeme1234
# --------------------------------------------
# REQUIRED: BASIC APP SETTINGS
# --------------------------------------------
APP_ENV=develop
APP_DEBUG=false
# please regenerate the APP_KEY value by calling `docker-compose run --rm snipeit bash` and then `php artisan key:generate --show` and then copy paste the value here
APP_KEY=base64:3ilviXqB9u6DX1NRcyWGJ+sjySF+H18CPDGb3+IVwMQ=
APP_URL=http://localhost:8000
APP_TIMEZONE='UTC'
APP_LOCALE=en
MAX_RESULTS=500
# --------------------------------------------
# REQUIRED: UPLOADED FILE STORAGE SETTINGS
# --------------------------------------------
PRIVATE_FILESYSTEM_DISK=local
PUBLIC_FILESYSTEM_DISK=local_public
# --------------------------------------------
# REQUIRED: DATABASE SETTINGS
# --------------------------------------------
DB_CONNECTION=mysql
DB_HOST=mariadb
DB_DATABASE=snipeit
DB_USERNAME=snipeit
DB_PASSWORD=changeme1234
DB_PREFIX=null
DB_DUMP_PATH='/usr/bin'
DB_CHARSET=utf8mb4
DB_COLLATION=utf8mb4_unicode_ci
# --------------------------------------------
# OPTIONAL: SSL DATABASE SETTINGS
# --------------------------------------------
DB_SSL=false
DB_SSL_IS_PAAS=false
DB_SSL_KEY_PATH=null
DB_SSL_CERT_PATH=null
DB_SSL_CA_PATH=null
DB_SSL_CIPHER=null
# --------------------------------------------
# REQUIRED: OUTGOING MAIL SERVER SETTINGS
# --------------------------------------------
MAIL_DRIVER=smtp
MAIL_HOST=mailhog
MAIL_PORT=1025
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
MAIL_FROM_ADDR=you@example.com
MAIL_FROM_NAME='Snipe-IT'
MAIL_REPLYTO_ADDR=you@example.com
MAIL_REPLYTO_NAME='Snipe-IT'
MAIL_AUTO_EMBED_METHOD='attachment'
# --------------------------------------------
# REQUIRED: IMAGE LIBRARY
# This should be gd or imagick
# --------------------------------------------
IMAGE_LIB=gd
# --------------------------------------------
# OPTIONAL: BACKUP SETTINGS
# --------------------------------------------
MAIL_BACKUP_NOTIFICATION_DRIVER=null
MAIL_BACKUP_NOTIFICATION_ADDRESS=null
BACKUP_ENV=true
# --------------------------------------------
# OPTIONAL: SESSION SETTINGS
# --------------------------------------------
SESSION_LIFETIME=12000
EXPIRE_ON_CLOSE=false
ENCRYPT=false
COOKIE_NAME=snipeit_session
COOKIE_DOMAIN=null
SECURE_COOKIES=false
API_TOKEN_EXPIRATION_YEARS=40
# --------------------------------------------
# OPTIONAL: SECURITY HEADER SETTINGS
# --------------------------------------------
APP_TRUSTED_PROXIES=192.168.1.1,10.0.0.1
ALLOW_IFRAMING=false
REFERRER_POLICY=same-origin
ENABLE_CSP=false
CORS_ALLOWED_ORIGINS=null
ENABLE_HSTS=false
# --------------------------------------------
# OPTIONAL: CACHE SETTINGS
# --------------------------------------------
CACHE_DRIVER=file
SESSION_DRIVER=file
QUEUE_DRIVER=sync
CACHE_PREFIX=snipeit
# --------------------------------------------
# OPTIONAL: REDIS SETTINGS
# --------------------------------------------
REDIS_HOST=redis
REDIS_PASSWORD=null
REDIS_PORT=6379
# --------------------------------------------
# OPTIONAL: MEMCACHED SETTINGS
# --------------------------------------------
MEMCACHED_HOST=null
MEMCACHED_PORT=null
# --------------------------------------------
# OPTIONAL: PUBLIC S3 Settings
# --------------------------------------------
PUBLIC_AWS_SECRET_ACCESS_KEY=null
PUBLIC_AWS_ACCESS_KEY_ID=null
PUBLIC_AWS_DEFAULT_REGION=null
PUBLIC_AWS_BUCKET=null
PUBLIC_AWS_URL=null
PUBLIC_AWS_BUCKET_ROOT=null
# --------------------------------------------
# OPTIONAL: PRIVATE S3 Settings
# --------------------------------------------
PRIVATE_AWS_ACCESS_KEY_ID=null
PRIVATE_AWS_SECRET_ACCESS_KEY=null
PRIVATE_AWS_DEFAULT_REGION=null
PRIVATE_AWS_BUCKET=null
PRIVATE_AWS_URL=null
PRIVATE_AWS_BUCKET_ROOT=null
# --------------------------------------------
# OPTIONAL: LOGIN THROTTLING
# --------------------------------------------
LOGIN_MAX_ATTEMPTS=5
LOGIN_LOCKOUT_DURATION=60
RESET_PASSWORD_LINK_EXPIRES=900
# --------------------------------------------
# OPTIONAL: MISC
# --------------------------------------------
APP_LOG=stderr
APP_LOG_MAX_FILES=10
APP_LOCKED=false
APP_CIPHER=AES-256-CBC
GOOGLE_MAPS_API=
LDAP_MEM_LIM=500M
LDAP_TIME_LIM=600

View File

@@ -51,7 +51,7 @@ MAIL_FROM_ADDR=you@example.com
MAIL_FROM_NAME='Snipe-IT'
MAIL_REPLYTO_ADDR=you@example.com
MAIL_REPLYTO_NAME='Snipe-IT'
MAIL_BACKUP_NOTIFICATION_ADDRESS=you@example.com
MAIL_AUTO_EMBED_METHOD='attachment'
# --------------------------------------------
# REQUIRED: IMAGE LIBRARY
@@ -59,6 +59,15 @@ MAIL_BACKUP_NOTIFICATION_ADDRESS=you@example.com
# --------------------------------------------
IMAGE_LIB=gd
# --------------------------------------------
# OPTIONAL: BACKUP SETTINGS
# --------------------------------------------
MAIL_BACKUP_NOTIFICATION_DRIVER=null
MAIL_BACKUP_NOTIFICATION_ADDRESS=null
BACKUP_ENV=true
# --------------------------------------------
# OPTIONAL: SESSION SETTINGS
# --------------------------------------------
@@ -68,6 +77,7 @@ ENCRYPT=false
COOKIE_NAME=snipeit_session
COOKIE_DOMAIN=null
SECURE_COOKIES=false
API_TOKEN_EXPIRATION_YEARS=15
# --------------------------------------------
# OPTIONAL: SECURITY HEADER SETTINGS
@@ -125,6 +135,7 @@ PRIVATE_AWS_BUCKET_ROOT=null
# --------------------------------------------
LOGIN_MAX_ATTEMPTS=5
LOGIN_LOCKOUT_DURATION=60
RESET_PASSWORD_LINK_EXPIRES=900
# --------------------------------------------
# OPTIONAL: MISC
@@ -133,7 +144,11 @@ APP_LOG=single
APP_LOG_MAX_FILES=10
APP_LOCKED=false
APP_CIPHER=AES-256-CBC
APP_FORCE_TLS=false
GOOGLE_MAPS_API=
BACKUP_ENV=true
LDAP_MEM_LIM=500M
LDAP_TIME_LIM=600
IMPORT_TIME_LIMIT=600
IMPORT_MEMORY_LIMIT=500M
REPORT_TIME_LIMIT=12000

View File

@@ -48,6 +48,7 @@ If applicable, add screenshots to help explain your problem.
- Any errors that appear in your browser's error console.
- Confirm whether the error is reproducible on the demo: https://snipeitapp.com/demo.
- Include any additional information you can find in `storage/logs` and your webserver's logs.
- Include the output from `php -m` (this should display what modules you have enabled.)
**Additional context**
- Is this a fresh install or an upgrade?

49
.github/workflows/codacy-analysis.yml vendored Normal file
View File

@@ -0,0 +1,49 @@
# This workflow checks out code, performs a Codacy security scan
# and integrates the results with the
# GitHub Advanced Security code scanning feature. For more information on
# the Codacy security scan action usage and parameters, see
# https://github.com/codacy/codacy-analysis-cli-action.
# For more information on Codacy Analysis CLI in general, see
# https://github.com/codacy/codacy-analysis-cli.
name: Codacy Security Scan
on:
push:
branches: [ master ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ master ]
schedule:
- cron: '36 23 * * 3'
jobs:
codacy-security-scan:
name: Codacy Security Scan
runs-on: ubuntu-latest
steps:
# Checkout the repository to the GitHub Actions runner
- name: Checkout code
uses: actions/checkout@v2
# Execute Codacy Analysis CLI and generate a SARIF output with the security issues identified during the analysis
- name: Run Codacy Analysis CLI
uses: codacy/codacy-analysis-cli-action@1.1.0
with:
# Check https://github.com/codacy/codacy-analysis-cli#project-token to get your project token from your Codacy repository
# You can also omit the token and run the tools that support default configurations
project-token: ${{ secrets.CODACY_PROJECT_TOKEN }}
verbose: true
output: results.sarif
format: sarif
# Adjust severity of non-security issues
gh-code-scanning-compat: true
# Force 0 exit code to allow SARIF file generation
# This will handover control about PR rejection to the GitHub side
max-allowed-issues: 2147483647
# Upload the SARIF file generated in the previous step
- name: Upload SARIF results file
uses: github/codeql-action/upload-sarif@v1
with:
sarif_file: results.sarif

5
.gitignore vendored
View File

@@ -48,10 +48,12 @@ tests/_support/_generated/*
/npm-debug.log
/storage/oauth-private.key
/storage/oauth-public.key
logs/*
*.cache
.vagrant
*.log
*.retry
\.php_cs\.dist
@@ -60,3 +62,4 @@ phpmd\.xml
_ide_helper.php
.phpstorm.meta.php
_ide_helper_models.php
/.phplint-cache

View File

@@ -5,7 +5,15 @@
# Make sure .env files not not browseable if in a sub-directory.
<FilesMatch "\.env$">
Deny from all
# Apache 2.2
<IfModule !authz_core_module>
Deny from all
</IfModule>
# Apache 2.4+
<IfModule authz_core_module>
Require all denied
</IfModule>
</FilesMatch>
</IfModule>

View File

@@ -1,80 +0,0 @@
addons:
code_climate:
repo_token:
secure: "C/bUAEpwfZB82dkzI2Nxx3PW5w/BzbKkSyCkp6YjT046jD2/QKvz6ngCFlt3tAWV11TXWFI6D8DzkMmdWOrQl3SGlPZXRD8QOvCiz0HiGMDvlxjAaPaQecGaQZdx/H4m6xTUXRNUVaYmxlMgkkFCWhAp+HZDs0iyOEVamp0Jszg="
hosts:
- localhost
sudo: false
# see http://about.travis-ci.org/docs/user/languages/php/ for more hints
language: php
services:
- mysql
# list any PHP version you want to test against
php:
- 7.2
- 7.3.0
- 7.4
# execute any number of scripts before the test run, custom env's are available as variables
before_script:
- phpenv config-add .github/travis-memory.ini
- phantomjs --webdriver=4444 &
- sleep 4
- mysql -e 'CREATE DATABASE snipeit_unit;'
- mysql -e 'CREATE USER "travis'@'localhost";'
- mysql -e 'GRANT ALL PRIVILEGES ON * . * TO "travis'@'localhost";'
- mysql -e 'FLUSH PRIVILEGES;'
- cp .env.testing-ci .env
- composer self-update
- composer install -n --prefer-source
- chmod -R 777 storage
- php artisan migrate --env=testing-ci --database=mysql --force
- ./vendor/bin/codecept build
- php artisan --env=testing-ci key:generate
- php artisan --env=testing-ci snipeit:travisci-install
- php artisan --env=testing-ci db:seed --database=mysql --force
- php artisan --env=testing-ci snipeit:create-admin --first_name=Alison --last_name=Foobar --email=me@example.com --username=snipe --password=password
- php artisan --env=testing-ci passport:install
- php artisan serve --env=testing-ci --port=8000 --host=localhost &
- sleep 5
- pip install --user codecov
- sleep 5
# omitting "script:" will default to phpunit
# use the $DB env variable to determine the phpunit.xml to use
# script: ./vendor/bin/codecept run --env testing-ci
script:
- ./vendor/bin/codecept run unit
# - ./vendor/bin/codecept run acceptance --env=testing-ci
- ./vendor/bin/codecept run functional --env=functional-travis -g func1
- ./vendor/bin/codecept run functional --env=functional-travis -g func2
- ./vendor/bin/codecept run api --env=functional-travis
after_script:
- vendor/bin/test-reporter
after_success:
- codecov
after_failure:
- cat tests/_output/*.fail.html
- curl http://localhost:8000/login
- cat storage/logs/laravel.log
# configure notifications (email, IRC, campfire etc)
notifications:
email: false
slack:
secure: vv9we1RxB9RsrMbomSdq6D7vz/okobw87pEkgIZjB+hj1QpQ2by90gsPsOa+NgsJEFaEP7e4KlT6SH8kK+zhbmuKaUd3d1//XdcancE22LZXi6tkiB5yuR/Jhhb1LLDqyGJTB4D92hMnnCPiUjpxNA3r437ttNeYRdYIEEP3drA=
webhooks:
urls:
- https://webhooks.gitter.im/e/5e136eb0c1965f3918d0
on_success: change # options: [always|never|change] default: always
on_failure: change # options: [always|never|change] default: always
on_start: false # default: false

View File

@@ -1,6 +1,10 @@
FROM ubuntu:bionic
LABEL maintainer Brady Wetherington <uberbrady@gmail.com>
# No need to add `apt-get clean` here, reference:
# - https://github.com/snipe/snipe-it/pull/9201
# - https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#apt-get
RUN export DEBIAN_FRONTEND=noninteractive; \
export DEBCONF_NONINTERACTIVE_SEEN=true; \
echo 'tzdata tzdata/Areas select Etc' | debconf-set-selections; \
@@ -37,7 +41,6 @@ libmcrypt-dev \
php7.2-dev \
ca-certificates \
unzip \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
@@ -94,6 +97,7 @@ RUN \
&& mkdir -p "/var/lib/snipeit/keys" && ln -fs "/var/lib/snipeit/keys/oauth-private.key" "/var/www/html/storage/oauth-private.key" \
&& ln -fs "/var/lib/snipeit/keys/oauth-public.key" "/var/www/html/storage/oauth-public.key" \
&& chown docker "/var/lib/snipeit/keys/" \
&& chown -h docker "/var/www/html/storage/" \
&& chmod +x /var/www/html/artisan \
&& echo "Finished setting up application in /var/www/html"

View File

@@ -1,6 +1,6 @@
FROM alpine:3.8
FROM alpine:3.13
# Apache + PHP
RUN apk add --update --no-cache \
RUN apk add --no-cache \
apache2 \
php7 \
php7-common \
@@ -23,6 +23,8 @@ RUN apk add --update --no-cache \
php7-fileinfo \
php7-simplexml \
php7-session \
php7-dom \
php7-xmlwriter \
curl \
wget \
vim \
@@ -55,6 +57,7 @@ RUN \
&& mkdir -p "/var/lib/snipeit/dumps" && rm -r "/var/www/html/storage/app/backups" && ln -fs "/var/lib/snipeit/dumps" "/var/www/html/storage/app/backups" \
&& mkdir -p "/var/lib/snipeit/keys" && ln -fs "/var/lib/snipeit/keys/oauth-private.key" "/var/www/html/storage/oauth-private.key" \
&& ln -fs "/var/lib/snipeit/keys/oauth-public.key" "/var/www/html/storage/oauth-public.key" \
&& chown -hR apache "/var/www/html/storage/" \
&& chown -R apache "/var/lib/snipeit"
# Install composer

102
Dockerfile.fpm-alpine Normal file
View File

@@ -0,0 +1,102 @@
ARG ENVIRONMENT=production
ARG SNIPEIT_RELEASE=5.1.3
ARG PHP_VERSION=7.4.16
ARG PHP_ALPINE_VERSION=3.13
ARG COMPOSER_VERSION=2.0.11
# Cannot use arguments with 'COPY --from' workaround
# https://github.com/moby/moby/issues/34482#issuecomment-454716952
FROM composer:${COMPOSER_VERSION} AS composer
# Final stage
FROM php:${PHP_VERSION}-fpm-alpine${PHP_ALPINE_VERSION} AS source
LABEL maintainer="Mateus Villar <mromeravillar@gmail.com>"
ARG PACKAGES="\
mysql-client \
"
ARG DEV_PACKAGES="\
git \
"
ARG ENVIRONMENT
ENV ENVIRONMENT ${ENVIRONMENT}
ARG SNIPEIT_RELEASE
ENV SNIPEIT_RELEASE ${SNIPEIT_RELEASE}
# Cribbed from wordpress-fpm-alpine image
# set recommended PHP.ini settings
# see https://secure.php.net/manual/en/opcache.installation.php
RUN set -eux; \
docker-php-ext-enable opcache; \
{ \
echo 'opcache.memory_consumption=128'; \
echo 'opcache.interned_strings_buffer=8'; \
echo 'opcache.max_accelerated_files=4000'; \
echo 'opcache.revalidate_freq=2'; \
echo 'opcache.fast_shutdown=1'; \
} > /usr/local/etc/php/conf.d/opcache-recommended.ini
# https://wordpress.org/support/article/editing-wp-config-php/#configure-error-logging
RUN { \
# https://www.php.net/manual/en/errorfunc.constants.php
# https://github.com/docker-library/wordpress/issues/420#issuecomment-517839670
echo 'error_reporting = E_ERROR | E_WARNING | E_PARSE | E_CORE_ERROR | E_CORE_WARNING | E_COMPILE_ERROR | E_COMPILE_WARNING | E_RECOVERABLE_ERROR'; \
echo 'display_errors = Off'; \
echo 'display_startup_errors = Off'; \
echo 'log_errors = On'; \
echo 'error_log = /dev/stderr'; \
echo 'log_errors_max_len = 1024'; \
echo 'ignore_repeated_errors = On'; \
echo 'ignore_repeated_source = Off'; \
echo 'html_errors = Off'; \
} > /usr/local/etc/php/conf.d/error-logging.ini
# Install php extensions inside docker containers easily
# https://github.com/mlocati/docker-php-extension-installer
COPY --from=mlocati/php-extension-installer:1.2.19 /usr/bin/install-php-extensions /usr/local/bin/
RUN set -eux; \
install-php-extensions \
bcmath \
gd \
ldap \
mysqli \
pdo_mysql \
zip; \
rm -f /usr/local/bin/install-php-extensions; \
# Install prerequisites packages
apk add --no-cache \
${PACKAGES};
COPY --from=composer /usr/bin/composer /usr/local/bin
ARG COMPOSER_ALLOW_SUPERUSER=1
RUN set -eux; \
# Download and extract snipeit tarball
curl -o snipeit.tar.gz -fL "https://github.com/snipe/snipe-it/archive/v$SNIPEIT_RELEASE.tar.gz"; \
tar -xzf snipeit.tar.gz --strip-components=1 -C /var/www/html/; \
rm snipeit.tar.gz; \
# Install composer php dependencies
if [ "$ENVIRONMENT" = "production" ]; then \
echo "production enviroment detected!"; \
composer update \
--no-cache \
--no-dev \
--optimize-autoloader \
--working-dir=/var/www/html; \
else \
echo "development enviroment detected!"; \
apk add --no-cache \
${DEV_PACKAGES}; \
composer update \
--no-cache \
--prefer-source \
--optimize-autoloader \
--working-dir=/var/www/html; \
fi; \
rm -f /usr/local/bin/composer; \
chown -R www-data:www-data /var/www/html;
VOLUME [ "/var/lib/snipeit" ]
COPY --chown=www-data:www-data docker/docker-secrets.env /var/www/html/.env
COPY --chmod=655 docker/docker-entrypoint.sh /usr/local/bin/docker-snipeit-entrypoint
ENTRYPOINT [ "/usr/local/bin/docker-snipeit-entrypoint" ]
CMD [ "/usr/local/bin/docker-php-entrypoint", "php-fpm" ]

View File

@@ -1,13 +1,11 @@
[![Build Status](https://travis-ci.org/snipe/snipe-it.svg?branch=master)](https://travis-ci.org/snipe/snipe-it) [![Crowdin](https://d322cqt584bo4o.cloudfront.net/snipe-it/localized.svg)](https://crowdin.com/project/snipe-it) [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/snipe/snipe-it?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![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-189-orange.svg?style=flat-square)](#contributors) [![Open Source Helpers](https://www.codetriage.com/snipe/snipe-it/badges/users.svg)](https://www.codetriage.com/snipe/snipe-it)
[![All Contributors](https://img.shields.io/badge/all_contributors-212-orange.svg?style=flat-square)](#contributors) [![Open Source Helpers](https://www.codetriage.com/snipe/snipe-it/badges/users.svg)](https://www.codetriage.com/snipe/snipe-it)
![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) [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/snipe/snipe-it?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![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-259-orange.svg?style=flat-square)](#contributors)
## Snipe-IT - Open Source Asset Management System
This is a FOSS project for asset management in IT Operations. Knowing who has which laptop, when it was purchased in order to depreciate it correctly, handling software licenses, etc.
It is built on [Laravel 5.5](http://laravel.com).
It is built on [Laravel 6](http://laravel.com).
Snipe-IT is actively developed and we [release quite frequently](https://github.com/snipe/snipe-it/releases). ([Check out the live demo here](https://snipeitapp.com/demo/).)
@@ -64,7 +62,8 @@ Since the release of the JSON REST API, several third-party developers have been
- [Snipe-IT plugin for Jira Service Desk (beta)](https://marketplace.atlassian.com/apps/1220379/snipe-it-for-jira-service-desk-beta?hosting=cloud&tab=overview) - for the upcoming Snipe-IT v5 only
- [Python 3 CSV importer](https://github.com/gastamper/snipeit-csvimporter) - allows importing assets into Snipe-IT based on Item Name rather than Asset Tag.
- [Snipe-IT Kubernetes Helm Chart](https://github.com/t3n/helm-charts/tree/master/snipeit) - For more information, [click here](https://hub.helm.sh/charts/t3n/snipeit).
- [Snipe-IT Bulk Edit](https://github.com/bricelabelle/snipe-it-bulkedit) - Google Script files to use Google Sheets as a bulk checkout/checkin/edit tool for Snipe-it.
As these were created by third-parties, Snipe-IT cannot provide support for these project, and you should contact the developers directly if you need assistance. Additionally, Snipe-IT makes no guarantees as to the reliability, accuracy or maintainability of these libraries. Use at your own risk. :)
-----
@@ -91,7 +90,7 @@ Thanks goes to all of these wonderful people ([emoji key](https://github.com/ken
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
| [<img src="https://avatars3.githubusercontent.com/u/197404?v=3" width="110px;"/><br /><sub>snipe</sub>](http://www.snipe.net)<br />[💻](https://github.com/snipe/snipe-it/commits?author=snipe "Code") [🚇](#infra-snipe "Infrastructure (Hosting, Build-Tools, etc)") [📖](https://github.com/snipe/snipe-it/commits?author=snipe "Documentation") [⚠️](https://github.com/snipe/snipe-it/commits?author=snipe "Tests") [🐛](https://github.com/snipe/snipe-it/issues?q=author%3Asnipe "Bug reports") [🎨](#design-snipe "Design") [👀](#review-snipe "Reviewed Pull Requests") | [<img src="https://avatars0.githubusercontent.com/u/36335?v=3" width="110px;"/><br /><sub>Brady Wetherington</sub>](http://www.uberbrady.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=uberbrady "Code") [📖](https://github.com/snipe/snipe-it/commits?author=uberbrady "Documentation") [🚇](#infra-uberbrady "Infrastructure (Hosting, Build-Tools, etc)") [👀](#review-uberbrady "Reviewed Pull Requests") | [<img src="https://avatars0.githubusercontent.com/u/3803132?v=3" width="110px;"/><br /><sub>Daniel Meltzer</sub>](https://github.com/dmeltzer)<br />[💻](https://github.com/snipe/snipe-it/commits?author=dmeltzer "Code") [⚠️](https://github.com/snipe/snipe-it/commits?author=dmeltzer "Tests") [📖](https://github.com/snipe/snipe-it/commits?author=dmeltzer "Documentation") | [<img src="https://avatars0.githubusercontent.com/u/1609106?v=3" width="110px;"/><br /><sub>Michael T</sub>](http://www.tuckertechonline.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mtucker6784 "Code") | [<img src="https://avatars2.githubusercontent.com/u/3274937?v=3" width="110px;"/><br /><sub>madd15</sub>](https://github.com/madd15)<br />[📖](https://github.com/snipe/snipe-it/commits?author=madd15 "Documentation") [💬](#question-madd15 "Answering Questions") | [<img src="https://avatars2.githubusercontent.com/u/894126?v=3" width="110px;"/><br /><sub>Vincent Sposato</sub>](https://github.com/vsposato)<br />[💻](https://github.com/snipe/snipe-it/commits?author=vsposato "Code") | [<img src="https://avatars0.githubusercontent.com/u/1639757?v=3" width="110px;"/><br /><sub>Andrea Bergamasco</sub>](https://github.com/vjandrea)<br />[💻](https://github.com/snipe/snipe-it/commits?author=vjandrea "Code") |
| :---: | :---: | :---: | :---: | :---: | :---: | :---: |
| [<img src="https://avatars0.githubusercontent.com/u/10640152?v=3" width="110px;"/><br /><sub>Karol</sub>](https://github.com/kpawelski)<br />[🌍](#translation-kpawelski "Translation") [💻](https://github.com/snipe/snipe-it/commits?author=kpawelski "Code") | [<img src="https://avatars3.githubusercontent.com/u/600106?v=3" width="110px;"/><br /><sub>morph027</sub>](http://blog.morph027.de/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=morph027 "Code") | [<img src="https://avatars3.githubusercontent.com/u/22935755?v=3" width="110px;"/><br /><sub>fvleminckx</sub>](https://github.com/fvleminckx)<br />[🚇](#infra-fvleminckx "Infrastructure (Hosting, Build-Tools, etc)") | [<img src="https://avatars2.githubusercontent.com/u/15633547?v=3" width="110px;"/><br /><sub>itsupportcmsukorg</sub>](https://github.com/itsupportcmsukorg)<br />[💻](https://github.com/snipe/snipe-it/commits?author=itsupportcmsukorg "Code") [🐛](https://github.com/snipe/snipe-it/issues?q=author%3Aitsupportcmsukorg "Bug reports") | [<img src="https://avatars3.githubusercontent.com/u/12373799?v=3" width="110px;"/><br /><sub>Frank</sub>](https://override.io)<br />[💻](https://github.com/snipe/snipe-it/commits?author=base-zero "Code") | [<img src="https://avatars0.githubusercontent.com/u/10137?v=3" width="110px;"/><br /><sub>Deleted user</sub>](https://github.com/ghost)<br />[🌍](#translation-ghost "Translation") | [<img src="https://avatars1.githubusercontent.com/u/10802313?v=3" width="110px;"/><br /><sub>tiagom62</sub>](https://github.com/tiagom62)<br />[💻](https://github.com/snipe/snipe-it/commits?author=tiagom62 "Code") [🚇](#infra-tiagom62 "Infrastructure (Hosting, Build-Tools, etc)") |
| [<img src="https://avatars0.githubusercontent.com/u/10640152?v=3" width="110px;"/><br /><sub>Karol</sub>](https://github.com/kpawelski)<br />[🌍](#translation-kpawelski "Translation") [💻](https://github.com/snipe/snipe-it/commits?author=kpawelski "Code") | [<img src="https://avatars3.githubusercontent.com/u/600106?v=3" width="110px;"/><br /><sub>morph027</sub>](http://blog.morph027.de/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=morph027 "Code") | [<img src="https://avatars3.githubusercontent.com/u/22935755?v=3" width="110px;"/><br /><sub>fvleminckx</sub>](https://github.com/fvleminckx)<br />[🚇](#infra-fvleminckx "Infrastructure (Hosting, Build-Tools, etc)") | [<img src="https://avatars2.githubusercontent.com/u/15633547?v=3" width="110px;"/><br /><sub>itsupportcmsukorg</sub>](https://github.com/itsupportcmsukorg)<br />[💻](https://github.com/snipe/snipe-it/commits?author=itsupportcmsukorg "Code") [🐛](https://github.com/snipe/snipe-it/issues?q=author%3Aitsupportcmsukorg "Bug reports") | [<img src="https://avatars3.githubusercontent.com/u/12373799?v=3" width="110px;"/><br /><sub>Frank</sub>](https://override.io)<br />[💻](https://github.com/snipe/snipe-it/commits?author=base-zero "Code") | [<img src="https://avatars0.githubusercontent.com/u/10137?v=3" width="110px;"/><br /><sub>Deleted user</sub>](https://github.com/ghost)<br />[🌍](#translation-ghost "Translation") [💻](https://github.com/snipe/snipe-it/commits?author=ghost "Code") | [<img src="https://avatars1.githubusercontent.com/u/10802313?v=3" width="110px;"/><br /><sub>tiagom62</sub>](https://github.com/tiagom62)<br />[💻](https://github.com/snipe/snipe-it/commits?author=tiagom62 "Code") [🚇](#infra-tiagom62 "Infrastructure (Hosting, Build-Tools, etc)") |
| [<img src="https://avatars3.githubusercontent.com/u/2389047?v=3" width="110px;"/><br /><sub>Ryan Stafford</sub>](https://github.com/rystaf)<br />[💻](https://github.com/snipe/snipe-it/commits?author=rystaf "Code") | [<img src="https://avatars2.githubusercontent.com/u/10345935?v=3" width="110px;"/><br /><sub>Eammon Hanlon</sub>](https://github.com/ehanlon)<br />[💻](https://github.com/snipe/snipe-it/commits?author=ehanlon "Code") | [<img src="https://avatars0.githubusercontent.com/u/441924?v=3" width="110px;"/><br /><sub>zjean</sub>](https://github.com/zjean)<br />[💻](https://github.com/snipe/snipe-it/commits?author=zjean "Code") | [<img src="https://avatars0.githubusercontent.com/u/12660103?v=3" width="110px;"/><br /><sub>Matthias Frei</sub>](http://www.frei.media)<br />[💻](https://github.com/snipe/snipe-it/commits?author=FREImedia "Code") | [<img src="https://avatars0.githubusercontent.com/u/3767518?v=3" width="110px;"/><br /><sub>opsydev</sub>](https://github.com/opsydev)<br />[💻](https://github.com/snipe/snipe-it/commits?author=opsydev "Code") | [<img src="https://avatars1.githubusercontent.com/u/82290?v=3" width="110px;"/><br /><sub>Daniel Dreier</sub>](http://www.ddreier.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=ddreier "Code") | [<img src="https://avatars0.githubusercontent.com/u/23448?v=3" width="110px;"/><br /><sub>Nikolai Prokoschenko</sub>](http://rassie.org)<br />[💻](https://github.com/snipe/snipe-it/commits?author=rassie "Code") |
| [<img src="https://avatars0.githubusercontent.com/u/13452757?v=3" width="110px;"/><br /><sub>Drew</sub>](https://github.com/YetAnotherCodeMonkey)<br />[💻](https://github.com/snipe/snipe-it/commits?author=YetAnotherCodeMonkey "Code") | [<img src="https://avatars0.githubusercontent.com/u/1342320?v=3" width="110px;"/><br /><sub>Walter</sub>](https://github.com/merid14)<br />[💻](https://github.com/snipe/snipe-it/commits?author=merid14 "Code") | [<img src="https://avatars3.githubusercontent.com/u/11254614?v=3" width="110px;"/><br /><sub>Petr Baloun</sub>](https://github.com/balous)<br />[💻](https://github.com/snipe/snipe-it/commits?author=balous "Code") | [<img src="https://avatars0.githubusercontent.com/u/6117660?v=3" width="110px;"/><br /><sub>reidblomquist</sub>](https://github.com/reidblomquist)<br />[📖](https://github.com/snipe/snipe-it/commits?author=reidblomquist "Documentation") | [<img src="https://avatars0.githubusercontent.com/u/539914?v=3" width="110px;"/><br /><sub>Mathieu Kooiman</sub>](https://github.com/mathieuk)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mathieuk "Code") | [<img src="https://avatars3.githubusercontent.com/u/6606421?v=3" width="110px;"/><br /><sub>csayre</sub>](https://github.com/csayre)<br />[📖](https://github.com/snipe/snipe-it/commits?author=csayre "Documentation") | [<img src="https://avatars1.githubusercontent.com/u/768488?v=3" width="110px;"/><br /><sub>Adam Dunson</sub>](https://github.com/adamdunson)<br />[💻](https://github.com/snipe/snipe-it/commits?author=adamdunson "Code") |
| [<img src="https://avatars0.githubusercontent.com/u/5547470?v=3" width="110px;"/><br /><sub>Hereward</sub>](https://github.com/thehereward)<br />[💻](https://github.com/snipe/snipe-it/commits?author=thehereward "Code") | [<img src="https://avatars0.githubusercontent.com/u/5802977?v=3" width="110px;"/><br /><sub>swoopdk</sub>](https://github.com/swoopdk)<br />[💻](https://github.com/snipe/snipe-it/commits?author=swoopdk "Code") | [<img src="https://avatars1.githubusercontent.com/u/3470403?v=3" width="110px;"/><br /><sub>Abdullah Alansari</sub>](https://linkedin.com/in/ahimta)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Ahimta "Code") | [<img src="https://avatars0.githubusercontent.com/u/796443?v=3" width="110px;"/><br /><sub>Micael Rodrigues</sub>](https://github.com/MicaelRodrigues)<br />[💻](https://github.com/snipe/snipe-it/commits?author=MicaelRodrigues "Code") | [<img src="https://avatars0.githubusercontent.com/u/614564?v=3" width="110px;"/><br /><sub>Patrick Gallagher</sub>](http://macadmincorner.com)<br />[📖](https://github.com/snipe/snipe-it/commits?author=patgmac "Documentation") | [<img src="https://avatars3.githubusercontent.com/u/7165922?v=3" width="110px;"/><br /><sub>Miliamber</sub>](https://github.com/Miliamber)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Miliamber "Code") | [<img src="https://avatars3.githubusercontent.com/u/861766?v=3" width="110px;"/><br /><sub>hawk554</sub>](https://github.com/hawk554)<br />[💻](https://github.com/snipe/snipe-it/commits?author=hawk554 "Code") |
@@ -115,14 +114,18 @@ Thanks goes to all of these wonderful people ([emoji key](https://github.com/ken
| [<img src="https://avatars2.githubusercontent.com/u/982885?v=4" width="110px;"/><br /><sub>Martin Stub</sub>](http://martinstub.dk)<br />[🌍](#translation-stubben "Translation") | [<img src="https://avatars2.githubusercontent.com/u/28959963?v=4" width="110px;"/><br /><sub>Meyer Flavio</sub>](https://github.com/meyerf99)<br />[🌍](#translation-meyerf99 "Translation") | [<img src="https://avatars3.githubusercontent.com/u/796443?v=4" width="110px;"/><br /><sub>Micael Rodrigues</sub>](https://github.com/MicaelRodrigues)<br />[🌍](#translation-MicaelRodrigues "Translation") | [<img src="https://avatars0.githubusercontent.com/u/10481331?v=4" width="110px;"/><br /><sub>Mikael Rasmussen</sub>](http://rubixy.com/)<br />[🌍](#translation-mikaelssen "Translation") | [<img src="https://avatars1.githubusercontent.com/u/1544552?v=4" width="110px;"/><br /><sub>IxFail</sub>](https://github.com/IxFail)<br />[🌍](#translation-IxFail "Translation") | [<img src="https://avatars3.githubusercontent.com/u/18483118?v=4" width="110px;"/><br /><sub>Mohammed Fota</sub>](http://www.mohammedfota.com)<br />[🌍](#translation-MohammedFota "Translation") | [<img src="https://avatars0.githubusercontent.com/u/227080?v=4" width="110px;"/><br /><sub>Moayad Alserihi</sub>](https://github.com/omego)<br />[🌍](#translation-omego "Translation") |
| [<img src="https://avatars0.githubusercontent.com/u/1680266?v=4" width="110px;"/><br /><sub>saymd</sub>](https://github.com/saymd)<br />[🌍](#translation-saymd "Translation") | [<img src="https://avatars0.githubusercontent.com/u/1826808?v=4" width="110px;"/><br /><sub>Patrik Larsson</sub>](https://nordsken.se)<br />[🌍](#translation-pooot "Translation") | [<img src="https://avatars1.githubusercontent.com/u/20584746?v=4" width="110px;"/><br /><sub>drcryo</sub>](https://github.com/drcryo)<br />[🌍](#translation-drcryo "Translation") | [<img src="https://avatars1.githubusercontent.com/u/19408004?v=4" width="110px;"/><br /><sub>pawel1615</sub>](https://github.com/pawel1615)<br />[🌍](#translation-pawel1615 "Translation") | [<img src="https://avatars2.githubusercontent.com/u/23340468?v=4" width="110px;"/><br /><sub>bodrovics</sub>](https://github.com/bodrovics)<br />[🌍](#translation-bodrovics "Translation") | [<img src="https://avatars0.githubusercontent.com/u/3257654?v=4" width="110px;"/><br /><sub>priatna</sub>](https://github.com/priatna)<br />[🌍](#translation-priatna "Translation") | [<img src="https://avatars1.githubusercontent.com/u/5358374?v=4" width="110px;"/><br /><sub>Fan Jiang</sub>](https://amayume.net)<br />[🌍](#translation-ProfFan "Translation") |
| [<img src="https://avatars1.githubusercontent.com/u/22555451?v=4" width="110px;"/><br /><sub>ragnarcx</sub>](https://github.com/ragnarcx)<br />[🌍](#translation-ragnarcx "Translation") | [<img src="https://avatars2.githubusercontent.com/u/18654582?v=4" width="110px;"/><br /><sub>Rein van Haaren</sub>](http://www.reinvanhaaren.nl/)<br />[🌍](#translation-reinvanhaaren "Translation") | [<img src="https://avatars1.githubusercontent.com/u/386672?v=4" width="110px;"/><br /><sub>Teguh Dwicaksana</sub>](http://dheche.songolimo.net)<br />[🌍](#translation-dheche "Translation") | [<img src="https://avatars2.githubusercontent.com/u/2572552?v=4" width="110px;"/><br /><sub>fraccie</sub>](https://github.com/FRaccie)<br />[🌍](#translation-FRaccie "Translation") | [<img src="https://avatars0.githubusercontent.com/u/35182720?v=4" width="110px;"/><br /><sub>vinzruzell</sub>](https://github.com/vinzruzell)<br />[🌍](#translation-vinzruzell "Translation") | [<img src="https://avatars1.githubusercontent.com/u/7883603?v=4" width="110px;"/><br /><sub>Kevin Austin</sub>](http://kevinaustin.com)<br />[🌍](#translation-vipsystem "Translation") | [<img src="https://avatars3.githubusercontent.com/u/3861828?v=4" width="110px;"/><br /><sub>Wira Sandy</sub>](http://azuraweb.xyz)<br />[🌍](#translation-wira-sandy "Translation") |
| [<img src="https://avatars2.githubusercontent.com/u/8663789?v=4" width="110px;"/><br /><sub>Илья</sub>](https://github.com/GrayHoax)<br />[🌍](#translation-GrayHoax "Translation") | [<img src="https://avatars3.githubusercontent.com/u/30119111?v=4" width="110px;"/><br /><sub>GodUseVPN</sub>](https://github.com/godusevpn)<br />[🌍](#translation-godusevpn "Translation") | [<img src="https://avatars1.githubusercontent.com/u/745576?v=4" width="110px;"/><br /><sub>周周</sub>](https://github.com/EngrZhou)<br />[🌍](#translation-EngrZhou "Translation") | [<img src="https://avatars3.githubusercontent.com/u/1631095?v=4" width="110px;"/><br /><sub>Sam</sub>](https://github.com/takuy)<br />[💻](https://github.com/snipe/snipe-it/commits?author=takuy "Code") | [<img src="https://avatars1.githubusercontent.com/u/264022?v=4" width="110px;"/><br /><sub>Azerothian</sub>](https://www.illisian.com.au)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Azerothian "Code") | [<img src="https://avatars1.githubusercontent.com/u/7632599?v=4" width="110px;"/><br /><sub>Tim Farmer</sub>](https://github.com/timothyfarmer)<br />[💻](https://github.com/snipe/snipe-it/commits?author=timothyfarmer "Code") | [<img src="https://avatars0.githubusercontent.com/u/17459600?v=4" width="110px;"/><br /><sub>Marián Skrip</sub>](https://github.com/mskrip)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mskrip "Code") |
| [<img src="https://avatars2.githubusercontent.com/u/47435081?v=4" width="110px;"/><br /><sub>Godfrey Martinez</sub>](https://github.com/Godmartinz)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Godmartinz "Code") | [<img src="https://avatars1.githubusercontent.com/u/2075128?v=4" width="110px;"/><br /><sub>bigtreeEdo</sub>](https://github.com/bigtreeEdo)<br />[💻](https://github.com/snipe/snipe-it/commits?author=bigtreeEdo "Code") | [<img src="https://avatars0.githubusercontent.com/u/5000430?v=4" width="110px;"/><br /><sub>Colin McNeil</sub>](https://colinmcneil.me/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=ColinMcNeil "Code") | [<img src="https://avatars0.githubusercontent.com/u/421625?v=4" width="110px;"/><br /><sub>JoKneeMo</sub>](https://github.com/JoKneeMo)<br />[💻](https://github.com/snipe/snipe-it/commits?author=JoKneeMo "Code") | [<img src="https://avatars0.githubusercontent.com/u/54849013?v=4" width="110px;"/><br /><sub>Joshi</sub>](http://www.redbridge.se)<br />[💻](https://github.com/snipe/snipe-it/commits?author=joshi-redbridge "Code") | [<img src="https://avatars2.githubusercontent.com/u/15731458?v=4" width="110px;"/><br /><sub>Anthony Burns</sub>](https://github.com/anthonypburns)<br />[💻](https://github.com/snipe/snipe-it/commits?author=anthonypburns "Code") | [<img src="https://avatars2.githubusercontent.com/u/1972329?v=4" width="110px;"/><br /><sub>Alexander Chibrikin</sub>](http://phpprofi.ru/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=alek13 "Code") |
| [<img src="https://avatars2.githubusercontent.com/u/8663789?v=4" width="110px;"/><br /><sub>Илья</sub>](https://github.com/GrayHoax)<br />[🌍](#translation-GrayHoax "Translation") | [<img src="https://avatars3.githubusercontent.com/u/30119111?v=4" width="110px;"/><br /><sub>GodUseVPN</sub>](https://github.com/godusevpn)<br />[🌍](#translation-godusevpn "Translation") | [<img src="https://avatars1.githubusercontent.com/u/745576?v=4" width="110px;"/><br /><sub>周周</sub>](https://github.com/EngrZhou)<br />[🌍](#translation-EngrZhou "Translation") | [<img src="https://avatars3.githubusercontent.com/u/1631095?v=4" width="110px;"/><br /><sub>Sam</sub>](https://github.com/takuy)<br />[💻](https://github.com/snipe/snipe-it/commits?author=takuy "Code") | [<img src="https://avatars1.githubusercontent.com/u/264022?v=4" width="110px;"/><br /><sub>Azerothian</sub>](https://www.illisian.com.au)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Azerothian "Code") | [<img src="https://avatars1.githubusercontent.com/u/4930051?v=4" width="110px;"/><br /><sub>Wes Hulette</sub>](http://macfoo.wordpress.com/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jwhulette "Code") | [<img src="https://avatars0.githubusercontent.com/u/8134591?v=4" width="110px;"/><br /><sub>patrict</sub>](https://github.com/patrict)<br />[💻](https://github.com/snipe/snipe-it/commits?author=patrict "Code") |
| [<img src="https://avatars3.githubusercontent.com/u/2611616?v=4" width="110px;"/><br /><sub>Dmitriy Minaev</sub>](https://github.com/VELIKII-DIVAN)<br />[💻](https://github.com/snipe/snipe-it/commits?author=VELIKII-DIVAN "Code") | [<img src="https://avatars0.githubusercontent.com/u/5132245?v=4" width="110px;"/><br /><sub>liquidhorse</sub>](https://github.com/liquidhorse)<br />[💻](https://github.com/snipe/snipe-it/commits?author=liquidhorse "Code") | [<img src="https://avatars1.githubusercontent.com/u/183678?v=4" width="110px;"/><br /><sub>Jordi Boggiano</sub>](https://seld.be/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Seldaek "Code") | [<img src="https://avatars0.githubusercontent.com/u/653557?v=4" width="110px;"/><br /><sub>Ivan Nieto</sub>](https://github.com/inietov)<br />[💻](https://github.com/snipe/snipe-it/commits?author=inietov "Code") | [<img src="https://avatars2.githubusercontent.com/u/6764151?v=4" width="110px;"/><br /><sub>Ben RUBSON</sub>](https://github.com/benrubson)<br />[💻](https://github.com/snipe/snipe-it/commits?author=benrubson "Code") | [<img src="https://avatars2.githubusercontent.com/u/8554558?v=4" width="110px;"/><br /><sub>NMathar</sub>](https://github.com/NMathar)<br />[💻](https://github.com/snipe/snipe-it/commits?author=NMathar "Code") | [<img src="https://avatars1.githubusercontent.com/u/139566?v=4" width="110px;"/><br /><sub>Steffen</sub>](https://github.com/smb)<br />[💻](https://github.com/snipe/snipe-it/commits?author=smb "Code") |
| [<img src="https://avatars0.githubusercontent.com/u/6609453?v=4" width="110px;"/><br /><sub>Sxderp</sub>](https://github.com/Sxderp)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Sxderp "Code") | [<img src="https://avatars1.githubusercontent.com/u/4807843?v=4" width="110px;"/><br /><sub>fanta8897</sub>](https://github.com/fanta8897)<br />[💻](https://github.com/snipe/snipe-it/commits?author=fanta8897 "Code") | [<img src="https://avatars2.githubusercontent.com/u/2576509?v=4" width="110px;"/><br /><sub>Andrey Bolonin</sub>](https://andreybolonin.com/phpconsulting/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=andreybolonin "Code") | [<img src="https://avatars3.githubusercontent.com/u/2173307?v=4" width="110px;"/><br /><sub>shinayoshi</sub>](http://www.shinayoshi.net/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=shinayoshi "Code") | [<img src="https://avatars3.githubusercontent.com/u/2130159?v=4" width="110px;"/><br /><sub>Hubert</sub>](https://github.com/reuser)<br />[💻](https://github.com/snipe/snipe-it/commits?author=reuser "Code") | [<img src="https://avatars0.githubusercontent.com/u/6865789?v=4" width="110px;"/><br /><sub>KeenRivals</sub>](https://brashear.me)<br />[💻](https://github.com/snipe/snipe-it/commits?author=KeenRivals "Code") | [<img src="https://avatars3.githubusercontent.com/u/2902513?v=4" width="110px;"/><br /><sub>omyno</sub>](https://github.com/omyno)<br />[💻](https://github.com/snipe/snipe-it/commits?author=omyno "Code") |
| [<img src="https://avatars1.githubusercontent.com/u/6271335?v=4" width="110px;"/><br /><sub>Evgeny</sub>](https://github.com/jackka)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jackka "Code") | [<img src="https://avatars2.githubusercontent.com/u/1169963?v=4" width="110px;"/><br /><sub>Colin Campbell</sub>](https://digitalist.se)<br />[💻](https://github.com/snipe/snipe-it/commits?author=colin-campbell "Code") | [<img src="https://avatars3.githubusercontent.com/u/2872098?v=4" width="110px;"/><br /><sub>Ľubomír Kučera</sub>](https://github.com/lubo)<br />[💻](https://github.com/snipe/snipe-it/commits?author=lubo "Code") | [<img src="https://avatars3.githubusercontent.com/u/570639?v=4" width="110px;"/><br /><sub>Martin Meredith</sub>](https://www.sourceguru.net)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Mezzle "Code") | [<img src="https://avatars1.githubusercontent.com/u/7632599?v=4" width="110px;"/><br /><sub>Tim Farmer</sub>](https://github.com/timothyfarmer)<br />[💻](https://github.com/snipe/snipe-it/commits?author=timothyfarmer "Code") | [<img src="https://avatars0.githubusercontent.com/u/17459600?v=4" width="110px;"/><br /><sub>Marián Skrip</sub>](https://github.com/mskrip)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mskrip "Code") | [<img src="https://avatars2.githubusercontent.com/u/47435081?v=4" width="110px;"/><br /><sub>Godfrey Martinez</sub>](https://github.com/Godmartinz)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Godmartinz "Code") |
| [<img src="https://avatars1.githubusercontent.com/u/2075128?v=4" width="110px;"/><br /><sub>bigtreeEdo</sub>](https://github.com/bigtreeEdo)<br />[💻](https://github.com/snipe/snipe-it/commits?author=bigtreeEdo "Code") | [<img src="https://avatars0.githubusercontent.com/u/5000430?v=4" width="110px;"/><br /><sub>Colin McNeil</sub>](https://colinmcneil.me/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=ColinMcNeil "Code") | [<img src="https://avatars0.githubusercontent.com/u/421625?v=4" width="110px;"/><br /><sub>JoKneeMo</sub>](https://github.com/JoKneeMo)<br />[💻](https://github.com/snipe/snipe-it/commits?author=JoKneeMo "Code") | [<img src="https://avatars0.githubusercontent.com/u/54849013?v=4" width="110px;"/><br /><sub>Joshi</sub>](http://www.redbridge.se)<br />[💻](https://github.com/snipe/snipe-it/commits?author=joshi-redbridge "Code") | [<img src="https://avatars2.githubusercontent.com/u/15731458?v=4" width="110px;"/><br /><sub>Anthony Burns</sub>](https://github.com/anthonypburns)<br />[💻](https://github.com/snipe/snipe-it/commits?author=anthonypburns "Code") | [<img src="https://avatars1.githubusercontent.com/u/63399474?v=4" width="110px;"/><br /><sub>johnson-yi</sub>](https://github.com/johnson-yi)<br />[💻](https://github.com/snipe/snipe-it/commits?author=johnson-yi "Code") | [<img src="https://avatars1.githubusercontent.com/u/1862720?v=4" width="110px;"/><br /><sub>Sanjay Govind</sub>](https://tangentmc.net)<br />[💻](https://github.com/snipe/snipe-it/commits?author=sanjay900 "Code") |
| [<img src="https://avatars0.githubusercontent.com/u/1255375?v=4" width="110px;"/><br /><sub>Peter Upfold</sub>](https://peter.upfold.org.uk/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=PeterUpfold "Code") | [<img src="https://avatars2.githubusercontent.com/u/961717?v=4" width="110px;"/><br /><sub>Jared Biel</sub>](https://github.com/jbiel)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jbiel "Code") |
| [<img src="https://avatars0.githubusercontent.com/u/1255375?v=4" width="110px;"/><br /><sub>Peter Upfold</sub>](https://peter.upfold.org.uk/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=PeterUpfold "Code") | [<img src="https://avatars2.githubusercontent.com/u/961717?v=4" width="110px;"/><br /><sub>Jared Biel</sub>](https://github.com/jbiel)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jbiel "Code") | [<img src="https://avatars1.githubusercontent.com/u/1733625?v=4" width="110px;"/><br /><sub>Dampfklon</sub>](https://github.com/dampfklon)<br />[💻](https://github.com/snipe/snipe-it/commits?author=dampfklon "Code") | [<img src="https://avatars2.githubusercontent.com/u/52973156?v=4" width="110px;"/><br /><sub>Charles Hamilton</sub>](https://communityclosing.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=chamilton-ccn "Code") | [<img src="https://avatars.githubusercontent.com/u/551789?v=4" width="110px;"/><br /><sub>Giuseppe Iannello</sub>](https://github.com/giannello)<br />[💻](https://github.com/snipe/snipe-it/commits?author=giannello "Code") | [<img src="https://avatars.githubusercontent.com/u/3691490?v=4" width="110px;"/><br /><sub>Peter Dave Hello</sub>](https://www.peterdavehello.org/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=PeterDaveHello "Code") | [<img src="https://avatars.githubusercontent.com/u/6106332?v=4" width="110px;"/><br /><sub>sigmoidal</sub>](https://github.com/sigmoidal)<br />[💻](https://github.com/snipe/snipe-it/commits?author=sigmoidal "Code") |
| [<img src="https://avatars.githubusercontent.com/u/2082554?v=4" width="110px;"/><br /><sub>Vincent Lainé</sub>](https://github.com/phenixdotnet)<br />[💻](https://github.com/snipe/snipe-it/commits?author=phenixdotnet "Code") | [<img src="https://avatars.githubusercontent.com/u/1943040?v=4" width="110px;"/><br /><sub>Lucas Pleß</sub>](http://www.lucas-pless.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=derlucas "Code") | [<img src="https://avatars.githubusercontent.com/u/472804?v=4" width="110px;"/><br /><sub>Ian Littman</sub>](http://twitter.com/iansltx)<br />[💻](https://github.com/snipe/snipe-it/commits?author=iansltx "Code") | [<img src="https://avatars.githubusercontent.com/u/3519029?v=4" width="110px;"/><br /><sub>João Paulo</sub>](https://github.com/PauloLuna)<br />[💻](https://github.com/snipe/snipe-it/commits?author=PauloLuna "Code") | [<img src="https://avatars.githubusercontent.com/u/70443365?v=4" width="110px;"/><br /><sub>ThoBur</sub>](https://github.com/ThoBur)<br />[💻](https://github.com/snipe/snipe-it/commits?author=ThoBur "Code") | [<img src="https://avatars.githubusercontent.com/u/1972329?v=4" width="110px;"/><br /><sub>Alexander Chibrikin</sub>](http://phpprofi.ru/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=alek13 "Code") | [<img src="https://avatars.githubusercontent.com/u/438332?v=4" width="110px;"/><br /><sub>Anthony Winstanley</sub>](https://github.com/winstan)<br />[💻](https://github.com/snipe/snipe-it/commits?author=winstan "Code") |
| [<img src="https://avatars.githubusercontent.com/u/3075214?v=4" width="110px;"/><br /><sub>Folke</sub>](https://github.com/fashberg)<br />[💻](https://github.com/snipe/snipe-it/commits?author=fashberg "Code") | [<img src="https://avatars.githubusercontent.com/u/1351571?v=4" width="110px;"/><br /><sub>Bennett Blodinger</sub>](https://github.com/benwa)<br />[💻](https://github.com/snipe/snipe-it/commits?author=benwa "Code") | [<img src="https://avatars.githubusercontent.com/u/2974631?v=4" width="110px;"/><br /><sub>NMC</sub>](https://nmc.dev)<br />[💻](https://github.com/snipe/snipe-it/commits?author=ncareau "Code") | [<img src="https://avatars.githubusercontent.com/u/52182449?v=4" width="110px;"/><br /><sub>andres-baller</sub>](https://github.com/andres-baller)<br />[💻](https://github.com/snipe/snipe-it/commits?author=andres-baller "Code") | [<img src="https://avatars.githubusercontent.com/u/67109348?v=4" width="110px;"/><br /><sub>sean-borg</sub>](https://github.com/sean-borg)<br />[💻](https://github.com/snipe/snipe-it/commits?author=sean-borg "Code") | [<img src="https://avatars.githubusercontent.com/u/32170051?v=4" width="110px;"/><br /><sub>EDVLeer</sub>](https://github.com/EDVLeer)<br />[💻](https://github.com/snipe/snipe-it/commits?author=EDVLeer "Code") | [<img src="https://avatars.githubusercontent.com/u/23075196?v=4" width="110px;"/><br /><sub>Kurokat</sub>](https://github.com/Kurokat)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Kurokat "Code") |
| [<img src="https://avatars.githubusercontent.com/u/915514?v=4" width="110px;"/><br /><sub>Kevin Köllmann</sub>](https://www.kevinkoellmann.de)<br />[💻](https://github.com/snipe/snipe-it/commits?author=koelle25 "Code") | [<img src="https://avatars.githubusercontent.com/u/49025941?v=4" width="110px;"/><br /><sub>sw-mreyes</sub>](https://github.com/sw-mreyes)<br />[💻](https://github.com/snipe/snipe-it/commits?author=sw-mreyes "Code") | [<img src="https://avatars.githubusercontent.com/u/70129?v=4" width="110px;"/><br /><sub>Joel Pittet</sub>](https://pittet.ca)<br />[💻](https://github.com/snipe/snipe-it/commits?author=joelpittet "Code") | [<img src="https://avatars.githubusercontent.com/u/792695?v=4" width="110px;"/><br /><sub>Eli Young</sub>](https://elyscape.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=elyscape "Code") | [<img src="https://avatars.githubusercontent.com/u/317015?v=4" width="110px;"/><br /><sub>Raell Dottin</sub>](https://github.com/raelldottin)<br />[💻](https://github.com/snipe/snipe-it/commits?author=raelldottin "Code") | [<img src="https://avatars.githubusercontent.com/u/1446856?v=4" width="110px;"/><br /><sub>Tom Misilo</sub>](https://github.com/misilot)<br />[💻](https://github.com/snipe/snipe-it/commits?author=misilot "Code") | [<img src="https://avatars.githubusercontent.com/u/4496300?v=4" width="110px;"/><br /><sub>David Davenne</sub>](http://david.davenne.be)<br />[💻](https://github.com/snipe/snipe-it/commits?author=JuustoMestari "Code") |
| [<img src="https://avatars.githubusercontent.com/u/9255772?v=4" width="110px;"/><br /><sub>Mark Stenglein</sub>](https://markstenglein.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=ocelotsloth "Code") | [<img src="https://avatars.githubusercontent.com/u/35658596?v=4" width="110px;"/><br /><sub>ajsy</sub>](https://github.com/ajsy)<br />[💻](https://github.com/snipe/snipe-it/commits?author=ajsy "Code") | [<img src="https://avatars.githubusercontent.com/u/3628035?v=4" width="110px;"/><br /><sub>Jan Kiesewetter</sub>](https://github.com/t3easy)<br />[💻](https://github.com/snipe/snipe-it/commits?author=t3easy "Code") | [<img src="https://avatars.githubusercontent.com/u/79449630?v=4" width="110px;"/><br /><sub>Tetrachloromethane250</sub>](https://github.com/Tetrachloromethane250)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Tetrachloromethane250 "Code") | [<img src="https://avatars.githubusercontent.com/u/22004482?v=4" width="110px;"/><br /><sub>Lars Kajes</sub>](https://www.kajes.se/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=kajes "Code") | [<img src="https://avatars.githubusercontent.com/u/13993216?v=4" width="110px;"/><br /><sub>Joly0</sub>](https://github.com/Joly0)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Joly0 "Code") | [<img src="https://avatars.githubusercontent.com/u/1501022?v=4" width="110px;"/><br /><sub>theburger</sub>](https://github.com/limeless)<br />[💻](https://github.com/snipe/snipe-it/commits?author=limeless "Code") |
| [<img src="https://avatars.githubusercontent.com/u/36065681?v=4" width="110px;"/><br /><sub>David Valin Alonso</sub>](https://github.com/deivishome)<br />[💻](https://github.com/snipe/snipe-it/commits?author=deivishome "Code") | [<img src="https://avatars.githubusercontent.com/u/8290389?v=4" width="110px;"/><br /><sub>andreaci</sub>](https://github.com/andreaci)<br />[💻](https://github.com/snipe/snipe-it/commits?author=andreaci "Code") | [<img src="https://avatars.githubusercontent.com/u/1828542?v=4" width="110px;"/><br /><sub>Jelle Sebreghts</sub>](http://www.jellesebreghts.be)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Jelle-S "Code") | [<img src="https://avatars.githubusercontent.com/u/11180862?v=4" width="110px;"/><br /><sub>Michael Pietsch</sub>](https://github.com/Skywalker-11)<br /> | [<img src="https://avatars.githubusercontent.com/u/22068886?v=4" width="110px;"/><br /><sub>Masudul Haque Shihab</sub>](https://github.com/sh1hab)<br />[💻](https://github.com/snipe/snipe-it/commits?author=sh1hab "Code") | [<img src="https://avatars.githubusercontent.com/u/16099942?v=4" width="110px;"/><br /><sub>Supapong Areeprasertkul</sub>](http://www.freedomdive.com/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=zybersup "Code") | [<img src="https://avatars.githubusercontent.com/u/207358?v=4" width="110px;"/><br /><sub>Peter Sarossy</sub>](https://github.com/psarossy)<br />[💻](https://github.com/snipe/snipe-it/commits?author=psarossy "Code") |
| [<img src="https://avatars.githubusercontent.com/u/11823649?v=4" width="110px;"/><br /><sub>Renee Margaret McConahy</sub>](https://github.com/nepella)<br />[💻](https://github.com/snipe/snipe-it/commits?author=nepella "Code") | [<img src="https://avatars.githubusercontent.com/u/5553884?v=4" width="110px;"/><br /><sub>JohnnyPicnic</sub>](https://github.com/JohnnyPicnic)<br />[💻](https://github.com/snipe/snipe-it/commits?author=JohnnyPicnic "Code") | [<img src="https://avatars.githubusercontent.com/u/8799594?v=4" width="110px;"/><br /><sub>markbrule</sub>](https://github.com/markbrule)<br />[💻](https://github.com/snipe/snipe-it/commits?author=markbrule "Code") | [<img src="https://avatars.githubusercontent.com/u/1962801?v=4" width="110px;"/><br /><sub>Mike Campbell</sub>](https://github.com/mikecmpbll)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mikecmpbll "Code") | [<img src="https://avatars.githubusercontent.com/u/11973217?v=4" width="110px;"/><br /><sub>tbrconnect</sub>](https://github.com/tbrconnect)<br />[💻](https://github.com/snipe/snipe-it/commits?author=tbrconnect "Code") | [<img src="https://avatars.githubusercontent.com/u/12447225?v=4" width="110px;"/><br /><sub>kcoyo</sub>](https://github.com/kcoyo)<br />[💻](https://github.com/snipe/snipe-it/commits?author=kcoyo "Code") | [<img src="https://avatars.githubusercontent.com/u/494017?v=4" width="110px;"/><br /><sub>Travis Miller</sub>](https://travismiller.com/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=travismiller "Code") |
<!-- ALL-CONTRIBUTORS-LIST:END -->
This project follows the [all-contributors](https://github.com/kentcdodds/all-contributors) specification. Contributions of any kind welcome!

27
SECURITY.md Normal file
View File

@@ -0,0 +1,27 @@
# Security Policy
We take security issues very seriously, and will always attempt to address any
vulnerabilities as quickly as possible.
## Supported Versions
We try to make a reasonable effort to support older versions of Snipe-IT,
however there are times when library dependencies and/or PHP/MySQL dependencies
make it impossible to backport security fixes on older versions.
| Version | Supported |
| ------- | ------------------ |
| 5.1.x | :white_check_mark: |
| 5.0.x | :x: |
| 4.0.x | :white_check_mark: |
| < 4.0 | :x: |
## Reporting a Vulnerability
Security vulnerabilities should be sent to security@snipeitapp.com. You can typically expect a
response within two business days, and we typically have fixes out in under a week from the initial disclosure.
This obviously varies based on the severity of the security issue and the difficulty in remediation,
but those have historically been the timelines we worm around.
For a full breakdown of our security policies, please see https://snipeitapp.com/security.

27
Vagrantfile vendored
View File

@@ -8,25 +8,34 @@ Vagrant.configure("2") do |config|
config.vm.define "bionic" do |bionic|
bionic.vm.box = "ubuntu/bionic64"
bionic.vm.hostname = 'bionic'
bionic.vm.network "public_network", bridge: NETWORK_BRIDGE
bionic.vm.provision :shell, :inline => "wget #{SNIPEIT_SH_URL}"
bionic.vm.provision :shell, :inline => "chmod 755 snipeit.sh"
bionic.vm.network "forwarded_port", guest: 80, host: 8080
bionic.vm.synced_folder ".", "/vagrant", :owner => 'www-data',
:group => 'vagrant', :mount_options => ['dmode=775', 'fmode=775']
bionic.vm.provision "ansible_local" do |ansible|
ansible.playbook = "ansible/ubuntu/vagrant_playbook.yml"
end
end
config.vm.define "xenial" do |xenial|
xenial.vm.box = "ubuntu/xenial64"
xenial.vm.hostname = 'xenial'
xenial.vm.network "public_network", bridge: NETWORK_BRIDGE
xenial.vm.provision :shell, :inline => "wget #{SNIPEIT_SH_URL}"
xenial.vm.provision :shell, :inline => "chmod 755 snipeit.sh"
xenial.vm.network "forwarded_port", guest: 80, host: 8080
xenial.vm.synced_folder ".", "/vagrant", :owner => 'www-data',
:group => 'vagrant', :mount_options => ['dmode=775', 'fmode=775']
xenial.vm.provision "ansible_local" do |ansible|
ansible.playbook = "ansible/ubuntu/vagrant_playbook.yml"
end
end
config.vm.define "trusty" do |trusty|
trusty.vm.box = "ubuntu/trusty32"
trusty.vm.hostname = 'trusty'
trusty.vm.network "public_network", bridge: NETWORK_BRIDGE
trusty.vm.provision :shell, :inline => "wget #{SNIPEIT_SH_URL}"
trusty.vm.provision :shell, :inline => "chmod 755 snipeit.sh"
trusty.vm.network "forwarded_port", guest: 80, host: 8080
trusty.vm.synced_folder ".", "/vagrant", :owner => 'www-data',
:group => 'vagrant', :mount_options => ['dmode=775', 'fmode=775']
trusty.vm.provision "ansible_local" do |ansible|
ansible.playbook = "ansible/ubuntu/vagrant_playbook.yml"
end
end
config.vm.define "centos7" do |centos7|

View File

@@ -0,0 +1,10 @@
<VirtualHost *:80>
<Directory {{ app_path }}/public>
Allow From All
AllowOverride All
Options -Indexes
</Directory>
DocumentRoot {{ app_path }}/public
ServerName {{ fqdn }}
</VirtualHost>

View File

@@ -0,0 +1,226 @@
---
- name: Set up local server
hosts: all
remote_user: vagrant
become_user: root
become_method: sudo
vars:
app_path: "/var/www/snipeit"
fqdn: "localhost"
tasks:
- name: Update and upgrade existing apt packages
become: true
apt:
upgrade: yes
update_cache: yes
- name: Install Utilities
become: true
apt:
name: "{{ packages }}"
state: present
vars:
packages:
- nano
- vim
- name: Installing Apache httpd, PHP, MariaDB and other requirements.
become: true
apt:
name: "{{ packages }}"
state: present
vars:
packages:
- mariadb-client
- php
- php-curl
- php-mysql
- php-gd
- php-ldap
- php-zip
- php-mbstring
- php-xml
- php-bcmath
- curl
- git
- unzip
- python-pymysql
#
# Install the lastest version of composer
#
- name: Composer check
stat:
path: /usr/local/bin/composer
register: composer_exits
- name: Install Composer
shell: |
EXPECTED_SIGNATURE=$(wget -q -O - https://composer.github.io/installer.sig)
php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
ACTUAL_SIGNATURE=$(php -r "echo hash_file('SHA384', 'composer-setup.php');")
if [ "$EXPECTED_SIGNATURE" != "$ACTUAL_SIGNATURE" ]
then
>&2 echo 'ERROR: Invalid installer signature'
rm composer-setup.php
exit 1
fi
php composer-setup.php --quiet
RESULT=$?
rm composer-setup.php
mv composer.phar /usr/local/bin/composer
exit $RESULT
when: not composer_exits.stat.exists
args:
creates: /usr/local/bin/composer
become: true
#
# Install and Configure MariaDB
#
- name: Install MariaDB
become: true
apt:
name: mariadb-server
state: present
register: sql_server
- name: Start and Enable MySQL server
become: true
systemd:
state: started
enabled: yes
name: mariadb
- name: Create Vagrant mysql password
become: true
mysql_user:
name: vagrant
password: vagrant
login_unix_socket: /var/run/mysqld/mysqld.sock
priv: "*.*:ALL"
state: present
- name: Enable remote mysql
replace:
path: /etc/mysql/mariadb.conf.d/50-server.cnf
regexp: "127.0.0.1"
replace: "0.0.0.0"
become: true
notify:
- restart mysql
- name: Create snipeit database
become: true
mysql_db:
name: snipeit
state: present
login_unix_socket: /var/run/mysqld/mysqld.sock
#
# Install Apache Web Server
#
- name: Install Apache 2.4
apt:
name: "{{ packages }}"
state: present
vars:
packages:
- apache2
- libapache2-mod-php
become: true
register: apache2_server
- name: Start and Enable Apache2 Server
become: true
systemd:
name: apache2
state: started
enabled: yes
#- name: Disable Apache modules
# become: true
# apache2_module:
# state: absent
# name: "{{ item }}"
# with_items:
# #- mpm_prefork
# notify:
# - restart apache2
- name: Enable Apache modules
become: true
apache2_module:
state: present
name: "{{ item }}"
with_items:
- rewrite
- vhost_alias
- deflate
- expires
- proxy_fcgi
- proxy
notify:
- restart apache2
- name: Install Apache VirtualHost File
become: true
template:
src: apachevirtualhost.conf.j2
dest: "/etc/apache2/sites-available/snipeit.conf"
- name: Enable VirtualHost
become: true
command: a2ensite snipeit
args:
creates: /etc/apache2/sites-enabled/snipeit.conf
notify:
- restart apache2
- name: Map apache dir to local folder
become: true
file:
src: /vagrant
dest: "{{ app_path }}"
state: link
notify:
- restart apache2
#
# Install dependencies from composer
#
- name: Install dependencies from composer
composer:
command: install
working_dir: "{{ app_path }}"
notify:
- restart apache2
#
# Configure .env file
#
- name: Copy .env file
copy:
src: "{{ app_path }}/.env.example"
dest: "{{ app_path }}/.env"
- name: Configure .env file
lineinfile:
path: "{{ app_path }}/.env"
regexp: "{{ item.regexp }}"
line: "{{ item.line }}"
with_items:
- { regexp: '^DB_HOST=', line: 'DB_HOST=127.0.0.1'}
- { regexp: '^DB_DATABASE=', line: 'DB_DATABASE=snipeit' }
- { regexp: '^DB_USERNAME=', line: 'DB_USERNAME=vagrant' }
- { regexp: '^DB_PASSWORD=', line: 'DB_PASSWORD=vagrant' }
- { regexp: '^APP_URL=', line: "APP_URL=http://{{ fqdn }}" }
- { regexp: '^APP_ENV=', line: "APP_ENV=development" }
- { regexp: '^APP_DEBUG=', line: "APP_ENV=true" }
- name: Generate application key
shell: "php {{ app_path }}/artisan key:generate --force"
- name: Artisan Migrate
shell: "php {{ app_path }}/artisan migrate --force"
#
# Create Cron Job
#
- name: Create scheduler cron job
become: true
cron:
name: "Snipe-IT Artisan Scheduler"
job: "/usr/bin/php {{ app_path }}/artisan schedule:run"
handlers:
- name: restart apache2
become: true
systemd:
name: apache2
state: restarted
- name: restart mysql
become: true
systemd:
name: mysql
state: restarted

View File

@@ -0,0 +1,105 @@
<?php
namespace App\Console\Commands;
use App\Models\Actionlog;
use App\Models\Asset;
use Illuminate\Console\Command;
class FixMismatchedAssetsAndLogs extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'snipeit:fix-assets-and-logs {--dryrun : Run the sync process but don\'t update the database}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'This script attempts to check the log table and check that the assets.assigned_to matches the last checkout.';
/**
* Is dry-run?
*
* @var bool
*/
private $dryrun = false;
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
if ($this->option('dryrun')) {
$this->dryrun = true;
}
if ($this->dryrun) {
$this->info('This is a DRY RUN - no changes will be saved.' );
}
$mismatch_count = 0;
$assets = Asset::whereNotNull('assigned_to')
->where('assigned_type', '=', 'App\\Models\\User')
->orderBy('id', 'ASC')->get();
foreach ($assets as $asset) {
// get the last checkout of the asset
if ($checkout_log = Actionlog::where('target_type', '=', 'App\\Models\\User')
->where('action_type', '=', 'checkout')
->where('item_id', '=', $asset->id)
->orderBy('created_at', 'DESC')
->first()) {
// Now check for a subsequent checkin log - we want to ignore those
if (!$checkin_log = Actionlog::where('target_type', '=', 'App\\Models\\User')
->where('action_type', '=', 'checkin from')
->where('item_id', '=', $asset->id)
->whereDate('created_at', '>', $checkout_log->created_at)
->orderBy('created_at', 'DESC')
->first()) {
//print_r($asset);
if ($checkout_log->target_id != $asset->assigned_to) {
$this->error('Log ID: '.$checkout_log->id.' -- Asset ID '. $checkout_log->item_id.' SHOULD BE checked out to User '.$checkout_log->target_id.' but its assigned_to is '.$asset->assigned_to );
if (!$this->dryrun) {
$asset->assigned_to = $checkout_log->target_id;
if ($asset->save()) {
$this->info('Asset record updated.');
} else {
$this->error('Error updating asset: '.$asset->getErrors());
}
}
$mismatch_count++;
}
} else {
//$this->info('Asset ID '.$asset->id.': There is a checkin '.$checkin_log->created_at.' after this checkout '.$checkout_log->created_at);
}
}
}
$this->info($mismatch_count.' mismatched assets.');
}
}

View File

@@ -88,11 +88,13 @@ class ImportLocations extends Command
if (array_key_exists('Parent Name', $row)) {
$parent_name = trim($row['Parent Name']);
} else {
$parent_name = null;
}
// Set the location attributes to save
if (array_key_exists('Name', $row)) {
$location = Location::firstOrNew(array('name' => trim($row['Name'])));
$location = Location::firstOrCreate(array('name' => trim($row['Name'])));
$location->name = trim($row['Name']);
$this->info('Checking location: '.$location->name);
} else {

560
app/Console/Commands/LdapSync.php Normal file → Executable file
View File

@@ -1,24 +1,15 @@
<?php
declare(strict_types=1);
namespace App\Console\Commands;
use Log;
use Exception;
use App\Models\User;
use App\Services\LdapAd;
use App\Models\Location;
use App\Models\Department;
use Illuminate\Console\Command;
use Adldap\Models\User as AdldapUser;
use App\Models\Setting;
use App\Models\Ldap;
use App\Models\User;
use App\Models\Location;
use Log;
/**
* LDAP / AD sync command.
*
* @author Wes Hulette <jwhulette@gmail.com>
*
* @since 5.0.0
*/
class LdapSync extends Command
{
/**
@@ -26,79 +17,23 @@ class LdapSync extends Command
*
* @var string
*/
protected $signature = 'snipeit:ldap-sync
{--location= : A location name }
{--location_id= : A location id}
{--base_dn= : A diffrent base DN to use }
{--summary : Print summary }
{--json_summary : Print summary in json format }
{--dryrun : Run the sync process but don\'t update the database}';
protected $signature = 'snipeit:ldap-sync {--location=} {--location_id=} {--base_dn=} {--summary} {--json_summary}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Command line LDAP/AD sync';
/**
* An LdapAd instance.
*
* @var \App\Models\LdapAd
*/
private $ldap;
/**
* LDAP settings collection.
*
* @var \Illuminate\Support\Collection
*/
private $settings = null;
/**
* A default location collection.
*
* @var \Illuminate\Support\Collection
*/
private $defaultLocation = null;
/**
* Mapped locations collection.
*
* @var \Illuminate\Support\Collection
*/
private $mappedLocations = null;
/**
* The summary collection.
*
* @var \Illuminate\Support\Collection
*/
private $summary;
/**
* Is dry-run?
*
* @var bool
*/
private $dryrun = false;
/**
* Show users to be imported.
*
* @var array
*/
private $userlist = [];
protected $description = 'Command line LDAP sync';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct(LdapAd $ldap)
public function __construct()
{
parent::__construct();
$this->ldap = $ldap;
$this->settings = $this->ldap->ldapSettings;
$this->summary = collect();
}
/**
@@ -108,274 +43,259 @@ class LdapSync extends Command
*/
public function handle()
{
ini_set('max_execution_time', '600'); //600 seconds = 10 minutes
ini_set('memory_limit', '500M');
$old_error_reporting = error_reporting(); // grab old error_reporting .ini setting, for later re-enablement
error_reporting($old_error_reporting & ~E_DEPRECATED); // disable deprecation warnings, for LDAP in PHP 7.4 (and greater)
ini_set('max_execution_time', env('LDAP_TIME_LIM', 600)); //600 seconds = 10 minutes
ini_set('memory_limit', env('LDAP_MEM_LIM', '500M'));
$ldap_result_username = Setting::getSettings()->ldap_username_field;
$ldap_result_last_name = Setting::getSettings()->ldap_lname_field;
$ldap_result_first_name = Setting::getSettings()->ldap_fname_field;
if ($this->option('dryrun')) {
$this->dryrun = true;
}
$this->checkIfLdapIsEnabled();
$this->checkLdapConnetion();
$this->setBaseDn();
$this->getUserDefaultLocation();
/*
* Use the default location if set, this is needed for the LDAP users sync page
*/
if (!$this->option('base_dn') && null == $this->defaultLocation) {
$this->getMappedLocations();
}
$this->processLdapUsers();
// Print table of users
if ($this->dryrun) {
$this->info('The following users will be synced!');
$headers = ['First Name', 'Last Name', 'Username', 'Email', 'Employee #', 'Location Id', 'Status'];
$this->table($headers, $this->summary->toArray());
}
$ldap_result_active_flag = Setting::getSettings()->ldap_active_flag_field;
$ldap_result_emp_num = Setting::getSettings()->ldap_emp_num;
$ldap_result_email = Setting::getSettings()->ldap_email;
$ldap_result_phone = Setting::getSettings()->ldap_phone_field;
$ldap_result_jobtitle = Setting::getSettings()->ldap_jobtitle;
$ldap_result_country = Setting::getSettings()->ldap_country;
$ldap_result_dept = Setting::getSettings()->ldap_dept;
error_reporting($old_error_reporting); // re-enable deprecation warnings.
return $this->getSummary();
}
/**
* Generate the LDAP sync summary.
*
* @author Wes Hulette <jwhulette@gmail.com>
*
* @since 5.0.0
*
* @return string
*/
private function getSummary(): string
{
if ($this->option('summary') && null === $this->dryrun) {
$this->summary->each(function ($item) {
$this->info('USER: '.$item['note']);
if ('ERROR' === $item['status']) {
$this->error('ERROR: '.$item['note']);
}
});
} elseif ($this->option('json_summary')) {
$json_summary = [
'error' => false,
'error_message' => '',
'summary' => $this->summary->toArray(),
];
$this->info(json_encode($json_summary));
}
return '';
}
/**
* Create a new user or update an existing user.
*
* @author Wes Hulette <jwhulette@gmail.com>
*
* @since 5.0.0
*
* @param \Adldap\Models\User $snipeUser
*/
private function updateCreateUser(AdldapUser $snipeUser): void
{
$user = $this->ldap->processUser($snipeUser, $this->defaultLocation, $this->mappedLocations);
$summary = [
'firstname' => $user->first_name,
'lastname' => $user->last_name,
'username' => $user->username,
'employee_number' => $user->employee_num,
'email' => $user->email,
'location_id' => $user->location_id,
];
// Only update the database if is not a dry run
if (!$this->dryrun) {
if ($user->save()) {
$summary['note'] = ($user->wasRecentlyCreated ? 'CREATED' : 'UPDATED');
$summary['status'] = 'SUCCESS';
} else {
$errors = '';
foreach ($user->getErrors()->getMessages() as $error) {
$errors .= implode(", ",$error);
}
$summary['note'] = $snipeUser->getDN().' was not imported. REASON: '.$errors;
$summary['status'] = 'ERROR';
}
}
// $summary['note'] = ($user->getOriginal('username') ? 'UPDATED' : 'CREATED'); // this seems, kinda, like, superfluous, relative to the $summary['note'] thing above, yeah?
$this->summary->push($summary);
}
/**
* Process the users to update / create.
*
* @author Wes Hulette <jwhulette@gmail.com>
*
* @since 5.0.0
*
* @param int $page The page to get the result set
*/
private function processLdapUsers(int $page=0): void
{
try {
$ldapUsers = $this->ldap->getLdapUsers($page);
} catch (Exception $e) {
$this->outputError($e);
exit($e->getMessage());
}
if (0 == $ldapUsers->count()) {
$msg = 'ERROR: No users found!';
Log::error($msg);
if ($this->dryrun) {
$this->error($msg);
$ldapconn = Ldap::connectToLdap();
Ldap::bindAdminToLdap($ldapconn);
} catch (\Exception $e) {
if ($this->option('json_summary')) {
$json_summary = [ "error" => true, "error_message" => $e->getMessage(), "summary" => [] ];
$this->info(json_encode($json_summary));
}
exit($msg);
LOG::info($e);
return [];
}
// Process each individual users
foreach ($ldapUsers as $user) {
$this->updateCreateUser($user);
$summary = array();
try {
if ($this->option('base_dn') != '') {
$search_base = $this->option('base_dn');
LOG::debug('Importing users from specified base DN: \"'.$search_base.'\".');
} else {
$search_base = null;
}
$results = Ldap::findLdapUsers($search_base);
} catch (\Exception $e) {
if ($this->option('json_summary')) {
$json_summary = [ "error" => true, "error_message" => $e->getMessage(), "summary" => [] ];
$this->info(json_encode($json_summary));
}
LOG::info($e);
return [];
}
if ($ldapUsers->getCurrentPage() < $ldapUsers->getPages()-1) {
$this->processLdapUsers($ldapUsers->getCurrentPage() + 1);
}
}
/* Determine which location to assign users to by default. */
$location = NULL; // FIXME - this would be better called "$default_location", which is more explicit about its purpose
/**
* Get the mapped locations if a base_dn is provided.
*
* @author Wes Hulette <jwhulette@gmail.com>
*
* @since 5.0.0
*/
private function getMappedLocations()
{
$ldapOuLocation = Location::where('ldap_ou', '!=', '')->select(['id', 'ldap_ou'])->get();
$locations = $ldapOuLocation->sortBy(function ($ou, $key) {
return strlen($ou->ldap_ou);
});
if ($locations->count() > 0) {
$msg = 'Some locations have special OUs set. Locations will be automatically set for users in those OUs.';
LOG::debug($msg);
if ($this->dryrun) {
$this->info($msg);
if ($this->option('location')!='') {
$location = Location::where('name', '=', $this->option('location'))->first();
LOG::debug('Location name '.$this->option('location').' passed');
LOG::debug('Importing to '.$location->name.' ('.$location->id.')');
} elseif ($this->option('location_id')!='') {
$location = Location::where('id', '=', $this->option('location_id'))->first();
LOG::debug('Location ID '.$this->option('location_id').' passed');
LOG::debug('Importing to '.$location->name.' ('.$location->id.')');
}
if (!isset($location)) {
LOG::debug('That location is invalid or a location was not provided, so no location will be assigned by default.');
}
/* Process locations with explicitly defined OUs, if doing a full import. */
if ($this->option('base_dn')=='') {
// Retrieve locations with a mapped OU, and sort them from the shallowest to deepest OU (see #3993)
$ldap_ou_locations = Location::where('ldap_ou', '!=', '')->get()->toArray();
$ldap_ou_lengths = array();
foreach ($ldap_ou_locations as $ou_loc) {
$ldap_ou_lengths[] = strlen($ou_loc["ldap_ou"]);
}
$this->mappedLocations = $locations->pluck('ldap_ou', 'id'); // TODO: this seems ok-ish, but the key-> value is going location_id -> OU name, and the primary action here is the opposite of that - going from OU's to location ID's.
}
}
array_multisort($ldap_ou_lengths, SORT_ASC, $ldap_ou_locations);
/**
* Set the base dn if supplied.
*
* @author Wes Hulette <jwhulette@gmail.com>
*
* @since 5.0.0
*/
private function setBaseDn(): void
{
if ($this->option('base_dn')) {
$this->ldap->baseDn = $this->option('base_dn');
$msg = sprintf('Importing users from specified base DN: "%s"', $this->ldap->baseDn);
LOG::debug($msg);
if ($this->dryrun) {
$this->info($msg);
if (sizeof($ldap_ou_locations) > 0) {
LOG::debug('Some locations have special OUs set. Locations will be automatically set for users in those OUs.');
}
}
}
/**
* Get a default location id for imported users.
*
* @author Wes Hulette <jwhulette@gmail.com>
*
* @since 5.0.0
*/
private function getUserDefaultLocation(): void
{
$location = $this->option('location_id') ?? $this->option('location');
if ($location) {
$userLocation = Location::where('name', '=', $location)
->orWhere('id', '=', intval($location))
->select(['name', 'id'])
->first();
if ($userLocation) {
$msg = 'Importing users with default location: '.$userLocation->name.' ('.$userLocation->id.')';
LOG::debug($msg);
// Inject location information fields
for ($i = 0; $i < $results["count"]; $i++) {
$results[$i]["ldap_location_override"] = false;
$results[$i]["location_id"] = 0;
}
// Grab subsets based on location-specific DNs, and overwrite location for these users.
foreach ($ldap_ou_locations as $ldap_loc) {
try {
$location_users = Ldap::findLdapUsers($ldap_loc["ldap_ou"]);
} catch (\Exception $e) { // FIXME: this is stolen from line 77 or so above
if ($this->option('json_summary')) {
$json_summary = [ "error" => true, "error_message" => trans('admin/users/message.error.ldap_could_not_search')." Location: ".$ldap_loc['name']." (ID: ".$ldap_loc['id'].") cannot connect to \"".$ldap_loc["ldap_ou"]."\" - ".$e->getMessage(), "summary" => [] ];
$this->info(json_encode($json_summary));
}
LOG::info($e);
return [];
}
$usernames = array();
for ($i = 0; $i < $location_users["count"]; $i++) {
if (array_key_exists($ldap_result_username, $location_users[$i])) {
$location_users[$i]["ldap_location_override"] = true;
$location_users[$i]["location_id"] = $ldap_loc["id"];
$usernames[] = $location_users[$i][$ldap_result_username][0];
}
if ($this->dryrun) {
$this->info($msg);
}
$this->defaultLocation = collect([
$userLocation->id => $userLocation->name,
// Delete located users from the general group.
foreach ($results as $key => $generic_entry) {
if ((is_array($generic_entry)) && (array_key_exists($ldap_result_username, $generic_entry))) {
if (in_array($generic_entry[$ldap_result_username][0], $usernames)) {
unset($results[$key]);
}
}
}
$global_count = $results['count'];
$results = array_merge($location_users, $results);
$results['count'] = $global_count;
}
}
/* Create user account entries in Snipe-IT */
$tmp_pass = substr(str_shuffle("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"), 0, 20);
$pass = bcrypt($tmp_pass);
for ($i = 0; $i < $results["count"]; $i++) {
if (empty($ldap_result_active_flag) || $results[$i][$ldap_result_active_flag][0] == "TRUE") {
$item = array();
$item["username"] = isset($results[$i][$ldap_result_username][0]) ? $results[$i][$ldap_result_username][0] : "";
$item["employee_number"] = isset($results[$i][$ldap_result_emp_num][0]) ? $results[$i][$ldap_result_emp_num][0] : "";
$item["lastname"] = isset($results[$i][$ldap_result_last_name][0]) ? $results[$i][$ldap_result_last_name][0] : "";
$item["firstname"] = isset($results[$i][$ldap_result_first_name][0]) ? $results[$i][$ldap_result_first_name][0] : "";
$item["email"] = isset($results[$i][$ldap_result_email][0]) ? $results[$i][$ldap_result_email][0] : "" ;
$item["ldap_location_override"] = isset($results[$i]["ldap_location_override"]) ? $results[$i]["ldap_location_override"]:"";
$item["location_id"] = isset($results[$i]["location_id"]) ? $results[$i]["location_id"]:"";
$item["telephone"] = isset($results[$i][$ldap_result_phone][0]) ? $results[$i][$ldap_result_phone][0] : "";
$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] : "";
$department = Department::firstOrCreate([
'name' => $item["department"],
]);
} else {
$msg = 'The supplied location is invalid!';
LOG::error($msg);
if ($this->dryrun) {
$this->error($msg);
$user = User::where('username', $item["username"])->first();
if ($user) {
// Updating an existing user.
$item["createorupdate"] = 'updated';
} else {
// Creating a new user.
$user = new User;
$user->password = $pass;
$user->activated = 0;
$item["createorupdate"] = 'created';
}
exit(0);
$user->first_name = $item["firstname"];
$user->last_name = $item["lastname"];
$user->username = $item["username"];
$user->email = $item["email"];
$user->employee_num = e($item["employee_number"]);
$user->phone = $item["telephone"];
$user->jobtitle = $item["jobtitle"];
$user->country = $item["country"];
$user->department_id = $department->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
some users may have been dependent upon the previous behavior, and this
could cause additional access to be available to users they don't want
to allow to log in.
$useraccountcontrol = $results[$i]['useraccountcontrol'][0];
if(
// based on MS docs at: https://support.microsoft.com/en-us/help/305144/how-to-use-useraccountcontrol-to-manipulate-user-account-properties
($useraccountcontrol & 0x200) && // is a NORMAL_ACCOUNT
!($useraccountcontrol & 0x02) && // *and* _not_ ACCOUNTDISABLE
!($useraccountcontrol & 0x10) // *and* _not_ LOCKOUT
) {
$user->activated = 1;
} else {
$user->activated = 0;
} */
$enabled_accounts = [
'512', // 0x200 NORMAL_ACCOUNT
'544', // 0x220 NORMAL_ACCOUNT, PASSWD_NOTREQD
'66048', // 0x10200 NORMAL_ACCOUNT, DONT_EXPIRE_PASSWORD
'66080', // 0x10220 NORMAL_ACCOUNT, PASSWD_NOTREQD, DONT_EXPIRE_PASSWORD
'262656', // 0x40200 NORMAL_ACCOUNT, SMARTCARD_REQUIRED
'262688', // 0x40220 NORMAL_ACCOUNT, PASSWD_NOTREQD, SMARTCARD_REQUIRED
'328192', // 0x50200 NORMAL_ACCOUNT, SMARTCARD_REQUIRED, DONT_EXPIRE_PASSWORD
'328224', // 0x50220 NORMAL_ACCOUNT, PASSWD_NOT_REQD, SMARTCARD_REQUIRED, DONT_EXPIRE_PASSWORD
'4260352',// 0x410200 NORMAL_ACCOUNT, DONT_EXPIRE_PASSWORD, DONT_REQ_PREAUTH
'1049088',// 0x100200 NORMAL_ACCOUNT, NOT_DELEGATED
];
$user->activated = ( in_array($results[$i]['useraccountcontrol'][0], $enabled_accounts) ) ? 1 : 0;
}
// If we're not using AD, and there isn't an activated flag set, activate all users
elseif (empty($ldap_result_active_flag)) {
$user->activated = 1;
}
if ($item['ldap_location_override'] == true) {
$user->location_id = $item['location_id'];
} elseif ((isset($location)) && (!empty($location))) {
if ((is_array($location)) && (array_key_exists('id', $location))) {
$user->location_id = $location['id'];
} elseif (is_object($location)) {
$user->location_id = $location->id;
}
}
$user->ldap_import = 1;
$errors = '';
if ($user->save()) {
$item["note"] = $item["createorupdate"];
$item["status"]='success';
} else {
foreach ($user->getErrors()->getMessages() as $key => $err) {
$errors .= $err[0];
}
$item["note"] = $errors;
$item["status"]='error';
}
array_push($summary, $item);
}
}
}
/**
* Check if LDAP intergration is enabled.
*
* @author Wes Hulette <jwhulette@gmail.com>
*
* @since 5.0.0
*/
private function checkIfLdapIsEnabled(): void
{
if (false === $this->settings['ldap_enabled']) {
$msg = 'LDAP intergration is not enabled. Exiting sync process.';
$this->info($msg);
Log::info($msg);
exit(0);
}
}
/**
* Check to make sure we can access the server.
*
* @author Wes Hulette <jwhulette@gmail.com>
*
* @since 5.0.0
*/
private function checkLdapConnetion(): void
{
try {
$this->ldap->testLdapAdUserConnection();
$this->ldap->testLdapAdBindConnection();
} catch (Exception $e) {
$this->outputError($e);
exit(0);
}
}
/**
* Output the json summary to the screen if enabled.
*
* @param Exception $error
*/
private function outputError($error): void
{
if ($this->option('json_summary')) {
$json_summary = [
'error' => true,
'error_message' => $error->getMessage(),
'summary' => [],
];
if ($this->option('summary')) {
for ($x = 0; $x < count($summary); $x++) {
if ($summary[$x]['status']=='error') {
$this->error('ERROR: '.$summary[$x]['firstname'].' '.$summary[$x]['lastname'].' (username: '.$summary[$x]['username'].') was not imported: '.$summary[$x]['note']);
} else {
$this->info('User '.$summary[$x]['firstname'].' '.$summary[$x]['lastname'].' (username: '.$summary[$x]['username'].') was '.strtoupper($summary[$x]['createorupdate']).'.');
}
}
} else if ($this->option('json_summary')) {
$json_summary = [ "error" => false, "error_message" => "", "summary" => $summary ]; // hardcoding the error to false and the error_message to blank seems a bit weird
$this->info(json_encode($json_summary));
} else {
return $summary;
}
$this->error($error->getMessage());
LOG::error($error);
}
}

View File

@@ -0,0 +1,399 @@
<?php
declare(strict_types=1);
namespace App\Console\Commands;
use Log;
use Exception;
use App\Models\User;
use App\Services\LdapAd;
use App\Models\Location;
use Illuminate\Console\Command;
use Adldap\Models\User as AdldapUser;
/**
* LDAP / AD sync command.
*
* @author Wes Hulette <jwhulette@gmail.com>
*
* @since 5.0.0
*/
class LdapSyncNg extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'snipeit:ldap-sync-ng
{--location= : A location name }
{--location_id= : A location id}
{--base_dn= : A diffrent base DN to use }
{--summary : Print summary }
{--json_summary : Print summary in json format }
{--dryrun : Run the sync process but don\'t update the database}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Command line LDAP/AD sync';
/**
* An LdapAd instance.
*
* @var \App\Models\LdapAd
*/
private $ldap;
/**
* LDAP settings collection.
*
* @var \Illuminate\Support\Collection
*/
private $settings = null;
/**
* A default location collection.
*
* @var \Illuminate\Support\Collection
*/
private $defaultLocation = null;
/**
* Mapped locations collection.
*
* @var \Illuminate\Support\Collection
*/
private $mappedLocations = null;
/**
* The summary collection.
*
* @var \Illuminate\Support\Collection
*/
private $summary;
/**
* Is dry-run?
*
* @var bool
*/
private $dryrun = false;
/**
* Show users to be imported.
*
* @var array
*/
private $userlist = [];
/**
* Create a new command instance.
*/
public function __construct(LdapAd $ldap)
{
parent::__construct();
$this->ldap = $ldap;
$this->settings = $this->ldap->ldapSettings;
$this->summary = collect();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$dispatcher = \Adldap\Adldap::getEventDispatcher();
// Listen for all model events.
$dispatcher->listen('Adldap\Models\Events\*', function ($eventName, array $data) {
echo $eventName; // Returns 'Adldap\Models\Events\Updating'
var_dump($data); // Returns [0] => (object) Adldap\Models\Events\Updating;
\Log::debug("Event: ".$eventName." data - ".print_r($data, true));
});
$dispatcher->listen('Adldap\Auth\Events\*', function ($eventName, array $data) {
echo $eventName; // Returns 'Adldap\Models\Events\Updating'
var_dump($data); // Returns [0] => (object) Adldap\Models\Events\Updating;
\Log::debug("Event: ".$eventName." data - ".print_r($data, true));
});
ini_set('max_execution_time', env('LDAP_TIME_LIM', "600")); //600 seconds = 10 minutes
ini_set('memory_limit', '500M');
$old_error_reporting = error_reporting(); // grab old error_reporting .ini setting, for later re-enablement
error_reporting($old_error_reporting & ~E_DEPRECATED); // disable deprecation warnings, for LDAP in PHP 7.4 (and greater)
if ($this->option('dryrun')) {
$this->dryrun = true;
}
$this->checkIfLdapIsEnabled();
$this->checkLdapConnection();
$this->setBaseDn();
$this->getUserDefaultLocation();
/*
* Use the default location if set, this is needed for the LDAP users sync page
*/
if (!$this->option('base_dn') && null == $this->defaultLocation) {
$this->getMappedLocations();
}
$this->processLdapUsers();
// Print table of users
if ($this->dryrun) {
$this->info('The following users will be synced!');
$headers = ['First Name', 'Last Name', 'Username', 'Email', 'Employee #', 'Location Id', 'Status'];
$this->table($headers, $this->summary->toArray());
}
error_reporting($old_error_reporting); // re-enable deprecation warnings.
return $this->getSummary();
}
/**
* Generate the LDAP sync summary.
*
* @author Wes Hulette <jwhulette@gmail.com>
*
* @since 5.0.0
*
* @return string
*/
private function getSummary(): string
{
if ($this->option('summary') && null === $this->dryrun) {
$this->summary->each(function ($item) {
$this->info('USER: '.$item['note']);
if ('ERROR' === $item['status']) {
$this->error('ERROR: '.$item['note']);
}
});
} elseif ($this->option('json_summary')) {
$json_summary = [
'error' => false,
'error_message' => '',
'summary' => $this->summary->toArray(),
];
$this->info(json_encode($json_summary));
}
return '';
}
/**
* Create a new user or update an existing user.
*
* @author Wes Hulette <jwhulette@gmail.com>
*
* @since 5.0.0
*
* @param \Adldap\Models\User $snipeUser
*/
private function updateCreateUser(AdldapUser $snipeUser): void
{
$user = $this->ldap->processUser($snipeUser, $this->defaultLocation, $this->mappedLocations);
$summary = [
'firstname' => $user->first_name,
'lastname' => $user->last_name,
'username' => $user->username,
'employee_number' => $user->employee_num,
'email' => $user->email,
'location_id' => $user->location_id,
];
// Only update the database if is not a dry run
if (!$this->dryrun) {
if ($user->isDirty()) { //if nothing on the user changed, don't bother trying to save anything nor put anything in the summary
if ($user->save()) {
$summary['note'] = ($user->wasRecentlyCreated ? 'CREATED' : 'UPDATED');
$summary['status'] = 'SUCCESS';
} else {
$errors = '';
foreach ($user->getErrors()->getMessages() as $error) {
$errors .= implode(", ",$error);
}
$summary['note'] = $snipeUser->getDN().' was not imported. REASON: '.$errors;
$summary['status'] = 'ERROR';
}
} else {
$summary = null;
}
}
// $summary['note'] = ($user->getOriginal('username') ? 'UPDATED' : 'CREATED'); // this seems, kinda, like, superfluous, relative to the $summary['note'] thing above, yeah?
if($summary) { //if the $user wasn't dirty, $summary was set to null so that we will skip the following push()
$this->summary->push($summary);
}
}
/**
* Process the users to update / create.
*
* @author Wes Hulette <jwhulette@gmail.com>
*
* @since 5.0.0
*
*/
private function processLdapUsers(): void
{
try {
\Log::debug("CAL:LING GET LDAP SUSERS");
$ldapUsers = $this->ldap->getLdapUsers();
\Log::debug("END CALLING GET LDAP USERS");
} catch (Exception $e) {
$this->outputError($e);
exit($e->getMessage());
}
if (0 == $ldapUsers->count()) {
$msg = 'ERROR: No users found!';
Log::error($msg);
if ($this->dryrun) {
$this->error($msg);
}
exit($msg);
}
// Process each individual users
foreach ($ldapUsers->getResults() as $user) { // AdLdap2's paginate() method is weird, it gets *everything* and ->getResults() returns *everything*
$this->updateCreateUser($user);
}
}
/**
* Get the mapped locations if a base_dn is provided.
*
* @author Wes Hulette <jwhulette@gmail.com>
*
* @since 5.0.0
*/
private function getMappedLocations()
{
$ldapOuLocation = Location::where('ldap_ou', '!=', '')->select(['id', 'ldap_ou'])->get();
$locations = $ldapOuLocation->sortBy(function ($ou, $key) {
return strlen($ou->ldap_ou);
});
if ($locations->count() > 0) {
$msg = 'Some locations have special OUs set. Locations will be automatically set for users in those OUs.';
LOG::debug($msg);
if ($this->dryrun) {
$this->info($msg);
}
$this->mappedLocations = $locations->pluck('ldap_ou', 'id'); // TODO: this seems ok-ish, but the key-> value is going location_id -> OU name, and the primary action here is the opposite of that - going from OU's to location ID's.
}
}
/**
* Set the base dn if supplied.
*
* @author Wes Hulette <jwhulette@gmail.com>
*
* @since 5.0.0
*/
private function setBaseDn(): void
{
if ($this->option('base_dn')) {
$this->ldap->baseDn = $this->option('base_dn');
$msg = sprintf('Importing users from specified base DN: "%s"', $this->ldap->baseDn);
LOG::debug($msg);
if ($this->dryrun) {
$this->info($msg);
}
}
}
/**
* Get a default location id for imported users.
*
* @author Wes Hulette <jwhulette@gmail.com>
*
* @since 5.0.0
*/
private function getUserDefaultLocation(): void
{
$location = $this->option('location_id') ?? $this->option('location');
if ($location) {
$userLocation = Location::where('name', '=', $location)
->orWhere('id', '=', intval($location))
->select(['name', 'id'])
->first();
if ($userLocation) {
$msg = 'Importing users with default location: '.$userLocation->name.' ('.$userLocation->id.')';
LOG::debug($msg);
if ($this->dryrun) {
$this->info($msg);
}
$this->defaultLocation = collect([
$userLocation->id => $userLocation->name,
]);
} else {
$msg = 'The supplied location is invalid!';
LOG::error($msg);
if ($this->dryrun) {
$this->error($msg);
}
exit(0);
}
}
}
/**
* Check if LDAP intergration is enabled.
*
* @author Wes Hulette <jwhulette@gmail.com>
*
* @since 5.0.0
*/
private function checkIfLdapIsEnabled(): void
{
if (false === $this->settings['ldap_enabled']) {
$msg = 'LDAP intergration is not enabled. Exiting sync process.';
$this->info($msg);
Log::info($msg);
exit(0);
}
}
/**
* Check to make sure we can access the server.
*
* @author Wes Hulette <jwhulette@gmail.com>
*
* @since 5.0.0
*/
private function checkLdapConnection(): void
{
try {
$this->ldap->testLdapAdUserConnection();
$this->ldap->testLdapAdBindConnection();
} catch (Exception $e) {
$this->outputError($e);
exit(0);
}
}
/**
* Output the json summary to the screen if enabled.
*
* @param Exception $error
*/
private function outputError($error): void
{
if ($this->option('json_summary')) {
$json_summary = [
'error' => true,
'error_message' => $error->getMessage(),
'summary' => [],
];
$this->info(json_encode($json_summary));
}
$this->error($error->getMessage());
LOG::error($error);
}
}

View File

@@ -54,7 +54,10 @@ class MergeUsersByUsername extends Command
foreach ($bad_user->assets as $asset) {
$this->info( 'Updating asset '.$asset->asset_tag.' '.$asset->id.' to user '.$user->id);
$asset->assigned_to = $user->id;
$asset->save();
if (!$asset->save()) {
$this->error( 'Could not update assigned_to field on asset '.$asset->asset_tag.' '.$asset->id.' to user '.$user->id);
$this->error( 'Error saving: '.$asset->getErrors());
}
}
// Walk the list of licenses

View File

@@ -47,33 +47,33 @@ class MoveUploadsToNewDisk extends Command
}
$delete_local = $this->argument('delete_local');
$public_uploads['accessories'] = glob('storage/app/public/accessories'."/*.*");
$public_uploads['assets'] = glob('storage/app/public/assets'."/*.*");
$public_uploads['avatars'] = glob('storage/app/public/avatars'."/*.*");
$public_uploads['categories'] = glob('storage/app/public/categories'."/*.*");
$public_uploads['companies'] = glob('storage/app/public/companies'."/*.*");
$public_uploads['components'] = glob('storage/app/public/components'."/*.*");
$public_uploads['consumables'] = glob('storage/app/public/consumables'."/*.*");
$public_uploads['departments'] = glob('storage/app/public/departments'."/*.*");
$public_uploads['locations'] = glob('storage/app/public/locations'."/*.*");
$public_uploads['manufacturers'] = glob('storage/app/public/manufacturers'."/*.*");
$public_uploads['suppliers'] = glob('storage/app/public/suppliers'."/*.*");
$public_uploads['assetmodels'] = glob('storage/app/public/models'."/*.*");
$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'."/*.*");
// iterate files
foreach($public_uploads as $public_type => $public_upload)
{
$type_count = 0;
$this->info("\nThere 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($public_type.'/'.$filename, file_get_contents($public_upload[$i]));
$new_url = Storage::disk('public')->url($public_type.'/'.$filename, $filename);
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);
@@ -84,15 +84,16 @@ class MoveUploadsToNewDisk extends Command
}
$logos = glob('public/uploads'."/setting*.*");
$this->info("\nThere are ".count($logos).' files that might be logos.');
$logos = glob("public/uploads/setting*.*");
$this->info("- There are ".count($logos).' files that might be logos.');
$type_count = 0;
for ($l = 0; $l < count($logos); $l++) {
foreach ($logos as $logo) {
$this->info($logo);
$type_count++;
$filename = basename($logos[$l]);
$new_url = Storage::disk('public')->url($logos[$l], file_get_contents($public_upload[$i]));
$this->info($type_count.'. LOGO: '.$filename.' was copied to '.$new_url);
$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);
}
$private_uploads['assets'] = glob('storage/private_uploads/assets'."/*.*");
@@ -107,8 +108,7 @@ class MoveUploadsToNewDisk extends Command
foreach($private_uploads as $private_type => $private_upload)
{
$this->info("\nThere are ".count($private_upload).' PRIVATE '.$private_type.' files.');
// $this->info(print_r($private_upload, true));
$this->info("- There are ".count($private_upload).' PRIVATE '.$private_type.' files.');
$type_count = 0;
for ($x = 0; $x < count($private_upload); $x++) {
@@ -116,7 +116,7 @@ class MoveUploadsToNewDisk extends Command
$filename = basename($private_upload[$x]);
try {
Storage::disk('private_uploads')->put($private_type.'/'.$filename, file_get_contents($public_upload[$i]));
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);

View File

@@ -5,8 +5,8 @@ use Illuminate\Console\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;
ini_set('max_execution_time', 600); //600 seconds = 10 minutes
ini_set('memory_limit', '500M');
ini_set('max_execution_time', env('IMPORT_TIME_LIMIT', 600)); //600 seconds = 10 minutes
ini_set('memory_limit', env('IMPORT_MEMORY_LIMIT', '500M'));
/**
* Class ObjectImportCommand
@@ -55,8 +55,10 @@ class ObjectImportCommand extends Command
->setShouldNotify($this->option('send-welcome'))
->setUsernameFormat($this->option('username_format'));
$logFile = $this->option('logfile');
\Log::useFiles($logFile);
// This $logFile/useFiles() bit is currently broken, so commenting it out for now
// $logFile = $this->option('logfile');
// \Log::useFiles($logFile);
$this->comment('======= Importing Items from '.$filename.' =========');
$importer->import();

View File

@@ -0,0 +1,44 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
class PurgeLoginAttempts extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'snipeit:purge-logins';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Clears the login_attempts table';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
if ($this->confirm("\n****************************************************\nTHIS WILL DELETE ALL OF THE YOUR LOGIN ATTEMPT RECORDS. \nThere is NO undo! \n****************************************************\n\nDo you wish to continue? No backsies! [y|N]")) {
\DB::statement('delete from login_attempts');
}
}
}

View File

@@ -64,11 +64,8 @@ class ResetDemoSettings extends Command
$settings->thumbnail_max_h = '30';
$settings->locale = 'en';
$settings->version_footer = 'on';
$settings->support_footer = 'on';
$settings->support_footer = null;
$settings->saml_enabled = '0';
$settings->saml_sp_entitiyid = '0';
$settings->saml_sp_acs_url = null;
$settings->saml_sp_sls_url = null;
$settings->saml_sp_x509cert = null;
$settings->saml_idp_metadata = null;
$settings->saml_attr_mapping_username = null;
@@ -84,7 +81,9 @@ class ResetDemoSettings extends Command
$user->save();
}
\Storage::disk('local_public')->put('snipe-logo.png', file_get_contents(public_path('img/demo/snipe-logo.png')));
\Storage::disk('local_public')->put('snipe-logo-lg.png', file_get_contents(public_path('img/demo/snipe-logo-lg.png')));
}
}

View File

@@ -0,0 +1,292 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use ZipArchive;
class RestoreFromBackup extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'snipeit:restore
{--force : Skip the danger prompt; assuming you hit "y"}
{filename : The zip file to be migrated}
{--no-progress : Don\'t show a progress bar}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Restore from a previously created backup';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$dir = getcwd();
print "Current working directory is: $dir\n";
//
$filename = $this->argument('filename');
if (!$filename) {
return $this->error("Missing required filename");
}
if (!$this->option('force') && !$this->confirm('Are you sure you wish to restore from the given backup file? This can lead to MASSIVE DATA LOSS!')) {
return $this->error("Data loss not confirmed");
}
if (config('database.default') != 'mysql') {
return $this->error("DB_CONNECTION must be MySQL in order to perform a restore. Detected: ".config('database.default'));
}
$za = new ZipArchive();
$errcode = $za->open($filename, ZipArchive::RDONLY);
if ($errcode !== true) {
$errors = [
ZipArchive::ER_EXISTS => "File already exists.",
ZipArchive::ER_INCONS => "Zip archive inconsistent.",
ZipArchive::ER_INVAL => "Invalid argument.",
ZipArchive::ER_MEMORY => "Malloc failure.",
ZipArchive::ER_NOENT => "No such file.",
ZipArchive::ER_NOZIP => "Not a zip archive.",
ZipArchive::ER_OPEN => "Can't open file.",
ZipArchive::ER_READ => "Read error.",
ZipArchive::ER_SEEK => "Seek error."
];
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',
'storage/private_uploads/imports',
'storage/private_uploads/assetmodels',
'storage/private_uploads/users',
'storage/private_uploads/licenses',
'storage/private_uploads/signatures'
];
$private_files = [
'storage/oauth-private.key',
'storage/oauth-public.key'
];
$public_dirs = [
'public/uploads/companies',
'public/uploads/components',
'public/uploads/categories',
'public/uploads/manufacturers',
//'public/uploads/barcodes', // we don't want this, let the barcodes be regenerated
'public/uploads/consumables',
'public/uploads/departments',
'public/uploads/avatars',
'public/uploads/suppliers',
'public/uploads/assets', // these are asset _pictures_, not asset files
'public/uploads/locations',
'public/uploads/accessories',
'public/uploads/models',
'public/uploads/categories',
'public/uploads/avatars',
'public/uploads/manufacturers'
];
$public_files = [
'public/uploads/logo.*',
'public/uploads/setting-email_logo*',
'public/uploads/setting-label_logo*',
'public/uploads/setting-logo*',
'public/uploads/favicon.*',
'public/uploads/favicon-uploaded.*'
];
$all_files = $private_dirs + $public_dirs;
$sqlfiles = [];
$sqlfile_indices = [];
$interesting_files = [];
$boring_files = [];
for ($i=0; $i<$za->numFiles;$i++) {
$stat_results = $za->statIndex($i);
// echo "index: $i\n";
// print_r($stat_results);
$raw_path = $stat_results['name'];
if(strpos($raw_path,'\\')!==false) { //found a backslash, swap it to forward-slash
$raw_path = strtr($raw_path,'\\','/');
//print "Translating file: ".$stat_results['name']." to: ".$raw_path."\n";
}
// skip macOS resource fork files (?!?!?!)
if(strpos($raw_path,"__MACOSX")!==false && strpos($raw_path,"._") !== false) {
//print "SKIPPING macOS Resource fork file: $raw_path\n";
$boring_files[] = $raw_path;
continue;
}
if(@pathinfo($raw_path)['extension'] == "sql") {
print "Found a sql file!\n";
$sqlfiles[] = $raw_path;
$sqlfile_indices[] = $i;
continue;
}
foreach(array_merge($private_dirs,$public_dirs) as $dir) {
$last_pos = strrpos($raw_path,$dir.'/');
if($last_pos !== false ) {
//print("INTERESTING - last_pos is $last_pos when searching $raw_path for $dir - last_pos+strlen(\$dir) is: ".($last_pos+strlen($dir))." and strlen(\$rawpath) is: ".strlen($raw_path)."\n");
//print("We would copy $raw_path to $dir.\n"); //FIXME append to a path?
$interesting_files[$raw_path] = ['dest' =>$dir, 'index' => $i];
continue 2;
if($last_pos + strlen($dir) +1 == strlen($raw_path)) {
// we don't care about that; we just want files with the appropriate prefix
//print("FOUND THE EXACT DIRECTORY: $dir AT: $raw_path!!!\n");
}
}
}
$good_extensions = ["png","gif","jpg","svg","jpeg","doc","docx","pdf","txt",
"zip","rar","xls","xlsx","lic","xml","rtf", "webp","key","ico"];
foreach(array_merge($private_files, $public_files) as $file) {
$has_wildcard = (strpos($file,"*") !== false);
if($has_wildcard) {
$file = substr($file,0,-1); //trim last character (which should be the wildcard)
}
$last_pos = strrpos($raw_path,$file); // no trailing slash!
if($last_pos !== false ) {
$extension = strtolower(pathinfo($raw_path, PATHINFO_EXTENSION));
if(!in_array($extension, $good_extensions)) {
$this->warn("Potentially unsafe file ".$raw_path." is being skipped");
$boring_files[] = $raw_path;
continue 2;
}
//print("INTERESTING - last_pos is $last_pos when searching $raw_path for $file - last_pos+strlen(\$file) is: ".($last_pos+strlen($file))." and strlen(\$rawpath) is: ".strlen($raw_path)."\n");
//no wildcards found in $file, process 'normally'
if($last_pos + strlen($file) == strlen($raw_path) || $has_wildcard) { //again, no trailing slash. or this is a wildcard and we just take it.
// print("FOUND THE EXACT FILE: $file AT: $raw_path!!!\n"); //we *do* care about this, though.
$interesting_files[$raw_path] = ['dest' => dirname($file),'index' => $i];
continue 2;
}
}
}
$boring_files[] = $raw_path; //if we've gotten to here and haven't continue'ed our way into the next iteration, we don't want this file
} // end of pre-processing the ZIP file for-loop
// print_r($interesting_files);exit(-1);
if( count($sqlfiles) != 1) {
return $this->error("There should be exactly *one* sql backup file found, found: ".( count($sqlfiles) == 0 ? "None" : implode(", ",$sqlfiles)));
}
if( strpos($sqlfiles[0], "db-dumps") === false ) {
//return $this->error("SQL backup file is missing 'db-dumps' component of full pathname: ".$sqlfiles[0]);
//older Snipe-IT installs don't have the db-dumps subdirectory component
}
//how to invoke the restore?
$pipes = [];
$env_vars = getenv();
$env_vars['MYSQL_PWD'] = config("database.connections.mysql.password");
$proc_results = proc_open("mysql -h ".escapeshellarg(config('database.connections.mysql.host'))." -u ".escapeshellarg(config('database.connections.mysql.username'))." ".escapeshellarg(config('database.connections.mysql.database')), // yanked -p since we pass via ENV
[0 => ['pipe','r'],1 => ['pipe','w'],2 => ['pipe','w']],
$pipes,
null,
$env_vars); // this is not super-duper awesome-secure, but definitely more secure than showing it on the CLI, or dropping temporary files with passwords in them.
if($proc_results === false) {
return $this->error("Unable to invoke mysql via CLI");
}
// $this->info("Stdout says? ".fgets($pipes[1])); //FIXME: I think we might need to set non-blocking mode to use this properly?
// $this->info("Stderr says? ".fgets($pipes[2])); //FIXME: ditto, same.
// should we read stdout?
// fwrite($pipes[0],config("database.connections.mysql.password")."\n"); //this doesn't work :(
//$sql_contents = fopen($sqlfiles[0], "r"); //NOPE! This isn't a real file yet, silly-billy!
$sql_stat = $za->statIndex($sqlfile_indices[0]);
//$this->info("SQL Stat is: ".print_r($sql_stat,true));
$sql_contents = $za->getStream($sql_stat['name']);
if ($sql_contents === false) {
$stdout = fgets($pipes[1]);
$this->info($stdout);
$stderr = fgets($pipes[2]);
$this->info($stderr);
return false;
}
while(($buffer = fgets($sql_contents)) !== false ) {
//$this->info("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;
}
}
fclose($pipes[0]);
fclose($sql_contents);
fclose($pipes[1]);
fclose($pipes[2]);
//wait, have to do fclose() on all pipes first?
$close_results = proc_close($proc_results);
if($close_results != 0) {
return $this->error("There may have been a problem with the database import: Error number ".$close_results);
}
//and now copy the files over too (right?)
//FIXME - we don't prune the filesystem space yet!!!!
if($this->option('no-progress')) {
$bar = null;
} else {
$bar = $this->output->createProgressBar(count($interesting_files));
}
foreach($interesting_files AS $pretty_file_name => $file_details) {
$ugly_file_name = $za->statIndex($file_details['index'])['name'];
$fp = $za->getStream($ugly_file_name);
//$this->info("Weird problem, here are file details? ".print_r($file_details,true));
$migrated_file = fopen($file_details['dest']."/".basename($pretty_file_name),"w");
while(($buffer = fgets($fp))!== false) {
fwrite($migrated_file,$buffer);
}
fclose($migrated_file);
fclose($fp);
//$this->info("Wrote $ugly_file_name to $pretty_file_name");
if($bar) {
$bar->advance();
}
}
if($bar) {
$bar->finish();
$this->line("");
} else {
$this->info(count($interesting_files)." files were succesfully transferred");
}
foreach($boring_files as $boring_file) {
$this->warn($boring_file." was skipped.");
}
}
}

View File

@@ -8,6 +8,7 @@ use App\Notifications\ExpectedCheckinAdminNotification;
use App\Notifications\ExpectedCheckinNotification;
use Carbon\Carbon;
use Illuminate\Console\Command;
use App\Models\Recipients\AlertRecipient;
class SendExpectedCheckinAlerts extends Command
{
@@ -58,7 +59,7 @@ class SendExpectedCheckinAlerts extends Command
$recipients = collect(explode(',', $settings->alert_email))->map(function ($item, $key) {
return new AlertRecipient($item);
});
Notification::send($recipients, new ExpectedCheckinAdminNotification($assets));
\Notification::send($recipients, new ExpectedCheckinAdminNotification($assets));
}
}
}

View File

@@ -55,14 +55,14 @@ class SendExpirationAlerts extends Command
$assets = Asset::getExpiringWarrantee($threshold);
if ($assets->count() > 0) {
$this->info(trans_choice('mail.assets_warrantee_alert', $assets->count(), ['count' => $assets->count(), 'threshold' => $threshold]));
Notification::send($recipients, new ExpiringAssetsNotification($assets, $threshold));
\Notification::send($recipients, new ExpiringAssetsNotification($assets, $threshold));
}
// Expiring licenses
$licenses = License::getExpiringLicenses($threshold);
if ($licenses->count() > 0) {
$this->info(trans_choice('mail.license_expiring_alert', $licenses->count(), ['count' => $licenses->count(), 'threshold' => $threshold]));
Notification::send($recipients, new ExpiringLicenseNotification($licenses, $threshold));
\Notification::send($recipients, new ExpiringLicenseNotification($licenses, $threshold));
}
} else {
if ($settings->alert_email == '') {

View File

@@ -52,7 +52,7 @@ class SendInventoryAlerts extends Command
return new AlertRecipient($item);
});
Notification::send($recipients, new InventoryAlert($items, $settings->alert_threshold));
\Notification::send($recipients, new InventoryAlert($items, $settings->alert_threshold));
}
} else {
if ($settings->alert_email == '') {

View File

@@ -25,6 +25,7 @@ class Handler extends ExceptionHandler
\Illuminate\Session\TokenMismatchException::class,
\Illuminate\Validation\ValidationException::class,
\Intervention\Image\Exception\NotSupportedException::class,
\League\OAuth2\Server\Exception\OAuthServerException::class,
];
/**
@@ -38,7 +39,7 @@ class Handler extends ExceptionHandler
public function report(Exception $exception)
{
if ($this->shouldReport($exception)) {
Log::error($exception);
\Log::error($exception);
return parent::report($exception);
}
}
@@ -105,7 +106,7 @@ class Handler extends ExceptionHandler
protected function unauthenticated($request, AuthenticationException $exception)
{
if ($request->expectsJson()) {
return response()->json(['error' => 'Unauthorized.'], 401);
return response()->json(['error' => 'Unauthorized or unauthenticated.'], 401);
}
return redirect()->guest('login');

View File

@@ -55,27 +55,313 @@ class Helper
/**
* Static colors for pie charts.
* This is inelegant, and could be refactored later.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v3.3]
* @return Array
*/
public static function chartColors()
public static function defaultChartColors($index = 0)
{
$colors = [
'#f56954',
'#00a65a',
'#f39c12',
'#00c0ef',
'#3c8dbc',
'#d2d6de',
'#3c8dbc',
'#3c8dbc',
'#3c8dbc',
"#008941",
"#FF4A46",
"#006FA6",
"#A30059",
"#1CE6FF",
"#FFDBE5",
"#7A4900",
"#0000A6",
"#63FFAC",
"#B79762",
"#004D43",
"#8FB0FF",
"#997D87",
"#5A0007",
"#809693",
"#FEFFE6",
"#1B4400",
"#4FC601",
"#3B5DFF",
"#4A3B53",
"#FF2F80",
"#61615A",
"#BA0900",
"#6B7900",
"#00C2A0",
"#FFAA92",
"#FF90C9",
"#B903AA",
"#D16100",
"#DDEFFF",
"#000035",
"#7B4F4B",
"#A1C299",
"#300018",
"#0AA6D8",
"#013349",
"#00846F",
"#372101",
"#FFB500",
"#C2FFED",
"#A079BF",
"#CC0744",
"#C0B9B2",
"#C2FF99",
"#001E09",
"#00489C",
"#6F0062",
"#0CBD66",
"#EEC3FF",
"#456D75",
"#B77B68",
"#7A87A1",
"#788D66",
"#885578",
"#FAD09F",
"#FF8A9A",
"#D157A0",
"#BEC459",
"#456648",
"#0086ED",
"#886F4C",
"#34362D",
"#B4A8BD",
"#00A6AA",
"#452C2C",
"#636375",
"#A3C8C9",
"#FF913F",
"#938A81",
"#575329",
"#00FECF",
"#B05B6F",
"#8CD0FF",
"#3B9700",
"#04F757",
"#C8A1A1",
"#1E6E00",
"#7900D7",
"#A77500",
"#6367A9",
"#A05837",
"#6B002C",
"#772600",
"#D790FF",
"#9B9700",
"#549E79",
"#FFF69F",
"#201625",
"#72418F",
"#BC23FF",
"#99ADC0",
"#3A2465",
"#922329",
"#5B4534",
"#FDE8DC",
"#404E55",
"#0089A3",
"#CB7E98",
"#A4E804",
"#324E72",
"#6A3A4C",
"#83AB58",
"#001C1E",
"#D1F7CE",
"#004B28",
"#C8D0F6",
"#A3A489",
"#806C66",
"#222800",
"#BF5650",
"#E83000",
"#66796D",
"#DA007C",
"#FF1A59",
"#8ADBB4",
"#1E0200",
"#5B4E51",
"#C895C5",
"#320033",
"#FF6832",
"#66E1D3",
"#CFCDAC",
"#D0AC94",
"#7ED379",
"#012C58",
"#7A7BFF",
"#D68E01",
"#353339",
"#78AFA1",
"#FEB2C6",
"#75797C",
"#837393",
"#943A4D",
"#B5F4FF",
"#D2DCD5",
"#9556BD",
"#6A714A",
"#001325",
"#02525F",
"#0AA3F7",
"#E98176",
"#DBD5DD",
"#5EBCD1",
"#3D4F44",
"#7E6405",
"#02684E",
"#962B75",
"#8D8546",
"#9695C5",
"#E773CE",
"#D86A78",
"#3E89BE",
"#CA834E",
"#518A87",
"#5B113C",
"#55813B",
"#E704C4",
"#00005F",
"#A97399",
"#4B8160",
"#59738A",
"#FF5DA7",
"#F7C9BF",
"#643127",
"#513A01",
"#6B94AA",
"#51A058",
"#A45B02",
"#1D1702",
"#E20027",
"#E7AB63",
"#4C6001",
"#9C6966",
"#64547B",
"#97979E",
"#006A66",
"#391406",
"#F4D749",
"#0045D2",
"#006C31",
"#DDB6D0",
"#7C6571",
"#9FB2A4",
"#00D891",
"#15A08A",
"#BC65E9",
"#FFFFFE",
"#C6DC99",
"#203B3C",
"#671190",
"#6B3A64",
"#F5E1FF",
"#FFA0F2",
"#CCAA35",
"#374527",
"#8BB400",
"#797868",
"#C6005A",
"#3B000A",
"#C86240",
"#29607C",
"#402334",
"#7D5A44",
"#CCB87C",
"#B88183",
"#AA5199",
"#B5D6C3",
"#A38469",
"#9F94F0",
"#A74571",
"#B894A6",
"#71BB8C",
"#00B433",
"#789EC9",
"#6D80BA",
"#953F00",
"#5EFF03",
"#E4FFFC",
"#1BE177",
"#BCB1E5",
"#76912F",
"#003109",
"#0060CD",
"#D20096",
"#895563",
"#29201D",
"#5B3213",
"#A76F42",
"#89412E",
"#1A3A2A",
"#494B5A",
"#A88C85",
"#F4ABAA",
"#A3F3AB",
"#00C6C8",
"#EA8B66",
"#958A9F",
"#BDC9D2",
"#9FA064",
"#BE4700",
"#658188",
"#83A485",
"#453C23",
"#47675D",
"#3A3F00",
"#061203",
"#DFFB71",
"#868E7E",
"#98D058",
"#6C8F7D",
"#D7BFC2",
"#3C3E6E",
"#D83D66",
"#2F5D9B",
"#6C5E46",
"#D25B88",
"#5B656C",
"#00B57F",
"#545C46",
"#866097",
"#365D25",
"#252F99",
"#00CCFF",
"#674E60",
"#FC009C",
"#92896B",
];
return $colors;
return $colors[$index];
}
/**
* Increases or decreases the brightness of a color by a percentage of the current brightness.
*
* @param string $hexCode Supported formats: `#FFF`, `#FFFFFF`, `FFF`, `FFFFFF`
* @param float $adjustPercent A number between -1 and 1. E.g. 0.3 = 30% lighter; -0.4 = 40% darker.
*
* @return string
*/
public static function adjustBrightness($hexCode, $adjustPercent) {
$hexCode = ltrim($hexCode, '#');
if (strlen($hexCode) == 3) {
$hexCode = $hexCode[0] . $hexCode[0] . $hexCode[1] . $hexCode[1] . $hexCode[2] . $hexCode[2];
}
$hexCode = array_map('hexdec', str_split($hexCode, 2));
foreach ($hexCode as & $color) {
$adjustableLimit = $adjustPercent < 0 ? $color : 255 - $color;
$adjustAmount = ceil($adjustableLimit * $adjustPercent);
$color = str_pad(dechex($color + $adjustAmount), 2, '0', STR_PAD_LEFT);
}
return '#' . implode($hexCode);
}
@@ -142,6 +428,26 @@ class Helper
return $statuslabel_list;
}
/**
* Get the list of deployable status labels in an array to make a dropdown menu
*
* @todo This should probably be a selectlist, same as the other endpoints
* and we should probably add to the API controllers to make sure that
* the status_id submitted is actually really deployable.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v5.1.0]
* @return Array
*/
public static function deployableStatusLabelList()
{
$statuslabel_list = Statuslabel::where('deployable', '=', '1')->orderBy('default_label', 'desc')
->orderBy('name','asc')
->orderBy('deployable','desc')
->pluck('name', 'id')->toArray();
return $statuslabel_list;
}
/**
* Get the list of status label types in an array to make a dropdown menu
*
@@ -468,10 +774,9 @@ class Helper
/**
* Gracefully handle decrypting the legacy data (encrypted via mcrypt) and use the new
* decryption method instead.
* Gracefully handle decrypting encrypted fields (custom fields, etc).
*
* This is not currently used, but will be.
* @todo allow this to handle more than just strings (arrays, etc)
*
* @author A. Gianotto
* @since 3.6
@@ -697,38 +1002,38 @@ class Helper
* @return string path to uploaded image or false if something went wrong
*/
public static function processUploadedImage(String $image_data, String $save_path) {
if ($image_data != null && $save_path != null) {
// After modification, the image is prefixed by mime info like the following:
// data:image/jpeg;base64,; This causes the image library to be unhappy, so we need to remove it.
$header = explode(';', $image_data, 2)[0];
// Grab the image type from the header while we're at it.
$extension = substr($header, strpos($header, '/')+1);
// Start reading the image after the first comma, postceding the base64.
$image = substr($image_data, strpos($image_data, ',')+1);
$file_name = str_random(25).".".$extension;
$directory= public_path($save_path);
// Check if the uploads directory exists. If not, try to create it.
if (!file_exists($directory)) {
mkdir($directory, 0755, true);
}
$path = public_path($save_path.$file_name);
try {
Image::make($image)->resize(500, 500, function ($constraint) {
$constraint->aspectRatio();
$constraint->upsize();
})->save($path);
} catch (\Exception $e) {
return false;
}
return $file_name;
if ($image_data == null || $save_path == null) {
return false;
}
return false;
// After modification, the image is prefixed by mime info like the following:
// data:image/jpeg;base64,; This causes the image library to be unhappy, so we need to remove it.
$header = explode(';', $image_data, 2)[0];
// Grab the image type from the header while we're at it.
$extension = substr($header, strpos($header, '/')+1);
// Start reading the image after the first comma, postceding the base64.
$image = substr($image_data, strpos($image_data, ',')+1);
$file_name = str_random(25).".".$extension;
$directory= public_path($save_path);
// Check if the uploads directory exists. If not, try to create it.
if (!file_exists($directory)) {
mkdir($directory, 0755, true);
}
$path = public_path($save_path.$file_name);
try {
Image::make($image)->resize(500, 500, function ($constraint) {
$constraint->aspectRatio();
$constraint->upsize();
})->save($path);
} catch (\Exception $e) {
return false;
}
return $file_name;
}
}

View File

@@ -0,0 +1,23 @@
<?php
namespace App\Helpers;
use Illuminate\Support\Facades\Storage;
class StorageHelper
{
static function downloader($filename, $disk = 'default') {
if($disk == 'default') {
$disk = config('filesystems.default');
}
switch(config("filesystems.disks.$disk.driver")) {
case 'local':
return response()->download(Storage::disk($disk)->path($filename)); //works for PRIVATE or public?!
case 's3':
return redirect()->away(Storage::disk($disk)->temporaryUrl($filename, now()->addMinutes(5))); //works for private or public, I guess?
default:
return Storage::disk($disk)->download($filename);
}
}
}

View File

@@ -75,7 +75,8 @@ class AccessoryCheckoutController extends Controller
'accessory_id' => $accessory->id,
'created_at' => Carbon::now(),
'user_id' => Auth::id(),
'assigned_to' => $request->get('assigned_to')
'assigned_to' => $request->get('assigned_to'),
'note' => $request->input('note')
]);
DB::table('accessories_users')->where('assigned_to', '=', $accessory->assigned_to)->where('accessory_id', '=', $accessory->id)->first();

View File

@@ -94,7 +94,7 @@ class AcceptanceController extends Controller {
if (!Storage::exists('private_uploads/signatures')) Storage::makeDirectory('private_uploads/signatures', 775);
$sig_filename = '';
if ($request->filled('signature_output')) {
$sig_filename = "siglog-" .Str::uuid() . '-'.date('Y-m-d-his').".png";
$data_uri = e($request->input('signature_output'));

View File

@@ -154,7 +154,6 @@ class AccessoriesController extends Controller
$offset = request('offset', 0);
$limit = request('limit', 50);
$accessory->lastCheckoutArray = $accessory->lastCheckout->toArray();
$accessory_users = $accessory->users;
$total = $accessory_users->count();
@@ -255,7 +254,8 @@ class AccessoriesController extends Controller
'accessory_id' => $accessory->id,
'created_at' => Carbon::now(),
'user_id' => Auth::id(),
'assigned_to' => $request->get('assigned_to')
'assigned_to' => $request->get('assigned_to'),
'note' => $request->get('note')
]);
$accessory->logCheckout($request->input('note'), $user);
@@ -287,7 +287,7 @@ class AccessoriesController extends Controller
$accessory = Accessory::find($accessory_user->accessory_id);
$this->authorize('checkin', $accessory);
$logaction = $accessory->logCheckin(User::find($accessoryUserId), $request->input('note'));
$logaction = $accessory->logCheckin(User::find($accessory_user->user_id), $request->input('note'));
// Was the accessory updated?
if (DB::table('accessories_users')->where('id', '=', $accessory_user->id)->delete()) {

View File

@@ -30,7 +30,20 @@ class AssetModelsController extends Controller
public function index(Request $request)
{
$this->authorize('view', AssetModel::class);
$allowed_columns = ['id','image','name','model_number','eol','notes','created_at','manufacturer','requestable', 'assets_count'];
$allowed_columns =
[
'id',
'image',
'name',
'model_number',
'eol',
'notes',
'created_at',
'manufacturer',
'requestable',
'assets_count',
'category'
];
$assetmodels = AssetModel::select([
'models.id',
@@ -75,13 +88,14 @@ class AssetModelsController extends Controller
case 'manufacturer':
$assetmodels->OrderManufacturer($order);
break;
case 'category':
$assetmodels->OrderCategory($order);
break;
default:
$assetmodels->orderBy($sort, $order);
break;
}
$total = $assetmodels->count();
$assetmodels = $assetmodels->skip($offset)->take($limit)->get();
return (new AssetModelsTransformer)->transformAssetModels($assetmodels, $total);
@@ -239,17 +253,17 @@ class AssetModelsController extends Controller
$assetmodel->use_text = '';
if ($settings->modellistCheckedValue('category')) {
$assetmodel->use_text .= (($assetmodel->category) ? e($assetmodel->category->name).' - ' : '');
$assetmodel->use_text .= (($assetmodel->category) ? $assetmodel->category->name.' - ' : '');
}
if ($settings->modellistCheckedValue('manufacturer')) {
$assetmodel->use_text .= (($assetmodel->manufacturer) ? e($assetmodel->manufacturer->name).' ' : '');
$assetmodel->use_text .= (($assetmodel->manufacturer) ? $assetmodel->manufacturer->name.' ' : '');
}
$assetmodel->use_text .= e($assetmodel->name);
$assetmodel->use_text .= $assetmodel->name;
if (($settings->modellistCheckedValue('model_number')) && ($assetmodel->model_number!='')) {
$assetmodel->use_text .= ' (#'.e($assetmodel->model_number).')';
$assetmodel->use_text .= ' (#'.$assetmodel->model_number.')';
}
$assetmodel->use_image = ($settings->modellistCheckedValue('image') && ($assetmodel->image)) ? Storage::disk('public')->url('models/'.e($assetmodel->image)) : null;

View File

@@ -141,8 +141,6 @@ class AssetsController extends Controller
}
$request->filled('order_number') ? $assets = $assets->where('assets.order_number', '=', e($request->get('order_number'))) : '';
$offset = (($assets) && (request('offset') > $assets->count())) ? 0 : request('offset', 0);
// 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.
@@ -480,13 +478,36 @@ class AssetsController extends Controller
$model = AssetModel::find($request->get('model_id'));
if (($model) && ($model->fieldset)) {
foreach ($model->fieldset->fields as $field) {
if ($field->field_encrypted=='1') {
if (Gate::allows('admin')) {
$asset->{$field->convertUnicodeDbSlug()} = \Crypt::encrypt($request->input($field->convertUnicodeDbSlug()));
}
} else {
$asset->{$field->convertUnicodeDbSlug()} = $request->input($field->convertUnicodeDbSlug());
// Set the field value based on what was sent in the request
$field_val = $request->input($field->convertUnicodeDbSlug(), null);
// If input value is null, use custom field's default value
if ($field_val == null) {
\Log::debug('Field value for '.$field->convertUnicodeDbSlug().' is null');
$field_val = $field->defaultValue($request->get('model_id'));
\Log::debug('Use the default fieldset value of '.$field->defaultValue($request->get('model_id')));
}
// if the field is set to encrypted, make sure we encrypt the value
if ($field->field_encrypted == '1') {
\Log::debug('This model field is encrypted in this fieldset.');
if (Gate::allows('admin')) {
// If input value is null, use custom field's default value
if (($field_val == null) && ($request->has('model_id')!='')){
$field_val = \Crypt::encrypt($field->defaultValue($request->get('model_id')));
} else {
$field_val = \Crypt::encrypt($request->input($field->convertUnicodeDbSlug()));
}
}
}
$asset->{$field->convertUnicodeDbSlug()} = $field_val;
}
}
@@ -812,7 +833,7 @@ class AssetsController extends Controller
$asset->location_id = $request->input('location_id');
}
$asset->last_audit_date = date('Y-m-d h:i:s');
$asset->last_audit_date = date('Y-m-d H:i:s');
if ($asset->save()) {
$log = $asset->logAudit(request('note'),request('location_id'));

View File

@@ -95,7 +95,14 @@ class CustomFieldsController extends Controller
$field = new CustomField;
$data = $request->all();
$validator = Validator::make($data, $field->validationRules());
$regex_format = null;
if (str_contains($data["format"], "regex:")){
$regex_format = $data["format"];
}
$validator = Validator::make($data, $field->validationRules($regex_format));
if ($validator->fails()) {
return response()->json(Helper::formatStandardApiResponse('error', null, $validator->errors()));
}

View File

@@ -20,7 +20,7 @@ class DepreciationsController extends Controller
public function index(Request $request)
{
$this->authorize('view', Depreciation::class);
$allowed_columns = ['id','name','created_at'];
$allowed_columns = ['id','name','months','created_at'];
$depreciations = Depreciation::select('id','name','months','user_id','created_at','updated_at');

View File

@@ -49,10 +49,12 @@ class ImportController extends Controller
if (!in_array($file->getMimeType(), array(
'application/vnd.ms-excel',
'text/csv',
'application/csv',
'text/x-Algol68', // because wtf CSV files?
'text/plain',
'text/comma-separated-values',
'text/tsv'))) {
$results['error']='File type must be CSV';
$results['error']='File type must be CSV. Uploaded file is '.$file->getMimeType();
return response()->json(Helper::formatStandardApiResponse('error', null, $results['error']), 500);
}

View File

@@ -0,0 +1,141 @@
<?php
namespace App\Http\Controllers\Api;
use App\Helpers\Helper;
use App\Http\Controllers\Controller;
use App\Http\Transformers\LicenseSeatsTransformer;
use App\Models\Asset;
use App\Models\License;
use App\Models\LicenseSeat;
use App\Models\User;
use Auth;
use Illuminate\Http\Request;
class LicenseSeatsController extends Controller
{
/**
* Display a listing of the resource.
*
* @param \Illuminate\Http\Request $request
* @param int $licenseId
* @return \Illuminate\Http\Response
*/
public function index(Request $request, $licenseId)
{
//
if ($license = License::find($licenseId)) {
$this->authorize('view', $license);
$seats = LicenseSeat::with('license', 'user', 'asset', 'user.department')
->where('license_seats.license_id', $licenseId);
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
if ($request->input('sort')=='department') {
$seats->OrderDepartments($order);
} else {
$seats->orderBy('id', $order);
}
$total = $seats->count();
$offset = (($seats) && (request('offset') > $total)) ? 0 : request('offset', 0);
$limit = request('limit', 50);
$seats = $seats->skip($offset)->take($limit)->get();
if ($seats) {
return (new LicenseSeatsTransformer)->transformLicenseSeats($seats, $total);
}
}
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/licenses/message.does_not_exist')), 200);
}
/**
* Display the specified resource.
*
* @param int $licenseId
* @param int $seatId
* @return \Illuminate\Http\Response
*/
public function show($licenseId, $seatId)
{
//
$this->authorize('view', License::class);
// sanity checks:
// 1. does the license seat exist?
if (!$licenseSeat = LicenseSeat::find($seatId)) {
return response()->json(Helper::formatStandardApiResponse('error', null, 'Seat not found'));
}
// 2. does the seat belong to the specified license?
if (!$license = $licenseSeat->license()->first() || $license->id != intval($licenseId)) {
return response()->json(Helper::formatStandardApiResponse('error', null, 'Seat does not belong to the specified license'));
}
return (new LicenseSeatsTransformer)->transformLicenseSeat($licenseSeat);
}
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param int $licenseId
* @param int $seatId
* @return \Illuminate\Http\Response
*/
public function update(Request $request, $licenseId, $seatId)
{
$this->authorize('checkout', License::class);
// sanity checks:
// 1. does the license seat exist?
if (!$licenseSeat = LicenseSeat::find($seatId)) {
return response()->json(Helper::formatStandardApiResponse('error', null, 'Seat not found'));
}
// 2. does the seat belong to the specified license?
if (!$license = $licenseSeat->license()->first() || $license->id != intval($licenseId)) {
return response()->json(Helper::formatStandardApiResponse('error', null, 'Seat does not belong to the specified license'));
}
$oldUser = $licenseSeat->user()->first();
$oldAsset = $licenseSeat->asset()->first();
// attempt to update the license seat
$licenseSeat->fill($request->all());
$licenseSeat->user_id = Auth::user()->id;
// check if this update is a checkin operation
// 1. are relevant fields touched at all?
$touched = $licenseSeat->isDirty('assigned_to') || $licenseSeat->isDirty('asset_id');
// 2. are they cleared? if yes then this is a checkin operation
$is_checkin = ($touched && $licenseSeat->assigned_to === null && $licenseSeat->asset_id === null);
if (!$touched) {
// nothing to update
return response()->json(Helper::formatStandardApiResponse('success', $licenseSeat, trans('admin/licenses/message.update.success')));
}
if ($licenseSeat->save()) {
// the logging functions expect only one "target". if both asset and user are present in the request,
// we simply let assets take precedence over users...
$changes = $licenseSeat->getChanges();
if (array_key_exists('assigned_to', $changes)) {
$target = $is_checkin ? $oldUser : User::find($changes['assigned_to']);
}
if (array_key_exists('asset_id', $changes)) {
$target = $is_checkin ? $oldAsset : Asset::find($changes['asset_id']);
}
if ($is_checkin) {
$licenseSeat->logCheckin($target, $request->input('note'));
return response()->json(Helper::formatStandardApiResponse('success', $licenseSeat, trans('admin/licenses/message.update.success')));
}
// in this case, relevant fields are touched but it's not a checkin operation. so it must be a checkout operation.
$licenseSeat->logCheckout($request->input('note'), $target);
return response()->json(Helper::formatStandardApiResponse('success', $licenseSeat, trans('admin/licenses/message.update.success')));
}
return Helper::formatStandardApiResponse('error', null, $licenseSeat->getErrors());
}
}

View File

@@ -103,11 +103,33 @@ class LicensesController extends Controller
case 'category':
$licenses = $licenses->leftJoin('categories', 'licenses.category_id', '=', 'categories.id')->orderBy('categories.name', $order);
break;
case 'depreciation':
$licenses = $licenses->leftJoin('depreciations', 'licenses.depreciation_id', '=', 'depreciations.id')->orderBy('depreciations.name', $order);
break;
case 'company':
$licenses = $licenses->leftJoin('companies', 'licenses.company_id', '=', 'companies.id')->orderBy('companies.name', $order);
break;
default:
$allowed_columns = ['id','name','purchase_cost','expiration_date','purchase_order','order_number','notes','purchase_date','serial','company','category','license_name','license_email','free_seats_count','seats'];
$allowed_columns =
[
'id',
'name',
'purchase_cost',
'expiration_date',
'purchase_order',
'order_number',
'notes',
'purchase_date',
'serial',
'company',
'category',
'license_name',
'license_email',
'free_seats_count',
'seats',
'termination_date',
'depreciation_id'
];
$sort = in_array($request->input('sort'), $allowed_columns) ? e($request->input('sort')) : 'created_at';
$licenses = $licenses->orderBy($sort, $order);
break;
@@ -215,50 +237,6 @@ class LicensesController extends Controller
}
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/licenses/message.assoc_users')));
}
/**
* Get license seat listing
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v1.0]
* @param int $licenseId
* @return \Illuminate\Contracts\View\View
*/
public function seats(Request $request, $licenseId)
{
if ($license = License::find($licenseId)) {
$this->authorize('view', $license);
$seats = LicenseSeat::with('license', 'user', 'asset', 'user.department')
->where('license_seats.license_id', $licenseId);
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
if ($request->input('sort')=='department') {
$seats->OrderDepartments($order);
} else {
$seats->orderBy('id', $order);
}
$offset = (($seats) && (request('offset') > $seats->count())) ? 0 : request('offset', 0);
$limit = request('limit', 50);
$total = $seats->count();
$seats = $seats->skip($offset)->take($limit)->get();
if ($seats) {
return (new LicenseSeatsTransformer)->transformLicenseSeats($seats, $total);
}
}
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/licenses/message.does_not_exist')), 200);
}
/**
* Gets a paginated collection for the select2 menus

View File

@@ -26,7 +26,7 @@ class LocationsController extends Controller
$allowed_columns = [
'id','name','address','address2','city','state','country','zip','created_at',
'updated_at','manager_id','image',
'assigned_assets_count','users_count','assets_count','currency'];
'assigned_assets_count','users_count','assets_count','currency','ldap_ou'];
$locations = Location::with('parent', 'manager', 'children')->select([
'locations.id',
@@ -42,6 +42,7 @@ class LocationsController extends Controller
'locations.created_at',
'locations.updated_at',
'locations.image',
'locations.ldap_ou',
'locations.currency'
])->withCount('assignedAssets as assigned_assets_count')
->withCount('assets as assets_count')
@@ -53,7 +54,7 @@ class LocationsController extends Controller
$offset = (($locations) && (request('offset') > $locations->count())) ? 0 : request('offset', 0);
$offset = (($locations) && (request('offset') > $locations->count())) ? $locations->count() : request('offset', 0);
// Check to make sure the limit is not higher than the max allowed
((config('app.max_results') >= $request->input('limit')) && ($request->filled('limit'))) ? $limit = $request->input('limit') : $limit = config('app.max_results');

View File

@@ -49,6 +49,7 @@ class ReportsController extends Controller
'created_at',
'target_id',
'user_id',
'accept_signature',
'action_type',
'note'
];

View File

@@ -15,6 +15,9 @@ use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Notification;
use GuzzleHttp\Client;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\Validator;
use App\Models\Ldap; // forward-port of v4 LDAP model for Sync
class SettingsController extends Controller
{
@@ -33,13 +36,17 @@ class SettingsController extends Controller
public function ldapAdSettingsTest(LdapAd $ldap): JsonResponse
{
if(!$ldap->init()) {
Log::info('LDAP is not enabled cannot test.');
Log::info('LDAP is not enabled so we cannot test.');
return response()->json(['message' => 'LDAP is not enabled, cannot test.'], 400);
}
// The connect, bind and resulting users message
$message = [];
// This is all kinda fucked right now. The connection test doesn't actually do what you think,
// // and the way we parse the errors
// on the JS side is horrible.
Log::info('Preparing to test LDAP user login');
// Test user can connect to the LDAP server
try {
@@ -48,13 +55,11 @@ class SettingsController extends Controller
'message' => 'Successfully connected to LDAP server.'
];
} catch (\Exception $ex) {
\Log::debug('LDAP connected but Bind failed. Please check your LDAP settings and try again.');
return response()->json([
'message' => 'Error logging into LDAP server, error: ' . $ex->getMessage() . ' - Verify your that your username and password are correct']);
\Log::debug('Connection to LDAP server '.Setting::getSettings()->ldap_server.' failed. Please check your LDAP settings and try again. Server Responded with error: ' . $ex->getMessage());
return response()->json(
['message' => 'Connection to LDAP server '.Setting::getSettings()->ldap_server." failed. Verify that the LDAP hostname is entered correctly and that it can be reached from this web server. \n\nServer Responded with error: " . $ex->getMessage()
} catch (\Exception $e) {
\Log::info('LDAP connection failed but we cannot debug it any further on our end.');
return response()->json(['message' => 'The LDAP connection failed but we cannot debug it any further on our end. The error from the server is: '.$e->getMessage()], 500);
], 400);
}
Log::info('Preparing to test LDAP bind connection');
@@ -63,25 +68,44 @@ class SettingsController extends Controller
Log::info('Testing Bind');
$ldap->testLdapAdBindConnection();
$message['bind'] = [
'message' => 'Successfully binded to LDAP server.'
'message' => 'Successfully bound to LDAP server.'
];
} catch (\Exception $ex) {
Log::info('LDAP Bind failed');
return response()->json([
'message' => 'Error binding to LDAP server, error: ' . $ex->getMessage()
return response()->json(['message' => 'Connection to LDAP successful, but we were unable to Bind the LDAP user '.Setting::getSettings()->ldap_uname.". Verify your that your LDAP Bind username and password are correct. \n\nServer Responded with error: " . $ex->getMessage()
], 400);
}
Log::info('Preparing to get sample user set from LDAP directory');
// Get a sample of 10 users so user can verify the data is correct
$settings = Setting::getSettings();
try {
Log::info('Testing LDAP sync');
error_reporting(E_ALL & ~E_DEPRECATED); // workaround for php7.4, which deprecates ldap_control_paged_result
$users = $ldap->testUserImportSync();
$message['user_sync'] = [
'users' => $users
];
// $users = $ldap->testUserImportSync(); // from AdLdap2 from v5, disabling and falling back to v4's sync code
$users = collect(Ldap::findLdapUsers())->slice(0, 11)->filter(function ($value, $key) { //choosing ELEVEN because one is going to be the count, which we're about to filter out in the next line
return is_int($key);
})->map(function ($item) use ($settings) {
return (object) [
'username' => $item[$settings['ldap_username_field']][0] ?? null,
'employee_number' => $item[$settings['ldap_emp_num']][0] ?? null,
'lastname' => $item[$settings['ldap_lname_field']][0] ?? null,
'firstname' => $item[$settings['ldap_fname_field']][0] ?? null,
'email' => $item[$settings['ldap_email']][0] ?? null,
];
});
if ($users->count() > 0) {
$message['user_sync'] = [
'users' => $users
];
} else {
$message['user_sync'] = [
'message' => 'Connection to LDAP was successful, however there were no users returned from your query. You should confirm the Base Bind DN above.'
];
return response()->json($message, 400);
}
} catch (\Exception $ex) {
Log::info('LDAP sync failed');
$message['user_sync'] = [
@@ -93,6 +117,51 @@ class SettingsController extends Controller
return response()->json($message, 200);
}
public function ldaptestlogin(Request $request, LdapAd $ldap)
{
if (Setting::getSettings()->ldap_enabled!='1') {
\Log::debug('LDAP is not enabled. Cannot test.');
return response()->json(['message' => 'LDAP is not enabled, cannot test.'], 400);
}
$rules = array(
'ldaptest_user' => 'required',
'ldaptest_password' => 'required'
);
$validator = Validator::make($request->all(), $rules);
if ($validator->fails()) {
\Log::debug('LDAP Validation test failed.');
$validation_errors = implode(' ',$validator->errors()->all());
return response()->json(['message' => $validator->errors()->all()], 400);
}
\Log::debug('Preparing to test LDAP login');
try {
DB::beginTransaction(); //this was the easiest way to invoke a full test of an LDAP login without adding new users to the DB (which may not be desired)
// $results = $ldap->ldap->auth()->attempt($request->input('ldaptest_username'), $request->input('ldaptest_password'), true);
// can't do this because that's a protected property.
$results = $ldap->ldapLogin($request->input('ldaptest_user'), $request->input('ldaptest_password')); // this would normally create a user on success (if they didn't already exist), but for the transaction
if($results) {
return response()->json(['message' => 'It worked! '. $request->input('ldaptest_user').' successfully binded to LDAP.'], 200);
} else {
return response()->json(['message' => 'Login Failed. '. $request->input('ldaptest_user').' did not successfully bind to LDAP.'], 400);
}
} catch (\Exception $e) {
\Log::debug('Connection failed');
return response()->json(['message' => $e->getMessage()], 400);
} finally {
DB::rollBack(); // ALWAYS rollback, whether success or failure
}
}
public function slacktest(Request $request)
{
@@ -137,7 +206,7 @@ class SettingsController extends Controller
try {
Notification::send(Setting::first(), new MailTest());
return response()->json(['message' => 'Mail sent to '.config('mail.reply_to.address')], 200);
} catch (Exception $e) {
} catch (\Exception $e) {
return response()->json(['message' => $e->getMessage()], 500);
}
}

View File

@@ -167,25 +167,31 @@ class StatuslabelsController extends Controller
{
$this->authorize('view', Statuslabel::class);
$statuslabels = Statuslabel::with('assets')->groupBy('id')->withCount('assets as assets_count')->get();
$statuslabels = Statuslabel::with('assets')
->groupBy('id')
->withCount('assets as assets_count')
->get();
$labels=[];
$points=[];
$colors=[];
$default_color_count = 0;
$colors_array = array();
foreach ($statuslabels as $statuslabel) {
if ($statuslabel->assets_count > 0) {
$labels[]=$statuslabel->name. ' ('.number_format($statuslabel->assets_count).')';
$points[]=$statuslabel->assets_count;
if ($statuslabel->color!='') {
$colors[]=$statuslabel->color;
$colors_array[] = $statuslabel->color;
} else {
$colors_array[] = Helper::defaultChartColors($default_color_count);
$default_color_count++;
}
}
}
$colors_array = array_merge($colors, Helper::chartColors());
$result= [
"labels" => $labels,
"datasets" => [ [

View File

@@ -49,6 +49,7 @@ class UsersController extends Controller
'users.jobtitle',
'users.last_login',
'users.last_name',
'users.locale',
'users.location_id',
'users.manager_id',
'users.notes',
@@ -60,6 +61,7 @@ class UsersController extends Controller
'users.updated_at',
'users.username',
'users.zip',
'users.ldap_import',
])->with('manager', 'groups', 'userloc', 'company', 'department','assets','licenses','accessories','consumables')
->withCount('assets as assets_count','licenses as licenses_count','accessories as accessories_count','consumables as consumables_count');
@@ -67,7 +69,9 @@ class UsersController extends Controller
if (($request->filled('deleted')) && ($request->input('deleted')=='true')) {
$users = $users->GetDeleted();
$users = $users->onlyTrashed();
} elseif (($request->filled('all')) && ($request->input('all')=='true')) {
$users = $users->withTrashed();
}
if ($request->filled('company_id')) {
@@ -129,7 +133,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'
'country', 'zip', 'id', 'ldap_import'
];
$sort = in_array($request->get('sort'), $allowed_columns) ? $request->get('sort') : 'first_name';
@@ -182,16 +186,16 @@ class UsersController extends Controller
foreach ($users as $user) {
$name_str = '';
if ($user->last_name!='') {
$name_str .= e($user->last_name).', ';
$name_str .= $user->last_name.', ';
}
$name_str .= e($user->first_name);
$name_str .= $user->first_name;
if ($user->username!='') {
$name_str .= ' ('.e($user->username).')';
$name_str .= ' ('.$user->username.')';
}
if ($user->employee_num!='') {
$name_str .= ' - #'.e($user->employee_num);
$name_str .= ' - #'.$user->employee_num;
}
$user->use_text = $name_str;

View File

@@ -100,9 +100,9 @@ class AssetMaintenancesController extends Controller
$assetMaintenance = new AssetMaintenance();
$assetMaintenance->supplier_id = $request->input('supplier_id');
$assetMaintenance->is_warranty = $request->input('is_warranty');
$assetMaintenance->cost = e($request->input('cost'));
$assetMaintenance->notes = e($request->input('notes'));
$asset = Asset::find(e($request->input('asset_id')));
$assetMaintenance->cost = $request->input('cost');
$assetMaintenance->notes = $request->input('notes');
$asset = Asset::find($request->input('asset_id'));
if ((!Company::isCurrentUserHasAccess($asset)) && ($asset!=null)) {
return static::getInsufficientPermissionsRedirect();

View File

@@ -76,9 +76,32 @@ class AssetCheckinController extends Controller
$asset->status_id = e($request->get('status_id'));
}
// This is just meant to correct legacy issues where some user data would have 0
// as a location ID, which isn't valid. Later versions of Snipe-IT have stricter validation
// rules, so it's necessary to fix this for long-time users. It's kinda gross, but will help
// people (and their data) in the long run
if ($asset->rtd_location_id=='0') {
\Log::debug('Manually override the RTD location IDs');
\Log::debug('Original RTD Location ID: '.$asset->rtd_location_id);
$asset->rtd_location_id = '';
\Log::debug('New RTD Location ID: '.$asset->rtd_location_id);
}
if ($asset->location_id=='0') {
\Log::debug('Manually override the location IDs');
\Log::debug('Original Location ID: '.$asset->location_id);
$asset->location_id = '';
\Log::debug('New RTD Location ID: '.$asset->location_id);
}
$asset->location_id = $asset->rtd_location_id;
\Log::debug('After Location ID: '.$asset->location_id);
\Log::debug('After RTD Location ID: '.$asset->rtd_location_id);
if ($request->filled('location_id')) {
\Log::debug('NEW Location ID: '.$request->get('location_id'));
$asset->location_id = e($request->get('location_id'));
}
@@ -97,6 +120,6 @@ class AssetCheckinController extends Controller
return redirect()->route("hardware.index")->with('success', trans('admin/hardware/message.checkin.success'));
}
// Redirect to the asset management page with error
return redirect()->route("hardware.index")->with('error', trans('admin/hardware/message.checkin.error'));
return redirect()->route("hardware.index")->with('error', trans('admin/hardware/message.checkin.error').$asset->getErrors());
}
}

View File

@@ -4,6 +4,7 @@ namespace App\Http\Controllers\Assets;
use App\Exceptions\CheckoutNotAllowed;
use App\Helpers\Helper;
use App\Http\Controllers\CheckInOutRequest;
use App\Http\Controllers\Controller;
use App\Http\Requests\AssetCheckoutRequest;
@@ -33,7 +34,8 @@ class AssetCheckoutController extends Controller
$this->authorize('checkout', $asset);
if ($asset->availableForCheckout()) {
return view('hardware/checkout', compact('asset'));
return view('hardware/checkout', compact('asset'))
->with('statusLabel_list', Helper::deployableStatusLabelList());
}
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.checkout.not_available'));
@@ -75,12 +77,16 @@ class AssetCheckoutController extends Controller
$expected_checkin = $request->get('expected_checkin');
}
if ($request->filled('status_id')) {
$asset->status_id = $request->get('status_id');
}
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'));
}
// Redirect to the asset management page with error
return redirect()->to("hardware/$assetId/checkout")->with('error', trans('admin/hardware/message.checkout.error'))->withErrors($asset->getErrors());
return redirect()->to("hardware/$assetId/checkout")->with('error', trans('admin/hardware/message.checkout.error').$asset->getErrors());
} catch (ModelNotFoundException $e) {
return redirect()->back()->with('error', trans('admin/hardware/message.checkout.error'))->withErrors($asset->getErrors());
} catch (CheckoutNotAllowed $e) {

View File

@@ -9,6 +9,7 @@ use App\Models\Actionlog;
use App\Models\Asset;
use Illuminate\Support\Facades\Response;
use Illuminate\Support\Facades\Storage;
use App\Helpers\StorageHelper;
class AssetFilesController extends Controller
{
@@ -86,7 +87,7 @@ class AssetFilesController extends Controller
}
return JsonResponse::create(["error" => "Failed validation: "], 500);
}
return Storage::download($file);
return StorageHelper::downloader($file);
}
// Prepare the error message
$error = trans('admin/hardware/message.does_not_exist', ['id' => $fileId]);
@@ -109,15 +110,15 @@ class AssetFilesController extends Controller
{
$asset = Asset::find($assetId);
$this->authorize('update', $asset);
$rel_path = 'storage/private_uploads/assets';
$rel_path = 'private_uploads/assets';
// the asset is valid
if (isset($asset->id)) {
$this->authorize('update', $asset);
$log = Actionlog::find($fileId);
if ($log) {
if (file_exists(base_path().'/'.$rel_path.'/'.$log->filename)) {
Storage::disk('public')->delete($rel_path.'/'.$log->filename);
if (Storage::exists($rel_path.'/'.$log->filename)) {
Storage::delete($rel_path.'/'.$log->filename);
}
$log->delete();
return redirect()->back()->with('success', trans('admin/hardware/message.deletefile.success'));

View File

@@ -14,6 +14,7 @@ use App\Models\Setting;
use App\Models\User;
use Auth;
use Carbon\Carbon;
use Intervention\Image\Facades\Image;
use DB;
use Gate;
use Illuminate\Http\Request;
@@ -165,10 +166,17 @@ class AssetsController extends Controller
foreach ($model->fieldset->fields as $field) {
if ($field->field_encrypted=='1') {
if (Gate::allows('admin')) {
$asset->{$field->convertUnicodeDbSlug()} = \Crypt::encrypt($request->input($field->convertUnicodeDbSlug()));
}
if(is_array($request->input($field->convertUnicodeDbSlug()))){
$asset->{$field->convertUnicodeDbSlug()} = \Crypt::encrypt(e(implode(', ', $request->input($field->convertUnicodeDbSlug()))));
}else{
$asset->{$field->convertUnicodeDbSlug()} = \Crypt::encrypt(e($request->input($field->convertUnicodeDbSlug())));
} }
} else {
$asset->{$field->convertUnicodeDbSlug()} = $request->input($field->convertUnicodeDbSlug());
if(is_array($request->input($field->convertUnicodeDbSlug()))){
$asset->{$field->convertUnicodeDbSlug()} = implode(', ', $request->input($field->convertUnicodeDbSlug()));
}else{
$asset->{$field->convertUnicodeDbSlug()} = $request->input($field->convertUnicodeDbSlug());
}
}
}
}
@@ -342,10 +350,18 @@ class AssetsController extends Controller
foreach ($model->fieldset->fields as $field) {
if ($field->field_encrypted=='1') {
if (Gate::allows('admin')) {
$asset->{$field->convertUnicodeDbSlug()} = \Crypt::encrypt(e($request->input($field->convertUnicodeDbSlug())));
if(is_array($request->input($field->convertUnicodeDbSlug()))){
$asset->{$field->convertUnicodeDbSlug()} = \Crypt::encrypt(e(implode(', ', $request->input($field->convertUnicodeDbSlug()))));
}else{
$asset->{$field->convertUnicodeDbSlug()} = \Crypt::encrypt(e($request->input($field->convertUnicodeDbSlug())));
}
}
} else {
$asset->{$field->convertUnicodeDbSlug()} = $request->input($field->convertUnicodeDbSlug());
if(is_array($request->input($field->convertUnicodeDbSlug()))){
$asset->{$field->convertUnicodeDbSlug()} = implode(', ', $request->input($field->convertUnicodeDbSlug()));
}else{
$asset->{$field->convertUnicodeDbSlug()} = $request->input($field->convertUnicodeDbSlug());
}
}
}
}
@@ -468,13 +484,19 @@ class AssetsController extends Controller
return response()->file($barcode_file, $header);
} else {
// Calculate barcode width in pixel based on label width (inch)
$barcode_width = ($settings->labels_width - $settings->labels_display_sgutter) * 96.000000000001;
$barcode_width = ($settings->labels_width - $settings->labels_display_sgutter) * 200.000000000001;
$barcode = new \Com\Tecnick\Barcode\Barcode();
$barcode_obj = $barcode->getBarcodeObj($settings->alt_barcode,$asset->asset_tag,($barcode_width < 300 ? $barcode_width : 300),50);
try {
$barcode_obj = $barcode->getBarcodeObj($settings->alt_barcode,$asset->asset_tag,($barcode_width < 300 ? $barcode_width : 300),50);
file_put_contents($barcode_file, $barcode_obj->getPngData());
return response($barcode_obj->getPngData())->header('Content-type', 'image/png');
} catch(\Exception $e) {
\Log::debug('The barcode format is invalid.');
return response(file_get_contents(public_path('uploads/barcodes/invalid_barcode.gif')))->header('Content-type', 'image/gif');
}
file_put_contents($barcode_file, $barcode_obj->getPngData());
return response($barcode_obj->getPngData())->header('Content-type', 'image/png');
}
}
}
@@ -550,7 +572,12 @@ class AssetsController extends Controller
*
* This needs a LOT of love. It's done very inelegantly right now, and there are
* a ton of optimizations that could (and should) be done.
*
*
* Updated to respect checkin dates:
* No checkin column, assume all items are checked in (todays date)
* Checkin date in the past, update history.
* Checkin date in future or empty, check the item out to the user.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v3.3]
* @return View
@@ -567,6 +594,8 @@ class AssetsController extends Controller
}
$csv = Reader::createFromPath($request->file('user_import_csv'));
$csv->setHeaderOffset(0);
$header = $csv->getHeader();
$isCheckinHeaderExplicit = in_array("checkin date", (array_map('strtolower', $header)));
$results = $csv->getRecords();
$item = array();
$status = array();
@@ -581,8 +610,19 @@ class AssetsController extends Controller
}
$batch_counter = count($item[$asset_tag]);
$item[$asset_tag][$batch_counter]['checkout_date'] = Carbon::parse(Helper::array_smart_fetch($row, "checkout date"))->format('Y-m-d H:i:s');
$item[$asset_tag][$batch_counter]['checkin_date'] = Carbon::parse(Helper::array_smart_fetch($row, "checkin date"))->format('Y-m-d H:i:s');
\Log::debug($item[$asset_tag][$batch_counter]['checkin_date']);
if ($isCheckinHeaderExplicit){
//checkin date not empty, assume past transaction or future checkin date (expected)
if (!empty(Helper::array_smart_fetch($row, "checkin date"))) {
$item[$asset_tag][$batch_counter]['checkin_date'] = Carbon::parse(Helper::array_smart_fetch($row, "checkin date"))->format('Y-m-d H:i:s');
} else {
$item[$asset_tag][$batch_counter]['checkin_date'] = '';
}
} else {
//checkin header missing, assume data is unavailable and make checkin date explicit (now) so we don't encounter invalid state.
$item[$asset_tag][$batch_counter]['checkin_date'] = Carbon::parse(now())->format('Y-m-d H:i:s');
}
$item[$asset_tag][$batch_counter]['asset_tag'] = Helper::array_smart_fetch($row, "asset tag");
$item[$asset_tag][$batch_counter]['name'] = Helper::array_smart_fetch($row, "name");
$item[$asset_tag][$batch_counter]['email'] = Helper::array_smart_fetch($row, "email");
@@ -610,16 +650,24 @@ class AssetsController extends Controller
$user_query .= ', or on username '.$firstname['username'];
}
if ($request->input('match_email')=='1') {
if ($item[$asset_tag][$batch_counter]['email']=='') {
if ($item[$asset_tag][$batch_counter]['name']=='') {
$item[$asset_tag][$batch_counter]['username'][] = $user_email = User::generateEmailFromFullName($item[$asset_tag][$batch_counter]['name']);
$user->orWhere('username', '=', $user_email);
$user_query .= ', or on username '.$user_email;
}
}
if ($request->input('match_username') == '1'){
// Added #8825: add explicit username lookup
$raw_username = $item[$asset_tag][$batch_counter]['name'];
$user->orWhere('username', '=', $raw_username);
$user_query .= ', or on username ' . $raw_username;
}
// A matching user was found
if ($user = $user->first()) {
$item[$asset_tag][$batch_counter]['checkedout_to'] = $user->id;
//$user is now matched user from db
$item[$asset_tag][$batch_counter]['user_id'] = $user->id;
Actionlog::firstOrCreate(array(
'item_id' => $asset->id,
'item_type' => Asset::class,
@@ -630,14 +678,44 @@ class AssetsController extends Controller
'created_at' => $item[$asset_tag][$batch_counter]['checkout_date'],
'action_type' => 'checkout',
));
$asset->assigned_to = $user->id;
$checkin_date = $item[$asset_tag][$batch_counter]['checkin_date'];
if ($isCheckinHeaderExplicit) {
//if checkin date header exists, assume that empty or future date is still checked out
//if checkin is before todays date, assume it's checked in and do not assign user ID, if checkin date is in the future or blank, this is the expected checkin date, items is checked out
if ((strtotime($checkin_date) > strtotime(Carbon::now())) || (empty($checkin_date))
) {
//only do this if item is checked out
$asset->assigned_to = $user->id;
$asset->assigned_type = User::class;
}
}
if (!empty($checkin_date)) {
//only make a checkin there is a valid checkin date or we created one on import.
Actionlog::firstOrCreate(array(
'item_id' =>
$item[$asset_tag][$batch_counter]['asset_id'],
'item_type' => Asset::class,
'user_id' => Auth::user()->id,
'note' => 'Checkin imported by ' . Auth::user()->present()->fullName() . ' from history importer',
'target_id' => null,
'created_at' => $checkin_date,
'action_type' => 'checkin'
));
}
if ($asset->save()) {
$status['success'][]['asset'][$asset_tag]['msg'] = 'Asset successfully matched for '.Helper::array_smart_fetch($row, "name").$user_query.' on '.$item[$asset_tag][$batch_counter]['checkout_date'];
} else {
$status['error'][]['asset'][$asset_tag]['msg'] = 'Asset and user was matched but could not be saved.';
}
} else {
$item[$asset_tag][$batch_counter]['checkedout_to'] = null;
$item[$asset_tag][$batch_counter]['user_id'] = null;
$status['error'][]['user'][Helper::array_smart_fetch($row, "name")]['msg'] = 'User does not exist so no checkin log was created.';
}
} else {
@@ -646,31 +724,6 @@ class AssetsController extends Controller
}
}
}
// Loop through and backfill the checkins
foreach ($item as $key => $asset_batch) {
$total_in_batch = count($asset_batch);
for ($x = 0; $x < $total_in_batch; $x++) {
$next = $x + 1;
// Only do this if a matching user was found
if ((array_key_exists('checkedout_to', $asset_batch[$x])) && ($asset_batch[$x]['checkedout_to']!='')) {
if (($total_in_batch > 1) && ($x < $total_in_batch) && (array_key_exists($next, $asset_batch))) {
$checkin_date = Carbon::parse($asset_batch[$next]['checkin_date'])->format('Y-m-d H:i:s');
$asset_batch[$x]['real_checkin'] = $checkin_date;
\Log::debug($asset_batch[$next]['checkin_date']);
\Log::debug($checkin_date);
Actionlog::firstOrCreate(array(
'item_id' => $asset_batch[$x]['asset_id'],
'item_type' => Asset::class,
'user_id' => Auth::user()->id,
'note' => 'Checkin imported by ' . Auth::user()->present()->fullName() . ' from history importer',
'target_id' => null,
'created_at' => $checkin_date,
'action_type' => 'checkin'
));
}
}
}
}
return view('hardware/history')->with('status', $status);
}
@@ -760,7 +813,7 @@ class AssetsController extends Controller
$asset->unsetEventDispatcher();
$asset->next_audit_date = $request->input('next_audit_date');
$asset->last_audit_date = date('Y-m-d h:i:s');
$asset->last_audit_date = date('Y-m-d H:i:s');
// Check to see if they checked the box to update the physical location,
// not just note it in the audit notes

View File

@@ -5,7 +5,6 @@ namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\SendsPasswordResetEmails;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
class ForgotPasswordController extends Controller
{
@@ -60,7 +59,7 @@ class ForgotPasswordController extends Controller
*/
$request->validate([
'email' => ['required', 'email', 'max:255'],
'username' => ['required', 'max:255'],
]);
@@ -74,16 +73,16 @@ class ForgotPasswordController extends Controller
*/
$response = $this->broker()->sendResetLink(
array_merge(
$request->only('email'),
$request->only('username'),
['activated' => '1'],
['ldap_import' => '0']
)
);
if ($response === \Password::RESET_LINK_SENT) {
\Log::info('Password reset attempt: User '.$request->input('email').' found, password reset sent');
\Log::info('Password reset attempt: User '.$request->input('username').' WAS found, password reset sent');
} else {
\Log::info('Password reset attempt: User '.$request->input('email').' not found or user is inactive');
\Log::info('Password reset attempt: User matching username '.$request->input('username').' NOT FOUND or user is inactive');
}

View File

@@ -58,12 +58,12 @@ class LoginController extends Controller
*
* @return void
*/
public function __construct(LdapAd $ldap, Saml $saml)
public function __construct(/*LdapAd $ldap, */ Saml $saml)
{
parent::__construct();
$this->middleware('guest', ['except' => ['logout','postTwoFactorAuth','getTwoFactorAuth','getTwoFactorEnroll']]);
Session::put('backUrl', \URL::previous());
$this->ldap = $ldap;
// $this->ldap = $ldap;
$this->saml = $saml;
}
@@ -105,13 +105,13 @@ class LoginController extends Controller
$samlData = $request->session()->get('saml_login');
if ($saml->isEnabled() && !empty($samlData)) {
try {
LOG::debug("Attempting to log user in by SAML authentication.");
Log::debug("Attempting to log user in by SAML authentication.");
$user = $saml->samlLogin($samlData);
if(!is_null($user)) {
Auth::login($user, true);
Auth::login($user);
} else {
$username = $saml->getUsername();
LOG::debug("SAML user '$username' could not be found in database.");
\Log::warning("SAML user '$username' could not be found in database.");
$request->session()->flash('error', trans('auth/message.signin.error'));
$saml->clearData();
}
@@ -121,7 +121,7 @@ class LoginController extends Controller
$user->save();
}
} catch (\Exception $e) {
LOG::debug("There was an error authenticating the SAML user: " . $e->getMessage());
\Log::warning("There was an error authenticating the SAML user: " . $e->getMessage());
throw new \Exception($e->getMessage());
}
}
@@ -142,8 +142,9 @@ class LoginController extends Controller
*/
private function loginViaLdap(Request $request): User
{
$ldap = \App::make( LdapAd::class);
try {
return $this->ldap->ldapLogin($request->input('username'), $request->input('password'));
return $ldap->ldapLogin($request->input('username'), $request->input('password'));
} catch (\Exception $ex) {
LOG::debug("LDAP user login: " . $ex->getMessage());
throw new \Exception($ex->getMessage());
@@ -157,15 +158,32 @@ class LoginController extends Controller
if (Setting::getSettings()->login_remote_user_enabled == "1" && isset($remote_user) && !empty($remote_user)) {
Log::debug("Authenticating via HTTP header $header_name.");
$pos = strpos($remote_user, '\\');
$strip_prefixes = [
// IIS/AD
// https://github.com/snipe/snipe-it/pull/5862
'\\',
// Google Cloud IAP
// https://cloud.google.com/iap/docs/identity-howto#getting_the_users_identity_with_signed_headers
'accounts.google.com:',
];
$pos = 0;
foreach ($strip_prefixes as $needle) {
if (($pos = strpos($remote_user, $needle)) !== FALSE) {
$pos += strlen($needle);
break;
}
}
if ($pos > 0) {
$remote_user = substr($remote_user, $pos + 1);
$remote_user = substr($remote_user, $pos);
};
try {
$user = User::where('username', '=', $remote_user)->whereNull('deleted_at')->where('activated', '=', '1')->first();
Log::debug("Remote user auth lookup complete");
if(!is_null($user)) Auth::login($user, true);
if(!is_null($user)) Auth::login($user, $request->input('remember'));
} catch(Exception $e) {
Log::debug("There was an error authenticating the Remote user: " . $e->getMessage());
}
@@ -189,8 +207,8 @@ class LoginController extends Controller
return redirect()->back()->withInput()->withErrors($validator);
}
$this->maxLoginAttempts = config('auth.throttle.max_attempts');
$this->lockoutTime = config('auth.throttle.lockout_duration');
$this->maxLoginAttempts = config('auth.passwords.users.throttle.max_attempts');
$this->lockoutTime = config('auth.passwords.users.throttle.lockout_duration');
if ($lockedOut = $this->hasTooManyLoginAttempts($request)) {
$this->fireLockoutEvent($request);
@@ -200,12 +218,12 @@ class LoginController extends Controller
$user = null;
// Should we even check for LDAP users?
if ($this->ldap->init()) {
if (Setting::getSettings()->ldap_enabled) { // avoid hitting the $this->ldap
LOG::debug("LDAP is enabled.");
try {
LOG::debug("Attempting to log user in by LDAP authentication.");
$user = $this->loginViaLdap($request);
Auth::login($user, true);
Auth::login($user, $request->input('remember'));
// If the user was unable to login via LDAP, log the error and let them fall through to
// local authentication.
@@ -234,6 +252,7 @@ class LoginController extends Controller
if ($user = Auth::user()) {
$user->last_login = \Carbon::now();
$user->activated = 1;
$user->save();
}
// Redirect to the users page
@@ -451,8 +470,8 @@ class LoginController extends Controller
*/
protected function hasTooManyLoginAttempts(Request $request)
{
$lockoutTime = config('auth.throttle.lockout_duration');
$maxLoginAttempts = config('auth.throttle.max_attempts');
$lockoutTime = config('auth.passwords.users.throttle.lockout_duration');
$maxLoginAttempts = config('auth.passwords.users.throttle.max_attempts');
return $this->limiter()->tooManyAttempts(
$this->throttleKey($request),

View File

@@ -3,9 +3,13 @@
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Http\Requests\SaveUserRequest;
use App\Models\Setting;
use App\Models\User;
use Illuminate\Foundation\Auth\ResetsPasswords;
use Illuminate\Http\Request;
use Illuminate\Validation\Rule;
use Illuminate\Validation\Validator;
class ResetPasswordController extends Controller
{
@@ -29,6 +33,8 @@ class ResetPasswordController extends Controller
*/
protected $redirectTo = '/';
protected $username = 'username';
/**
* Create a new controller instance.
*
@@ -44,7 +50,7 @@ class ResetPasswordController extends Controller
return [
'token' => 'required',
'username' => 'required',
'password' => 'required|confirmed|'.Setting::passwordComplexityRulesSaving('update'),
'password' => 'confirmed|'.Setting::passwordComplexityRulesSaving('store'),
];
}
@@ -55,7 +61,7 @@ class ResetPasswordController extends Controller
'username', 'password', 'password_confirmation', 'token'
);
}
public function showResetForm(Request $request, $token = null)
{
@@ -67,11 +73,48 @@ class ResetPasswordController extends Controller
);
}
public function reset(Request $request)
{
$messages = [
'password.not_in' => trans('validation.disallow_same_pwd_as_user_fields'),
];
$request->validate($this->rules(), $request->all(), $this->validationErrorMessages());
// Check to see if the user even exists
$user = User::where('username', '=', $request->input('username'))->first();
$broker = $this->broker();
if (strpos(Setting::passwordComplexityRulesSaving('store'), 'disallow_same_pwd_as_user_fields') !== FALSE) {
$request->validate(
[
'password' => 'required|notIn:["'.$user->email.'","'.$user->username.'","'.$user->first_name.'","'.$user->last_name.'"'
], $messages);
}
$response = $broker->reset(
$this->credentials($request), function ($user, $password) {
$this->resetPassword($user, $password);
}
);
return $response == \Password::PASSWORD_RESET
? $this->sendResetResponse($request, $response)
: $this->sendResetFailedResponse($request, $response);
}
protected function sendResetFailedResponse(Request $request, $response)
{
return redirect()->back()
->withInput(['username'=> $request->input('username')])
->withErrors(['username' => trans($response)]);
->withErrors(['username' => trans($response), 'password' => trans($response)]);
}
}
}

View File

@@ -53,8 +53,10 @@ class SamlController extends Controller
if (empty($metadata)) {
return response()->view('errors.403', [], 403);
}
return response($metadata)->header('Content-Type', 'text/xml');
return response()->streamDownload(function () use ($metadata) {
echo $metadata;
}, 'snipe-it-metadata.xml', ['Content-Type' => 'text/xml']);
}
/**
@@ -99,8 +101,8 @@ class SamlController extends Controller
$errors = $auth->getErrors();
if (!empty($errors)) {
Log::debug("There was an error with SAML ACS: " . implode(', ', $errors));
Log::debug("Reason: " . $auth->getLastErrorReason());
Log::error("There was an error with SAML ACS: " . implode(', ', $errors));
Log::error("Reason: " . $auth->getLastErrorReason());
return redirect()->route('login')->with('error', trans('auth/message.signin.error'));
}
@@ -113,7 +115,7 @@ class SamlController extends Controller
* Receives LogoutRequest/LogoutResponse from IdP and flashes
* back to the LoginController for logging out.
*
* /saml/slo
* /saml/sls
*
* @author Johnson Yi <jyi.dev@outlook.com>
*
@@ -126,12 +128,13 @@ class SamlController extends Controller
public function sls(Request $request)
{
$auth = $this->saml->getAuth();
$sloUrl = $auth->processSLO(true, null, null, null, true);
$retrieveParametersFromServer = $this->saml->getSetting('retrieveParametersFromServer', false);
$sloUrl = $auth->processSLO(true, null, $retrieveParametersFromServer, null, true);
$errors = $auth->getErrors();
if (!empty($errors)) {
Log::debug("There was an error with SAML SLS: " . implode(', ', $errors));
Log::debug("Reason: " . $auth->getLastErrorReason());
Log::error("There was an error with SAML SLS: " . implode(', ', $errors));
Log::error("Reason: " . $auth->getLastErrorReason());
return view('errors.403');
}

View File

@@ -124,7 +124,7 @@ class ComponentsController extends Controller
}
$min = $component->numCHeckedOut();
$validator = Validator::make($request->all(), [
"qty" => "required|numeric|gt:$min"
"qty" => "required|numeric|min:$min"
]);
if ($validator->fails()) {

View File

@@ -40,6 +40,24 @@ class CustomFieldsController extends Controller
}
/**
* Just redirect the user back if they try to view the details of a field.
* We already show those details on the listing page.
*
* @see CustomFieldsController::storeField()
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v5.1.5]
* @return Redirect
* @throws \Illuminate\Auth\Access\AuthorizationException
*/
public function show()
{
return redirect()->route("fields.index");
}
/**
* Returns a view with a form to create a new custom field.
*
@@ -133,16 +151,19 @@ class CustomFieldsController extends Controller
*/
public function destroy($field_id)
{
$field = CustomField::find($field_id);
if ($field = CustomField::find($field_id)) {
$this->authorize('delete', $field);
$this->authorize('delete', $field);
if ($field->fieldset->count()>0) {
return redirect()->back()->withErrors(['message' => "Field is in-use"]);
if (($field->fieldset) && ($field->fieldset->count() > 0)) {
return redirect()->back()->withErrors(['message' => "Field is in-use"]);
}
$field->delete();
return redirect()->route("fields.index")
->with("success", trans('admin/custom_fields/message.field.delete.success'));
}
$field->delete();
return redirect()->route("fields.index")
->with("success", trans('admin/custom_fields/message.field.delete.success'));
return redirect()->back()->withErrors(['message' => "Field does not exist"]);
}

View File

@@ -0,0 +1,23 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Routing\Controller as BaseController;
/**
* This controller provide the healthz route for
* the Snipe-IT Asset Management application.
*
* @version v1.0
*/
class HealthController extends BaseController
{
/**
* Returns a fixed JSON content ({ "status": "ok"}) which indicate the app is up and running
*/
public function get() {
return response()->json([
"status" => "ok"
]);
}
}

View File

@@ -10,6 +10,7 @@ use Illuminate\Support\Facades\Input;
use Illuminate\Support\Facades\Response;
use Illuminate\Support\Facades\Storage;
use Symfony\Component\HttpFoundation\JsonResponse;
use App\Helpers\StorageHelper;
class LicenseFilesController extends Controller
{
@@ -39,7 +40,8 @@ class LicenseFilesController extends Controller
$upload_success = false;
foreach ($request->file('file') as $file) {
$file_name = 'license-'.date('Y-m-d-His').'-'.$file->getBasename().'.'.$file->getClientOriginalExtension();
$file_name = 'license-'.$license->id.'-'.str_random(8).'-'.str_slug(basename($file->getClientOriginalName(), '.'.$file->getClientOriginalExtension())).'.'.$file->getClientOriginalExtension();
$upload_success = $file->storeAs('private_uploads/licenses', $file_name);
@@ -142,18 +144,18 @@ class LicenseFilesController extends Controller
// We have to override the URL stuff here, since local defaults in Laravel's Flysystem
// won't work, as they're not accessible via the web
if (config('filesystems.default') == 'local') {
return Storage::download($file);
if (config('filesystems.default') == 'local') { // TODO - is there any way to fix this at the StorageHelper layer?
return StorageHelper::downloader($file);
} else {
if ($download != 'true') {
\Log::debug('display the file');
if ($contents = file_get_contents(Storage::url($file))) {
if ($contents = file_get_contents(Storage::url($file))) { // TODO - this will fail on private S3 files or large public ones
return Response::make(Storage::url($file)->header('Content-Type', mime_content_type($file)));
}
return JsonResponse::create(["error" => "Failed validation: "], 500);
}
return Storage::download($file);
return StorageHelper::downloader($file);
}
}

View File

@@ -4,7 +4,9 @@ namespace App\Http\Controllers;
use App\Http\Requests\ImageUploadRequest;
use App\Models\Location;
use App\Models\User;
use Illuminate\Support\Facades\Auth;
use App\Models\Asset;
use Illuminate\Support\Facades\Storage;
/**
@@ -67,7 +69,6 @@ class LocationsController extends Controller
{
$this->authorize('create', Location::class);
$location = new Location();
$location->id = null; // This is required to make Laravels different validation work, it errors if the parameter doesn't exist (maybe a bug)?
$location->name = $request->input('name');
$location->parent_id = $request->input('parent_id', null);
$location->currency = $request->input('currency', '$');
@@ -132,7 +133,6 @@ class LocationsController extends Controller
return redirect()->route('locations.index')->with('error', trans('admin/locations/message.does_not_exist'));
}
// Update the location data
$location->name = $request->input('name');
$location->parent_id = $request->input('parent_id', null);
@@ -212,5 +212,29 @@ class LocationsController extends Controller
return redirect()->route('locations.index')->with('error', trans('admin/locations/message.does_not_exist'));
}
public function print_assigned($id)
{
}
$location = Location::where('id',$id)->first();
$parent = Location::where('id',$location->parent_id)->first();
$manager = User::where('id',$location->manager_id)->first();
$users = User::where('location_id', $id)->with('company', 'department', 'location')->get();
$assets = Asset::where('assigned_to', $id)->where('assigned_type', Location::class)->with('model', 'model.category')->get();
return view('locations/print')->with('assets', $assets)->with('users',$users)->with('location', $location)->with('parent', $parent)->with('manager', $manager);
}
public function print_all_assigned($id)
{
$location = Location::where('id',$id)->first();
$parent = Location::where('id',$location->parent_id)->first();
$manager = User::where('id',$location->manager_id)->first();
$users = User::where('location_id', $id)->with('company', 'department', 'location')->get();
$assets = Asset::where('location_id', $id)->with('model', 'model.category')->get();
return view('locations/print')->with('assets', $assets)->with('users',$users)->with('location', $location)->with('parent', $parent)->with('manager', $manager);
}
}

View File

@@ -70,7 +70,7 @@ class ManufacturersController extends Controller
$manufacturer->support_url = $request->input('support_url');
$manufacturer->support_phone = $request->input('support_phone');
$manufacturer->support_email = $request->input('support_email');
$manufacturer = $request->handleImages($manufacturer,600, public_path().'/uploads/manufacturers');
$manufacturer = $request->handleImages($manufacturer);
@@ -137,7 +137,7 @@ class ManufacturersController extends Controller
$manufacturer->image = null;
}
$manufacturer = $request->handleImages($manufacturer,600, public_path().'/uploads/manufacturers');
$manufacturer = $request->handleImages($manufacturer);
if ($manufacturer->save()) {
@@ -158,7 +158,7 @@ class ManufacturersController extends Controller
public function destroy($manufacturerId)
{
$this->authorize('delete', Manufacturer::class);
if (is_null($manufacturer = Manufacturer::withCount('models as models_count')->find($manufacturerId))) {
if (is_null($manufacturer = Manufacturer::withTrashed()->withCount('models as models_count')->find($manufacturerId))) {
return redirect()->route('manufacturers.index')->with('error', trans('admin/manufacturers/message.not_found'));
}
@@ -174,8 +174,12 @@ class ManufacturersController extends Controller
}
}
// Delete the manufacturer
$manufacturer->delete();
// Soft delete the manufacturer if active, permanent delete if is already deleted
if($manufacturer->deleted_at === NULL) {
$manufacturer->delete();
} else {
$manufacturer->forceDelete();
}
// Redirect to the manufacturers management page
return redirect()->route('manufacturers.index')->with('success', trans('admin/manufacturers/message.delete.success'));
}

View File

@@ -48,10 +48,9 @@ class ProfileController extends Controller
$user->last_name = $request->input('last_name');
$user->website = $request->input('website');
$user->gravatar = $request->input('gravatar');
$user->skin = $request->input('skin');
$user->phone = $request->input('phone');
if (!config('app.lock_passwords')) {
$user->locale = $request->input('locale', 'en');
}
@@ -156,6 +155,28 @@ class ProfileController extends Controller
if (!Hash::check($request->input('current_password'), $user->password)) {
$validator->errors()->add('current_password', trans('validation.hashed_pass'));
}
// This checks to make sure that the user's password isn't the same as their username,
// email address, first name or last name (see https://github.com/snipe/snipe-it/issues/8661)
// While this is handled via SaveUserRequest form request in other places, we have to do this manually
// here because we don't have the username, etc form fields available in the profile password change
// form.
// There may be a more elegant way to do this in the future.
// First let's see if that option is enabled in the settings
if (strpos(Setting::passwordComplexityRulesSaving('store'), 'disallow_same_pwd_as_user_fields') !== FALSE) {
if (($request->input('password') == $user->username) ||
($request->input('password') == $user->email) ||
($request->input('password') == $user->first_name) ||
($request->input('password') == $user->last_name))
{
$validator->errors()->add('password', trans('validation.disallow_same_pwd_as_user_fields'));
}
}
});

View File

@@ -217,6 +217,99 @@ class ReportsController extends Controller
}
/**
* Exports the activity report to CSV
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v5.0.7]
* @return \Illuminate\Http\Response
*/
public function postActivityReport(Request $request)
{
ini_set('max_execution_time', 12000);
$this->authorize('reports.view');
\Debugbar::disable();
$response = new StreamedResponse(function () {
\Log::debug('Starting streamed response');
// Open output stream
$handle = fopen('php://output', 'w');
stream_set_timeout($handle, 2000);
$header = [
trans('general.date'),
trans('general.admin'),
trans('general.action'),
trans('general.type'),
trans('general.item'),
'To',
trans('general.notes'),
'Changed',
];
$executionTime = microtime(true) - $_SERVER["REQUEST_TIME_FLOAT"];
\Log::debug('Starting headers: '.$executionTime);
fputcsv($handle, $header);
$executionTime = microtime(true) - $_SERVER["REQUEST_TIME_FLOAT"];
\Log::debug('Added headers: '.$executionTime);
$actionlogs = Actionlog::with('item', 'user', 'target','location')
->orderBy('created_at', 'DESC')
->chunk(20, function($actionlogs) use($handle) {
$executionTime = microtime(true) - $_SERVER["REQUEST_TIME_FLOAT"];
\Log::debug('Walking results: '.$executionTime);
$count = 0;
foreach ($actionlogs as $actionlog) {
$count++;
$target_name = '';
if ($actionlog->target) {
if ($actionlog->targetType()=='user') {
$target_name = $actionlog->target->getFullNameAttribute();
} else {
$target_name = $actionlog->target->getDisplayNameAttribute();
}
}
$row = [
$actionlog->created_at,
($actionlog->user) ? e($actionlog->user->getFullNameAttribute()) : '',
$actionlog->present()->actionType(),
e($actionlog->itemType()),
($actionlog->itemType()=='user') ? $actionlog->filename : e($actionlog->item->getDisplayNameAttribute()),
$target_name,
($actionlog->note) ? e($actionlog->note): '',
$actionlog->log_meta,
];
fputcsv($handle, $row);
}
});
// Close the output stream
fclose($handle);
$executionTime = microtime(true) - $_SERVER["REQUEST_TIME_FLOAT"];
\Log::debug('-- SCRIPT COMPLETED IN '. $executionTime);
}, 200, [
'Content-Type' => 'text/csv',
'Content-Disposition'
=> 'attachment; filename="activity-report-'.date('Y-m-d-his').'.csv"',
]);
return $response;
}
/**
* Displays license report
*
@@ -310,7 +403,7 @@ class ReportsController extends Controller
*/
public function postCustom(Request $request)
{
ini_set('max_execution_time', 12000);
ini_set('max_execution_time', env('REPORT_TIME_LIMIT', 12000)); //12000 seconds = 200 minutes
$this->authorize('reports.view');
@@ -539,8 +632,16 @@ class ReportsController extends Controller
if (($request->filled('expected_checkin_start')) && ($request->filled('expected_checkin_end'))) {
$assets->whereBetween('assets.expected_checkin', [$request->input('expected_checkin_start'), $request->input('expected_checkin_end')]);
}
if (($request->filled('last_audit_start')) && ($request->filled('last_audit_end'))) {
$assets->whereBetween('assets.last_audit_date', [$request->input('last_audit_start'), $request->input('last_audit_end')]);
}
if (($request->filled('next_audit_start')) && ($request->filled('next_audit_end'))) {
$assets->whereBetween('assets.next_audit_date', [$request->input('next_audit_start'), $request->input('next_audit_end')]);
}
$assets->orderBy('assets.created_at', 'ASC')->chunk(20, function($assets) use($handle, $customfields, $request) {
$assets->orderBy('assets.id', 'ASC')->chunk(20, function($assets) use($handle, $customfields, $request) {
$executionTime = microtime(true) - $_SERVER["REQUEST_TIME_FLOAT"];
\Log::debug('Walking results: '.$executionTime);
@@ -682,7 +783,7 @@ class ReportsController extends Controller
$diff = ($asset->purchase_cost - $depreciation);
$row[] = Helper::formatCurrencyOutput($depreciation);
$row[] = Helper::formatCurrencyOutput($diff);
$row[] = ($asset->depreciated_date()!='') ? $asset->depreciated_date()->format('Y-m-d') : '';
$row[] = ($asset->depreciation) ? $asset->depreciated_date()->format('Y-m-d') : '';
}
if ($request->filled('checkout_date')) {

View File

@@ -21,6 +21,7 @@ use Image;
use Input;
use Redirect;
use Response;
use App\Helpers\StorageHelper;
/**
* This controller handles all actions related to Settings for
@@ -399,6 +400,7 @@ class SettingsController extends Controller
$setting->version_footer = $request->input('version_footer');
$setting->footer_text = $request->input('footer_text');
$setting->skin = $request->input('skin');
$setting->allow_user_skin = $request->input('allow_user_skin');
$setting->show_url_in_emails = $request->input('show_url_in_emails', '0');
$setting->logo_print_assets = $request->input('logo_print_assets', '0');
@@ -577,6 +579,7 @@ class SettingsController extends Controller
$setting->default_currency = $request->input('default_currency', '$');
$setting->date_display_format = $request->input('date_display_format');
$setting->time_display_format = $request->input('time_display_format');
$setting->digit_separator = $request->input('digit_separator');
if ($setting->save()) {
return redirect()->route('settings.index')
@@ -940,6 +943,10 @@ class SettingsController extends Controller
$setting->ldap_tls = $request->input('ldap_tls', '0');
$setting->ldap_pw_sync = $request->input('ldap_pw_sync', '0');
$setting->custom_forgot_pass_url = $request->input('custom_forgot_pass_url');
$setting->ldap_phone_field = $request->input('ldap_phone');
$setting->ldap_jobtitle = $request->input('ldap_jobtitle');
$setting->ldap_country = $request->input('ldap_country');
$setting->ldap_dept = $request->input('ldap_dept');
}
@@ -991,6 +998,11 @@ class SettingsController extends Controller
$setting->saml_sp_x509cert = $request->input('saml_sp_x509cert');
$setting->saml_sp_privatekey = $request->input('saml_sp_privatekey');
}
if (!empty($request->input('saml_sp_x509certNew'))) {
$setting->saml_sp_x509certNew = $request->input('saml_sp_x509certNew');
} else {
$setting->saml_sp_x509certNew = "";
}
$setting->saml_custom_settings = $request->input('saml_custom_settings');
if ($setting->save()) {
@@ -1015,7 +1027,7 @@ class SettingsController extends Controller
$path = 'app/backups';
$backup_files = Storage::files($path);
$files = [];
$files_raw = [];
if (count($backup_files) > 0) {
for ($f = 0; $f < count($backup_files); ++$f) {
@@ -1023,7 +1035,7 @@ class SettingsController extends Controller
// Skip dotfiles like .gitignore and .DS_STORE
if ((substr(basename($backup_files[$f]), 0, 1) != '.')) {
$files[] = [
$files_raw[] = [
'filename' => basename($backup_files[$f]),
'filesize' => Setting::fileSizeConvert(Storage::size($backup_files[$f])),
'modified' => Storage::lastModified($backup_files[$f]),
@@ -1035,6 +1047,9 @@ class SettingsController extends Controller
}
}
// Reverse the array so it lists oldest first
$files = array_reverse($files_raw);
return view('settings/backups', compact('path', 'files'));
}
@@ -1087,7 +1102,7 @@ class SettingsController extends Controller
if (! config('app.lock_passwords')) {
if (Storage::exists($path . '/' . $filename)) {
return Storage::download($path . '/' . $filename);
return StorageHelper::downloader($path . '/' . $filename);
} else {
// Redirect to the backup page
return redirect()->route('settings.backups.index')->with('error', trans('admin/settings/message.backup.file_not_found'));
@@ -1138,6 +1153,7 @@ class SettingsController extends Controller
*/
public function getPurge()
{
\Log::warning('User ID '.Auth::user()->id.' is attempting a PURGE');
return view('settings.purge-form');
}
@@ -1154,6 +1170,8 @@ class SettingsController extends Controller
{
if (! config('app.lock_passwords')) {
if ('DELETE' == $request->input('confirm_purge')) {
\Log::warning('User ID '.Auth::user()->id.' initiated a PURGE!');
// Run a backup immediately before processing
Artisan::call('backup:run');
Artisan::call('snipeit:purge', ['--force' => 'true', '--no-interaction' => true]);
@@ -1213,4 +1231,4 @@ class SettingsController extends Controller
{
return view('settings.logins');
}
}
}

View File

@@ -174,7 +174,7 @@ class BulkUsersController extends Controller
}
$users = User::whereIn('id', $user_raw_array)->get();
$assets = Asset::whereIn('assigned_to', $user_raw_array)->get();
$assets = Asset::whereIn('assigned_to', $user_raw_array)->where('assigned_type', 'App\Models\User')->get();
$accessories = DB::table('accessories_users')->whereIn('assigned_to', $user_raw_array)->get();
$licenses = DB::table('license_seats')->whereIn('assigned_to', $user_raw_array)->get();

View File

@@ -6,6 +6,7 @@ use App\Http\Controllers\Controller;
use App\Services\LdapAd;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Artisan;
use App\Models\User; // Note that this is awful close to 'Users' the namespace above; be careful
class LDAPImportController extends Controller
{
@@ -65,6 +66,7 @@ class LDAPImportController extends Controller
*/
public function store(Request $request)
{
$this->authorize('update', User::class);
// Call Artisan LDAP import command.
$location_id = $request->input('location_id');
Artisan::call('snipeit:ldap-sync', ['--location_id' => $location_id, '--json_summary' => true]);

View File

@@ -33,12 +33,16 @@ class UserFilesController extends Controller
$logActions = [];
$files = $request->file('file');
if (is_null($files)){
return redirect()->back()->with('error', trans('admin/users/message.upload.nofiles'));
}
foreach($files as $file) {
$extension = $file->getClientOriginalExtension();
$filename = 'user-' . $user->id . '-' . str_random(8);
$filename .= '-' . str_slug($file->getClientOriginalName()) . '.' . $extension;
if (!$file->move($destinationPath, $filename)) {
return JsonResponse::create(["error" => "Unabled to move file"], 500);
return redirect()->back()->with('error', trans('admin/users/message.upload.invalidfiles'));
}
//Log the uploaded file to the log
$logAction = new Actionlog();
@@ -57,10 +61,10 @@ class UserFilesController extends Controller
}
$logActions[] = $logAction;
}
// dd($logActions);
return JsonResponse::create($logActions);
// dd($logActions);
return redirect()->back()->with('success', trans('admin/users/message.upload.success'));
}
return JsonResponse::create(["error" => "No User associated with this request"], 500);
return redirect()->back()->with('error', trans('admin/users/message.upload.nofiles'));
}
@@ -117,7 +121,7 @@ class UserFilesController extends Controller
$log = Actionlog::find($fileId);
$file = $log->get_src('users');
return Response::download($file);
return Response::download($file); //FIXME this doesn't use the new StorageHelper yet, but it's complicated...
}
// Prepare the error message
$error = trans('admin/users/message.user_not_found', ['id' => $userId]);

View File

@@ -15,6 +15,7 @@ use App\Models\User;
use App\Notifications\WelcomeNotification;
use Auth;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Support\Facades\Password;
use Input;
use Redirect;
use Str;
@@ -617,4 +618,31 @@ class UsersController extends Controller
->with('show_user', $show_user)
->with('settings', Setting::getSettings());
}
/**
* Send individual password reset email
*
* @author A. Gianotto
* @since [v5.0.15]
* @return \Illuminate\Http\RedirectResponse
*/
public function sendPasswordReset($id) {
if (($user = User::find($id)) && ($user->activated == '1') && ($user->email!='') && ($user->ldap_import == '0')) {
$credentials = ['email' => $user->email];
try {
\Password::sendResetLink($credentials, function (Message $message) use ($user) {
$message->subject($this->getEmailSubject());
});
return redirect()->back()->with('success', trans('admin/users/message.password_reset_sent', ['email' => $user->email]));
} catch (\Exception $e) {
return redirect()->back()->with('error', ' Error sending email. :( ');
}
}
return redirect()->back()->with('error', 'User is not activated, is LDAP synced, or does not have an email address ');
}
}

View File

@@ -10,8 +10,7 @@ use App\Models\User;
use App\Notifications\RequestAssetCancelation;
use App\Notifications\RequestAssetNotification;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Input;
use Redirect;
use Illuminate\Http\Request;
/**
* This controller handles all actions related to the ability for users

View File

@@ -14,6 +14,7 @@ class Kernel extends HttpKernel
* @var array
*/
protected $middleware = [
\App\Http\Middleware\NoSessionStore::class,
\Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class,
\Illuminate\Session\Middleware\StartSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
@@ -38,6 +39,7 @@ class Kernel extends HttpKernel
\App\Http\Middleware\CheckLocale::class,
\App\Http\Middleware\CheckForTwoFactor::class,
\Laravel\Passport\Http\Middleware\CreateFreshApiToken::class,
\App\Http\Middleware\AssetCountForSidebar::class,
],
'api' => [

View File

@@ -0,0 +1,58 @@
<?php
namespace App\Http\Middleware;
use Auth;
use App\Models\Asset;
use Closure;
class AssetCountForSidebar
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
try
{
$total_rtd_sidebar = Asset::RTD()->count();
view()->share('total_rtd_sidebar', $total_rtd_sidebar);
} catch (\Exception $e) {
\Log::debug($e);
}
try {
$total_deployed_sidebar = Asset::Deployed()->count();
view()->share('total_deployed_sidebar', $total_deployed_sidebar);
} catch (\Exception $e) {
\Log::debug($e);
}
try {
$total_archived_sidebar = Asset::Archived()->count();
view()->share('total_archived_sidebar', $total_archived_sidebar);
} catch (\Exception $e) {
\Log::debug($e);
}
try {
$total_pending_sidebar = Asset::Pending()->count();
view()->share('total_pending_sidebar', $total_pending_sidebar);
} catch (\Exception $e) {
\Log::debug($e);
}
try {
$total_undeployable_sidebar = Asset::Undeployable()->count();
view()->share('total_undeployable_sidebar', $total_undeployable_sidebar);
} catch (\Exception $e) {
\Log::debug($e);
}
return $next($request);
}
}

View File

@@ -27,7 +27,7 @@ class CheckForSetup
}
} else {
if (!($request->is('setup*')) && !($request->is('.env'))) {
if (!($request->is('setup*')) && !($request->is('.env')) && !($request->is('health'))) {
return redirect(url('/').'/setup');
}

View File

@@ -0,0 +1,29 @@
<?php
namespace App\Http\Middleware;
use Closure;
class NoSessionStore
{
protected $except = [
'health'
];
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
foreach ($this->except as $except) {
if ($request->is($except)) {
config()->set('session.driver', 'array');
}
}
return $next($request);
}
}

View File

@@ -99,14 +99,14 @@ class SecurityHeaders
// We have to exclude debug mode here because debugbar pulls from a CDN or two
// and it will break things.
if ((config('app.debug')!='true') || (config('app.enable_csp')=='true')) {
if ((config('app.debug')!='true') && (config('app.enable_csp')=='true')) {
$csp_policy[] = "default-src 'self'";
$csp_policy[] = "style-src 'self' 'unsafe-inline'";
$csp_policy[] = "script-src 'self' 'unsafe-inline' 'unsafe-eval'";
$csp_policy[] = "connect-src 'self'";
$csp_policy[] = "object-src 'none'";
$csp_policy[] = "font-src 'self' data:";
$csp_policy[] = "img-src 'self' data: gravatar.com maps.google.com maps.gstatic.com *.googleapis.com";
$csp_policy[] = "img-src 'self' data: ".config('app.url')." ".env('PUBLIC_AWS_URL')." https://secure.gravatar.com http://gravatar.com maps.google.com maps.gstatic.com *.googleapis.com";
$csp_policy = join(';', $csp_policy);
$response->headers->set('Content-Security-Policy', $csp_policy);
}

View File

@@ -12,5 +12,6 @@ class VerifyCsrfToken extends BaseVerifier
* @var array
*/
protected $except = [
'health'
];
}

View File

@@ -24,7 +24,7 @@ class AssetFileRequest extends Request
{
$max_file_size = \App\Helpers\Helper::file_upload_max_size();
return [
'file.*' => 'required|mimes:png,gif,jpg,svg,jpeg,doc,docx,pdf,txt,zip,rar,xls,lic,xml,rtf|max:'.$max_file_size,
'file.*' => 'required|mimes:png,gif,jpg,svg,jpeg,doc,docx,pdf,txt,zip,rar,xls,xlsx,lic,xml,rtf,webp|max:'.$max_file_size,
];
}
}

View File

@@ -27,8 +27,8 @@ class ImageUploadRequest extends Request
public function rules()
{
return [
'image' => 'mimes:png,gif,jpg,jpeg,svg,bmp,svg+xml',
'avatar' => 'mimes:png,gif,jpg,jpeg,svg,bmp,svg+xml',
'image' => 'mimes:png,gif,jpg,jpeg,svg,bmp,svg+xml,webp',
'avatar' => 'mimes:png,gif,jpg,jpeg,svg,bmp,svg+xml,webp',
];
}
@@ -91,8 +91,8 @@ class ImageUploadRequest extends Request
\Log::info('File name will be: '.$file_name);
\Log::debug('File extension is: '. $ext);
if ($image->getClientOriginalExtension()!=='svg') {
\Log::debug('Not an SVG - resize');
if (($image->getClientOriginalExtension()!=='webp') && ($image->getClientOriginalExtension()!=='svg')) {
\Log::debug('Not an SVG or webp - resize');
\Log::debug('Trying to upload to: '.$path.'/'.$file_name);
$upload = Image::make($image->getRealPath())->resize(null, $w, function ($constraint) {
$constraint->aspectRatio();
@@ -102,20 +102,27 @@ class ImageUploadRequest extends Request
// This requires a string instead of an object, so we use ($string)
Storage::disk('public')->put($path.'/'.$file_name, (string)$upload->encode());
// If the file is an SVG, we need to clean it and NOT encode it
} else {
\Log::debug('This is an SVG');
$sanitizer = new Sanitizer();
$dirtySVG = file_get_contents($image->getRealPath());
$cleanSVG = $sanitizer->sanitize($dirtySVG);
// If the file is a webp, we need to just move it since webp support
// needs to be compiled into gd for resizing to be available
if ($image->getClientOriginalExtension()=='webp') {
\Log::debug('This is a webp, just move it');
Storage::disk('public')->put($path.'/'.$file_name, file_get_contents($image));
// If the file is an SVG, we need to clean it and NOT encode it
} else {
try {
\Log::debug('Trying to upload to: '.$path.'/'.$file_name);
Storage::disk('public')->put($path.'/'.$file_name, $cleanSVG);
} catch (\Exception $e) {
\Log::debug('Upload no workie :( ');
\Log::debug($e);
\Log::debug('This is an SVG');
$sanitizer = new Sanitizer();
$dirtySVG = file_get_contents($image->getRealPath());
$cleanSVG = $sanitizer->sanitize($dirtySVG);
try {
\Log::debug('Trying to upload to: '.$path.'/'.$file_name);
Storage::disk('public')->put($path.'/'.$file_name, $cleanSVG);
} catch (\Exception $e) {
\Log::debug('Upload no workie :( ');
\Log::debug($e);
}
}
}

View File

@@ -32,8 +32,9 @@ class ItemImportRequest extends FormRequest
public function import(Import $import)
{
ini_set('max_execution_time', 600); //600 seconds = 10 minutes
ini_set('memory_limit', '500M');
ini_set('max_execution_time', env('IMPORT_TIME_LIMIT', 600)); //600 seconds = 10 minutes
ini_set('memory_limit', env('IMPORT_MEMORY_LIMIT', '500M'));
$filename = config('app.private_uploads') . '/imports/' . $import->file_path;
$import->import_type = $this->input('import-type');
$class = title_case($import->import_type);

View File

@@ -34,7 +34,7 @@ class SaveUserRequest extends FormRequest
{
$rules = [
'manager_id' => "nullable|exists:users,id|different:users.id"
'manager_id' => "nullable|exists:users,id"
];
switch($this->method())

View File

@@ -5,11 +5,13 @@ namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
use OneLogin\Saml2\IdPMetadataParser as OneLogin_Saml2_IdPMetadataParser;
use OneLogin\Saml2\Utils as OneLogin_Saml2_Utils;
use App\Models\Setting;
/**
* This handles validating and cleaning SAML settings provided by the user.
*
* @author Johnson Yi <jyi.dev@outlook.com>
* @author Michael Pietsch <skywalker-11@mi-pietsch.de>
*
* @since 5.0.0
*/
@@ -55,40 +57,11 @@ class SettingsSamlRequest extends FormRequest
}
}
if ($this->input('saml_sp_regenerate_keypair') == '1' || !$this->has('saml_sp_x509cert')) {
$dn = [
"countryName" => "US",
"stateOrProvinceName" => "N/A",
"localityName" => "N/A",
"organizationName" => "Snipe-IT",
"commonName" => "Snipe-IT",
];
$pkey = openssl_pkey_new([
"private_key_bits" => 2048,
"private_key_type" => OPENSSL_KEYTYPE_RSA,
]);
$csr = openssl_csr_new($dn, $pkey, ['digest_alg' => 'sha256']);
$x509 = openssl_csr_sign($csr, null, $pkey, 3650, ['digest_alg' => 'sha256']);
openssl_x509_export($x509, $x509cert);
openssl_pkey_export($pkey, $privateKey);
$errors = [];
while (($error = openssl_error_string() !== false)) {
$errors[] = $error;
}
if (!(empty($x509cert) && empty($privateKey))) {
$this->merge([
'saml_sp_x509cert' => $x509cert,
'saml_sp_privatekey' => $privateKey,
]);
}
}
$was_custom_x509cert = strpos(Setting::getSettings()->saml_custom_settings, 'sp_x509cert') !== false;
$custom_x509cert='';
$custom_privateKey='';
$custom_x509certNew='';
if (!empty($this->input('saml_custom_settings'))) {
$req_custom_settings = preg_split('/\r\n|\r|\n/', $this->input('saml_custom_settings'));
$custom_settings = [];
@@ -103,11 +76,86 @@ class SettingsSamlRequest extends FormRequest
if (!empty($split[0])) {
$custom_settings[] = implode('=', $split);
}
if ($split[0] == 'sp_x509cert') {
$custom_x509cert = $split[1];
} elseif ($split[0] == 'sp_privateKey') {
$custom_privateKey = $split[1];
} elseif ($split[0] == 'sp_x509certNew') {
//to prepare for Key rollover
$custom_x509certNew = $split[1];
}
}
}
$this->merge(['saml_custom_settings' => implode(PHP_EOL, $custom_settings) . PHP_EOL]);
}
$cert_updated=false;
if (!empty($custom_x509cert) && !empty($custom_privateKey)) {
// custom certificate and private key are defined
$cert_updated=true;
$x509 = openssl_x509_read($custom_x509cert);
$pkey = openssl_pkey_get_private($custom_privateKey);
} elseif ($this->input('saml_sp_regenerate_keypair') == '1' || !$this->has('saml_sp_x509cert') || $was_custom_x509cert) {
// key regeneration requested, no certificate defined yet or previous custom certicate was removed
error_log("regen");
$cert_updated=true;
$dn = [
"countryName" => "US",
"stateOrProvinceName" => "N/A",
"localityName" => "N/A",
"organizationName" => "Snipe-IT",
"commonName" => "Snipe-IT",
];
$pkey = openssl_pkey_new([
"private_key_bits" => 2048,
"private_key_type" => OPENSSL_KEYTYPE_RSA,
]);
$csr = openssl_csr_new($dn, $pkey, ['digest_alg' => 'sha256']);
if ($csr) {
$x509 = openssl_csr_sign($csr, null, $pkey, 3650, ['digest_alg' => 'sha256']);
openssl_x509_export($x509, $x509cert);
openssl_pkey_export($pkey, $privateKey);
$errors = [];
while (($error = openssl_error_string() !== false)) {
$errors[] = $error;
}
if (!(empty($x509cert) && empty($privateKey))) {
$this->merge([
'saml_sp_x509cert' => $x509cert,
'saml_sp_privatekey' => $privateKey,
]);
}
} else {
$validator->errors()->add('saml_integration', 'openssl.cnf is missing/invalid');
}
}
if ($custom_x509certNew) {
$x509New = openssl_x509_read($custom_x509certNew);
openssl_x509_export($x509New, $x509certNew);
while (($error = openssl_error_string() !== false)) {
$errors[] = $error;
}
if (!empty($x509certNew)) {
$this->merge([
'saml_sp_x509certNew' => $x509certNew
]);
}
} else {
$this->merge([
'saml_sp_x509certNew' => ""
]);
}
});
}
}

View File

@@ -27,7 +27,7 @@ class SetupUserRequest extends Request
'last_name' => 'required|string|min:1',
'username' => 'required|string|min:2|unique:users,username,NULL,deleted_at',
'email' => 'email|unique:users,email',
'password' => 'required|min:6|confirmed',
'password' => 'required|min:8|confirmed',
'email_domain' => 'required|min:4',
];
}

View File

@@ -68,8 +68,13 @@ class AccessoriesTransformer
$array = array();
foreach ($accessory_users as $user) {
\Log::debug(print_r($user->pivot, true));
\Log::debug(print_r($user->pivot, true));
$array[] = [
'assigned_pivot_id' => $user->pivot->id,
'id' => (int) $user->id,
'username' => e($user->username),
@@ -77,7 +82,8 @@ class AccessoriesTransformer
'first_name'=> e($user->first_name),
'last_name'=> e($user->last_name),
'employee_number' => e($user->employee_num),
'checkout_notes' => $accessory->lastCheckoutArray[0]['note'],
'checkout_notes' => $user->pivot->note,
'last_checkout' => Helper::getFormattedDateObject($user->pivot->created_at, 'datetime'),
'type' => 'user',
'available_actions' => ['checkin' => true]
];

View File

@@ -113,7 +113,7 @@ class ActionlogsTransformer
] : null,
'note' => ($actionlog->note) ? e($actionlog->note): null,
'signature_file' => ($actionlog->signature_filename) ? route('log.signature.view', ['filename' => $actionlog->signature_filename ]) : null,
'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'): null,

View File

@@ -20,12 +20,11 @@ class LicenseSeatsTransformer
return (new DatatablesTransformer)->transformDatatables($array, $total);
}
public function transformLicenseSeat (LicenseSeat $seat, $seat_count)
public function transformLicenseSeat (LicenseSeat $seat, $seat_count=0)
{
$array = [
'id' => (int) $seat->id,
'license_id' => (int) $seat->license->id,
'name' => 'Seat '.$seat_count,
'assigned_user' => ($seat->user) ? [
'id' => (int) $seat->user->id,
'name'=> e($seat->user->present()->fullName),
@@ -49,6 +48,10 @@ class LicenseSeatsTransformer
'user_can_checkout' => (($seat->assigned_to=='') && ($seat->asset_id=='')),
];
if($seat_count != 0) {
$array['name'] = 'Seat '.$seat_count;
}
$permissions_array['available_actions'] = [
'checkout' => Gate::allows('checkout', License::class),
'checkin' => Gate::allows('checkin', License::class),

View File

@@ -29,6 +29,8 @@ class LicensesTransformer
'order_number' => e($license->order_number),
'purchase_order' => e($license->purchase_order),
'purchase_date' => Helper::getFormattedDateObject($license->purchase_date, 'date'),
'termination_date' => Helper::getFormattedDateObject($license->termination_date, 'date'),
'depreciation' => ($license->depreciation) ? ['id' => (int) $license->depreciation->id,'name'=> e($license->depreciation->name)] : null,
'purchase_cost' => e($license->purchase_cost),
'notes' => e($license->notes),
'expiration_date' => Helper::getFormattedDateObject($license->expiration_date, 'date'),

View File

@@ -47,6 +47,7 @@ class LocationsTransformer
'assets_count' => (int) $location->assets_count,
'users_count' => (int) $location->users_count,
'currency' => ($location->currency) ? e($location->currency) : null,
'ldap_ou' => ($location->ldap_ou) ? e($location->ldap_ou) : null,
'created_at' => Helper::getFormattedDateObject($location->created_at, 'datetime'),
'updated_at' => Helper::getFormattedDateObject($location->updated_at, 'datetime'),
'parent' => ($location->parent) ? [

View File

@@ -25,15 +25,15 @@ class SelectlistTransformer
foreach ($select_items as $select_item) {
$items_array[]= [
'id' => (int) $select_item->id,
'text' => ($select_item->use_text) ? e($select_item->use_text) : e($select_item->name),
'image' => ($select_item->use_image) ? e($select_item->use_image) : null,
'text' => ($select_item->use_text) ? $select_item->use_text : $select_item->name,
'image' => ($select_item->use_image) ? $select_item->use_image : null,
];
}
$results = [
'items' => $items_array,
'results' => $items_array,
'pagination' =>
[
'more' => ($select_items->currentPage() >= $select_items->lastPage()) ? false : true,

View File

@@ -27,6 +27,7 @@ class UsersTransformer
'first_name' => e($user->first_name),
'last_name' => e($user->last_name),
'username' => e($user->username),
'locale' => ($user->locale) ? e($user->locale) : null,
'employee_num' => e($user->employee_num),
'manager' => ($user->manager) ? [
'id' => (int) $user->manager->id,
@@ -52,9 +53,9 @@ class UsersTransformer
'notes'=> e($user->notes),
'permissions' => $user->decodePermissions(),
'activated' => ($user->activated =='1') ? true : false,
'ldap_import' => ($user->ldap_import =='1') ? true : false,
'two_factor_activated' => ($user->two_factor_active()) ? true : false,
'two_factor_enrolled' => ($user->two_factor_active_and_enrolled()) ? true : false,
'assets_count' => (int) $user->assets_count,
'licenses_count' => (int) $user->licenses_count,
'accessories_count' => (int) $user->accessories_count,
@@ -63,6 +64,7 @@ class UsersTransformer
'created_at' => Helper::getFormattedDateObject($user->created_at, 'datetime'),
'updated_at' => Helper::getFormattedDateObject($user->updated_at, 'datetime'),
'last_login' => Helper::getFormattedDateObject($user->last_login, 'datetime'),
'deleted_at' => ($user->deleted_at) ? Helper::getFormattedDateObject($user->deleted_at, 'datetime') : null,
];
$permissions_array['available_actions'] = [

View File

@@ -120,8 +120,10 @@ class AssetImporter extends ItemImporter
$this->log('Asset ' . $this->item["name"] . ' with serial number ' . $this->item['serial'] . ' was created');
// If we have a target to checkout to, lets do so.
//-- user_id is a property of the abstract class Importer, which this class inherits from and it's setted by
//-- the class that needs to use it (command importer or GUI importer inside the project).
if(isset($target)) {
$asset->fresh()->checkOut($target);
$asset->fresh()->checkOut($target, $this->user_id);
}
return;
}

View File

@@ -125,7 +125,10 @@ class UserImporter extends ItemImporter
if ($department) {
$this->log('A matching department ' . $department_name . ' already exists');
return $department->id;
} else {
return null;
}
$department = new department();
$department->name = $department_name;
$department->user_id = $this->user_id;
@@ -134,7 +137,8 @@ class UserImporter extends ItemImporter
$this->log('department ' . $department_name . ' was created');
return $department->id;
}
$this->logError($department, 'Company');
$this->logError($department, 'Department');
return null;
}

View File

@@ -234,7 +234,7 @@ class Accessory extends SnipeModel
public function users()
{
return $this->belongsToMany('\App\Models\User', 'accessories_users', 'accessory_id', 'assigned_to')->withPivot('id')->withTrashed();
return $this->belongsToMany('\App\Models\User', 'accessories_users', 'accessory_id', 'assigned_to')->withPivot('id', 'created_at', 'note')->withTrashed();
}
/**

View File

@@ -130,6 +130,8 @@ class Asset extends Depreciable
'supplier_id',
'warranty_months',
'requestable',
'last_checkout',
'expected_checkin',
];
use Searchable;
@@ -164,6 +166,7 @@ class Asset extends Depreciable
'supplier' => ['name'],
'company' => ['name'],
'defaultLoc' => ['name'],
'location' => ['name'],
'model' => ['name', 'model_number'],
'model.category' => ['name'],
'model.manufacturer' => ['name'],
@@ -177,11 +180,6 @@ class Asset extends Depreciable
*/
public function save(array $params = [])
{
$settings = \App\Models\Setting::getSettings();
// I don't remember why we have this here? Asset tag would always be required, even if auto increment is on...
$this->rules['asset_tag'] = ($settings->auto_increment_assets == '1') ? 'max:255' : 'required';
if($this->model_id != '') {
$model = AssetModel::find($this->model_id);
@@ -232,7 +230,10 @@ class Asset extends Depreciable
}
/**
* Determines if an asset is available for checkout
* Determines if an asset is available for checkout.
* This checks to see if the it's checked out to an invalid (deleted) user
* OR if the assigned_to and deleted_at fields on the asset are empty AND
* that the status is deployable
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v3.0]
@@ -240,12 +241,18 @@ class Asset extends Depreciable
*/
public function availableForCheckout()
{
if (
(empty($this->assigned_to)) &&
(empty($this->deleted_at)) &&
(($this->assetstatus) && ($this->assetstatus->deployable == 1)))
{
return true;
// This asset is not currently assigned to anyone and is not deleted...
if ((!$this->assigned_to) && (!$this->deleted_at)) {
// The asset status is not archived and is deployable
if (($this->assetstatus) && ($this->assetstatus->archived == '0')
&& ($this->assetstatus->deployable == '1'))
{
return true;
}
}
return false;
}
@@ -301,8 +308,14 @@ class Asset extends Depreciable
}
if ($this->save()) {
event(new CheckoutableCheckedOut($this, $target, Auth::user(), $note));
if (is_integer($admin)){
$checkedOutBy = User::findOrFail($admin);
} elseif (get_class($admin) === 'App\Models\User') {
$checkedOutBy = $admin;
} else {
$checkedOutBy = Auth::user();
}
event(new CheckoutableCheckedOut($this, $target, $checkedOutBy, $note));
$this->increment('checkout_counter', 1);
return true;
@@ -424,7 +437,7 @@ class Asset extends Depreciable
*/
public function assignedTo()
{
return $this->morphTo('assigned', 'assigned_type', 'assigned_to');
return $this->morphTo('assigned', 'assigned_type', 'assigned_to')->withTrashed();
}
/**

View File

@@ -165,6 +165,8 @@ class Category extends SnipeModel
return $this->components()->count();
case 'consumable':
return $this->consumables()->count();
case 'license':
return $this->licenses()->count();
}
return '0';
}

View File

@@ -78,7 +78,7 @@ final class Company extends SnipeModel
$company_id = null;
}
$table = ($table_name) ? DB::getTablePrefix().$table_name."." : '';
$table = ($table_name) ? $table_name."." : '';
if(\Schema::hasColumn($query->getModel()->getTable(), $column)){
return $query->where($table.$column, '=', $company_id);

View File

@@ -285,9 +285,13 @@ class CustomField extends Model
*/
public function formatFieldValuesAsArray()
{
$result = [];
$arr = preg_split("/\\r\\n|\\r|\\n/", $this->field_values);
$result[''] = 'Select '.strtolower($this->format);
if (($this->element!='checkbox') && ($this->element!='radio')) {
$result[''] = 'Select '.strtolower($this->format);
}
for ($x = 0; $x < count($arr); $x++) {
$arr_parts = explode('|', $arr[$x]);
@@ -349,16 +353,16 @@ class CustomField extends Model
* @since [v4.1.10]
* @return array
*/
public function validationRules()
public function validationRules($regex_format = null)
{
return [
"name" => "required|unique:custom_fields",
"element" => [
"required",
Rule::in(['text', 'listbox'])
Rule::in(['text', 'listbox', 'textarea', 'checkbox', 'radio'])
],
'format' => [
Rule::in(array_merge(array_keys(CustomField::PREDEFINED_FORMATS), CustomField::PREDEFINED_FORMATS))
Rule::in(array_merge(array_keys(CustomField::PREDEFINED_FORMATS), CustomField::PREDEFINED_FORMATS, [$regex_format]))
],
'field_encrypted' => "nullable|boolean"
];

311
app/Models/Ldap.php Normal file
View File

@@ -0,0 +1,311 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use App\Models\User;
use App\Models\Setting;
use Exception;
use Input;
use Log;
class Ldap extends Model
{
/**
* Makes a connection to LDAP using the settings in Admin > Settings.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v3.0]
* @return connection
*/
public static function connectToLdap()
{
$ldap_host = Setting::getSettings()->ldap_server;
$ldap_version = Setting::getSettings()->ldap_version;
$ldap_server_cert_ignore = Setting::getSettings()->ldap_server_cert_ignore;
$ldap_use_tls = Setting::getSettings()->ldap_tls;
// If we are ignoring the SSL cert we need to setup the environment variable
// before we create the connection
if ($ldap_server_cert_ignore=='1') {
putenv('LDAPTLS_REQCERT=never');
}
// If the user specifies where CA Certs are, make sure to use them
if (env("LDAPTLS_CACERT")) {
putenv("LDAPTLS_CACERT=".env("LDAPTLS_CACERT"));
}
$connection = @ldap_connect($ldap_host);
if (!$connection) {
throw new Exception('Could not connect to LDAP server at '.$ldap_host.'. Please check your LDAP server name and port number in your settings.');
}
// Needed for AD
ldap_set_option($connection, LDAP_OPT_REFERRALS, 0);
ldap_set_option($connection, LDAP_OPT_PROTOCOL_VERSION, $ldap_version);
ldap_set_option($connection, LDAP_OPT_NETWORK_TIMEOUT, 20);
if ($ldap_use_tls=='1') {
ldap_start_tls($connection);
}
return $connection;
}
/**
* Binds/authenticates the user to LDAP, and returns their attributes.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v3.0]
* @param $username
* @param $password
* @param bool|false $user
* @return bool true if the username and/or password provided are valid
* false if the username and/or password provided are invalid
* array of ldap_attributes if $user is true
*
*/
static function findAndBindUserLdap($username, $password)
{
$settings = Setting::getSettings();
$connection = Ldap::connectToLdap();
$ldap_username_field = $settings->ldap_username_field;
$baseDn = $settings->ldap_basedn;
$userDn = $ldap_username_field.'='.$username.','.$settings->ldap_basedn;
if ($settings->is_ad =='1') {
// Check if they are using the userprincipalname for the username field.
// If they are, we can skip building the UPN to authenticate against AD
if ($ldap_username_field=='userprincipalname') {
$userDn = $username;
} else {
// In case they haven't added an AD domain
$userDn = ($settings->ad_domain != '') ? $username.'@'.$settings->ad_domain : $username.'@'.$settings->email_domain;
}
}
\Log::debug('Attempting to login using distinguished name:'.$userDn);
$filterQuery = $settings->ldap_auth_filter_query . $username;
$filter = Setting::getSettings()->ldap_filter;
$filterQuery = "({$filter}({$filterQuery}))";
\Log::debug('Filter query: '.$filterQuery);
if (!$ldapbind = @ldap_bind($connection, $userDn, $password)) {
if(!$ldapbind = Ldap::bindAdminToLdap($connection)){
return false;
}
}
if (!$results = ldap_search($connection, $baseDn, $filterQuery)) {
throw new Exception('Could not search LDAP: ');
}
if (!$entry = ldap_first_entry($connection, $results)) {
return false;
}
if (!$user = ldap_get_attributes($connection, $entry)) {
return false;
}
return array_change_key_case($user);
}
/**
* Binds/authenticates an admin to LDAP for LDAP searching/syncing.
* Here we also return a better error if the app key is donked.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v3.0]
* @param bool|false $user
* @return bool true if the username and/or password provided are valid
* false if the username and/or password provided are invalid
*
*/
static function bindAdminToLdap($connection)
{
$ldap_username = Setting::getSettings()->ldap_uname;
// Lets return some nicer messages for users who donked their app key, and disable LDAP
try {
$ldap_pass = \Crypt::decrypt(Setting::getSettings()->ldap_pword);
} catch (Exception $e) {
throw new Exception('Your app key has changed! Could not decrypt LDAP password using your current app key, so LDAP authentication has been disabled. Login with a local account, update the LDAP password and re-enable it in Admin > Settings.');
}
if (!$ldapbind = @ldap_bind($connection, $ldap_username, $ldap_pass)) {
throw new Exception('Could not bind to LDAP: '.ldap_error($connection));
}
}
/**
* Parse and map LDAP attributes based on settings
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v3.0]
*
* @param $ldapatttibutes
* @return array|bool
*/
static function parseAndMapLdapAttributes($ldapattributes)
{
//Get LDAP attribute config
$ldap_result_username = Setting::getSettings()->ldap_username_field;
$ldap_result_emp_num = Setting::getSettings()->ldap_emp_num;
$ldap_result_last_name = Setting::getSettings()->ldap_lname_field;
$ldap_result_first_name = Setting::getSettings()->ldap_fname_field;
$ldap_result_email = Setting::getSettings()->ldap_email;
$ldap_result_phone = Setting::getSettings()->ldap_phone;
$ldap_result_jobtitle = Setting::getSettings()->ldap_jobtitle;
$ldap_result_country = Setting::getSettings()->ldap_country;
$ldap_result_dept = Setting::getSettings()->ldap_dept;
// Get LDAP user data
$item = array();
$item["username"] = isset($ldapattributes[$ldap_result_username][0]) ? $ldapattributes[$ldap_result_username][0] : "";
$item["employee_number"] = isset($ldapattributes[$ldap_result_emp_num][0]) ? $ldapattributes[$ldap_result_emp_num][0] : "";
$item["lastname"] = isset($ldapattributes[$ldap_result_last_name][0]) ? $ldapattributes[$ldap_result_last_name][0] : "";
$item["firstname"] = isset($ldapattributes[$ldap_result_first_name][0]) ? $ldapattributes[$ldap_result_first_name][0] : "";
$item["email"] = isset($ldapattributes[$ldap_result_email][0]) ? $ldapattributes[$ldap_result_email][0] : "" ;
$item["telephone"] = isset($ldapattributes[$ldap_result_phone][0]) ?$ldapattributes[$ldap_result_phone][0] : "";
$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] : "";
return $item;
}
/**
* Create user from LDAP attributes
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v3.0]
* @param $ldapatttibutes
* @return array|bool
*/
static function createUserFromLdap($ldapatttibutes)
{
$item = Ldap::parseAndMapLdapAttributes($ldapatttibutes);
// Create user from LDAP data
if (!empty($item["username"])) {
$user = new User;
$user->first_name = $item["firstname"];
$user->last_name = $item["lastname"];
$user->username = $item["username"];
$user->email = $item["email"];
if (Setting::getSettings()->ldap_pw_sync=='1') {
$user->password = bcrypt(Input::get("password"));
} else {
$pass = substr(str_shuffle("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"), 0, 25);
$user->password = bcrypt($pass);
}
$user->activated = 1;
$user->ldap_import = 1;
$user->notes = 'Imported on first login from LDAP';
if ($user->save()) {
return $user;
} else {
LOG::debug('Could not create user.'.$user->getErrors());
throw new Exception("Could not create user: ".$user->getErrors());
}
}
return false;
}
/**
* Searches LDAP
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v3.0]
* @param $ldapatttibutes
* @param $base_dn
* @return array|bool
*/
static function findLdapUsers($base_dn = null)
{
$ldapconn = Ldap::connectToLdap();
$ldap_bind = Ldap::bindAdminToLdap($ldapconn);
// Default to global base DN if nothing else is provided.
if (is_null($base_dn)) {
$base_dn = Setting::getSettings()->ldap_basedn;
}
$filter = Setting::getSettings()->ldap_filter;
// Set up LDAP pagination for very large databases
$page_size = 500;
$cookie = '';
$result_set = array();
$global_count = 0;
// Perform the search
do {
// Paginate (non-critical, if not supported by server)
if (!$ldap_paging = @ldap_control_paged_result($ldapconn, $page_size, false, $cookie)) {
throw new Exception('Problem with your LDAP connection. Try checking the Use TLS setting in Admin > Settings. ');
}
if ($filter != '' && substr($filter, 0, 1) != '(') { // wrap parens around NON-EMPTY filters that DON'T have them, for back-compatibility with AdLdap2-based filters
$filter = "($filter)";
} elseif ($filter == '') {
$filter = "(cn=*)";
}
$search_results = ldap_search($ldapconn, $base_dn, $filter);
if (!$search_results) {
return redirect()->route('users.index')->with('error', trans('admin/users/message.error.ldap_could_not_search').ldap_error($ldapconn)); // FIXME this is never called in any routed context - only from the Artisan command. So this redirect will never work.
}
// Get results from page
$results = ldap_get_entries($ldapconn, $search_results);
if (!$results) {
return redirect()->route('users.index')->with('error', trans('admin/users/message.error.ldap_could_not_get_entries').ldap_error($ldapconn)); // FIXME this is never called in any routed context - only from the Artisan command. So this redirect will never work.
}
// Add results to result set
$global_count += $results['count'];
$result_set = array_merge($result_set, $results);
@ldap_control_paged_result_response($ldapconn, $search_results, $cookie);
} while ($cookie !== null && $cookie != '');
// Clean up after search
$result_set['count'] = $global_count;
$results = $result_set;
@ldap_control_paged_result($ldapconn, 0);
return $results;
}
}

View File

@@ -108,6 +108,7 @@ class License extends Depreciable
'manufacturer' => ['name'],
'company' => ['name'],
'category' => ['name'],
'depreciation' => ['name'],
];
/**

View File

@@ -20,6 +20,16 @@ class LicenseSeat extends SnipeModel implements ICompanyableChild
protected $guarded = 'id';
protected $table = 'license_seats';
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'assigned_to',
'asset_id'
];
use Acceptable;
public function getCompanyableParents()

View File

@@ -23,12 +23,12 @@ class Location extends SnipeModel
protected $rules = array(
'name' => 'required|min:2|max:255|unique_undeleted',
'city' => 'min:2|max:255|nullable',
'country' => 'min:2|max:2|nullable',
'country' => 'min:2|max:255|nullable',
'address' => 'max:80|nullable',
'address2' => 'max:80|nullable',
'zip' => 'min:3|max:10|nullable',
'manager_id' => 'exists:users,id|nullable',
'parent_id' => 'nullable|exists:locations,id|different:id',
'parent_id' => 'non_circular:locations,id'
);
protected $casts = [
@@ -76,7 +76,7 @@ class Location extends SnipeModel
*
* @var array
*/
protected $searchableAttributes = ['name', 'address', 'city', 'state', 'zip', 'created_at'];
protected $searchableAttributes = ['name', 'address', 'city', 'state', 'zip', 'created_at', 'ldap_ou'];
/**
* The relations and their attributes that should be included when searching the model.

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