mirror of
https://github.com/snipe/snipe-it.git
synced 2024-12-24 05:04:07 -08:00
Merge branch 'hotfixes/2fa_qr' into develop
# Conflicts: # .all-contributorsrc # Dockerfile # README.md # app/Console/Commands/LdapSync.php # app/Http/Controllers/Api/ImportController.php # app/Http/Controllers/AssetModelsController.php # app/Http/Controllers/Assets/AssetsController.php # app/Http/Controllers/Auth/LoginController.php # app/Http/Controllers/CategoriesController.php # app/Http/Controllers/CompaniesController.php # app/Http/Controllers/DepartmentsController.php # app/Http/Controllers/ImportsController.php # app/Http/Controllers/LocationsController.php # app/Http/Controllers/ManufacturersController.php # app/Http/Controllers/SuppliersController.php # app/Http/Requests/ItemImportRequest.php # app/Http/Transformers/ActionlogsTransformer.php # composer.json # composer.lock # config/app.php # config/version.php # docker/startup.sh # public/css/build/all.css # public/css/dist/all.css # public/js/build/all.js # public/js/build/vue.js # public/js/build/vue.js.map # public/js/dist/all.js # public/mix-manifest.json
This commit is contained in:
commit
bca82684a1
18
Dockerfile
18
Dockerfile
|
@ -26,6 +26,7 @@ vim \
|
|||
git \
|
||||
cron \
|
||||
mysql-client \
|
||||
supervisor \
|
||||
cron \
|
||||
gcc \
|
||||
make \
|
||||
|
@ -92,7 +93,9 @@ RUN \
|
|||
&& 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 docker "/var/lib/snipeit/keys/"
|
||||
&& chown docker "/var/lib/snipeit/keys/" \
|
||||
&& chmod +x /var/www/html/artisan \
|
||||
&& echo "Finished setting up application in /var/www/html"
|
||||
|
||||
############## DEPENDENCIES via COMPOSER ###################
|
||||
|
||||
|
@ -119,16 +122,11 @@ VOLUME ["/var/lib/snipeit"]
|
|||
|
||||
##### START SERVER
|
||||
|
||||
COPY docker/entrypoint.sh /entrypoint.sh
|
||||
RUN chmod +x /entrypoint.sh
|
||||
COPY docker/startup.sh docker/supervisord.conf /
|
||||
COPY docker/supervisor-exit-event-listener /usr/bin/supervisor-exit-event-listener
|
||||
RUN chmod +x /startup.sh /usr/bin/supervisor-exit-event-listener
|
||||
|
||||
# Add Tini
|
||||
ENV TINI_VERSION v0.14.0
|
||||
ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /tini
|
||||
RUN chmod +x /tini
|
||||
ENTRYPOINT ["/tini", "--"]
|
||||
|
||||
CMD ["/entrypoint.sh"]
|
||||
CMD ["/startup.sh"]
|
||||
|
||||
EXPOSE 80
|
||||
EXPOSE 443
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
[![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&utm_medium=referral&utm_content=snipe/snipe-it&utm_campaign=Badge_Grade)
|
||||
[![All Contributors](https://img.shields.io/badge/all_contributors-201-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-180-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)
|
||||
|
||||
|
||||
## Snipe-IT - Open Source Asset Management System
|
||||
|
|
|
@ -179,7 +179,7 @@ class AssetModelsController extends Controller
|
|||
try {
|
||||
Storage::disk('public')->delete('assetmodels/'.$assetmodel->image);
|
||||
} catch (\Exception $e) {
|
||||
\Log::error($e);
|
||||
\Log::info($e);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@ class ImportController extends Controller
|
|||
*/
|
||||
public function index()
|
||||
{
|
||||
//
|
||||
$this->authorize('import');
|
||||
$imports = Import::latest()->get();
|
||||
return (new ImportsTransformer)->transformImports($imports);
|
||||
|
||||
|
@ -39,10 +39,8 @@ class ImportController extends Controller
|
|||
*/
|
||||
public function store()
|
||||
{
|
||||
//
|
||||
if (!Company::isCurrentUserAuthorized()) {
|
||||
return redirect()->route('hardware.index')->with('error', trans('general.insufficient_permissions'));
|
||||
} elseif (!config('app.lock_passwords')) {
|
||||
$this->authorize('import');
|
||||
if (!config('app.lock_passwords')) {
|
||||
$files = Input::file('files');
|
||||
$path = config('app.private_uploads').'/imports';
|
||||
$results = [];
|
||||
|
@ -119,7 +117,7 @@ class ImportController extends Controller
|
|||
*/
|
||||
public function process(ItemImportRequest $request, $import_id)
|
||||
{
|
||||
$this->authorize('create', Asset::class);
|
||||
$this->authorize('import');
|
||||
// Run a backup immediately before processing
|
||||
Artisan::call('backup:run');
|
||||
$errors = $request->import(Import::find($import_id));
|
||||
|
|
|
@ -54,6 +54,7 @@ class UsersController extends Controller
|
|||
'users.phone',
|
||||
'users.state',
|
||||
'users.two_factor_enrolled',
|
||||
'users.two_factor_optin',
|
||||
'users.updated_at',
|
||||
'users.username',
|
||||
'users.zip',
|
||||
|
|
|
@ -195,7 +195,7 @@ class AssetModelsController extends Controller
|
|||
try {
|
||||
Storage::disk('public')->delete('models/'.$model->image);
|
||||
} catch (\Exception $e) {
|
||||
\Log::error($e);
|
||||
\Log::info($e);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -308,7 +308,7 @@ class AssetsController extends Controller
|
|||
unlink(public_path().'/uploads/assets/'.$asset->image);
|
||||
$asset->image = '';
|
||||
} catch (\Exception $e) {
|
||||
\Log::error($e);
|
||||
\Log::info($e);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -15,6 +15,11 @@ use Illuminate\Support\Facades\Log;
|
|||
use Illuminate\Support\Facades\Session;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Redirect;
|
||||
use Log;
|
||||
use View;
|
||||
use Otp\Otp;
|
||||
use Otp\GoogleAuthenticator;
|
||||
use ParagonIE\ConstantTime\Encoding;
|
||||
|
||||
/**
|
||||
* This controller handles authentication for the user, including local
|
||||
|
@ -198,22 +203,24 @@ class LoginController extends Controller
|
|||
return redirect()->route('login')->with('error', 'You must be logged in.');
|
||||
}
|
||||
|
||||
$user = Auth::user();
|
||||
$google2fa = app()->make('pragmarx.google2fa');
|
||||
|
||||
if ($user->two_factor_secret=='') {
|
||||
$user->two_factor_secret = $google2fa->generateSecretKey(32);
|
||||
$user->save();
|
||||
$settings = Setting::getSettings();
|
||||
$user = Auth::user();
|
||||
|
||||
if (($user->two_factor_secret!='') && ($user->two_factor_enrolled==1)) {
|
||||
return redirect()->route('two-factor')->with('error', 'Your device is already enrolled.');
|
||||
}
|
||||
|
||||
|
||||
$google2fa_url = $google2fa->getQRCodeInline(
|
||||
urlencode(Setting::getSettings()->site_name),
|
||||
urlencode($user->username),
|
||||
$user->two_factor_secret
|
||||
);
|
||||
|
||||
return view('auth.two_factor_enroll')->with('google2fa_url', $google2fa_url);
|
||||
new Otp();
|
||||
$secret = GoogleAuthenticator::generateRandom();
|
||||
$user->two_factor_secret = $secret;
|
||||
$user->save();
|
||||
|
||||
$barcode = new \Com\Tecnick\Barcode\Barcode();
|
||||
$barcode_obj = $barcode->getBarcodeObj('QRCODE', 'otpauth://totp/'.urlencode($settings->site_name).':'.urlencode($user->username).'?secret='.urlencode($secret).'&issuer=Snipe-IT&period=30', 300, 300, 'black', array(-2, -2, -2, -2));
|
||||
return view('auth.two_factor_enroll')->with('barcode_obj', $barcode_obj);
|
||||
|
||||
}
|
||||
|
||||
|
@ -242,18 +249,23 @@ class LoginController extends Controller
|
|||
return redirect()->route('login')->with('error', 'You must be logged in.');
|
||||
}
|
||||
|
||||
$user = Auth::user();
|
||||
$secret = $request->get('two_factor_secret');
|
||||
$google2fa = app()->make('pragmarx.google2fa');
|
||||
$valid = $google2fa->verifyKey($user->two_factor_secret, $secret);
|
||||
if (!$request->has('two_factor_secret')) {
|
||||
return redirect()->route('two-factor')->with('error', 'Two-factor code is required.');
|
||||
}
|
||||
|
||||
if ($valid) {
|
||||
$user = Auth::user();
|
||||
$otp = new Otp();
|
||||
|
||||
|
||||
|
||||
if ($otp->checkTotp(Encoding::base32DecodeUpper($user->two_factor_secret), $request->get('two_factor_secret'))) {
|
||||
$user->two_factor_enrolled = 1;
|
||||
$user->save();
|
||||
$request->session()->put('2fa_authed', 'true');
|
||||
return redirect()->route('home')->with('success', 'You are logged in!');
|
||||
}
|
||||
|
||||
\Log::debug('Did not match');
|
||||
return redirect()->route('two-factor')->with('error', 'Invalid two-factor code');
|
||||
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ class ImportsController extends Controller
|
|||
*/
|
||||
public function index()
|
||||
{
|
||||
$this->authorize('create', Asset::class);
|
||||
$this->authorize('import');
|
||||
$imports = (new ImportsTransformer)->transformImports(Import::latest()->get());
|
||||
return view('importer/import')->with('imports', $imports);
|
||||
}
|
||||
|
|
|
@ -164,7 +164,7 @@ class ManufacturersController extends Controller
|
|||
try {
|
||||
Storage::disk('public')->delete('manufacturers/'.$manufacturer->image);
|
||||
} catch (\Exception $e) {
|
||||
\Log::error($e);
|
||||
\Log::info($e);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -52,6 +52,8 @@ class UsersTransformer
|
|||
'permissions' => $user->decodePermissions(),
|
||||
'activated' => ($user->activated =='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,
|
||||
|
|
|
@ -525,7 +525,11 @@ class User extends SnipeModel implements AuthenticatableContract, AuthorizableCo
|
|||
}
|
||||
|
||||
/**
|
||||
* Check whether two-factor authorization is required and the user has activated it
|
||||
* Check whether two-factor authorization is requiredfor this user
|
||||
*
|
||||
* 0 = 2FA disabled
|
||||
* 1 = 2FA optional
|
||||
* 2 = 2FA universally required
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0]
|
||||
|
@ -534,10 +538,45 @@ class User extends SnipeModel implements AuthenticatableContract, AuthorizableCo
|
|||
*/
|
||||
public function two_factor_active () {
|
||||
|
||||
if (Setting::getSettings()->two_factor_enabled !='0') {
|
||||
if (($this->two_factor_optin =='1') && ($this->two_factor_enrolled)) {
|
||||
return true;
|
||||
}
|
||||
// If the 2FA is optional and the user has opted in
|
||||
if ((Setting::getSettings()->two_factor_enabled =='1') && ($this->two_factor_optin =='1'))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
// If the 2FA is required for everyone so is implicitly active
|
||||
elseif (Setting::getSettings()->two_factor_enabled =='2')
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether two-factor authorization is required and the user has activated it
|
||||
* and enrolled a device
|
||||
*
|
||||
* 0 = 2FA disabled
|
||||
* 1 = 2FA optional
|
||||
* 2 = 2FA universally required
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.6.14]
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function two_factor_active_and_enrolled () {
|
||||
|
||||
// If the 2FA is optional and the user has opted in and is enrolled
|
||||
if ((Setting::getSettings()->two_factor_enabled =='1') && ($this->two_factor_optin =='1') && ($this->two_factor_enrolled =='1'))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
// If the 2FA is required for everyone and the user has enrolled
|
||||
elseif ((Setting::getSettings()->two_factor_enabled =='2') && ($this->two_factor_enrolled))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
|
|
|
@ -53,7 +53,7 @@ abstract class SnipePermissionsPolicy
|
|||
/**
|
||||
* Determine whether the user can view the accessory.
|
||||
*
|
||||
* @param \App\User $user
|
||||
* @param \App\Models\User $user
|
||||
* @return mixed
|
||||
*/
|
||||
public function view(User $user, $item = null)
|
||||
|
@ -64,7 +64,7 @@ abstract class SnipePermissionsPolicy
|
|||
/**
|
||||
* Determine whether the user can create accessories.
|
||||
*
|
||||
* @param \App\User $user
|
||||
* @param \App\Models\User $user
|
||||
* @return mixed
|
||||
*/
|
||||
public function create(User $user)
|
||||
|
@ -75,7 +75,7 @@ abstract class SnipePermissionsPolicy
|
|||
/**
|
||||
* Determine whether the user can update the accessory.
|
||||
*
|
||||
* @param \App\User $user
|
||||
* @param \App\Models\User $user
|
||||
* @return mixed
|
||||
*/
|
||||
public function update(User $user, $item = null)
|
||||
|
@ -86,7 +86,7 @@ abstract class SnipePermissionsPolicy
|
|||
/**
|
||||
* Determine whether the user can delete the accessory.
|
||||
*
|
||||
* @param \App\User $user
|
||||
* @param \App\Models\User $user
|
||||
* @return mixed
|
||||
*/
|
||||
public function delete(User $user, $item = null)
|
||||
|
@ -97,11 +97,13 @@ abstract class SnipePermissionsPolicy
|
|||
/**
|
||||
* Determine whether the user can manage the accessory.
|
||||
*
|
||||
* @param \App\User $user
|
||||
* @param \App\Models\User $user
|
||||
* @return mixed
|
||||
*/
|
||||
public function manage(User $user, $item = null)
|
||||
{
|
||||
return $user->hasAccess($this->columnName().'.edit');
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -223,14 +223,14 @@ class UserPresenter extends Presenter
|
|||
[
|
||||
"field" => "two_factor_enrolled",
|
||||
"searchable" => false,
|
||||
"sortable" => false,
|
||||
"sortable" => true,
|
||||
"switchable" => true,
|
||||
"title" => trans('admin/users/general.two_factor_enrolled'),
|
||||
"visible" => false,
|
||||
'formatter' => 'trueFalseFormatter'
|
||||
],
|
||||
[
|
||||
"field" => "two_factor_active",
|
||||
"field" => "two_factor_activated",
|
||||
"searchable" => false,
|
||||
"sortable" => false,
|
||||
"switchable" => true,
|
||||
|
@ -243,7 +243,7 @@ class UserPresenter extends Presenter
|
|||
"searchable" => false,
|
||||
"sortable" => true,
|
||||
"switchable" => true,
|
||||
"title" => trans('general.activated'),
|
||||
"title" => trans('general.login_enabled'),
|
||||
"visible" => true,
|
||||
'formatter' => 'trueFalseFormatter'
|
||||
],
|
||||
|
|
|
@ -113,6 +113,14 @@ class AuthServiceProvider extends ServiceProvider
|
|||
});
|
||||
|
||||
|
||||
// Can the user import CSVs?
|
||||
Gate::define('import', function ($user) {
|
||||
if ($user->hasAccess('import') ) {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
# -----------------------------------------
|
||||
# Reports
|
||||
# -----------------------------------------
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
"league/flysystem-sftp": "~1.0",
|
||||
"maknz/slack": "^1.7",
|
||||
"neitanod/forceutf8": "^2.0",
|
||||
"paragonie/constant_time_encoding": "^1.0",
|
||||
"patchwork/utf8": "~1.2",
|
||||
"phpdocumentor/reflection-docblock": "3.2.2",
|
||||
"phpspec/prophecy": "1.7.5",
|
||||
|
|
|
@ -27,6 +27,15 @@ return array(
|
|||
)
|
||||
),
|
||||
|
||||
'CSV Import' => array(
|
||||
array(
|
||||
'permission' => 'import',
|
||||
'label' => '',
|
||||
'note' => 'This will allow users to import even if access to users, assets, etc is denied elsewhere.',
|
||||
'display' => true,
|
||||
)
|
||||
),
|
||||
|
||||
'Reports' => array(
|
||||
array(
|
||||
'permission' => 'reports.view',
|
||||
|
|
|
@ -5,7 +5,7 @@ if [ -z "$APP_KEY" ]
|
|||
then
|
||||
echo "Please re-run this container with an environment variable \$APP_KEY"
|
||||
echo "An example APP_KEY you could use is: "
|
||||
php artisan key:generate --show
|
||||
/var/www/html/artisan key:generate --show
|
||||
exit
|
||||
fi
|
||||
|
||||
|
@ -47,6 +47,8 @@ then
|
|||
cp -ax /var/www/html/vendor/laravel/passport/database/migrations/* /var/www/html/database/migrations/
|
||||
fi
|
||||
|
||||
exec supervisord -c /supervisord.conf
|
||||
|
||||
php artisan migrate --force
|
||||
php artisan config:clear
|
||||
php artisan config:cache
|
19
docker/supervisor-exit-event-listener
Normal file
19
docker/supervisor-exit-event-listener
Normal file
|
@ -0,0 +1,19 @@
|
|||
#!/usr/bin/env python
|
||||
# A supervisor event listener which terminates supervisord if any of its child
|
||||
# processes enter the FATAL state.
|
||||
# https://stackoverflow.com/a/37527488/119527
|
||||
import os
|
||||
import signal
|
||||
|
||||
from supervisor import childutils
|
||||
|
||||
def main():
|
||||
while True:
|
||||
headers, payload = childutils.listener.wait()
|
||||
childutils.listener.ok()
|
||||
if headers['eventname'] != 'PROCESS_STATE_FATAL':
|
||||
continue
|
||||
os.kill(os.getppid(), signal.SIGTERM)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
27
docker/supervisord.conf
Normal file
27
docker/supervisord.conf
Normal file
|
@ -0,0 +1,27 @@
|
|||
[supervisord]
|
||||
nodaemon=true
|
||||
|
||||
[program:apache]
|
||||
; https://advancedweb.hu/2018/07/03/supervisor_docker/
|
||||
command=apache2ctl -DFOREGROUND
|
||||
killasgroup=true
|
||||
stopasgroup=true
|
||||
stdout_logfile=/dev/stdout
|
||||
stdout_logfile_maxbytes=0
|
||||
stderr_logfile=/dev/stderr
|
||||
stderr_logfile_maxbytes=0
|
||||
|
||||
[program:run_schedule]
|
||||
; Simply run the Laravel command scheduler every minute
|
||||
command=/bin/bash -c "while true; do /var/www/html/artisan schedule:run; sleep 1m; done"
|
||||
user=docker
|
||||
stdout_logfile=/dev/stdout
|
||||
stdout_logfile_maxbytes=0
|
||||
stderr_logfile=/dev/stderr
|
||||
stderr_logfile_maxbytes=0
|
||||
|
||||
|
||||
; https://stackoverflow.com/a/37527488/119527
|
||||
[eventlistener:exit_on_any_fatal]
|
||||
command=supervisor-exit-event-listener
|
||||
events=PROCESS_STATE_FATAL
|
BIN
public/css/build/all.css
Normal file
BIN
public/css/build/all.css
Normal file
Binary file not shown.
BIN
public/css/dist/all.css
vendored
Normal file
BIN
public/css/dist/all.css
vendored
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
public/js/dist/all.js
vendored
BIN
public/js/dist/all.js
vendored
Binary file not shown.
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
<<<<<<< HEAD
|
||||
"/js/app.js": "/js/app.js?id=be43e3109667a86c9a02",
|
||||
"/css/build/AdminLTE.css": "/css/build/AdminLTE.css?id=f7a5d783fef321018f4c",
|
||||
"/css/build/app.css": "/css/build/app.css?id=0dfc05b0fe1dcc9b6e3d",
|
||||
|
@ -14,4 +15,18 @@
|
|||
"/js/dist/bootstrap-table.js": "/js/dist/bootstrap-table.js?id=bc5e33610f678021cc48",
|
||||
"/js/dist/bootstrap-table-simple-view.js": "/js/dist/bootstrap-table-simple-view.js?id=3926b8f4aaad6ca20d31",
|
||||
"/css/dist/bootstrap-table.css": "/css/dist/bootstrap-table.css?id=6b4ccfd094c065f065ae"
|
||||
=======
|
||||
"/js/build/vue.js": "/js/build/vue.js?id=96f90510b797ac27a94b",
|
||||
"/css/AdminLTE.css": "/css/AdminLTE.css?id=5e72463a66acbcc740d5",
|
||||
"/css/app.css": "/css/app.css?id=407edb63cc6b6dc62405",
|
||||
"/css/overrides.css": "/css/overrides.css?id=2d81c3704393bac77011",
|
||||
"/js/build/vue.js.map": "/js/build/vue.js.map?id=423f16f63b86abd6b196",
|
||||
"/css/AdminLTE.css.map": "/css/AdminLTE.css.map?id=0be7790b84909dca6a0a",
|
||||
"/css/app.css.map": "/css/app.css.map?id=96b5c985e860716e6a16",
|
||||
"/css/overrides.css.map": "/css/overrides.css.map?id=f7ce9ca49027594ac402",
|
||||
"/css/dist/all.css": "/css/dist/all.css?id=98db4e9b7650453c8b00",
|
||||
"/js/dist/all.js": "/js/dist/all.js?id=114f1025a1b3e8975476",
|
||||
"/css/build/all.css": "/css/build/all.css?id=98db4e9b7650453c8b00",
|
||||
"/js/build/all.js": "/js/build/all.js?id=114f1025a1b3e8975476"
|
||||
>>>>>>> hotfixes/2fa_qr
|
||||
}
|
||||
|
|
|
@ -40,9 +40,8 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="alert col-md-12"
|
||||
<div class="alert col-md-12" style="text-align:left"
|
||||
:class="alertClass"
|
||||
style="text-align:left"
|
||||
v-if="statusText">
|
||||
{{ this.statusText }}
|
||||
</div>
|
||||
|
|
|
@ -346,7 +346,18 @@ $(document).ready(function () {
|
|||
}
|
||||
|
||||
function formatDataSelection (datalist) {
|
||||
return datalist.text;
|
||||
// This a heinous workaround for a known bug in Select2.
|
||||
// Without this, the rich selectlists are vulnerable to XSS.
|
||||
// Many thanks to @uberbrady for this fix. It ain't pretty,
|
||||
// but it resolves the issue until Select2 addresses it on their end.
|
||||
//
|
||||
// Bug was reported in 2016 :{
|
||||
// https://github.com/select2/select2/issues/4587
|
||||
|
||||
return datalist.text.replace(/>/g, '>')
|
||||
.replace(/</g, '<')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''');
|
||||
}
|
||||
|
||||
// This handles the radio button selectors for the checkout-to-foo options
|
||||
|
|
|
@ -226,4 +226,5 @@
|
|||
'zip' => 'Zip',
|
||||
'noimage' => 'No image uploaded or image not found.',
|
||||
'token_expired' => 'Your form session has expired. Please try again.',
|
||||
'login_enabled' => 'Login Enabled',
|
||||
];
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
</div>
|
||||
|
||||
<div class="col-md-12 text-center">
|
||||
<img src="{{ $google2fa_url }}" style="padding: 15px 0px 15px 0px">
|
||||
{!! $barcode_obj->getHtmlDiv() !!}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
|
|
@ -509,7 +509,7 @@
|
|||
</a>
|
||||
</li>
|
||||
@endcan
|
||||
@can('create', \App\Models\Asset::class)
|
||||
@can('import')
|
||||
<li{!! (Request::is('import/*') ? ' class="active"' : '') !!}>
|
||||
<a href="{{ route('imports.index') }}">
|
||||
<i class="fa fa-cloud-download"></i>
|
||||
|
|
|
@ -61,7 +61,7 @@
|
|||
<!-- Require signature for acceptance -->
|
||||
<div class="form-group {{ $errors->has('require_accept_signature') ? 'error' : '' }}">
|
||||
<div class="col-md-3">
|
||||
{{ Form::label('full_multiple_companies_support',
|
||||
{{ Form::label('require_accept_signature',
|
||||
trans('admin/settings/general.require_accept_signature')) }}
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
|
@ -166,7 +166,7 @@
|
|||
<!-- Default EULA -->
|
||||
<div class="form-group {{ $errors->has('default_eula_text') ? 'error' : '' }}">
|
||||
<div class="col-md-3">
|
||||
{{ Form::label('per_page', trans('admin/settings/general.default_eula_text')) }}
|
||||
{{ Form::label('default_eula_text', trans('admin/settings/general.default_eula_text')) }}
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
{{ Form::textarea('default_eula_text', Input::old('default_eula_text', $setting->default_eula_text), array('class' => 'form-control','placeholder' => 'Add your default EULA text')) }}
|
||||
|
|
|
@ -123,22 +123,22 @@
|
|||
<table class="table table-striped">
|
||||
@if (!is_null($user->company))
|
||||
<tr>
|
||||
<td>{{ trans('general.company') }}</td>
|
||||
<td class="text-nowrap">{{ trans('general.company') }}</td>
|
||||
<td>{{ $user->company->name }}</td>
|
||||
</tr>
|
||||
@endif
|
||||
|
||||
<tr>
|
||||
<td>{{ trans('admin/users/table.name') }}</td>
|
||||
<td class="text-nowrap">{{ trans('admin/users/table.name') }}</td>
|
||||
<td>{{ $user->present()->fullName() }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ trans('admin/users/table.username') }}</td>
|
||||
<td class="text-nowrap">{{ trans('admin/users/table.username') }}</td>
|
||||
<td>{{ $user->username }}</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>{{ trans('general.groups') }}</td>
|
||||
<td class="text-nowrap">{{ trans('general.groups') }}</td>
|
||||
<td>
|
||||
@if ($user->groups->count() > 0)
|
||||
@foreach ($user->groups as $group)
|
||||
|
@ -160,21 +160,21 @@
|
|||
|
||||
@if ($user->jobtitle)
|
||||
<tr>
|
||||
<td>{{ trans('admin/users/table.job') }}</td>
|
||||
<td class="text-nowrap">{{ trans('admin/users/table.job') }}</td>
|
||||
<td>{{ $user->jobtitle }}</td>
|
||||
</tr>
|
||||
@endif
|
||||
|
||||
@if ($user->employee_num)
|
||||
<tr>
|
||||
<td>{{ trans('admin/users/table.employee_num') }}</td>
|
||||
<td class="text-nowrap">{{ trans('admin/users/table.employee_num') }}</td>
|
||||
<td>{{ $user->employee_num }}</td>
|
||||
</tr>
|
||||
@endif
|
||||
|
||||
@if ($user->manager)
|
||||
<tr>
|
||||
<td>{{ trans('admin/users/table.manager') }}</td>
|
||||
<td class="text-nowrap">{{ trans('admin/users/table.manager') }}</td>
|
||||
<td>
|
||||
<a href="{{ route('users.show', $user->manager->id) }}">{{ $user->manager->getFullNameAttribute() }}</a>
|
||||
|
||||
|
@ -184,21 +184,21 @@
|
|||
|
||||
@if ($user->email)
|
||||
<tr>
|
||||
<td>{{ trans('admin/users/table.email') }}</td>
|
||||
<td class="text-nowrap">{{ trans('admin/users/table.email') }}</td>
|
||||
<td><a href="mailto:{{ $user->email }}">{{ $user->email }}</a></td>
|
||||
</tr>
|
||||
@endif
|
||||
|
||||
@if ($user->phone)
|
||||
<tr>
|
||||
<td>{{ trans('admin/users/table.phone') }}</td>
|
||||
<td class="text-nowrap">{{ trans('admin/users/table.phone') }}</td>
|
||||
<td><a href="tel:{{ $user->phone }}">{{ $user->phone }}</a></td>
|
||||
</tr>
|
||||
@endif
|
||||
|
||||
@if ($user->userloc)
|
||||
<tr>
|
||||
<td>{{ trans('admin/users/table.location') }}</td>
|
||||
<td class="text-nowrap">{{ trans('admin/users/table.location') }}</td>
|
||||
<td>{{ link_to_route('locations.show', $user->userloc->name, [$user->userloc->id]) }}</td>
|
||||
|
||||
|
||||
|
@ -206,14 +206,14 @@
|
|||
@endif
|
||||
@if ($user->last_login)
|
||||
<tr>
|
||||
<td>{{ trans('general.last_login') }}</td>
|
||||
<td class="text-nowrap">{{ trans('general.last_login') }}</td>
|
||||
<td>{{ \App\Helpers\Helper::getFormattedDateObject($user->last_login, 'datetime', false) }}</td>
|
||||
</tr>
|
||||
@endif
|
||||
|
||||
@if (!is_null($user->department))
|
||||
<tr>
|
||||
<td>{{ trans('general.department') }}</td>
|
||||
<td class="text-nowrap">{{ trans('general.department') }}</td>
|
||||
<td><a href="{{ route('departments.show', $user->department) }}">{{ $user->department->name }}</a></td>
|
||||
</tr>
|
||||
@endif
|
||||
|
@ -223,6 +223,45 @@
|
|||
<td>{{ $user->created_at->format('F j, Y h:iA') }}</td>
|
||||
</tr>
|
||||
@endif
|
||||
<tr>
|
||||
<td class="text-nowrap">{{ trans('general.login_enabled') }}</td>
|
||||
<td>{{ ($user->activated=='1') ? trans('general.yes') : trans('general.no') }}</td>
|
||||
</tr>
|
||||
|
||||
@if ($user->activated=='1')
|
||||
<tr>
|
||||
<td class="text-nowrap">{{ trans('admin/users/general.two_factor_active') }}</td>
|
||||
<td>{{ ($user->two_factor_active()) ? trans('general.yes') : trans('general.no') }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="text-nowrap">{{ trans('admin/users/general.two_factor_enrolled') }}</td>
|
||||
<td class="two_factor_resetrow">
|
||||
<div class="row">
|
||||
<div class="col-md-1" id="two_factor_reset_toggle">
|
||||
{{ ($user->two_factor_active_and_enrolled()) ? trans('general.yes') : trans('general.no') }}
|
||||
</div>
|
||||
|
||||
@if ((Auth::user()->isSuperUser()) && ($snipeSettings->two_factor_enabled!='0'))
|
||||
<div class="col-md-11">
|
||||
<a class="btn btn-default btn-sm pull-left" id="two_factor_reset" style="margin-right: 10px;"> {{ trans('admin/settings/general.two_factor_reset') }}</a>
|
||||
<span id="two_factor_reseticon">
|
||||
</span>
|
||||
<span id="two_factor_resetresult">
|
||||
</span>
|
||||
<span id="two_factor_resetstatus">
|
||||
</span>
|
||||
|
||||
<br><br><p class="help-block">{{ trans('admin/settings/general.two_factor_reset_help') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@endif
|
||||
|
||||
|
||||
</table>
|
||||
</div>
|
||||
</div> <!--/col-md-8-->
|
||||
|
@ -532,6 +571,40 @@
|
|||
@include ('partials.bootstrap-table', ['simple_view' => true])
|
||||
<script nonce="{{ csrf_token() }}">
|
||||
$(function () {
|
||||
|
||||
$("#two_factor_reset").click(function(){
|
||||
$("#two_factor_resetrow").removeClass('success');
|
||||
$("#two_factor_resetrow").removeClass('danger');
|
||||
$("#two_factor_resetstatus").html('');
|
||||
$("#two_factor_reseticon").html('<i class="fa fa-spinner spin"></i>');
|
||||
$.ajax({
|
||||
url: '{{ route('api.users.two_factor_reset', ['id'=> $user->id]) }}',
|
||||
type: 'POST',
|
||||
data: {},
|
||||
headers: {
|
||||
"X-Requested-With": 'XMLHttpRequest',
|
||||
"X-CSRF-TOKEN": $('meta[name="csrf-token"]').attr('content')
|
||||
},
|
||||
dataType: 'json',
|
||||
|
||||
success: function (data) {
|
||||
$("#two_factor_reset_toggle").html('').html('{{ trans('general.no') }}');
|
||||
$("#two_factor_reseticon").html('');
|
||||
$("#two_factor_resetstatus").html('<i class="fa fa-check text-success"></i>' + data.message);
|
||||
|
||||
},
|
||||
|
||||
error: function (data) {
|
||||
$("#two_factor_reseticon").html('');
|
||||
$("#two_factor_reseticon").html('<i class="fa fa-exclamation-triangle text-danger"></i>');
|
||||
$('#two_factor_resetstatus').text(data.message);
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
//binds to onchange event of your input field
|
||||
var uploadedFileSize = 0;
|
||||
$('#fileupload').bind('change', function() {
|
||||
|
|
Loading…
Reference in a new issue