Skip to content
This repository has been archived by the owner on Aug 30, 2021. It is now read-only.

Commit

Permalink
Merge pull request #878 from jloveland/owasp-password-strength
Browse files Browse the repository at this point in the history
Adding stronger password security
  • Loading branch information
lirantal committed Sep 10, 2015
2 parents fba6eb7 + 1fd6bb6 commit 5c16db2
Show file tree
Hide file tree
Showing 20 changed files with 300 additions and 74 deletions.
3 changes: 2 additions & 1 deletion bower.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
"angular-ui-utils": "bower",
"angular-ui-router": "~0.2",
"angular-file-upload": "1.1.5",
"angular-messages": "1.3.17"
"angular-messages": "1.3.17",
"owasp-password-strength-test": "~1.3.0"
},
"resolutions": {
"angular": "~1.3"
Expand Down
3 changes: 2 additions & 1 deletion config/assets/default.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ module.exports = {
'public/lib/angular-ui-router/release/angular-ui-router.js',
'public/lib/angular-ui-utils/ui-utils.js',
'public/lib/angular-bootstrap/ui-bootstrap-tpls.js',
'public/lib/angular-file-upload/angular-file-upload.js'
'public/lib/angular-file-upload/angular-file-upload.js',
'public/lib/owasp-password-strength-test/owasp-password-strength-test.js'
],
tests: ['public/lib/angular-mocks/angular-mocks.js']
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ describe('Article Model Unit Tests:', function () {
displayName: 'Full Name',
email: 'test@test.com',
username: 'username',
password: 'password'
password: 'M3@n.jsI$Aw3$0m3'
});

user.save(function () {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ describe('Article CRUD tests', function () {
// Create user credentials
credentials = {
username: 'username',
password: 'password'
password: 'M3@n.jsI$Aw3$0m3'
};

// Create a new user
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
'use strict';

angular.module('users').controller('AuthenticationController', ['$scope', '$state', '$http', '$location', '$window', 'Authentication',
function ($scope, $state, $http, $location, $window, Authentication) {
angular.module('users').controller('AuthenticationController', ['$scope', '$state', '$http', '$location', '$window', 'Authentication', 'PasswordValidator',
function ($scope, $state, $http, $location, $window, Authentication, PasswordValidator) {
$scope.authentication = Authentication;
$scope.popoverMsg = PasswordValidator.getPopoverMsg();

// Get an eventual error defined in the URL query string:
$scope.error = $location.search().err;
Expand Down
21 changes: 17 additions & 4 deletions modules/users/client/controllers/password.client.controller.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,25 @@
'use strict';

angular.module('users').controller('PasswordController', ['$scope', '$stateParams', '$http', '$location', 'Authentication',
function ($scope, $stateParams, $http, $location, Authentication) {
angular.module('users').controller('PasswordController', ['$scope', '$stateParams', '$http', '$location', 'Authentication', 'PasswordValidator',
function ($scope, $stateParams, $http, $location, Authentication, PasswordValidator) {
$scope.authentication = Authentication;
$scope.popoverMsg = PasswordValidator.getPopoverMsg();

//If user is signed in then redirect back home
if ($scope.authentication.user) {
$location.path('/');
}

// Submit forgotten password account id
$scope.askForPasswordReset = function () {
$scope.askForPasswordReset = function (isValid) {
$scope.success = $scope.error = null;

if (!isValid) {
$scope.$broadcast('show-errors-check-validity', 'forgotPasswordForm');

return false;
}

$http.post('/api/auth/forgot', $scope.credentials).success(function (response) {
// Show user success message and clear form
$scope.credentials = null;
Expand All @@ -26,9 +33,15 @@ angular.module('users').controller('PasswordController', ['$scope', '$stateParam
};

// Change user password
$scope.resetUserPassword = function () {
$scope.resetUserPassword = function (isValid) {
$scope.success = $scope.error = null;

if (!isValid) {
$scope.$broadcast('show-errors-check-validity', 'resetPasswordForm');

return false;
}

$http.post('/api/auth/reset/' + $stateParams.token, $scope.passwordDetails).success(function (response) {
// If successful show success message and clear form
$scope.passwordDetails = null;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
'use strict';

angular.module('users').controller('ChangePasswordController', ['$scope', '$http', 'Authentication',
function ($scope, $http, Authentication) {
angular.module('users').controller('ChangePasswordController', ['$scope', '$http', 'Authentication', 'PasswordValidator',
function ($scope, $http, Authentication, PasswordValidator) {
$scope.user = Authentication.user;
$scope.popoverMsg = PasswordValidator.getPopoverMsg();

// Change user password
$scope.changeUserPassword = function (isValid) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
'use strict';

angular.module('users')
.directive('passwordValidator', ['PasswordValidator', function(PasswordValidator) {
return {
require: 'ngModel',
link: function(scope, element, attrs, modelCtrl) {
modelCtrl.$parsers.unshift(function (password) {
var result = PasswordValidator.getResult(password);
var strengthIdx = 0;

// Strength Meter - visual indicator for users
var strengthMeter = [
{ color: "danger", progress: "20" },
{ color: "warning", progress: "40"},
{ color: "info", progress: "60"},
{ color: "primary", progress: "80"},
{ color: "success", progress: "100"}
];
var strengthMax = strengthMeter.length;

if (result.errors.length < strengthMeter.length) {
strengthIdx = strengthMeter.length - result.errors.length - 1;
}

scope.strengthColor = strengthMeter[strengthIdx].color;
scope.strengthProgress = strengthMeter[strengthIdx].progress;

if (result.errors.length) {
scope.popoverMsg = PasswordValidator.getPopoverMsg();
scope.passwordErrors = result.errors;
modelCtrl.$setValidity('strength', false);
return undefined;
} else {
scope.popoverMsg = '';
modelCtrl.$setValidity('strength', true);
return password;
}
});
}
};
}]);
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
'use strict';

angular.module('users')
.directive("passwordVerify", function() {
return {
require: "ngModel",
scope: {
passwordVerify: '='
},
link: function(scope, element, attrs, modelCtrl) {
scope.$watch(function() {
var combined;
if (scope.passwordVerify || modelCtrl.$viewValue) {
combined = scope.passwordVerify + '_' + modelCtrl.$viewValue;
}
return combined;
}, function(value) {
if (value) {
modelCtrl.$parsers.unshift(function(viewValue) {
var origin = scope.passwordVerify;
if (origin !== viewValue) {
modelCtrl.$setValidity("passwordVerify", false);
return undefined;
} else {
modelCtrl.$setValidity("passwordVerify", true);
return viewValue;
}
});
}
});
}
};
});
19 changes: 19 additions & 0 deletions modules/users/client/services/password-validator.client.service.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
'use strict';

// PasswordValidator service used for testing the password strength
angular.module('users').factory('PasswordValidator', ['$window',
function ($window) {
var owaspPasswordStrengthTest = $window.owaspPasswordStrengthTest;

return {
getResult: function (password) {
var result = owaspPasswordStrengthTest.test(password);
return result;
},
getPopoverMsg: function () {
var popoverMsg = "Please enter a passphrase or password with greater than 10 characters, numbers, lowercase, upppercase, and special characters.";
return popoverMsg;
}
};
}
]);
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,20 @@ <h3 class="col-md-12 text-center">Or sign up using your email</h3>
</div>
<div class="form-group" show-errors>
<label for="password">Password</label>
<input type="password" id="password" name="password" class="form-control" ng-model="credentials.password" placeholder="Password" required ng-minlength="6">
<input type="password" id="password" name="password" class="form-control" ng-model="credentials.password" placeholder="Password" popover="{{popoverMsg}}" popover-trigger="focus" password-validator required>
<div ng-messages="userForm.password.$error" role="alert">
<p class="help-block error-text" ng-message="required">Password is required.</p>
<p class="help-block error-text" ng-message="minlength">Password is too short.</p>
<div ng-repeat="passwordError in passwordErrors">
<p class="help-block error-text" ng-show="userForm.password.$error.strength">{{passwordError}}</p>
</div>
</div>
</div>
<div class="form-group" ng-show="!userForm.password.$pristine">
<label>Password Strength</label>
<progressbar value="strengthProgress" type="{{strengthColor}}"><span style="color:white; white-space:nowrap;">{{strengthProgress}}%</span></progressbar>
</div>
<div class="text-center form-group">
<button type="submit" class="btn btn-large btn-primary">Sign up</button>
<button type="submit" class="btn btn-primary">Sign up</button>
&nbsp; or&nbsp;
<a ui-sref="authentication.signin" class="show-signup">Sign in</a>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,22 @@
<h3 class="col-md-12 text-center">Restore your password</h3>
<p class="small text-center">Enter your account username.</p>
<div class="col-xs-offset-2 col-xs-8 col-md-offset-5 col-md-2">
<form ng-submit="askForPasswordReset()" class="form-horizontal" autocomplete="off">
<form name="forgotPasswordForm" ng-submit="askForPasswordReset(forgotPasswordForm.$valid)" class="form-horizontal" novalidate autocomplete="off">
<fieldset>
<div class="form-group">
<input type="text" id="username" name="username" class="form-control" ng-model="credentials.username" placeholder="Username" lowercase>
<div class="form-group" show-errors>
<input type="text" id="username" name="username" class="form-control" ng-model="credentials.username" placeholder="Username" lowercase required>
<div ng-messages="forgotPasswordForm.username.$error" role="alert">
<p class="help-block error-text" ng-message="required">Enter a username.</p>
</div>
</div>
<div class="text-center form-group">
<button type="submit" class="btn btn-primary">Submit</button>
</div>
<div ng-show="error" class="text-center text-danger">
<strong>{{error}}</strong>
<strong ng-bind="error"></strong>
</div>
<div ng-show="success" class="text-center text-success">
<strong>{{success}}</strong>
<strong ng-bind="success"></strong>
</div>
</fieldset>
</form>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,38 @@
<section class="row" ng-controller="PasswordController">
<h3 class="col-md-12 text-center">Reset your password</h3>
<div class="col-xs-offset-2 col-xs-8 col-md-offset-5 col-md-2">
<form ng-submit="resetUserPassword()" class="signin form-horizontal" autocomplete="off">
<div class="col-xs-offset-2 col-xs-8 col-md-offset-4 col-md-4">
<form name="resetPasswordForm" ng-submit="resetUserPassword(resetPasswordForm.$valid)" class="signin form-horizontal" novalidate autocomplete="off">
<fieldset>
<div class="form-group">
<div class="form-group" show-errors>
<label for="newPassword">New Password</label>
<input type="password" id="newPassword" name="newPassword" class="form-control" ng-model="passwordDetails.newPassword" placeholder="New Password">
<input type="password" id="newPassword" name="newPassword" class="form-control" ng-model="passwordDetails.newPassword" placeholder="New Password" autocomplete="new-password" popover="{{popoverMsg}}" popover-trigger="focus" popover-placement="top" password-validator required>
<div ng-messages="resetPasswordForm.newPassword.$error" role="alert">
<p class="help-block error-text" ng-message="required">Enter a new password.</p>
<div ng-repeat="passwordError in passwordErrors">
<p class="help-block error-text" ng-show="resetPasswordForm.newPassword.$error.strength">{{passwordError}}</p>
</div>
</div>
</div>
<div class="form-group">
<div class="form-group" show-errors>
<label for="verifyPassword">Verify Password</label>
<input type="password" id="verifyPassword" name="verifyPassword" class="form-control" ng-model="passwordDetails.verifyPassword" placeholder="Verify Password">
<input type="password" id="verifyPassword" name="verifyPassword" class="form-control" ng-model="passwordDetails.verifyPassword" placeholder="Verify Password" password-verify="passwordDetails.newPassword" required>
<div ng-messages="resetPasswordForm.verifyPassword.$error" role="alert">
<p class="help-block error-text" ng-message="required">Enter the password again to verify.</p>
<p class="help-block error-text" ng-show=resetPasswordForm.verifyPassword.$error.passwordVerify>Passwords do not match.</p>
</div>
</div>
<div class="form-group" ng-show="!resetPasswordForm.newPassword.$pristine">
<label>Password Strength</label>
<progressbar value="strengthProgress" type="{{strengthColor}}"><span style="color:white; white-space:nowrap;">{{strengthProgress}}%</span></progressbar>
</div>
<div class="text-center form-group">
<button type="submit" class="btn btn-lg btn-primary">Update Password</button>
<button type="submit" class="btn btn-primary">Update Password</button>
</div>
<div ng-show="error" class="text-center text-danger">
<strong>{{error}}</strong>
<strong ng-bind="error"></strong>
</div>
<div ng-show="success" class="text-center text-success">
<strong>{{success}}</strong>
<strong ng-bind="success"></strong>
</div>
</fieldset>
</form>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,28 @@
</div>
<div class="form-group" show-errors>
<label for="newPassword">New Password</label>
<input type="password" id="newPassword" name="newPassword" class="form-control" ng-model="passwordDetails.newPassword" placeholder="New Password" required>
<input type="password" id="newPassword" name="newPassword" class="form-control" ng-model="passwordDetails.newPassword" placeholder="New Password" popover="{{popoverMsg}}" popover-trigger="focus" password-validator required>
<div ng-messages="passwordForm.newPassword.$error" role="alert">
<p class="help-block error-text" ng-message="required">Enter a new password.</p>
<div ng-repeat="passwordError in passwordErrors">
<p class="help-block error-text" ng-show="passwordForm.newPassword.$error.strength">{{passwordError}}</p>
</div>
</div>
</div>
<div class="form-group" show-errors>
<label for="verifyPassword">Verify Password</label>
<input type="password" id="verifyPassword" name="verifyPassword" class="form-control" ng-model="passwordDetails.verifyPassword" placeholder="Verify Password" required>
<input type="password" id="verifyPassword" name="verifyPassword" class="form-control" ng-model="passwordDetails.verifyPassword" placeholder="Verify Password" password-verify="passwordDetails.newPassword" required>
<div ng-messages="passwordForm.verifyPassword.$error" role="alert">
<p class="help-block error-text" ng-message="required">Verify your new password.</p>
<p class="help-block error-text" ng-show=passwordForm.verifyPassword.$error.passwordVerify>Passwords do not match.</p>
</div>
</div>
<div class="form-group" ng-show="!passwordForm.newPassword.$pristine">
<label>Password Strength</label>
<progressbar value="strengthProgress" type="{{strengthColor}}"><span style="color:white; white-space:nowrap;">{{strengthProgress}}%</span></progressbar>
</div>
<div class="text-center form-group">
<button type="submit" class="btn btn-lg btn-primary">Save Password</button>
<button type="submit" class="btn btn-primary">Save Password</button>
</div>
<div ng-show="success" class="text-center text-success">
<strong>Password Changed Successfully</strong>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
</div>
</div>
<div class="text-center form-group">
<button type="submit" class="btn btn-lg btn-primary">Save Profile</button>
<button type="submit" class="btn btn-primary">Save Profile</button>
</div>
<div ng-show="success" class="text-center text-success">
<strong>Profile Saved Successfully</strong>
Expand Down
Loading

0 comments on commit 5c16db2

Please sign in to comment.