Skip to content

Commit

Permalink
Password reset controller and password strength directive
Browse files Browse the repository at this point in the history
  • Loading branch information
rheasunshine committed Aug 10, 2017
1 parent 42cd1c3 commit e06608d
Show file tree
Hide file tree
Showing 3 changed files with 243 additions and 0 deletions.
149 changes: 149 additions & 0 deletions src/app/components/mno-password-strength/mno-password-strength.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
angular.module 'mnoEnterpriseAngular'
.directive 'mnoPasswordStrength', (MnoErrorsHandler) ->
require: "ngModel"
restrict: 'A'
scope:
passwordScore: '=' #outject score

link: (scope, element, attrs, ctrl) ->
measureStrength = (p) ->
matches =
pos: {}
neg: {}

counts =
pos: {}
neg:
seqLetter: 0
seqNumber: 0
seqSymbol: 0

tmp = undefined
strength = 0
letters = "abcdefghijklmnopqrstuvwxyz"
numbers = "01234567890"
symbols = "\\!@#$%&/()=?¿"
back = undefined
forth = undefined

if p
# Benefits
matches.pos.lower = p.match(/[a-z]/g)
matches.pos.upper = p.match(/[A-Z]/g)
matches.pos.numbers = p.match(/\d/g)
matches.pos.symbols = p.match(/[$-/:-?{-~!^_`\[\]]/g)
matches.pos.middleNumber = p.slice(1, -1).match(/\d/g)
matches.pos.middleSymbol = p.slice(1, -1).match(/[$-/:-?{-~!^_`\[\]]/g)
counts.pos.lower = (if matches.pos.lower then matches.pos.lower.length else 0)
counts.pos.upper = (if matches.pos.upper then matches.pos.upper.length else 0)
counts.pos.numbers = (if matches.pos.numbers then matches.pos.numbers.length else 0)
counts.pos.symbols = (if matches.pos.symbols then matches.pos.symbols.length else 0)
tmp = _.reduce(counts.pos, (memo, val) ->
# if has count will add 1
memo + Math.min(1, val)
, 0)
counts.pos.numChars = p.length
tmp += (if (counts.pos.numChars >= 8) then 1 else 0)
counts.pos.requirements = (if (tmp >= 3) then tmp else 0)
counts.pos.middleNumber = (if matches.pos.middleNumber then matches.pos.middleNumber.length else 0)
counts.pos.middleSymbol = (if matches.pos.middleSymbol then matches.pos.middleSymbol.length else 0)

# Deductions
matches.neg.consecLower = p.match(/(?=([a-z]{2}))/g)
matches.neg.consecUpper = p.match(/(?=([A-Z]{2}))/g)
matches.neg.consecNumbers = p.match(/(?=(\d{2}))/g)
matches.neg.onlyNumbers = p.match(/^[0-9]*$/g)
matches.neg.onlyLetters = p.match(/^([a-z]|[A-Z])*$/g)
counts.neg.consecLower = (if matches.neg.consecLower then matches.neg.consecLower.length else 0)
counts.neg.consecUpper = (if matches.neg.consecUpper then matches.neg.consecUpper.length else 0)
counts.neg.consecNumbers = (if matches.neg.consecNumbers then matches.neg.consecNumbers.length else 0)

# sequential letters (back and forth)
i = 0
while i < letters.length - 2
p2 = p.toLowerCase()
forth = letters.substring(i, parseInt(i + 3))
back = _(forth).split("").reverse()
counts.neg.seqLetter++ if p2.indexOf(forth) isnt -1 or p2.indexOf(back) isnt -1
i++

# sequential numbers (back and forth)
i = 0
while i < numbers.length - 2
forth = numbers.substring(i, parseInt(i + 3))
back = _(forth).split("").reverse()
counts.neg.seqNumber++ if p.indexOf(forth) isnt -1 or p.toLowerCase().indexOf(back) isnt -1
i++

# sequential symbols (back and forth)
i = 0
while i < symbols.length - 2
forth = symbols.substring(i, parseInt(i + 3))
back = _(forth).split("").reverse()
counts.neg.seqSymbol++ if p.indexOf(forth) isnt -1 or p.toLowerCase().indexOf(back) isnt -1
i++

# repeated chars
counts.neg.repeated = _.chain(p.toLowerCase().split("")).countBy((val) ->
val
).reject((val) ->
val is 1
).reduce((memo, val) ->
memo + val
, 0).value()

# Calculations
strength += counts.pos.numChars * 4
strength += (counts.pos.numChars - counts.pos.upper) * 2 if counts.pos.upper
strength += (counts.pos.numChars - counts.pos.lower) * 2 if counts.pos.lower
strength += counts.pos.numbers * 4 if counts.pos.upper or counts.pos.lower
strength += counts.pos.symbols * 6
strength += (counts.pos.middleSymbol + counts.pos.middleNumber) * 2
strength += counts.pos.requirements * 2
strength -= counts.neg.consecLower * 2
strength -= counts.neg.consecUpper * 2
strength -= counts.neg.consecNumbers * 2
strength -= counts.neg.seqNumber * 3
strength -= counts.neg.seqLetter * 3
strength -= counts.neg.seqSymbol * 3
strength -= counts.pos.numChars if matches.neg.onlyNumbers
strength -= counts.pos.numChars if matches.neg.onlyLetters
strength -= (counts.neg.repeated / counts.pos.numChars) * 10 if counts.neg.repeated
Math.max 0, Math.min(100, Math.round(strength))

getPwStrength = (s) ->
switch Math.round(s / 20)
when 0, 1
"weak"
when 2,3
"good"
when 4,5
"secure"

getClass = (s) ->
switch getPwStrength(s)
when 'weak'
"danger"
when 'good'
"warning"
when 'secure'
"success"

isPwStrong = (s) ->
switch getPwStrength(s)
when 'weak'
false
else
true

scope.$watch (-> ctrl.$modelValue), ->
scope.value = measureStrength(ctrl.$modelValue)
scope.pwStrength = getPwStrength(scope.value)
ctrl.$setValidity('password-strength', isPwStrong(scope.value))
if scope.passwordScore?
scope.passwordScore.value = scope.pwStrength
scope.passwordScore.class = getClass(scope.value)
scope.passwordScore.showTip = (ctrl.$modelValue? && ctrl.$modelValue != '' && !isPwStrong(scope.value))


return
37 changes: 37 additions & 0 deletions src/app/views/auth/password/reset.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
angular.module('mnoEnterpriseAngular')
.controller 'PasswordResetCtrl', ($state, $location, Auth, toastr, MnoErrorsHandler) ->
'ngInject'

vm = this
vm.resetConfirmed = false
vm.user = {
$pwdScore: {}
}

vm.password_reset = ->
if vm.form.$invalid && !MnoErrorsHandler.onlyServerError(vm.form)
return
else if vm.user.password != vm.user.password_confirmation
return

MnoErrorsHandler.resetErrors(vm.form)

vm.user.reset_password_token = $location.search().reset_password_token
Auth.resetPassword(vm.user).then(
->
toastr.info('devise.passwords.updated', {
timeOut: 0,
closeButton: true,
extendedTimeOut: 0
})
Auth.login(vm.user).then(
->
$state.go('home.impac')
).finally( -> vm.resetConfirmed = true)
(error) ->
MnoErrorsHandler.processServerError(error, vm.form)
).finally( -> vm.hasClicked = false)

return true

return
57 changes: 57 additions & 0 deletions src/app/views/auth/password/reset.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<div class='registration'>
<div class='container'>
<div class='row'>
<div class='login-box-wrapper'>
<div class='login-box-title'>
<h2>{{ 'mno_enterprise.auth.passwords.edit.title' | translate }}</h2>
</div>
<div class='login-box'>
<div class='brand-logo'></div>
<form class="form-horizontal autofill-detect" id="new_user" name="vm.form" ng-submit="vm.password_reset()" validate>
<div class='row'>
<div class='col-sm-12'>
<input mno-password-strength password-score="vm.user.$pwdScore" type="{{isShown ? 'text' : 'password'}}" placeholder="*Password" ng-model="vm.user.password" class="form-control" name="password" id="user_password" autocomplete="off" autofocus required/>
<span class="input-group-addon pw-strength-indicator" ng-class='vm.user.$pwdScore.class' style="display: inline;">
{{ vm.user.$pwdScore.value }}
</span>
</div>
</div>
<br>
<div class='row'>
<div class='col-sm-12'>
<input type="{{isShown ? 'text' : 'password'}}" placeholder="*Confirm Password" ng-model="vm.user.password_confirmation" class="form-control" name="password_confirmation" id="user_password_confirmation" ng-change="vm.resetConfirmed = false" autocomplete="off" autofocus required />
</div>
<div class="col-md-3 text-center" style="padding: 0px 2px;">
<label>Show</label>
<br />
<input type="checkbox" ng-model="isShown" />
</div>
</div>
<br>
<div class='row'>
<div class='col-sm-12'>
<div class='text-center'>
<button class='btn btn-warning' ng-click='vm.hasClicked = true' ng-disabled='vm.form.$invalid' ng-hide='vm.hasClicked' type='submit'>{{ 'mno_enterprise.auth.passwords.edit.change' | translate }}</button>
<img class="loader" ng-show='vm.hasClicked'>
</div>
</div>
</div>



<hr>
<a ui-sref="login">Log in</a><br />

<a ui-sref="signup">Sign up</a><br />

<a href="/mnoe/auth/users/confirmation/new">Didn&#39;t receive confirmation instructions?</a><br />

<a href="/mnoe/auth/users/unlock/new">Didn&#39;t receive unlock instructions?</a><br />

<!-- <a href="/mnoe/users/auth/intuit">Sign in with Intuit</a><br /> -->
</form>
</div>
</div>
</div>
</div>
</div> -->

0 comments on commit e06608d

Please sign in to comment.