ADDED: Password rules for complexity, min length, rejecting common passwords

This commit is contained in:
snipe 2017-08-22 20:32:39 -07:00
parent 1d7e243d0a
commit 9bda62d295
15 changed files with 303 additions and 61 deletions

View file

@ -8,6 +8,7 @@ use App\Http\Transformers\UsersTransformer;
use App\Models\Company;
use App\Models\User;
use App\Helpers\Helper;
use App\Http\Requests\SaveUserRequest;
class UsersController extends Controller
{
@ -102,7 +103,7 @@ class UsersController extends Controller
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
public function store(SaveUserRequest $request)
{
$this->authorize('view', User::class);
$user = new User;
@ -139,7 +140,7 @@ class UsersController extends Controller
* @param int $id
* @return \Illuminate\Http\Response
*/
public function update(Request $request, $id)
public function update(SaveUserRequest $request, $id)
{
$this->authorize('edit', User::class);
$user = User::findOrFail($id);

View file

@ -462,6 +462,15 @@ class SettingsController extends Controller
}
$setting->pwd_secure_uncommon = (int) $request->input('pwd_secure_uncommon');
$setting->pwd_secure_min = (int) $request->input('pwd_secure_min');
$setting->pwd_secure_complexity = '';
if ($request->has('pwd_secure_complexity')) {
$setting->pwd_secure_complexity = implode('|', $request->input('pwd_secure_complexity'));
}
if ($setting->save()) {
return redirect()->route('settings.index')

View file

@ -12,9 +12,7 @@ use App\Models\Company;
use App\Models\Location;
use App\Models\License;
use App\Models\Setting;
use App\Models\Statuslabel;
use App\Http\Requests\SaveUserRequest;
use App\Http\Requests\UpdateUserRequest;
use Symfony\Component\HttpFoundation\StreamedResponse;
use App\Models\User;
use App\Models\Ldap;
@ -23,7 +21,6 @@ use Config;
use Crypt;
use DB;
use HTML;
use Illuminate\Support\Facades\Log;
use Input;
use Lang;
use League\Csv\Reader;
@ -169,7 +166,7 @@ class UsersController extends Controller
* @since [v1.8]
* @return string JSON
*/
public function apiStore(Request $request)
public function apiStore(SaveUserRequest $request)
{
$this->authorize('create', User::class);
@ -270,7 +267,7 @@ class UsersController extends Controller
* @param int $id
* @return \Illuminate\Http\RedirectResponse
*/
public function update(UpdateUserRequest $request, $id = null)
public function update(SaveUserRequest $request, $id = null)
{
// We need to reverse the UI specific logic for our
// permissions here before we update the user.
@ -309,14 +306,11 @@ class UsersController extends Controller
}
}
// Do we want to update the user password?
if ($request->has('password')) {
$user->password = bcrypt($request->input('password'));
}
if ($request->has('username')) {
$user->username = e($request->input('username'));
$user->username = $request->input('username');
}
$user->email = e($request->input('email'));
$user->email = $request->input('email');
// Update the user
@ -334,6 +328,12 @@ class UsersController extends Controller
$user->notes = $request->input('notes');
$user->department_id = $request->input('department_id', null);
// Do we want to update the user password?
if ($request->has('password')) {
$user->password = bcrypt($request->input('password'));
}
// Strip out the superuser permission if the user isn't a superadmin
$permissions_array = $request->input('permission');

View file

@ -3,6 +3,7 @@
namespace App\Http\Requests;
use App\Http\Requests\Request;
use App\Models\Setting;
class SaveUserRequest extends Request
{
@ -23,12 +24,35 @@ class SaveUserRequest extends Request
*/
public function rules()
{
return [
'first_name' => 'required|string|min:1',
'email' => 'email',
'password' => 'required|min:6',
'password_confirm' => 'sometimes|required_with:password',
'username' => 'required|string|min:2|unique:users,username,NULL,deleted_at',
];
$settings = Setting::getSettings();
$rules = [];
$security_rules = '';
$rules['first_name'] = 'required|string|min:1';
$rules['username'] = 'required|string|min:1|unique_undeleted';
// Check if they have uncommon password enforcement selected in settings
if ($settings->pwd_secure_uncommon == 1) {
$security_rules .= '|dumbpwd';
}
// Check for any secure password complexity rules that may have been selected
if ($settings->pwd_secure_complexity!='') {
$security_rules .= '|'.$settings->pwd_secure_complexity;
}
if ((\Route::currentRouteName()=='api.users.update') || (\Route::currentRouteName()=='users.update')) {
$rules['password'] = 'nullable|min:'.$settings->pwd_secure_min.$security_rules;
} else {
$rules['password'] = 'required|min:'.$settings->pwd_secure_min.$security_rules;
}
$rules['password_confirm'] = 'sometimes|required_with:password';
return $rules;
}
}

View file

@ -1,32 +0,0 @@
<?php
namespace App\Http\Requests;
use App\Http\Requests\Request;
class UpdateUserRequest extends Request
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'first_name' => 'required|string|min:1',
'email' => 'email',
'password_confirm' => 'sometimes|required_with:password',
];
}
}

View file

@ -44,6 +44,7 @@ class Setting extends Model
"ldap_auth_filter_query" => 'sometimes|required_if:ldap_enabled,1|nullable',
"ldap_version" => 'sometimes|required_if:ldap_enabled,1|nullable',
"thumbnail_max_h" => 'numeric|max:500|min:25',
"pwd_secure_min" => "numeric|required|min:5",
];
protected $fillable = ['site_name','email_domain','email_format','username_format'];
@ -158,4 +159,34 @@ class Setting extends Model
// In the future this may want to be adapted for individual notifications.
return $this->slack_endpoint;
}
public function passwordComplexityStringToArray()
{
$this->pwd_secure_complexity = 'numbers|letters|case_diff';
$complexity_array_split = array();
$complexity_array = array();
if (($this->pwd_secure_complexity) && ($this->pwd_secure_complexity!='')) {
$complexity_array_split = explode('|',$this->pwd_secure_complexity);
}
for ($x = 0; $x < count($complexity_array_split); $x++) {
$complexity_array[$complexity_array_split[$x]] = 1;
}
return $complexity_array;
}
public static function passwordComplexityToFormattedString($array) {
// $array = array();
$string = '';
for ($x = 0; $x <= count($array); $x++) {
$string .= '|'.$array[$x];
}
return $string;
}
}

View file

@ -51,9 +51,9 @@ class User extends SnipeModel implements AuthenticatableContract, CanResetPasswo
protected $rules = [
'first_name' => 'required|string|min:1',
'username' => 'required|string|min:1|unique_undeleted',
'email' => 'email',
'email' => 'email|nullable',
'password' => 'required|min:6',
'locale' => 'max:10'
'locale' => 'max:10|nullable'
];

View file

@ -24,8 +24,10 @@
"neitanod/forceutf8": "^2.0",
"patchwork/utf8": "~1.2",
"pragmarx/google2fa": "^1.0",
"schuppo/password-strength": "~1.5",
"spatie/laravel-backup": "^3.0.0",
"tecnickcom/tc-lib-barcode": "^1.15",
"unicodeveloper/laravel-password": "^1.0",
"watson/validating": "^3.0"
},
"require-dev": {

107
composer.lock generated
View file

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
"content-hash": "dd84e08e5e7eca2c1e032c6357ca2500",
"content-hash": "da7d8408e0c3434a6131340252d2d228",
"packages": [
{
"name": "aws/aws-sdk-php",
@ -2888,6 +2888,56 @@
],
"time": "2017-08-04T13:39:04+00:00"
},
{
"name": "schuppo/password-strength",
"version": "v1.9",
"source": {
"type": "git",
"url": "https://github.com/schuppo/PasswordStrengthPackage.git",
"reference": "184d65517eb438651b491b116df8895238005b79"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/schuppo/PasswordStrengthPackage/zipball/184d65517eb438651b491b116df8895238005b79",
"reference": "184d65517eb438651b491b116df8895238005b79",
"shasum": ""
},
"require": {
"illuminate/support": "~5.0",
"illuminate/translation": "^5.1",
"php": ">=5.4.0"
},
"require-dev": {
"illuminate/validation": "~5.0",
"phpunit/phpunit": "^4.8"
},
"type": "library",
"autoload": {
"psr-0": {
"Schuppo\\PasswordStrength": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Oliver Schupp",
"email": "oliver.schupp@yahoo.de"
}
],
"description": "This package provides a validator for ensuring strong passwords in Laravel 4 applications.",
"keywords": [
"laravel",
"laravel 5",
"laravel5",
"password",
"password strength",
"validation"
],
"time": "2016-10-05T09:57:59+00:00"
},
{
"name": "spatie/db-dumper",
"version": "1.5.1",
@ -4171,6 +4221,61 @@
"homepage": "https://github.com/tijsverkoyen/CssToInlineStyles",
"time": "2016-09-20T12:50:39+00:00"
},
{
"name": "unicodeveloper/laravel-password",
"version": "1.0.2",
"source": {
"type": "git",
"url": "https://github.com/unicodeveloper/laravel-password.git",
"reference": "5c3bdc977c4b8065350caf2e57371b069ef6f5b4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/unicodeveloper/laravel-password/zipball/5c3bdc977c4b8065350caf2e57371b069ef6f5b4",
"reference": "5c3bdc977c4b8065350caf2e57371b069ef6f5b4",
"shasum": ""
},
"require": {
"php": "~5.6|~7.0"
},
"require-dev": {
"phpunit/phpunit": "~4.0||~5.0",
"scrutinizer/ocular": "~1.1",
"squizlabs/php_codesniffer": "~2.3"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0-dev"
}
},
"autoload": {
"psr-4": {
"Unicodeveloper\\DumbPassword\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": ":Prosper Otemuyiwa",
"email": "prosperotemuyiwa@gmail.com",
"homepage": "http://goodheads.io",
"role": "Developer"
}
],
"description": "Protect your users from entering dumb and common passwords",
"homepage": "https://github.com/unicodeveloper/laravel-password",
"keywords": [
"dumb",
"passwords",
"security",
"unicodeveloper"
],
"time": "2017-04-27T07:35:00+00:00"
},
{
"name": "vlucas/phpdotenv",
"version": "v2.4.0",

View file

@ -220,6 +220,8 @@ return [
PragmaRX\Google2FA\Vendor\Laravel\ServiceProvider::class,
Laravel\Passport\PassportServiceProvider::class,
Laravel\Tinker\TinkerServiceProvider::class,
Unicodeveloper\DumbPassword\DumbPasswordServiceProvider::class,
Schuppo\PasswordStrength\PasswordStrengthServiceProvider::class,
/*

View file

@ -0,0 +1,36 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class AddSecurePasswordOptions extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('settings', function (Blueprint $table) {
$table->boolean('pwd_secure_uncommon')->default('0');
$table->string('pwd_secure_complexity')->nullable()->default(NULL);
$table->integer('pwd_secure_min')->default('8');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('settings', function (Blueprint $table) {
$table->dropColumn('pwd_secure_uncommon');
$table->dropColumn('pwd_secure_complexity');
$table->dropColumn('pwd_secure_min');
});
}
}

View file

@ -73,6 +73,12 @@ return array(
'php' => 'PHP Version',
'php_gd_info' => 'You must install php-gd to display QR codes, see install instructions.',
'php_gd_warning' => 'PHP Image Processing and GD plugin is NOT installed.',
'pwd_secure_complexity' => 'Password Complexity',
'pwd_secure_complexity_help' => 'Select whichever password complexity rules you wish to enforce.',
'pwd_secure_min' => 'Password minimum characters',
'pwd_secure_min_help' => 'Minimum permitted value is 5',
'pwd_secure_uncommon' => 'Prevent common passwords',
'pwd_secure_uncommon_help' => 'This will disallow users from using common passwords from the top 10,000 passwoerds reported in breaches.',
'qr_help' => 'Enable QR Codes first to set this',
'qr_text' => 'QR Code Text',
'setting' => 'Setting',

View file

@ -36,6 +36,7 @@ return array(
"exists" => "The selected :attribute is invalid.",
"email_array" => "One or more email addresses is invalid.",
"hashed_pass" => "Your current password is incorrect",
'dumbpwd' => 'That password is too common.',
"image" => "The :attribute must be an image.",
"in" => "The selected :attribute is invalid.",
"integer" => "The :attribute must be an integer.",

View file

@ -14,11 +14,6 @@
{{-- Page content --}}
@section('content')
<style>
.checkbox label {
padding-right: 40px;
}
</style>
{{ Form::open(['method' => 'POST', 'files' => true, 'class' => 'form-horizontal', 'role' => 'form' ]) }}
@ -58,6 +53,68 @@
</div>
</div>
<!-- Min characters -->
<div class="form-group {{ $errors->has('pwd_secure_min') ? 'error' : '' }}">
<div class="col-md-3">
{{ Form::label('pwd_secure_min', trans('admin/settings/general.pwd_secure_min')) }}
</div>
<div class="col-md-9">
{{ Form::text('pwd_secure_min', Input::old('pwd_secure_min', $setting->pwd_secure_min), array('class' => 'form-control', 'style'=>'width: 50px;')) }}
{!! $errors->first('pwd_secure_min', '<span class="alert-msg">:message</span>') !!}
<p class="help-block">
{{ trans('admin/settings/general.pwd_secure_min_help') }}
</p>
</div>
</div>
<!-- Common Passwords -->
<div class="form-group {{ $errors->has('pwd_secure_uncommon') ? 'error' : '' }}">
<div class="col-md-3">
{{ Form::label('pwd_secure_text',
trans('admin/settings/general.pwd_secure_uncommon')) }}
</div>
<div class="col-md-9">
{{ Form::checkbox('pwd_secure_uncommon', '1', Input::old('pwd_secure_uncommon', $setting->pwd_secure_uncommon),array('class' => 'minimal')) }}
{{ Form::label('pwd_secure_uncommon', trans('general.yes')) }}
{!! $errors->first('pwd_secure_uncommon', '<span class="alert-msg">:message</span>') !!}
<p class="help-block">
{{ trans('admin/settings/general.pwd_secure_uncommon_help') }}
</p>
</div>
</div>
<!-- /.form-group -->
<!-- Common Passwords -->
<div class="form-group">
<div class="col-md-3">
{{ Form::label('pwd_secure_complexity', trans('admin/settings/general.pwd_secure_complexity')) }}
</div>
<div class="col-md-9">
{{ Form::checkbox("pwd_secure_complexity['letters']", 'letters', Input::old('pwd_secure_uncommon', strpos($setting->pwd_secure_complexity, 'letters')!==false), array('class' => 'minimal')) }}
Require at least one letter <br>
{{ Form::checkbox("pwd_secure_complexity['numbers']", 'numbers', Input::old('pwd_secure_uncommon', strpos($setting->pwd_secure_complexity, 'numbers')!==false), array('class' => 'minimal')) }}
Require at least one number<br>
{{ Form::checkbox("pwd_secure_complexity['symbols']", 'symbols', Input::old('pwd_secure_uncommon', strpos($setting->pwd_secure_complexity, 'symbols')!==false), array('class' => 'minimal')) }}
Require at least one symbol<br>
{{ Form::checkbox("pwd_secure_complexity['case_diff']", 'case_diff', Input::old('pwd_secure_uncommon', strpos($setting->pwd_secure_complexity, 'case_diff')!==false), array('class' => 'minimal')) }}
Require at least one uppercase and one lowercase
<p class="help-block">
{{ trans('admin/settings/general.pwd_secure_complexity_help') }}
</p>
</div>
</div>
<!-- /.form-group -->
</div>