diff --git a/LesionTracker/.meteor/versions b/LesionTracker/.meteor/versions
index 5bb385199..4fab1ac80 100644
--- a/LesionTracker/.meteor/versions
+++ b/LesionTracker/.meteor/versions
@@ -16,7 +16,7 @@ caching-compiler@1.0.0
caching-html-compiler@1.0.2
callback-hook@1.0.4
check@1.1.0
-clinical:active-entry@1.5.15
+clinical:active-entry@1.5.16
clinical:auto-resizing@0.1.2
clinical:error-pages@0.1.1
clinical:extended-api@2.2.2
@@ -28,7 +28,6 @@ clinical:router-location@2.0.14
clinical:router-middleware-stack@2.0.13
clinical:router-url@2.0.15
clinical:theming@0.4.7
-codetheweb:zxcvbn@4.0.1
coffeescript@1.0.11
cornerstone@0.0.1
ddp@1.2.2
diff --git a/LesionTracker/activeEntry.js b/LesionTracker/activeEntry.js
index 51a196b62..48cb0cafc 100644
--- a/LesionTracker/activeEntry.js
+++ b/LesionTracker/activeEntry.js
@@ -15,8 +15,9 @@ if (Meteor.isClient){
primary: ""
},
passwordOptions: {
- requireStrongPasswords: true,
- showPasswordStrengthIndicator: true
+ showPasswordStrengthIndicator: true,
+ requireRegexValidation: true
+ //requireStrongPasswords: false
}
});
}
diff --git a/Packages/active-entry/Contributing.md b/Packages/active-entry/Contributing.md
index bcfbe714a..aa34713b2 100644
--- a/Packages/active-entry/Contributing.md
+++ b/Packages/active-entry/Contributing.md
@@ -1,5 +1,10 @@
## Contributing
+**Submit your Pull Request on a Feature Branch**
+
+- To ensure your pull-request has the greatest chance of getting merged in, please submit it on a feature branch rather than directly to master.
+- Please see [A successful Git branching model](http://nvie.com/posts/a-successful-git-branching-model/) for more details.
+
**Quality Assurance**
- Pull Requests will be generally accepted as long as the QA tests pass on [Circle CI](https://circleci.com/gh/clinical-meteor/clinical-active-entry).
- Begin a Pull Request by logging an Issue for discussion.
diff --git a/Packages/active-entry/circle.yml b/Packages/active-entry/circle.yml
index 16a78dee3..f4a45a711 100644
--- a/Packages/active-entry/circle.yml
+++ b/Packages/active-entry/circle.yml
@@ -1,5 +1,7 @@
## Customize the test machine
machine:
+ node:
+ version: 0.10.33
# Timezone
timezone:
@@ -25,27 +27,48 @@ checkout:
## Customize dependencies
dependencies:
cache_directories:
- - "~/.meteor" # relative to the user's home directory
+ - ~/.meteor # relative to the user's home directory
+ - ~/nvm/v0.10.33/lib/node_modules/starrynight
+ - ~/nvm/v0.10.33/bin/starrynight
+
+ pre:
+ # Install Starrynight unless it is cached
+ - if [ ! -e ~/nvm/v0.10.33/bin/starrynight ]; then npm install -g starrynight; else echo "Starrynight seems to be cached"; fi;
+ # Install Meteor
+ - mkdir -p ${HOME}/.meteor
+ # If Meteor is already cached, do not need to build it again.
+ - if [ ! -e ${HOME}/.meteor/meteor ]; then curl https://install.meteor.com | /bin/sh; else echo "Meteor seems to be cached"; fi;
+ # Link the meteor executable into /usr/bin
+ - sudo ln -s $HOME/.meteor/meteor /usr/bin/meteor
+ # Check if the helloworld directory already exists, if it doesn't, create the helloworld app
+ # The following doesn't work, because it should be checking ${HOME}/active-entry/helloworld
+ # - if [ ! -e ${HOME}/helloworld ]; then meteor create --release METEOR@1.1.0.3 helloworld; else echo "helloworld app seems to be cached"; fi;
override:
- - meteor || curl https://install.meteor.com | /bin/sh
- - npm install starrynight -g
- - meteor create --release METEOR@1.1.0.3 helloworld
- - cd helloworld
- - cd helloworld && ls -la
- - cd helloworld && rm helloworld.*
- - cd helloworld && meteor add anti:gagarin@0.4.11 accounts-base accounts-password session meteor-platform clinical:active-entry clinical:user-model
- - cd helloworld && git clone http://github.com/clinical-meteor/clinical-active-entry packages/active-entry
- - cd helloworld && starrynight autoconfig
+ - cd ${HOME} && meteor create --release METEOR@1.1.0.3 helloworld
+ - cd ${HOME}/helloworld
+ - cd ${HOME}/helloworld && ls -la
+ - cd ${HOME}/helloworld && rm helloworld.*
+ - cd ${HOME}/helloworld && mkdir packages && mkdir packages/active-entry
+ - cp -R * ${HOME}/helloworld/packages/active-entry
+ - cd ${HOME}/helloworld && meteor add anti:gagarin@0.4.11 accounts-base accounts-password session meteor-platform clinical:user-model clinical:active-entry
+ - cd ${HOME}/helloworld && starrynight autoconfig
+ - cd ${HOME}/helloworld && meteor list
+ - cat tests/gagarin/activeEntryTests.js
+ - ls -la
+ - cd ~ && ls -la
+ - cd ${HOME} && pwd
+ - cd ${HOME} && ls -la
+ - cd ${HOME}/helloworld && ls -la
## Customize test commands
test:
pre:
- cd helloworld && meteor:
background: true
- - sleep 80
+ - sleep 30
override:
- - cd helloworld && starrynight run-tests --type package-verification
+ - cd ${HOME}/helloworld && starrynight run-tests --type package-verification
## Customize deployment commands
#deployment:
diff --git a/Packages/active-entry/components/changePassword/changePassword.html b/Packages/active-entry/components/changePassword/changePassword.html
new file mode 100755
index 000000000..add5897aa
--- /dev/null
+++ b/Packages/active-entry/components/changePassword/changePassword.html
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
Change Password
+
{{getChangePasswordMessage}}
+
+
+
+
+
+
+
diff --git a/Packages/active-entry/components/changePassword/changePassword.js b/Packages/active-entry/components/changePassword/changePassword.js
new file mode 100755
index 000000000..cf0c56a4d
--- /dev/null
+++ b/Packages/active-entry/components/changePassword/changePassword.js
@@ -0,0 +1,84 @@
+//==========================================
+
+Router.route('/changePassword', {
+ name: "changePassword",
+ template: "changePassword"
+});
+
+
+Template.changePassword.helpers({
+ getChangePasswordMessageColor: function (){
+ if (ActiveEntry.errorMessages.get('changePasswordError')) {
+ return "color: #a94442; background-color: #f2dede; border-color: #ebccd1;"
+ } else {
+ return "color: black;"
+ }
+ },
+ getChangePasswordMessage: function (){
+ if (ActiveEntry.errorMessages.get('changePasswordError')) {
+ return ActiveEntry.errorMessages.get('changePasswordError');
+ } else {
+ return Session.get('defaultSignInMessage');
+ }
+ },
+ getPasswordStyling: function () {
+ if (ActiveEntry.errorMessages.equals('password', "Password is required")) {
+ return "border: 1px solid #a94442";
+ } else if (ActiveEntry.errorMessages.equals('password', "Password is weak")) {
+ return "border: 1px solid #f2dede";
+ } else if (ActiveEntry.errorMessages.equals('password', "Password present")) {
+ return "border: 1px solid green";
+ } else {
+ return "border: 1px solid gray";
+ }
+ },
+ getConfirmPasswordStyling: function () {
+ if (ActiveEntry.errorMessages.equals('confirm', "Password is required")) {
+ return "border: 1px solid #a94442";
+ } else if (ActiveEntry.errorMessages.equals('confirm', "Passwords do not match")) {
+ return "border: 1px solid #a94442";
+ } else if (ActiveEntry.errorMessages.equals('confirm', "Password is weak")) {
+ return "border: 1px solid #f2dede";
+ } else if (ActiveEntry.errorMessages.equals('confirm', "Passwords match")) {
+ return "border: 1px solid green";
+ } else {
+ return "border: 1px solid gray";
+ }
+ }
+});
+
+Template.changePassword.events({
+ 'change, keyup #changePasswordPagePasswordInput': function (event, template) {
+ var password = $('[name="password"]').val();
+ var confirmPassword = $('[name="confirm"]').val();
+
+ ActiveEntry.verifyPassword(password);
+ ActiveEntry.errorMessages.set('changePasswordError', null);
+ },
+ 'change, keyup #changePasswordPagePasswordConfirmInput': function (event, template) {
+ var password = $('[name="password"]').val();
+ var confirmPassword = $('[name="confirm"]').val();
+
+ ActiveEntry.verifyConfirmPassword(password, confirmPassword);
+ ActiveEntry.errorMessages.set('changePasswordError', null);
+ },
+ "submit": function (event, template) {
+ event.preventDefault();
+
+ var oldPassword = $('[name="oldPassword"]').val();
+
+ var password = $('[name="password"]').val();
+ var confirmPassword = $('[name="confirm"]').val();
+
+ ActiveEntry.verifyConfirmPassword(password, confirmPassword);
+ ActiveEntry.errorMessages.set('changePasswordError', null);
+
+ Accounts.changePassword(oldPassword, confirmPassword, function(error) {
+ if (error) {
+ console.warn(error);
+ return;
+ }
+ console.log('Password changed!');
+ });
+ }
+});
diff --git a/Packages/active-entry/components/changePassword/changePassword.less b/Packages/active-entry/components/changePassword/changePassword.less
new file mode 100755
index 000000000..b68dbb62c
--- /dev/null
+++ b/Packages/active-entry/components/changePassword/changePassword.less
@@ -0,0 +1,5 @@
+#changePassword{
+ input{
+ padding-left: 40px;
+ }
+}
diff --git a/Packages/active-entry/components/entrySignIn/entrySignIn.js b/Packages/active-entry/components/entrySignIn/entrySignIn.js
index 3320925fc..c6420a56f 100755
--- a/Packages/active-entry/components/entrySignIn/entrySignIn.js
+++ b/Packages/active-entry/components/entrySignIn/entrySignIn.js
@@ -126,14 +126,13 @@ Template.entrySignIn.events({
ActiveEntry.signIn(emailValue, passwordValue);
event.preventDefault();
},
- 'keypress #entrySignIn': function(event, template) {
+ 'keyup #entrySignIn': function(event, template) {
if(event.keyCode == 13) {
ActiveEntry.verifyEmail($("#signInPageEmailInput").val());
- ActiveEntry.verifyPassword($("#signInPagePasswordInput").val());
if (!ActiveEntry.errorMessages.get('signInError') &&
ActiveEntry.successMessages.get('email') &&
- ActiveEntry.successMessages.get('password')) {
+ $("#signInPagePasswordInput").val()) {
$("#signInToAppButton").click();
}
}
diff --git a/Packages/active-entry/components/entrySignUp/entrySignUp.js b/Packages/active-entry/components/entrySignUp/entrySignUp.js
index 6a713c283..0bdddb3a4 100755
--- a/Packages/active-entry/components/entrySignUp/entrySignUp.js
+++ b/Packages/active-entry/components/entrySignUp/entrySignUp.js
@@ -71,6 +71,8 @@ Template.entrySignUp.helpers({
if (ActiveEntry.errorMessages.equals('confirm', "Password is required")) {
return "border: 1px solid #a94442";
} else if (ActiveEntry.errorMessages.equals('confirm', "Passwords do not match")) {
+ return "border: 1px solid #a94442";
+ } else if (ActiveEntry.errorMessages.equals('confirm', "Password is weak")) {
return "border: 1px solid #f2dede";
} else if (ActiveEntry.successMessages.equals('confirm', "Passwords match")) {
return "border: 1px solid green";
@@ -133,7 +135,7 @@ Template.entrySignUp.events({
$('#signUpPageFullNameInput').val()
);
},
- 'keypress #entrySignUp': function(event, template) {
+ 'keyup #entrySignUp': function(event, template) {
if(event.keyCode == 13) {
ActiveEntry.verifyFullName($("#signUpPageFullNameInput").val());
ActiveEntry.verifyEmail($("#signUpPageEmailInput").val());
@@ -153,13 +155,12 @@ Template.entrySignUp.events({
Template.entrySignUp.onRendered(function() {
// Password strength meter for password inputs
- if (passwordValidationSettings.requireStrongPasswords) {
+ if (passwordValidationSettings.showPasswordStrengthIndicator) {
this.$('#signUpPagePasswordInput').pwstrength(passwordValidationSettings.pwstrengthOptions);
}
- // Update password warning message if zxcvbn is active
- if(passwordValidationSettings.showPasswordStrengthIndicator) {
+ // Update password warning message if zxcvbn is active and zxcvbn function is defined
+ if(passwordValidationSettings.requireStrongPasswords) {
Session.set('passwordWarning', 'Password is weak');
}
});
-
diff --git a/Packages/active-entry/lib/ActiveEntry.js b/Packages/active-entry/lib/ActiveEntry.js
index 4dfa23f86..83710f7d9 100755
--- a/Packages/active-entry/lib/ActiveEntry.js
+++ b/Packages/active-entry/lib/ActiveEntry.js
@@ -24,12 +24,15 @@ if (Meteor.isClient) {
primary: ""
},
passwordOptions: {
- requireStrongPasswords: false,
- showPasswordStrengthIndicator: false
+ showPasswordStrengthIndicator: true,
+ requireRegexValidation: true
+ //requireStrongPasswords: false
}
});
}
+// requireRegexValidation toggles regex
+// reqiureStrongPasswords toggles zxcvbn
if (Meteor.isClient) {
ActiveEntry.errorMessages = new ReactiveDict('errorMessages');
@@ -56,7 +59,6 @@ ActiveEntry.verifyPassword = function (password) {
ActiveEntry.errorMessages.set('password', Session.get('passwordWarning'));
ActiveEntry.successMessages.set('password', null);
} else {
- //ActiveEntry.errorMessages.set('password', 'Password present');
ActiveEntry.errorMessages.set('password', null);
ActiveEntry.successMessages.set('password', 'Password present');
}
@@ -64,11 +66,15 @@ ActiveEntry.verifyPassword = function (password) {
};
ActiveEntry.verifyConfirmPassword = function (password, confirmPassword) {
- if (confirmPassword === password) {
- //ActiveEntry.errorMessages.set('confirm', 'Passwords match');
+ // we have two different logic checks happening in this function
+ // would be reasonable to separate them out into separate functions
+ if (confirmPassword === "") {
+ ActiveEntry.errorMessages.set('confirm', 'Password is required');
+ ActiveEntry.successMessages.set('confirm', null);
+ } else if (confirmPassword === password) {
ActiveEntry.errorMessages.set('confirm', null);
ActiveEntry.successMessages.set('confirm', 'Passwords match');
- } else{
+ } else {
ActiveEntry.errorMessages.set('confirm', 'Passwords do not match');
ActiveEntry.successMessages.set('confirm', null);
}
diff --git a/Packages/active-entry/lib/checkPasswordStrength.js b/Packages/active-entry/lib/checkPasswordStrength.js
index 9915f6e6a..a36b45887 100644
--- a/Packages/active-entry/lib/checkPasswordStrength.js
+++ b/Packages/active-entry/lib/checkPasswordStrength.js
@@ -1,46 +1,59 @@
passwordValidationSettings = {};
-var ActiveEntryConfiguration;
-Meteor.startup(function() {
- ActiveEntryConfiguration = Session.get('Photonic.ActiveEntry');
- var showPasswordStrengthIndicator = (ActiveEntryConfiguration && ActiveEntryConfiguration.passwordOptions && ActiveEntryConfiguration.passwordOptions.showPasswordStrengthIndicator || false);
- passwordValidationSettings.requireStrongPasswords = (ActiveEntryConfiguration && ActiveEntryConfiguration.passwordOptions && ActiveEntryConfiguration.passwordOptions.requireStrongPasswords || false);
- passwordValidationSettings.showPasswordStrengthIndicator = showPasswordStrengthIndicator;
- passwordValidationSettings.pwstrengthOptions = {
- common: {
- minChar: 8,
- zxcvbn: showPasswordStrengthIndicator
- },
- ui: {
- showVerdictsInsideProgressBar: true,
- showStatus: true
- },
- rules: {
- activated: {
- wordNotEmail: true,
- wordTwoCharacterClasses: true,
- wordRepetitions: true
+function getPasswordValidationSettings () {
+ var ActiveEntryConfiguration = Session.get('Photonic.ActiveEntry');
+ var validationSettings = {};
+
+ validationSettings.showPasswordStrengthIndicator = ActiveEntryConfiguration && ActiveEntryConfiguration.passwordOptions && ActiveEntryConfiguration.passwordOptions.showPasswordStrengthIndicator || false;
+ validationSettings.requireRegexValidation = ActiveEntryConfiguration && ActiveEntryConfiguration.passwordOptions && ActiveEntryConfiguration.passwordOptions.requireRegexValidation || false;
+
+ if (validationSettings.showPasswordStrengthIndicator) {
+ // Set password strength meter options
+ validationSettings.pwstrengthOptions = {
+ common: {
+ minChar: 8
+ },
+ ui: {
+ showVerdictsInsideProgressBar: true,
+ showStatus: true
}
+ };
+ }
+
+ // Check if codetheweb:zxcvbn is defined
+ if (typeof(zxcvbn) === typeof(Function)) {
+ validationSettings.requireStrongPasswords = ActiveEntryConfiguration && ActiveEntryConfiguration.passwordOptions && ActiveEntryConfiguration.passwordOptions.requireStrongPasswords || false;
+ // Set zxcvbn in pw strength meter
+ if (validationSettings.showPasswordStrengthIndicator) {
+ validationSettings.pwstrengthOptions.common.zxcvbn = passwordValidationSettings.requireStrongPasswords;
}
- };
+ }
+
+ return validationSettings;
+}
+
+Meteor.startup(function() {
+ passwordValidationSettings = getPasswordValidationSettings();
});
-// Check Password Strength: at least 8 characters in length and contain at least 1 uppercase, 1 lowercase and 1 number and 1 special character
checkPasswordStrength = function(password) {
- var iszxcvbnActive = (ActiveEntryConfiguration && ActiveEntryConfiguration.passwordOptions && ActiveEntryConfiguration.passwordOptions.showPasswordStrengthIndicator || false);
- if (iszxcvbnActive) {
- // Check zxcvbn rule
+ if (passwordValidationSettings.requireStrongPasswords) {
+ // Check zxcvbn
var zxcvbnResult = zxcvbn(password);
- if (zxcvbnResult.score > 2) {
+ if (zxcvbnResult && zxcvbnResult.score > 2) {
return true;
}
- } else{
+
+ return false;
+ } else if (passwordValidationSettings.requireRegexValidation) {
// Apply validation rule
var result = password.search(/^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[!@#$%^&*])[0-9a-zA-Z!@#$%^&*]{8,}$/i);
if (result > -1) {
return true;
}
+
+ return false;
}
- return false;
+ return true;
};
\ No newline at end of file
diff --git a/Packages/active-entry/lib/jquery.pwstrength.bootstrap.js b/Packages/active-entry/lib/jquery.pwstrength.bootstrap.js
index ce466d7fe..ddaaf1568 100644
--- a/Packages/active-entry/lib/jquery.pwstrength.bootstrap.js
+++ b/Packages/active-entry/lib/jquery.pwstrength.bootstrap.js
@@ -7,702 +7,731 @@
*/
(function (jQuery) {
-// Source: src/rules.js
+ // Source: src/rules.js
+ var rulesEngine = {};
- var rulesEngine = {};
+ try {
+ if (!jQuery && module && module.exports) {
+ var jQuery = require("jquery"),
+ jsdom = require("jsdom").jsdom;
+ jQuery = jQuery(jsdom().parentWindow);
+ }
+ } catch (ignore) {}
- try {
- if (!jQuery && module && module.exports) {
- var jQuery = require("jquery"),
- jsdom = require("jsdom").jsdom;
- jQuery = jQuery(jsdom().parentWindow);
- }
- } catch (ignore) {}
+ (function ($, rulesEngine) {
+ "use strict";
+ var validation = {};
- (function ($, rulesEngine) {
- "use strict";
- var validation = {};
-
- rulesEngine.forbiddenSequences = [
+ rulesEngine.forbiddenSequences = [
"0123456789", "abcdefghijklmnopqrstuvwxyz", "qwertyuiop", "asdfghjkl",
"zxcvbnm", "!@#$%^&*()_+"
];
- validation.wordNotEmail = function (options, word, score) {
- if (word.match(/^([\w\!\#$\%\&\'\*\+\-\/\=\?\^\`{\|\}\~]+\.)*[\w\!\#$\%\&\'\*\+\-\/\=\?\^\`{\|\}\~]+@((((([a-z0-9]{1}[a-z0-9\-]{0,62}[a-z0-9]{1})|[a-z])\.)+[a-z]{2,6})|(\d{1,3}\.){3}\d{1,3}(\:\d{1,5})?)$/i)) {
- return score;
+ validation.wordNotEmail = function (options, word, score) {
+ if (word.match(
+ /^([\w\!\#$\%\&\'\*\+\-\/\=\?\^\`{\|\}\~]+\.)*[\w\!\#$\%\&\'\*\+\-\/\=\?\^\`{\|\}\~]+@((((([a-z0-9]{1}[a-z0-9\-]{0,62}[a-z0-9]{1})|[a-z])\.)+[a-z]{2,6})|(\d{1,3}\.){3}\d{1,3}(\:\d{1,5})?)$/i
+ )) {
+ return score;
+ }
+ return 0;
+ };
+
+ validation.wordLength = function (options, word, score) {
+ var wordlen = word.length,
+ lenScore = Math.pow(wordlen, options.rules.raisePower);
+ if (wordlen < options.common.minChar) {
+ lenScore = (lenScore + score);
+ }
+ return lenScore;
+ };
+
+ validation.wordSimilarToUsername = function (options, word, score) {
+ var username = $(options.common.usernameField).val();
+ if (username && word.toLowerCase().match(username.replace(
+ /[\-\[\]\/\{\}\(\)\*\+\=\?\:\.\\\^\$\|\!\,]/g, "\\$&").toLowerCase())) {
+ return score;
+ }
+ return 0;
+ };
+
+ validation.wordTwoCharacterClasses = function (options, word, score) {
+ if (word.match(/([a-z].*[A-Z])|([A-Z].*[a-z])/) ||
+ (word.match(/([a-zA-Z])/) && word.match(/([0-9])/)) ||
+ (word.match(/(.[!,@,#,$,%,\^,&,*,?,_,~])/) && word.match(/[a-zA-Z0-9_]/))) {
+ return score;
+ }
+ return 0;
+ };
+
+ validation.wordRepetitions = function (options, word, score) {
+ if (word.match(/(.)\1\1/)) {
+ return score;
+ }
+ return 0;
+ };
+
+ validation.wordSequences = function (options, word, score) {
+ var found = false,
+ j;
+ if (word.length > 2) {
+ $.each(rulesEngine.forbiddenSequences, function (idx, seq) {
+ if (found) {
+ return;
+ }
+ var sequences = [seq, seq.split('').reverse().join('')];
+ $.each(sequences, function (idx, sequence) {
+ for (j = 0; j < (word.length - 2); j += 1) { // iterate the word trough a sliding window of size 3:
+ if (sequence.indexOf(word.toLowerCase().substring(j, j + 3)) > -1) {
+ found = true;
+ }
}
- return 0;
- };
-
- validation.wordLength = function (options, word, score) {
- var wordlen = word.length,
- lenScore = Math.pow(wordlen, options.rules.raisePower);
- if (wordlen < options.common.minChar) {
- lenScore = (lenScore + score);
- }
- return lenScore;
- };
-
- validation.wordSimilarToUsername = function (options, word, score) {
- var username = $(options.common.usernameField).val();
- if (username && word.toLowerCase().match(username.replace(/[\-\[\]\/\{\}\(\)\*\+\=\?\:\.\\\^\$\|\!\,]/g, "\\$&").toLowerCase())) {
- return score;
- }
- return 0;
- };
-
- validation.wordTwoCharacterClasses = function (options, word, score) {
- if (word.match(/([a-z].*[A-Z])|([A-Z].*[a-z])/) ||
- (word.match(/([a-zA-Z])/) && word.match(/([0-9])/)) ||
- (word.match(/(.[!,@,#,$,%,\^,&,*,?,_,~])/) && word.match(/[a-zA-Z0-9_]/))) {
- return score;
- }
- return 0;
- };
-
- validation.wordRepetitions = function (options, word, score) {
- if (word.match(/(.)\1\1/)) { return score; }
- return 0;
- };
-
- validation.wordSequences = function (options, word, score) {
- var found = false,
- j;
- if (word.length > 2) {
- $.each(rulesEngine.forbiddenSequences, function (idx, seq) {
- if (found) { return; }
- var sequences = [seq, seq.split('').reverse().join('')];
- $.each(sequences, function (idx, sequence) {
- for (j = 0; j < (word.length - 2); j += 1) { // iterate the word trough a sliding window of size 3:
- if (sequence.indexOf(word.toLowerCase().substring(j, j + 3)) > -1) {
- found = true;
- }
- }
- });
- });
- if (found) { return score; }
- }
- return 0;
- };
-
- validation.wordLowercase = function (options, word, score) {
- return word.match(/[a-z]/) && score;
- };
-
- validation.wordUppercase = function (options, word, score) {
- return word.match(/[A-Z]/) && score;
- };
-
- validation.wordOneNumber = function (options, word, score) {
- return word.match(/\d+/) && score;
- };
-
- validation.wordThreeNumbers = function (options, word, score) {
- return word.match(/(.*[0-9].*[0-9].*[0-9])/) && score;
- };
-
- validation.wordOneSpecialChar = function (options, word, score) {
- return word.match(/[!,@,#,$,%,\^,&,*,?,_,~]/) && score;
- };
-
- validation.wordTwoSpecialChar = function (options, word, score) {
- return word.match(/(.*[!,@,#,$,%,\^,&,*,?,_,~].*[!,@,#,$,%,\^,&,*,?,_,~])/) && score;
- };
-
- validation.wordUpperLowerCombo = function (options, word, score) {
- return word.match(/([a-z].*[A-Z])|([A-Z].*[a-z])/) && score;
- };
-
- validation.wordLetterNumberCombo = function (options, word, score) {
- return word.match(/([a-zA-Z])/) && word.match(/([0-9])/) && score;
- };
-
- validation.wordLetterNumberCharCombo = function (options, word, score) {
- return word.match(/([a-zA-Z0-9].*[!,@,#,$,%,\^,&,*,?,_,~])|([!,@,#,$,%,\^,&,*,?,_,~].*[a-zA-Z0-9])/) && score;
- };
-
- rulesEngine.validation = validation;
-
- rulesEngine.executeRules = function (options, word) {
- var totalScore = 0;
-
- $.each(options.rules.activated, function (rule, active) {
- if (active) {
- var score = options.rules.scores[rule],
- funct = rulesEngine.validation[rule],
- result,
- errorMessage;
-
- if (!$.isFunction(funct)) {
- funct = options.rules.extra[rule];
- }
-
- if ($.isFunction(funct)) {
- result = funct(options, word, score);
- if (result) {
- totalScore += result;
- }
- if (result < 0 || (!$.isNumeric(result) && !result)) {
- errorMessage = options.ui.spanError(options, rule);
- if (errorMessage.length > 0) {
- options.instances.errors.push(errorMessage);
- }
- }
- }
- }
- });
-
- return totalScore;
- };
- }(jQuery, rulesEngine));
-
- try {
- if (module && module.exports) {
- module.exports = rulesEngine;
+ });
+ });
+ if (found) {
+ return score;
}
- } catch (ignore) {}
+ }
+ return 0;
+ };
-// Source: src/options.js
+ validation.wordLowercase = function (options, word, score) {
+ return word.match(/[a-z]/) && score;
+ };
+
+ validation.wordUppercase = function (options, word, score) {
+ return word.match(/[A-Z]/) && score;
+ };
+
+ validation.wordOneNumber = function (options, word, score) {
+ return word.match(/\d+/) && score;
+ };
+
+ validation.wordThreeNumbers = function (options, word, score) {
+ return word.match(/(.*[0-9].*[0-9].*[0-9])/) && score;
+ };
+
+ validation.wordOneSpecialChar = function (options, word, score) {
+ return word.match(/[!,@,#,$,%,\^,&,*,?,_,~]/) && score;
+ };
+
+ validation.wordTwoSpecialChar = function (options, word, score) {
+ return word.match(/(.*[!,@,#,$,%,\^,&,*,?,_,~].*[!,@,#,$,%,\^,&,*,?,_,~])/) && score;
+ };
+
+ validation.wordUpperLowerCombo = function (options, word, score) {
+ return word.match(/([a-z].*[A-Z])|([A-Z].*[a-z])/) && score;
+ };
+
+ validation.wordLetterNumberCombo = function (options, word, score) {
+ return word.match(/([a-zA-Z])/) && word.match(/([0-9])/) && score;
+ };
+
+ validation.wordLetterNumberCharCombo = function (options, word, score) {
+ return word.match(
+ /([a-zA-Z0-9].*[!,@,#,$,%,\^,&,*,?,_,~])|([!,@,#,$,%,\^,&,*,?,_,~].*[a-zA-Z0-9])/) &&
+ score;
+ };
+
+ rulesEngine.validation = validation;
+
+ rulesEngine.executeRules = function (options, word) {
+ var totalScore = 0;
+
+ $.each(options.rules.activated, function (rule, active) {
+ if (active) {
+ var score = options.rules.scores[rule],
+ funct = rulesEngine.validation[rule],
+ result,
+ errorMessage;
+
+ if (!$.isFunction(funct)) {
+ funct = options.rules.extra[rule];
+ }
+
+ if ($.isFunction(funct)) {
+ result = funct(options, word, score);
+ if (result) {
+ totalScore += result;
+ }
+ if (result < 0 || (!$.isNumeric(result) && !result)) {
+ errorMessage = options.ui.spanError(options, rule);
+ if (errorMessage.length > 0) {
+ options.instances.errors.push(errorMessage);
+ }
+ }
+ }
+ }
+ });
+
+ return totalScore;
+ };
+ }(jQuery, rulesEngine));
+
+ try {
+ if (module && module.exports) {
+ module.exports = rulesEngine;
+ }
+ } catch (ignore) {}
+
+ // Source: src/options.js
+ var defaultOptions = {};
- var defaultOptions = {};
-
- defaultOptions.common = {};
- defaultOptions.common.minChar = 6;
- defaultOptions.common.usernameField = "#username";
- defaultOptions.common.userInputs = [
+ defaultOptions.common = {};
+ defaultOptions.common.minChar = 6;
+ defaultOptions.common.usernameField = "#username";
+ defaultOptions.common.userInputs = [
// Selectors for input fields with user input
];
- defaultOptions.common.onLoad = undefined;
- defaultOptions.common.onKeyUp = undefined;
- defaultOptions.common.zxcvbn = false;
- defaultOptions.common.zxcvbnTerms = [
+ defaultOptions.common.onLoad = undefined;
+ defaultOptions.common.onKeyUp = undefined;
+ defaultOptions.common.zxcvbn = false;
+ defaultOptions.common.zxcvbnTerms = [
// List of disrecommended words
];
- defaultOptions.common.debug = false;
+ defaultOptions.common.debug = false;
- defaultOptions.rules = {};
- defaultOptions.rules.extra = {};
- defaultOptions.rules.scores = {
- wordNotEmail: -100,
- wordLength: -50,
- wordSimilarToUsername: -100,
- wordSequences: -20,
- wordTwoCharacterClasses: 2,
- wordRepetitions: -25,
- wordLowercase: 1,
- wordUppercase: 3,
- wordOneNumber: 3,
- wordThreeNumbers: 5,
- wordOneSpecialChar: 3,
- wordTwoSpecialChar: 5,
- wordUpperLowerCombo: 2,
- wordLetterNumberCombo: 2,
- wordLetterNumberCharCombo: 2
- };
- defaultOptions.rules.activated = {
- wordNotEmail: true,
- wordLength: true,
- wordSimilarToUsername: true,
- wordSequences: true,
- wordTwoCharacterClasses: false,
- wordRepetitions: false,
- wordLowercase: true,
- wordUppercase: true,
- wordOneNumber: true,
- wordThreeNumbers: true,
- wordOneSpecialChar: true,
- wordTwoSpecialChar: true,
- wordUpperLowerCombo: true,
- wordLetterNumberCombo: true,
- wordLetterNumberCharCombo: true
- };
- defaultOptions.rules.raisePower = 1.4;
+ defaultOptions.rules = {};
+ defaultOptions.rules.extra = {};
+ defaultOptions.rules.scores = {
+ wordNotEmail: -100,
+ wordLength: -50,
+ wordSimilarToUsername: -100,
+ wordSequences: -20,
+ wordTwoCharacterClasses: 2,
+ wordRepetitions: -25,
+ wordLowercase: 1,
+ wordUppercase: 3,
+ wordOneNumber: 3,
+ wordThreeNumbers: 5,
+ wordOneSpecialChar: 3,
+ wordTwoSpecialChar: 5,
+ wordUpperLowerCombo: 2,
+ wordLetterNumberCombo: 2,
+ wordLetterNumberCharCombo: 2
+ };
+ defaultOptions.rules.activated = {
+ wordNotEmail: true,
+ wordLength: true,
+ wordSimilarToUsername: true,
+ wordSequences: true,
+ wordTwoCharacterClasses: false,
+ wordRepetitions: false,
+ wordLowercase: true,
+ wordUppercase: true,
+ wordOneNumber: true,
+ wordThreeNumbers: true,
+ wordOneSpecialChar: true,
+ wordTwoSpecialChar: true,
+ wordUpperLowerCombo: true,
+ wordLetterNumberCombo: true,
+ wordLetterNumberCharCombo: true
+ };
+ defaultOptions.rules.raisePower = 1.4;
- defaultOptions.ui = {};
- defaultOptions.ui.bootstrap2 = false;
- defaultOptions.ui.bootstrap4 = false;
- defaultOptions.ui.colorClasses = ["danger", "warning", "success"];
- defaultOptions.ui.showProgressBar = true;
- defaultOptions.ui.showPopover = false;
- defaultOptions.ui.popoverPlacement = "bottom";
- defaultOptions.ui.showStatus = false;
- defaultOptions.ui.spanError = function (options, key) {
- "use strict";
- var text = options.ui.errorMessages[key];
- if (!text) { return ''; }
- return '' + text + '';
- };
- defaultOptions.ui.popoverError = function (errors) {
- "use strict";
- var message = "
Errors:
";
+ defaultOptions.ui = {};
+ defaultOptions.ui.bootstrap2 = false;
+ defaultOptions.ui.bootstrap4 = false;
+ defaultOptions.ui.colorClasses = ["danger", "warning", "success"];
+ defaultOptions.ui.showProgressBar = true;
+ defaultOptions.ui.showPopover = false;
+ defaultOptions.ui.popoverPlacement = "bottom";
+ defaultOptions.ui.showStatus = false;
+ defaultOptions.ui.spanError = function (options, key) {
+ "use strict";
+ var text = options.ui.errorMessages[key];
+ if (!text) {
+ return '';
+ }
+ return '' + text + '';
+ };
+ defaultOptions.ui.popoverError = function (errors) {
+ "use strict";
+ var message = "Errors:
";
- jQuery.each(errors, function (idx, err) {
- message += "- " + err + "
";
+ jQuery.each(errors, function (idx, err) {
+ message += "- " + err + "
";
+ });
+ message += "
";
+ return message;
+ };
+ defaultOptions.ui.errorMessages = {
+ wordLength: "Your password is too short",
+ wordNotEmail: "Do not use your email as your password",
+ wordSimilarToUsername: "Your password cannot contain your username",
+ wordTwoCharacterClasses: "Use different character classes",
+ wordRepetitions: "Too many repetitions",
+ wordSequences: "Your password contains sequences"
+ };
+ defaultOptions.ui.verdicts = ["Weak", "Normal", "Medium", "Strong", "Very Strong"];
+ defaultOptions.ui.showVerdicts = true;
+ defaultOptions.ui.showVerdictsInsideProgressBar = false;
+ defaultOptions.ui.useVerdictCssClass = false;
+ defaultOptions.ui.showErrors = false;
+ defaultOptions.ui.container = undefined;
+ defaultOptions.ui.viewports = {
+ progress: undefined,
+ verdict: undefined,
+ errors: undefined
+ };
+ defaultOptions.ui.scores = [14, 26, 38, 50];
+
+ // Source: src/ui.js
+
+
+
+ var ui = {};
+
+ (function ($, ui) {
+ "use strict";
+
+ var statusClasses = ["error", "warning", "success"];
+
+ ui.getContainer = function (options, $el) {
+ var $container;
+
+ $container = $(options.ui.container);
+ if (!($container && $container.length === 1)) {
+ $container = $el.parent();
+ }
+ return $container;
+ };
+
+ ui.findElement = function ($container, viewport, cssSelector) {
+ if (viewport) {
+ return $container.find(viewport).find(cssSelector);
+ }
+ return $container.find(cssSelector);
+ };
+
+ ui.getUIElements = function (options, $el) {
+ var $container, selector, result;
+
+ if (options.instances.viewports) {
+ return options.instances.viewports;
+ }
+
+ $container = ui.getContainer(options, $el);
+
+ result = {};
+ if (options.ui.bootstrap4) {
+ selector = "progress.progress";
+ } else {
+ selector = "div.progress";
+ }
+ result.$progressbar = ui.findElement($container, options.ui.viewports.progress,
+ selector);
+ if (options.ui.showVerdictsInsideProgressBar) {
+ result.$verdict = result.$progressbar.find("span.password-verdict");
+ }
+
+ if (!options.ui.showPopover) {
+ if (!options.ui.showVerdictsInsideProgressBar) {
+ result.$verdict = ui.findElement($container, options.ui.viewports.verdict,
+ "span.password-verdict");
+ }
+ result.$errors = ui.findElement($container, options.ui.viewports.errors,
+ "ul.error-list");
+ }
+
+ options.instances.viewports = result;
+ return result;
+ };
+
+ ui.initProgressBar = function (options, $el) {
+ var $container = ui.getContainer(options, $el),
+ progressbar = "";
+ if (options.ui.bootstrap4) {
+ // Boostrap 4
+ progressbar = "
";
+ } else {
+ progressbar += "
";
+ }
+
+ if (options.ui.viewports.progress) {
+ $container.find(options.ui.viewports.progress).append(progressbar);
+ } else {
+ $(progressbar).insertAfter($el);
+ }
+ };
+
+ ui.initHelper = function (options, $el, html, viewport) {
+ var $container = ui.getContainer(options, $el);
+ if (viewport) {
+ $container.find(viewport).append(html);
+ } else {
+ $(html).insertAfter($el);
+ }
+ };
+
+ ui.initVerdict = function (options, $el) {
+ ui.initHelper(options, $el, "",
+ options.ui.viewports.verdict);
+ };
+
+ ui.initErrorList = function (options, $el) {
+ ui.initHelper(options, $el, "",
+ options.ui.viewports.errors);
+ };
+
+ ui.initPopover = function (options, $el) {
+ $el.popover("destroy");
+ $el.popover({
+ html: true,
+ placement: options.ui.popoverPlacement,
+ trigger: "manual",
+ content: " "
+ });
+ };
+
+ ui.initUI = function (options, $el) {
+ if (options.ui.showPopover) {
+ ui.initPopover(options, $el);
+ } else {
+ if (options.ui.showErrors) {
+ ui.initErrorList(options, $el);
+ }
+ if (options.ui.showVerdicts && !options.ui.showVerdictsInsideProgressBar) {
+ ui.initVerdict(options, $el);
+ }
+ }
+ if (options.ui.showProgressBar) {
+ ui.initProgressBar(options, $el);
+ }
+ };
+
+ ui.updateProgressBar = function (options, $el, cssClass, percentage) {
+ var $progressbar = ui.getUIElements(options, $el).$progressbar,
+ $bar = $progressbar.find(".progress-bar"),
+ cssPrefix = "progress-";
+
+ if (options.ui.bootstrap2) {
+ $bar = $progressbar.find(".bar");
+ cssPrefix = "";
+ }
+
+ $.each(options.ui.colorClasses, function (idx, value) {
+ if (options.ui.bootstrap4) {
+ $progressbar.removeClass(cssPrefix + value);
+ } else {
+ $bar.removeClass(cssPrefix + "bar-" + value);
+ }
+ });
+ if (options.ui.bootstrap4) {
+ $progressbar.addClass(cssPrefix + options.ui.colorClasses[cssClass]);
+ $progressbar.val(percentage);
+ } else {
+ $bar.addClass(cssPrefix + "bar-" + options.ui.colorClasses[cssClass]);
+ $bar.css("width", percentage + '%');
+ }
+ };
+
+ ui.updateVerdict = function (options, $el, cssClass, text) {
+ var $verdict = ui.getUIElements(options, $el).$verdict;
+ $verdict.removeClass(options.ui.colorClasses.join(' '));
+ if (cssClass > -1) {
+ $verdict.addClass(options.ui.colorClasses[cssClass]);
+ }
+ $verdict.html(text);
+ };
+
+ ui.updateErrors = function (options, $el) {
+ var $errors = ui.getUIElements(options, $el).$errors,
+ html = "";
+ $.each(options.instances.errors, function (idx, err) {
+ html += "- " + err + "
";
+ });
+ $errors.html(html);
+ };
+
+ ui.updatePopover = function (options, $el, verdictText) {
+ var popover = $el.data("bs.popover"),
+ html = "",
+ hide = true;
+
+ if (options.ui.showVerdicts &&
+ !options.ui.showVerdictsInsideProgressBar &&
+ verdictText.length > 0) {
+ html = "" + verdictText +
+ "
";
+ hide = false;
+ }
+ if (options.ui.showErrors) {
+ if (options.instances.errors.length > 0) {
+ hide = false;
+ }
+ html += options.ui.popoverError(options.instances.errors);
+ }
+
+ if (hide) {
+ $el.popover("hide");
+ return;
+ }
+
+ if (options.ui.bootstrap2) {
+ popover = $el.data("popover");
+ }
+
+ if (popover.$arrow && popover.$arrow.parents("body").length > 0) {
+ $el.find("+ .popover .popover-content").html(html);
+ } else {
+ // It's hidden
+ popover.options.content = html;
+ $el.popover("show");
+ }
+ };
+
+ ui.updateFieldStatus = function (options, $el, cssClass) {
+ var targetClass = options.ui.bootstrap2 ? ".control-group" : ".form-group",
+ $container = $el.parents(targetClass).first();
+
+ $.each(statusClasses, function (idx, css) {
+ if (!options.ui.bootstrap2) {
+ css = "has-" + css;
+ }
+ $container.removeClass(css);
+ });
+
+ cssClass = statusClasses[cssClass];
+ if (!options.ui.bootstrap2) {
+ cssClass = "has-" + cssClass;
+ }
+ $container.addClass(cssClass);
+ };
+
+ ui.percentage = function (score, maximun) {
+ var result = Math.floor(100 * score / maximun);
+ result = result <= 0 ? 1 : result; // Don't show the progress bar empty
+ result = result > 100 ? 100 : result;
+ return result;
+ };
+
+ ui.getVerdictAndCssClass = function (options, score) {
+ var cssClass, verdictText, level;
+
+ if (score <= 0) {
+ cssClass = 0;
+ level = -1;
+ verdictText = options.ui.verdicts[0];
+ } else if (score < options.ui.scores[0]) {
+ cssClass = 0;
+ level = 0;
+ verdictText = options.ui.verdicts[0];
+ } else if (score < options.ui.scores[1]) {
+ cssClass = 0;
+ level = 1;
+ verdictText = options.ui.verdicts[1];
+ } else if (score < options.ui.scores[2]) {
+ cssClass = 1;
+ level = 2;
+ verdictText = options.ui.verdicts[2];
+ } else if (score < options.ui.scores[3]) {
+ cssClass = 1;
+ level = 3;
+ verdictText = options.ui.verdicts[3];
+ } else {
+ cssClass = 2;
+ level = 4;
+ verdictText = options.ui.verdicts[4];
+ }
+
+ return [verdictText, cssClass, level];
+ };
+
+ ui.updateUI = function (options, $el, score) {
+ var cssClass, barPercentage, verdictText, verdictCssClass;
+
+ cssClass = ui.getVerdictAndCssClass(options, score);
+ verdictText = score === 0 ? '' : cssClass[0];
+ cssClass = cssClass[1];
+ verdictCssClass = options.ui.useVerdictCssClass ? cssClass : -1;
+
+ if (options.ui.showProgressBar) {
+ barPercentage = ui.percentage(score, options.ui.scores[3]);
+ ui.updateProgressBar(options, $el, cssClass, barPercentage);
+ if (options.ui.showVerdictsInsideProgressBar) {
+ ui.updateVerdict(options, $el, verdictCssClass, verdictText);
+ }
+ }
+
+ if (options.ui.showStatus) {
+ ui.updateFieldStatus(options, $el, cssClass);
+ }
+
+ if (options.ui.showPopover) {
+ ui.updatePopover(options, $el, verdictText);
+ } else {
+ if (options.ui.showVerdicts && !options.ui.showVerdictsInsideProgressBar) {
+ ui.updateVerdict(options, $el, verdictCssClass, verdictText);
+ }
+ if (options.ui.showErrors) {
+ ui.updateErrors(options, $el);
+ }
+ }
+ };
+ }(jQuery, ui));
+
+ // Source: src/methods.js
+
+
+
+ var methods = {};
+
+ (function ($, methods) {
+ "use strict";
+ var onKeyUp, applyToAll;
+
+ onKeyUp = function (event) {
+ var $el = $(event.target),
+ options = $el.data("pwstrength-bootstrap"),
+ word = $el.val(),
+ userInputs,
+ verdictText,
+ verdictLevel,
+ score;
+
+ if (options === undefined) {
+ return;
+ }
+
+ options.instances.errors = [];
+ if (word.length === 0) {
+ score = 0;
+ } else {
+ if (options.common.zxcvbn) {
+ userInputs = [];
+ $.each(options.common.userInputs.concat([options.common.usernameField]), function (
+ idx, selector) {
+ var value = $(selector).val();
+ if (value) {
+ userInputs.push(value);
+ }
+ });
+ userInputs = userInputs.concat(options.common.zxcvbnTerms);
+ score = Math.log2(zxcvbn(word, userInputs).guesses);
+ } else {
+ score = rulesEngine.executeRules(options, word);
+ }
+ }
+ ui.updateUI(options, $el, score);
+ verdictText = ui.getVerdictAndCssClass(options, score);
+ verdictLevel = verdictText[2];
+ verdictText = verdictText[0];
+
+ if (options.common.debug) {
+ console.log(score + ' - ' + verdictText);
+ }
+
+ if ($.isFunction(options.common.onKeyUp)) {
+ options.common.onKeyUp(event, {
+ score: score,
+ verdictText: verdictText,
+ verdictLevel: verdictLevel
});
- message += "
";
- return message;
+ }
};
- defaultOptions.ui.errorMessages = {
- wordLength: "Your password is too short",
- wordNotEmail: "Do not use your email as your password",
- wordSimilarToUsername: "Your password cannot contain your username",
- wordTwoCharacterClasses: "Use different character classes",
- wordRepetitions: "Too many repetitions",
- wordSequences: "Your password contains sequences"
+
+ methods.init = function (settings) {
+ this.each(function (idx, el) {
+ // Make it deep extend (first param) so it extends too the
+ // rules and other inside objects
+ var clonedDefaults = $.extend(true, {}, defaultOptions),
+ localOptions = $.extend(true, clonedDefaults, settings),
+ $el = $(el);
+
+ localOptions.instances = {};
+ $el.data("pwstrength-bootstrap", localOptions);
+ $el.on("keyup", onKeyUp);
+ $el.on("change", onKeyUp);
+ $el.on("paste", onKeyUp);
+
+ ui.initUI(localOptions, $el);
+ if ($.trim($el.val())) { // Not empty, calculate the strength
+ $el.trigger("keyup");
+ }
+
+ if ($.isFunction(localOptions.common.onLoad)) {
+ localOptions.common.onLoad();
+ }
+ });
+
+ return this;
};
- defaultOptions.ui.verdicts = ["Weak", "Normal", "Medium", "Strong", "Very Strong"];
- defaultOptions.ui.showVerdicts = true;
- defaultOptions.ui.showVerdictsInsideProgressBar = false;
- defaultOptions.ui.useVerdictCssClass = false;
- defaultOptions.ui.showErrors = false;
- defaultOptions.ui.container = undefined;
- defaultOptions.ui.viewports = {
- progress: undefined,
- verdict: undefined,
- errors: undefined
+
+ methods.destroy = function () {
+ this.each(function (idx, el) {
+ var $el = $(el),
+ options = $el.data("pwstrength-bootstrap"),
+ elements = ui.getUIElements(options, $el);
+ elements.$progressbar.remove();
+ elements.$verdict.remove();
+ elements.$errors.remove();
+ $el.removeData("pwstrength-bootstrap");
+ });
};
- defaultOptions.ui.scores = [14, 26, 38, 50];
-// Source: src/ui.js
-
-
-
-
- var ui = {};
-
- (function ($, ui) {
- "use strict";
-
- var statusClasses = ["error", "warning", "success"];
-
- ui.getContainer = function (options, $el) {
- var $container;
-
- $container = $(options.ui.container);
- if (!($container && $container.length === 1)) {
- $container = $el.parent();
- }
- return $container;
+ methods.forceUpdate = function () {
+ this.each(function (idx, el) {
+ var event = {
+ target: el
};
+ onKeyUp(event);
+ });
+ };
- ui.findElement = function ($container, viewport, cssSelector) {
- if (viewport) {
- return $container.find(viewport).find(cssSelector);
- }
- return $container.find(cssSelector);
- };
+ methods.addRule = function (name, method, score, active) {
+ this.each(function (idx, el) {
+ var options = $(el).data("pwstrength-bootstrap");
- ui.getUIElements = function (options, $el) {
- var $container, selector, result;
+ options.rules.activated[name] = active;
+ options.rules.scores[name] = score;
+ options.rules.extra[name] = method;
+ });
+ };
- if (options.instances.viewports) {
- return options.instances.viewports;
- }
+ applyToAll = function (rule, prop, value) {
+ this.each(function (idx, el) {
+ $(el).data("pwstrength-bootstrap").rules[prop][rule] = value;
+ });
+ };
- $container = ui.getContainer(options, $el);
+ methods.changeScore = function (rule, score) {
+ applyToAll.call(this, rule, "scores", score);
+ };
- result = {};
- if (options.ui.bootstrap4) {
- selector = "progress.progress";
- } else {
- selector = "div.progress";
- }
- result.$progressbar = ui.findElement($container, options.ui.viewports.progress, selector);
- if (options.ui.showVerdictsInsideProgressBar) {
- result.$verdict = result.$progressbar.find("span.password-verdict");
- }
+ methods.ruleActive = function (rule, active) {
+ applyToAll.call(this, rule, "activated", active);
+ };
- if (!options.ui.showPopover) {
- if (!options.ui.showVerdictsInsideProgressBar) {
- result.$verdict = ui.findElement($container, options.ui.viewports.verdict, "span.password-verdict");
- }
- result.$errors = ui.findElement($container, options.ui.viewports.errors, "ul.error-list");
- }
+ $.fn.pwstrength = function (method) {
+ var result;
- options.instances.viewports = result;
- return result;
- };
+ if (methods[method]) {
+ result = methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
+ } else if (typeof method === "object" || !method) {
+ result = methods.init.apply(this, arguments);
+ } else {
+ $.error("Method " + method + " does not exist on jQuery.pwstrength-bootstrap");
+ }
- ui.initProgressBar = function (options, $el) {
- var $container = ui.getContainer(options, $el),
- progressbar = "";
- if (options.ui.bootstrap4) {
- // Boostrap 4
- progressbar = "
";
- } else {
- progressbar += "
";
- }
-
- if (options.ui.viewports.progress) {
- $container.find(options.ui.viewports.progress).append(progressbar);
- } else {
- $(progressbar).insertAfter($el);
- }
- };
-
- ui.initHelper = function (options, $el, html, viewport) {
- var $container = ui.getContainer(options, $el);
- if (viewport) {
- $container.find(viewport).append(html);
- } else {
- $(html).insertAfter($el);
- }
- };
-
- ui.initVerdict = function (options, $el) {
- ui.initHelper(options, $el, "",
- options.ui.viewports.verdict);
- };
-
- ui.initErrorList = function (options, $el) {
- ui.initHelper(options, $el, "",
- options.ui.viewports.errors);
- };
-
- ui.initPopover = function (options, $el) {
- $el.popover("destroy");
- $el.popover({
- html: true,
- placement: options.ui.popoverPlacement,
- trigger: "manual",
- content: " "
- });
- };
-
- ui.initUI = function (options, $el) {
- if (options.ui.showPopover) {
- ui.initPopover(options, $el);
- } else {
- if (options.ui.showErrors) { ui.initErrorList(options, $el); }
- if (options.ui.showVerdicts && !options.ui.showVerdictsInsideProgressBar) {
- ui.initVerdict(options, $el);
- }
- }
- if (options.ui.showProgressBar) {
- ui.initProgressBar(options, $el);
- }
- };
-
- ui.updateProgressBar = function (options, $el, cssClass, percentage) {
- var $progressbar = ui.getUIElements(options, $el).$progressbar,
- $bar = $progressbar.find(".progress-bar"),
- cssPrefix = "progress-";
-
- if (options.ui.bootstrap2) {
- $bar = $progressbar.find(".bar");
- cssPrefix = "";
- }
-
- $.each(options.ui.colorClasses, function (idx, value) {
- if (options.ui.bootstrap4) {
- $progressbar.removeClass(cssPrefix + value);
- } else {
- $bar.removeClass(cssPrefix + "bar-" + value);
- }
- });
- if (options.ui.bootstrap4) {
- $progressbar.addClass(cssPrefix + options.ui.colorClasses[cssClass]);
- $progressbar.val(percentage);
- } else {
- $bar.addClass(cssPrefix + "bar-" + options.ui.colorClasses[cssClass]);
- $bar.css("width", percentage + '%');
- }
- };
-
- ui.updateVerdict = function (options, $el, cssClass, text) {
- var $verdict = ui.getUIElements(options, $el).$verdict;
- $verdict.removeClass(options.ui.colorClasses.join(' '));
- if (cssClass > -1) {
- $verdict.addClass(options.ui.colorClasses[cssClass]);
- }
- $verdict.html(text);
- };
-
- ui.updateErrors = function (options, $el) {
- var $errors = ui.getUIElements(options, $el).$errors,
- html = "";
- $.each(options.instances.errors, function (idx, err) {
- html += "" + err + "";
- });
- $errors.html(html);
- };
-
- ui.updatePopover = function (options, $el, verdictText) {
- var popover = $el.data("bs.popover"),
- html = "",
- hide = true;
-
- if (options.ui.showVerdicts &&
- !options.ui.showVerdictsInsideProgressBar &&
- verdictText.length > 0) {
- html = "" + verdictText +
- "
";
- hide = false;
- }
- if (options.ui.showErrors) {
- if (options.instances.errors.length > 0) {
- hide = false;
- }
- html += options.ui.popoverError(options.instances.errors);
- }
-
- if (hide) {
- $el.popover("hide");
- return;
- }
-
- if (options.ui.bootstrap2) { popover = $el.data("popover"); }
-
- if (popover.$arrow && popover.$arrow.parents("body").length > 0) {
- $el.find("+ .popover .popover-content").html(html);
- } else {
- // It's hidden
- popover.options.content = html;
- $el.popover("show");
- }
- };
-
- ui.updateFieldStatus = function (options, $el, cssClass) {
- var targetClass = options.ui.bootstrap2 ? ".control-group" : ".form-group",
- $container = $el.parents(targetClass).first();
-
- $.each(statusClasses, function (idx, css) {
- if (!options.ui.bootstrap2) { css = "has-" + css; }
- $container.removeClass(css);
- });
-
- cssClass = statusClasses[cssClass];
- if (!options.ui.bootstrap2) { cssClass = "has-" + cssClass; }
- $container.addClass(cssClass);
- };
-
- ui.percentage = function (score, maximun) {
- var result = Math.floor(100 * score / maximun);
- result = result <= 0 ? 1 : result; // Don't show the progress bar empty
- result = result > 100 ? 100 : result;
- return result;
- };
-
- ui.getVerdictAndCssClass = function (options, score) {
- var cssClass, verdictText, level;
-
- if (score <= 0) {
- cssClass = 0;
- level = -1;
- verdictText = options.ui.verdicts[0];
- } else if (score < options.ui.scores[0]) {
- cssClass = 0;
- level = 0;
- verdictText = options.ui.verdicts[0];
- } else if (score < options.ui.scores[1]) {
- cssClass = 0;
- level = 1;
- verdictText = options.ui.verdicts[1];
- } else if (score < options.ui.scores[2]) {
- cssClass = 1;
- level = 2;
- verdictText = options.ui.verdicts[2];
- } else if (score < options.ui.scores[3]) {
- cssClass = 1;
- level = 3;
- verdictText = options.ui.verdicts[3];
- } else {
- cssClass = 2;
- level = 4;
- verdictText = options.ui.verdicts[4];
- }
-
- return [verdictText, cssClass, level];
- };
-
- ui.updateUI = function (options, $el, score) {
- var cssClass, barPercentage, verdictText, verdictCssClass;
-
- cssClass = ui.getVerdictAndCssClass(options, score);
- verdictText = score === 0 ? '' : cssClass[0];
- cssClass = cssClass[1];
- verdictCssClass = options.ui.useVerdictCssClass ? cssClass : -1;
-
- if (options.ui.showProgressBar) {
- barPercentage = ui.percentage(score, options.ui.scores[3]);
- ui.updateProgressBar(options, $el, cssClass, barPercentage);
- if (options.ui.showVerdictsInsideProgressBar) {
- ui.updateVerdict(options, $el, verdictCssClass, verdictText);
- }
- }
-
- if (options.ui.showStatus) {
- ui.updateFieldStatus(options, $el, cssClass);
- }
-
- if (options.ui.showPopover) {
- ui.updatePopover(options, $el, verdictText);
- } else {
- if (options.ui.showVerdicts && !options.ui.showVerdictsInsideProgressBar) {
- ui.updateVerdict(options, $el, verdictCssClass, verdictText);
- }
- if (options.ui.showErrors) {
- ui.updateErrors(options, $el);
- }
- }
- };
- }(jQuery, ui));
-
-// Source: src/methods.js
-
-
-
-
- var methods = {};
-
- (function ($, methods) {
- "use strict";
- var onKeyUp, applyToAll;
-
- onKeyUp = function (event) {
- var $el = $(event.target),
- options = $el.data("pwstrength-bootstrap"),
- word = $el.val(),
- userInputs,
- verdictText,
- verdictLevel,
- score;
-
- if (options === undefined) { return; }
-
- options.instances.errors = [];
- if (word.length === 0) {
- score = 0;
- } else {
- if (options.common.zxcvbn) {
- userInputs = [];
- $.each(options.common.userInputs.concat([options.common.usernameField]), function (idx, selector) {
- var value = $(selector).val();
- if (value) { userInputs.push(value); }
- });
- userInputs = userInputs.concat(options.common.zxcvbnTerms);
- score = Math.log2(zxcvbn(word, userInputs).guesses);
- } else {
- score = rulesEngine.executeRules(options, word);
- }
- }
- ui.updateUI(options, $el, score);
- verdictText = ui.getVerdictAndCssClass(options, score);
- verdictLevel = verdictText[2];
- verdictText = verdictText[0];
-
- if (options.common.debug) { console.log(score + ' - ' + verdictText); }
-
- if ($.isFunction(options.common.onKeyUp)) {
- options.common.onKeyUp(event, {
- score: score,
- verdictText: verdictText,
- verdictLevel: verdictLevel
- });
- }
- };
-
- methods.init = function (settings) {
- this.each(function (idx, el) {
- // Make it deep extend (first param) so it extends too the
- // rules and other inside objects
- var clonedDefaults = $.extend(true, {}, defaultOptions),
- localOptions = $.extend(true, clonedDefaults, settings),
- $el = $(el);
-
- localOptions.instances = {};
- $el.data("pwstrength-bootstrap", localOptions);
- $el.on("keyup", onKeyUp);
- $el.on("change", onKeyUp);
- $el.on("paste", onKeyUp);
-
- ui.initUI(localOptions, $el);
- if ($.trim($el.val())) { // Not empty, calculate the strength
- $el.trigger("keyup");
- }
-
- if ($.isFunction(localOptions.common.onLoad)) {
- localOptions.common.onLoad();
- }
- });
-
- return this;
- };
-
- methods.destroy = function () {
- this.each(function (idx, el) {
- var $el = $(el),
- options = $el.data("pwstrength-bootstrap"),
- elements = ui.getUIElements(options, $el);
- elements.$progressbar.remove();
- elements.$verdict.remove();
- elements.$errors.remove();
- $el.removeData("pwstrength-bootstrap");
- });
- };
-
- methods.forceUpdate = function () {
- this.each(function (idx, el) {
- var event = { target: el };
- onKeyUp(event);
- });
- };
-
- methods.addRule = function (name, method, score, active) {
- this.each(function (idx, el) {
- var options = $(el).data("pwstrength-bootstrap");
-
- options.rules.activated[name] = active;
- options.rules.scores[name] = score;
- options.rules.extra[name] = method;
- });
- };
-
- applyToAll = function (rule, prop, value) {
- this.each(function (idx, el) {
- $(el).data("pwstrength-bootstrap").rules[prop][rule] = value;
- });
- };
-
- methods.changeScore = function (rule, score) {
- applyToAll.call(this, rule, "scores", score);
- };
-
- methods.ruleActive = function (rule, active) {
- applyToAll.call(this, rule, "activated", active);
- };
-
- $.fn.pwstrength = function (method) {
- var result;
-
- if (methods[method]) {
- result = methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
- } else if (typeof method === "object" || !method) {
- result = methods.init.apply(this, arguments);
- } else {
- $.error("Method " + method + " does not exist on jQuery.pwstrength-bootstrap");
- }
-
- return result;
- };
- }(jQuery, methods));
-}(jQuery));
\ No newline at end of file
+ return result;
+ };
+ }(jQuery, methods));
+}(jQuery));
diff --git a/Packages/active-entry/package.js b/Packages/active-entry/package.js
index f88ef0cd8..a4e5244fb 100755
--- a/Packages/active-entry/package.js
+++ b/Packages/active-entry/package.js
@@ -1,6 +1,6 @@
Package.describe({
name: 'clinical:active-entry',
- version: '1.5.15',
+ version: '1.5.16',
summary: 'SignIn, SignUp, and ForgotPassword pages for Clinical Framework.',
git: 'https://github.com/clinical-meteor/clinical-active-entry',
documentation: 'README.md'
@@ -16,15 +16,13 @@ Package.onUse(function (api) {
'grove:less@0.1.1',
'session',
'reactive-dict',
- 'accounts-base',
- 'accounts-password',
- 'codetheweb:zxcvbn'
+ //'codetheweb:zxcvbn'
], ['client']);
api.use([
'accounts-base',
'accounts-password'
- ], ['server']);
+ ]);
api.use([
'zuuk:stale-session@1.0.8'
@@ -59,6 +57,10 @@ Package.onUse(function (api) {
'components/forgotPassword/forgotPassword.js',
'components/forgotPassword/forgotPassword.less',
+ 'components/changePassword/changePassword.html',
+ 'components/changePassword/changePassword.js',
+ 'components/changePassword/changePassword.less',
+
], ['client']);
diff --git a/Packages/active-entry/tests/gagarin/activeEntryTests.js b/Packages/active-entry/tests/gagarin/activeEntryTests.js
index bbb45d0c7..5ba3a3f13 100644
--- a/Packages/active-entry/tests/gagarin/activeEntryTests.js
+++ b/Packages/active-entry/tests/gagarin/activeEntryTests.js
@@ -69,13 +69,20 @@ describe('clinical:active-entry', function () {
});
// ActiveEntry.verifyConfirmPassword
- it('Password match validation confirms that two passwords are the same.', function () {
+ it('Password match confirms that two passwords are the same.', function () {
return client.execute(function (a) {
ActiveEntry.verifyConfirmPassword('K1tt#kittens', 'kittens');
expect(ActiveEntry.errorMessages.get('confirm')).to.equal("Passwords do not match");
+ ActiveEntry.verifyConfirmPassword('kittens123', 'kittens');
+ expect(ActiveEntry.errorMessages.get('confirm')).to.equal("Passwords do not match");
+
+ ActiveEntry.verifyConfirmPassword('kittens123', 'kittens123');
+ expect(ActiveEntry.errorMessages.get('confirm')).to.equal("Passwords match");
+
ActiveEntry.verifyConfirmPassword('K1tt#ns123', 'K1tt#ns123');
expect(ActiveEntry.successMessages.get('confirm')).to.equal("Passwords match");
+
});
});
@@ -94,13 +101,28 @@ describe('clinical:active-entry', function () {
});
+ // // ActiveEntry.signIn
+ // it('Newly created user record should have role, profile, and name set.', function () {
+ // return client.execute(function () {
+ // ActiveEntry.signUp('janedoe@test.org', 'Janed*e123', 'Janed*e123', 'Jane Doe');
+ // expect(ActiveEntry.successMessages.get('fullName')).to.equal("Name present");
+ // }).then(function (){
+ // return server.wait(500, 'until account is created on the server', function () {
+ // return Meteor.users.findOne({'emails.address': 'janedoe@test.org'});
+ // }).then(function (user){
+ // expect(user.role).to.equal('user');
+ // expect(user.profile.fullName).to.equal('Jane Doe');
+ // });
+ // });
+ // });
// ActiveEntry.signIn
it('Newly created user record should have role, profile, and name set.', function () {
return client.execute(function () {
- ActiveEntry.signUp('janedoe@test.org', 'Janed*e123', 'Janed*e123', 'Jane Doe');
+ // ActiveEntry.signUp('janedoe@test.org', 'Janed*e123', 'Janed*e123', 'Jane Doe');
+ ActiveEntry.signUp('janedoe@test.org', 'Janedoe123', 'Janedoe123', 'Jane Doe');
expect(ActiveEntry.successMessages.get('fullName')).to.equal("Name present");
}).then(function (){
- return server.wait(300, 'until account is created on the server', function () {
+ return server.wait(500, 'until account is created on the server', function () {
return Meteor.users.findOne({'emails.address': 'janedoe@test.org'});
}).then(function (user){
expect(user.role).to.equal('user');
@@ -110,7 +132,6 @@ describe('clinical:active-entry', function () {
});
-
it("Newly created user should have fullName(), preferredName(), and familyName() methods.", function () {
return server.execute(function () {
var user = Meteor.users.findOne({'emails.address': 'janedoe@test.org'});
@@ -118,12 +139,12 @@ describe('clinical:active-entry', function () {
expect(user.fullName()).to.equal('Jane Doe');
expect(user.givenName()).to.equal('Jane');
expect(user.familyName()).to.equal('Doe');
- }).then(function(){
+ }).then(function (){
// client.wait(500, "until user is logged out", function(){
// Meteor.logout();
// });
return client.promise(function (resolve){
- Meteor.logout(function(error, result){
+ Meteor.logout(function (error, result){
resolve();
});
});
diff --git a/Packages/active-entry/tests/nightwatch/walkthroughs/activeEntryWalkthrough.js b/Packages/active-entry/tests/nightwatch/walkthroughs/activeEntryWalkthrough.js
index 86aa2fe9a..63ae8204c 100755
--- a/Packages/active-entry/tests/nightwatch/walkthroughs/activeEntryWalkthrough.js
+++ b/Packages/active-entry/tests/nightwatch/walkthroughs/activeEntryWalkthrough.js
@@ -15,6 +15,7 @@
// existing user should be able to sign in on desktop
// existing user should be able to sign in on tablet
// existing user should be able to sign in on phone
+// existing user should be able to change their password
// company logo should display on sign//in page
@@ -48,7 +49,6 @@ module.exports = {
.verify.elementPresent("#signUpPagePasswordInput")
.verify.elementPresent("#signUpPageJoinNowButton");
},
-
"guest should be notified if password is insecure": function (client) {
client
.clearValue("input")
@@ -158,8 +158,44 @@ module.exports = {
.click("#logoutButton").pause(200)
.verify.containsText("#usernameLink", "Sign In");
},
- "if anonymous user tries to log in with non-existing account, a message is shown": function (
- client) {
+ "existing user should be able to change their password" : function (client) {
+ client
+ .url("http://localhost:3000/entrySignIn")
+ .resizeWindow(1600, 1200)
+ .verify.containsText("#usernameLink", "Sign In")
+ .signIn("janicedoe@symptomatic.io", "janicedoe123").pause(500)
+ .verify.containsText("#usernameLink", "janicedoe@symptomatic.io")
+ .url("http://localhost:3000/changePassword")
+ .verify.elementPresent("#changePasswordPageOldPasswordInput")
+ .verify.elementPresent("#changePasswordPagePasswordInput")
+ .verify.elementPresent("#changePasswordPagePasswordConfirmInput")
+ .verify.elementPresent("#changePasswordButton")
+ },
+ "existing user should be notified if desired new password is insecure" : function (client) {
+ client
+ .url("http://localhost:3000/entrySignIn")
+ .resizeWindow(1600, 1200)
+ .verify.containsText("#usernameLink", "Sign In")
+ .signIn("janicedoe@symptomatic.io", "janicedoe123").pause(500)
+ .verify.containsText("#usernameLink", "janicedoe@symptomatic.io")
+ .url("http://localhost:3000/changePassword")
+ .verify.elementPresent("#changePasswordPageOldPasswordInput")
+ .verify.elementPresent("#changePasswordPagePasswordInput")
+ .verify.elementPresent("#changePasswordPagePasswordConfirmInput")
+ .verify.elementPresent("#changePasswordButton")
+ .verify.cssProperty('#changePasswordPagePasswordInput', 'border', '1px solid gray')
+ .setValue("#changePasswordPagePasswordInput", "jan")
+ .verify.cssProperty('#changePasswordPagePasswordInput', 'border', '1px solid rgb(242, 222, 222)')
+ .setValue("#changePasswordPagePasswordInput", "icedoe123")
+ .verify.cssProperty('#changePasswordPagePasswordInput', 'border', '1px solid green')
+ .verify.cssProperty('#changePasswordPagePasswordConfirmInput', 'border', '1px solid gray')
+ .setValue("#changePasswordPagePasswordConfirmInput", "ja")
+ .verify.cssProperty('#changePasswordPagePasswordConfirmInput', 'border', '1px solid rgb(242, 222, 222)')
+ .clearValue("#changePasswordPagePasswordConfirmInput")
+ .setValue("#changePasswordPagePasswordConfirmInput", "janicedoe123")
+ .verify.cssProperty('#changePasswordPagePasswordConfirmInput', 'border', '1px solid green')
+ },
+ "if anonymous user tries to log in with non-existing account, a message is shown" : function (client) {
client
.url("http://localhost:3000/entrySignIn")
.resizeWindow(1024, 768)