1
0
mirror of https://github.com/invoiceninja/invoiceninja.git synced 2024-11-08 20:22:42 +01:00

Added password strength check

This commit is contained in:
Hillel Coren 2018-04-10 18:07:10 +03:00
parent a09ba934cf
commit d0d510331a
12 changed files with 215 additions and 172 deletions

View File

@ -58,8 +58,9 @@ class ResetPasswordController extends Controller
public function showResetForm(Request $request, $token = null) public function showResetForm(Request $request, $token = null)
{ {
return view('auth.passwords.reset')->with( return view('auth.passwords.reset')->with([
['token' => $token] 'token' => $token,
); 'url' => '/password/reset'
]);
} }
} }

View File

@ -54,9 +54,10 @@ class ResetPasswordController extends Controller
public function showResetForm(Request $request, $token = null) public function showResetForm(Request $request, $token = null)
{ {
return view('clientauth.passwords.reset')->with( return view('auth.passwords.reset')->with([
['token' => $token] 'token' => $token,
); 'url' => '/client/password/reset'
]);
} }
} }

View File

@ -103,7 +103,7 @@ class Authenticate
} else { } else {
if ($guard == 'client') { if ($guard == 'client') {
$url = '/client/login'; $url = '/client/login';
if (Utils::isNinja()) { if (Utils::isNinjaProd()) {
if ($account && Utils::getSubdomain() == 'app') { if ($account && Utils::getSubdomain() == 'app') {
$url .= '?account_key=' . $account->account_key; $url .= '?account_key=' . $account->account_key;
} }

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1265,3 +1265,33 @@ function openUrlOnClick(url, event) {
window.location = url; window.location = url;
} }
} }
// https://stackoverflow.com/a/11268104/497368
function scorePassword(pass) {
var score = 0;
if (!pass)
return score;
// award every unique letter until 5 repetitions
var letters = new Object();
for (var i=0; i<pass.length; i++) {
letters[pass[i]] = (letters[pass[i]] || 0) + 1;
score += 5.0 / letters[pass[i]];
}
// bonus points for mixing it up
var variations = {
digits: /\d/.test(pass),
lower: /[a-z]/.test(pass),
upper: /[A-Z]/.test(pass),
nonWords: /\W/.test(pass),
}
variationCount = 0;
for (var check in variations) {
variationCount += (variations[check] == true) ? 1 : 0;
}
score += (variationCount - 1) * 10;
return parseInt(score);
}

View File

@ -2825,6 +2825,10 @@ $LANG = array(
'unapproved_proposal' => 'Unapproved Proposal', 'unapproved_proposal' => 'Unapproved Proposal',
'autofills_city_state' => 'Auto-fills city/state', 'autofills_city_state' => 'Auto-fills city/state',
'no_match_found' => 'No match found', 'no_match_found' => 'No match found',
'password_strength' => 'Password Strength',
'strength_weak' => 'Weak',
'strength_good' => 'Good',
'strength_strong' => 'Strong',
); );

View File

@ -129,7 +129,7 @@
{!! Former::password('current_password')->style('width:300px') !!} {!! Former::password('current_password')->style('width:300px') !!}
{!! Former::password('newer_password')->style('width:300px')->label(trans('texts.new_password')) !!} {!! Former::password('newer_password')->style('width:300px')->label(trans('texts.new_password')) !!}
{!! Former::password('confirm_password')->style('width:300px') !!} {!! Former::password('confirm_password')->style('width:300px')->help('<span id="passwordStrength">&nbsp;</span>') !!}
&nbsp; &nbsp;
<br/> <br/>
@ -205,7 +205,7 @@
var isValid = val; var isValid = val;
if (field != 'current_password') { if (field != 'current_password') {
isValid = val.length >= 6; isValid = val.length >= 8;
} }
if (isValid && field == 'confirm_password') { if (isValid && field == 'confirm_password') {
@ -221,6 +221,15 @@
$input.closest('div.form-group').addClass('has-error'); $input.closest('div.form-group').addClass('has-error');
} }
} }
if (field == 'newer_password') {
var score = scorePassword(val);
if (isValid) {
isValid = score > 50;
}
showPasswordStrength(val, score);
}
}); });
$('#changePasswordButton').prop('disabled', !isFormValid); $('#changePasswordButton').prop('disabled', !isFormValid);

View File

@ -3,10 +3,11 @@
@section('form') @section('form')
<div class="container"> <div class="container">
{!! Former::open('/password/reset') {!! Former::open($url)
->addClass('form-signin') ->addClass('form-signin')
->autocomplete('off') ->autocomplete('off')
->rules(array( ->rules(array(
'email' => 'required|email',
'password' => 'required', 'password' => 'required',
'password_confirmation' => 'required', 'password_confirmation' => 'required',
)) !!} )) !!}
@ -39,13 +40,17 @@
<input type="hidden" name="token" value="{{{ $token }}}"> <input type="hidden" name="token" value="{{{ $token }}}">
<div> <div onkeyup="validateForm()" onclick="validateForm()" onkeydown="validateForm(event)">
{!! Former::text('email')->placeholder(trans('texts.email'))->raw() !!} {!! Former::text('email')->placeholder(trans('texts.email'))->raw() !!}
{!! Former::password('password')->placeholder(trans('texts.password'))->autocomplete('new-password')->raw() !!} {!! Former::password('password')->placeholder(trans('texts.password'))->autocomplete('new-password')->raw() !!}
{!! Former::password('password_confirmation')->placeholder(trans('texts.confirm_password'))->autocomplete('new-password')->raw() !!} {!! Former::password('password_confirmation')->placeholder(trans('texts.confirm_password'))->autocomplete('new-password')->raw() !!}
</div> </div>
<p>{!! Button::success(trans('texts.save'))->large()->submit()->withAttributes(['class' => 'green'])->block() !!}</p> <div id="passwordStrength" style="font-weight:normal;padding:16px">
&nbsp;
</div>
<p>{!! Button::success(trans('texts.save'))->large()->submit()->withAttributes(['class' => 'green', 'id' => 'saveButton', 'disabled' => true])->block() !!}</p>
{!! Former::close() !!} {!! Former::close() !!}
@ -53,7 +58,32 @@
<script type="text/javascript"> <script type="text/javascript">
$(function() { $(function() {
$('#password').focus(); $('#password').focus();
validateForm();
}) })
function validateForm() {
var isValid = true;
if (! $('#email').val()) {
isValid = false;
}
var password = $('#password').val();
var confirm = $('#password_confirmation').val();
if (! password || password != confirm || password.length < 8) {
isValid = false;
}
var score = scorePassword(password);
if (score < 50) {
isValid = false;
}
showPasswordStrength(password, score);
$('#saveButton').prop('disabled', ! isValid);
}
</script> </script>
@endsection @endsection

View File

@ -1,57 +0,0 @@
@extends('login')
@section('form')
<div class="container">
{!! Former::open('/client/password/reset')
->addClass('form-signin')
->autocomplete('false')
->rules(array(
'password' => 'required',
'password_confirmation' => 'required',
)) !!}
@include('partials.autocomplete_fix')
<h2 class="form-signin-heading">{{ trans('texts.set_password') }}</h2>
<hr class="green">
@if (count($errors->all()))
<div class="alert alert-danger">
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</div>
@endif
<!-- if there are login errors, show them here -->
@if (Session::has('warning'))
<div class="alert alert-warning">{{ Session::get('warning') }}</div>
@endif
@if (Session::has('message'))
<div class="alert alert-info">{{ Session::get('message') }}</div>
@endif
@if (Session::has('error'))
<div class="alert alert-danger">{{ Session::get('error') }}</div>
@endif
<input type="hidden" name="token" value="{{{ $token }}}">
<div>
{!! Former::text('email')->placeholder(trans('texts.email'))->raw() !!}
{!! Former::password('password')->placeholder(trans('texts.password'))->autocomplete('new-password')->raw() !!}
{!! Former::password('password_confirmation')->placeholder(trans('texts.confirm_password'))->autocomplete('new-password')->raw() !!}
</div>
<p>{!! Button::success(trans('texts.save'))->large()->submit()->withAttributes(['class' => 'green'])->block() !!}</p>
{!! Former::close() !!}
</div>
<script type="text/javascript">
$(function () {
$('#password').focus();
})
</script>
@endsection

View File

@ -137,6 +137,22 @@
}); });
} }
function showPasswordStrength(password, score) {
if (password) {
var str = {!! json_encode(trans('texts.password_strength')) !!} + ': ';
if (password.length < 8 || score < 50) {
str += {!! json_encode(trans('texts.strength_weak')) !!};
} else if (score < 75) {
str += {!! json_encode(trans('texts.strength_good')) !!};
} else {
str += {!! json_encode(trans('texts.strength_strong')) !!};
}
$('#passwordStrength').html(str);
} else {
$('#passwordStrength').html('&nbsp;');
}
}
/* Set the defaults for DataTables initialisation */ /* Set the defaults for DataTables initialisation */
$.extend(true, $.fn.dataTable.defaults, { $.extend(true, $.fn.dataTable.defaults, {
"bSortClasses": false, "bSortClasses": false,

View File

@ -1,6 +1,6 @@
<script type="text/javascript"> <script type="text/javascript">
$(function() { $(function() {
validateSignUp(); validateSignUp();
@ -32,22 +32,22 @@
}); });
@endif @endif
}); });
function showSignUp() { function showSignUp() {
if (location.href.indexOf('/dashboard') == -1) { if (location.href.indexOf('/dashboard') == -1) {
location.href = "{{ url('/dashboard') }}?sign_up=true"; location.href = "{{ url('/dashboard') }}?sign_up=true";
} else { } else {
$('#signUpModal').modal('show'); $('#signUpModal').modal('show');
} }
} }
function hideSignUp() { function hideSignUp() {
$('#signUpModal').modal('hide'); $('#signUpModal').modal('hide');
} }
function setSignupEnabled(enabled) { function setSignupEnabled(enabled) {
$('.signup-form input[type=text]').prop('disabled', !enabled); $('.signup-form input[type=text]').prop('disabled', !enabled);
$('.signup-form input[type=password]').prop('disabled', !enabled); $('.signup-form input[type=password]').prop('disabled', !enabled);
if (enabled) { if (enabled) {
@ -55,15 +55,24 @@
} else { } else {
$('.signup-form a.btn').addClass('disabled'); $('.signup-form a.btn').addClass('disabled');
} }
} }
function validateSignUp(showError) function validateSignUp(showError) {
{
var isFormValid = true; var isFormValid = true;
$(['first_name','last_name','email','password']).each(function(i, field) { $(['first_name','last_name','email','password']).each(function(i, field) {
var $input = $('form.signUpForm #new_'+field), var $input = $('form.signUpForm #new_'+field),
val = $.trim($input.val()); val = $.trim($input.val());
var isValid = val && val.length >= (field == 'password' ? 6 : 1); var isValid = val && val.length >= (field == 'password' ? 8 : 1);
if (field == 'password') {
var score = scorePassword(val);
if (isValid) {
isValid = score > 50;
}
showPasswordStrength(val, score);
}
if (isValid && field == 'email') { if (isValid && field == 'email') {
isValid = isValidEmailAddress(val); isValid = isValidEmailAddress(val);
} }
@ -85,10 +94,9 @@
$('#saveSignUpButton').prop('disabled', !isFormValid); $('#saveSignUpButton').prop('disabled', !isFormValid);
return isFormValid; return isFormValid;
} }
function validateServerSignUp() function validateServerSignUp() {
{
if (!validateSignUp(true)) { if (!validateSignUp(true)) {
return; return;
} }
@ -111,9 +119,9 @@
} }
} }
}); });
} }
function submitSignUp() { function submitSignUp() {
$.ajax({ $.ajax({
type: 'POST', type: 'POST',
url: '{{ URL::to('signup/submit') }}', url: '{{ URL::to('signup/submit') }}',
@ -140,15 +148,15 @@
} }
} }
}); });
} }
function handleSignedUp() { function handleSignedUp() {
if (isStorageSupported()) { if (isStorageSupported()) {
localStorage.setItem('guest_key', ''); localStorage.setItem('guest_key', '');
} }
fbq('track', 'CompleteRegistration'); fbq('track', 'CompleteRegistration');
trackEvent('/account', '/signed_up'); trackEvent('/account', '/signed_up');
} }
</script> </script>
@ -234,7 +242,8 @@
->placeholder(trans('texts.password')) ->placeholder(trans('texts.password'))
->autocomplete('new-password') ->autocomplete('new-password')
->data_lpignore('true') ->data_lpignore('true')
->label(' ') !!} ->label(' ')
->help('<span id="passwordStrength">&nbsp;</span>') !!}
{{ Former::setOption('TwitterBootstrap3.labelWidths.large', 4) }} {{ Former::setOption('TwitterBootstrap3.labelWidths.large', 4) }}
{{ Former::setOption('TwitterBootstrap3.labelWidths.small', 4) }} {{ Former::setOption('TwitterBootstrap3.labelWidths.small', 4) }}