From 5a570e4b0b09bb43eeba3e7b51970e3c1f9a06c6 Mon Sep 17 00:00:00 2001 From: Dhara Patel Date: Thu, 28 Feb 2019 13:17:02 -0500 Subject: [PATCH 1/2] Added validationError(custom message) for Password requirement fail --- README.md | 3 ++- spec/PasswordPolicy.spec.js | 8 ++++++-- src/RestWrite.js | 20 ++++++++++++++++---- 3 files changed, 24 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 6c27622504..113856b3ff 100644 --- a/README.md +++ b/README.md @@ -326,8 +326,9 @@ var server = ParseServer({ // If both are specified, both checks must pass to accept the password // 1. a RegExp object or a regex string representing the pattern to enforce validatorPattern: /^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.{8,})/, // enforce password with at least 8 char with at least 1 lower case, 1 upper case and 1 digit - // 2. a callback function to be invoked to validate the password + // 2. a callback function to be invoked to validate the password validatorCallback: (password) => { return validatePassword(password) }, + valodationError: 'Password must contain at least 1 digit.' // optional error message to be sent instead of the default "Password does not meet the Password Policy requirements." message. doNotAllowUsername: true, // optional setting to disallow username in passwords maxPasswordAge: 90, // optional setting in days for password expiry. Login fails if user does not reset the password within this period after signup/last reset. maxPasswordHistory: 5, // optional setting to prevent reuse of previous n passwords. Maximum value that can be specified is 20. Not specifying it or specifying 0 will not enforce history. diff --git a/spec/PasswordPolicy.spec.js b/spec/PasswordPolicy.spec.js index 0f3ed1dad1..19c0731f12 100644 --- a/spec/PasswordPolicy.spec.js +++ b/spec/PasswordPolicy.spec.js @@ -667,7 +667,7 @@ describe('Password Policy: ', () => { .then(response => { expect(response.status).toEqual(302); expect(response.text).toEqual( - `Found. Redirecting to http://localhost:8378/1/apps/choose_password?username=user1&token=${token}&id=test&error=Password%20does%20not%20meet%20the%20Password%20Policy%20requirements.&app=passwordPolicy` + `Found. Redirecting to http://localhost:8378/1/apps/choose_password?username=user1&token=${token}&id=test&error=Password%20should%20contain%20at%20least%20one%20digit.&app=passwordPolicy` ); Parse.User.logIn('user1', 'has 1 digit') @@ -700,6 +700,7 @@ describe('Password Policy: ', () => { emailAdapter: emailAdapter, passwordPolicy: { validatorPattern: /[0-9]+/, // password should contain at least one digit + validationError: 'Password should contain at least one digit.', }, publicServerURL: 'http://localhost:8378/1', }).then(() => { @@ -764,6 +765,9 @@ describe('Password Policy: ', () => { }) .catch(error => { expect(error.code).toEqual(142); + expect(error.message).toEqual( + 'Password cannot contain your username.' + ); done(); }); }); @@ -853,7 +857,7 @@ describe('Password Policy: ', () => { .then(response => { expect(response.status).toEqual(302); expect(response.text).toEqual( - `Found. Redirecting to http://localhost:8378/1/apps/choose_password?username=user1&token=${token}&id=test&error=Password%20does%20not%20meet%20the%20Password%20Policy%20requirements.&app=passwordPolicy` + `Found. Redirecting to http://localhost:8378/1/apps/choose_password?username=user1&token=${token}&id=test&error=Password%20cannot%20contain%20your%20username.&app=passwordPolicy` ); Parse.User.logIn('user1', 'r@nd0m') diff --git a/src/RestWrite.js b/src/RestWrite.js index 315402cb00..14bb27c44c 100644 --- a/src/RestWrite.js +++ b/src/RestWrite.js @@ -611,8 +611,17 @@ RestWrite.prototype._validatePasswordPolicy = function() { RestWrite.prototype._validatePasswordRequirements = function() { // check if the password conforms to the defined password policy if configured - const policyError = - 'Password does not meet the Password Policy requirements.'; + // If we specified a custom error in our configuration use it. + // Example: "Passwords must include a Capital Letter, Lowercase Letter, and a number." + // + // This is especially useful on the generic "password reset" page, + // as it allows the programmer to communicate specific requirements instead of: + // a. making the user guess whats wrong + // b. making a custom password reset page that shows the requirements + const policyError = this.config.passwordPolicy.validationError + ? this.config.passwordPolicy.validationError + : 'Password does not meet the Password Policy requirements.'; + const containsUsernameError = 'Password cannot contain your username.'; // check whether the password meets the password strength requirements if ( @@ -632,7 +641,7 @@ RestWrite.prototype._validatePasswordRequirements = function() { // username is not passed during password reset if (this.data.password.indexOf(this.data.username) >= 0) return Promise.reject( - new Parse.Error(Parse.Error.VALIDATION_ERROR, policyError) + new Parse.Error(Parse.Error.VALIDATION_ERROR, containsUsernameError) ); } else { // retrieve the User object using objectId during password reset @@ -644,7 +653,10 @@ RestWrite.prototype._validatePasswordRequirements = function() { } if (this.data.password.indexOf(results[0].username) >= 0) return Promise.reject( - new Parse.Error(Parse.Error.VALIDATION_ERROR, policyError) + new Parse.Error( + Parse.Error.VALIDATION_ERROR, + containsUsernameError + ) ); return Promise.resolve(); }); From 5a328e7fcbaca89515bb289c4c545a4a3df7cd83 Mon Sep 17 00:00:00 2001 From: Dhara Patel Date: Thu, 28 Feb 2019 14:28:01 -0500 Subject: [PATCH 2/2] Changed validationError from valodationError in README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 113856b3ff..50719c3c23 100644 --- a/README.md +++ b/README.md @@ -328,7 +328,7 @@ var server = ParseServer({ validatorPattern: /^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.{8,})/, // enforce password with at least 8 char with at least 1 lower case, 1 upper case and 1 digit // 2. a callback function to be invoked to validate the password validatorCallback: (password) => { return validatePassword(password) }, - valodationError: 'Password must contain at least 1 digit.' // optional error message to be sent instead of the default "Password does not meet the Password Policy requirements." message. + validationError: 'Password must contain at least 1 digit.' // optional error message to be sent instead of the default "Password does not meet the Password Policy requirements." message. doNotAllowUsername: true, // optional setting to disallow username in passwords maxPasswordAge: 90, // optional setting in days for password expiry. Login fails if user does not reset the password within this period after signup/last reset. maxPasswordHistory: 5, // optional setting to prevent reuse of previous n passwords. Maximum value that can be specified is 20. Not specifying it or specifying 0 will not enforce history.