Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding support for optional Password Policy #3032

Merged
merged 26 commits into from
Nov 17, 2016
Merged

Adding support for optional Password Policy #3032

merged 26 commits into from
Nov 17, 2016

Conversation

bhaskaryasa
Copy link
Contributor

@bhaskaryasa bhaskaryasa commented Nov 9, 2016

Introducing two settings under passwordPolicy.

validators

Two settings can be used to enforce strong passwords as required by the application. They will be applied at both sign up and password reset. The settings are optional and either one or both can be specified. If both are specified then both the validations must match to accept the password.
Issue #2734

  1. validatorPattern
    Optional setting to specify a regular expression that the password must match
  2. validatorCallback
    Optional setting to specify a callback function that returns the validation result as true/false.

doNotAllowUsername

By enabling this setting, the parse server will not allow password to have username in it during signup or password reset

resetTokenValidityDuration

Currently the link that is sent to the user as result of the call to requestPasswordReset never expires. This setting allows the application to define the validity duration of these links. When this is enabled with a valid value, parse server will show invalid_link page if the link has expired.

more settings...

More work needed for additional settings under passwordPolicy. Some of them are:

  1. maxAge: defines the duration after which the password needs to be changed.
  2. historyLength: defines the number of recent passwords to store. The system will not allow reusing any password from this history.

// if the passwordPolicy.validator is configured as a string or regex then convert it to a function to process the pattern
static setupPasswordValidator(passwordPolicy) {
if (passwordPolicy && passwordPolicy.validator) {
if (typeof passwordPolicy.validator === 'string') {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we support a function here too? this could be useful if the user is not RegExp fluent :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@flovilmart
Yes, it already does. I added the support after seeing your comment in a related issue :)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest instead of having validator that can be a string, function, or regex, you have a regex option and a function option. Overloading interfaces by using typeoftends to get confusing IMO.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good point Drew !

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@drew-gross please correct me if my understanding is wrong.

There will be two mutually exclusive options - validatorPattern (RegExp) and validatorCallback (function)
One of them should be overriding if both options are specified. I think the callback function option should take priority.

Please confirm then I will go ahead and make changes.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would say both should be required if both are provided. So if you a provide a callback and a pattern, then the password must pass both to be considered valid. Either that or throw if both are provided.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got your point Drew. I think it's a better idea to require both to pass if both are provided.
@flovilmart @cherukumilli what do you think?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bhaskaryasa
I agree with @drew-gross. If both are provided then we should check both. If only one of them is provided then just check only that one.

@bhaskaryasa
Copy link
Contributor Author

@flovilmart
Do you see anything missing or to be taken care of before this can be merged to the main branch?

…pdatePassword thus avoid duplicate check for password validator in updateUserPassword.
@facebook-github-bot
Copy link

@bhaskaryasa updated the pull request - view changes

@facebook-github-bot
Copy link

@bhaskaryasa updated the pull request - view changes

Copy link
Contributor

@flovilmart flovilmart left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small thing with the fdescribe, otherwise it's 💯!

const request = require('request');
const Config = require('../src/Config');

fdescribe("Password Policy: ", () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ooops

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh sorry!! corrected that.

@facebook-github-bot
Copy link

@bhaskaryasa updated the pull request - view changes

@flovilmart
Copy link
Contributor

@cherukumilli you had concerns about the naming of a column, I don't want to go over as you already contributed a lot to that section of the platform. Let me know if there's any concern, otherwise I'll merge.

user.set('email', '[email protected]');
return user.signUp();
})
.then(user => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bhaskaryasa
one more small nit...
Can you please combine lines 31 and 32?

.then(user => {
Parse.User.requestPasswordReset("[email protected]");
})
.then(() => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bhaskaryasa
one more...
Can you please combine lines 34 and 35?

@@ -0,0 +1,697 @@
"use strict";

const request = require('request');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bhaskaryasa
Can you please userequest-promise or request-promise-native instead of just request?

setTimeout(() => {
expect(sendEmailOptions).not.toBeUndefined();

request.get(sendEmailOptions.link, {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bhaskaryasa
request-promise provides a nice promise way of calling get.
Can u pls look into using it?

},
publicServerURL: "http://localhost:8378/1"
})
.then(() => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bhaskaryasa
can u pls merge the above two lines?

user.set('email', '[email protected]');
return user.signUp();
})
.then(user => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can u pls merge the above two lines?

.then(user => {
Parse.User.requestPasswordReset("[email protected]");
})
.then(() => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can u pls merge the above two llines?

setTimeout(() => {
expect(sendEmailOptions).not.toBeUndefined();

request.get(sendEmailOptions.link, {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can u pls use request-promise or request-promise-native here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure. will change to request-promise as the dependency already exists.

},
publicServerURL: "http://localhost:8378/1"
})
.then(() => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can u please merge the two above lines?

fail('passwordPolicy.resetTokenValidityDuration "not a number" test failed');
done();
})
.catch(err => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pls merge the above two lines

},
publicServerURL: "http://localhost:8378/1"
})
.then(() => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can u pls merge the above two lines and apply the same change to other occurrences of it?

user.set('email', '[email protected]');
user.signUp().then(() => {
done();
}, (error) => {
Copy link
Contributor

@cherukumilli cherukumilli Nov 11, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we use .catch(error => { here? and also other occurrences of this pattern?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure. I think I used this pattern at few other places as well. Will change them.

@cherukumilli
Copy link
Contributor

@flovilmart
regarding the column name....
currently, password reset token is called _perishable_token. I don't think this name reflects the purpose.
@bhaskaryasa is creating a _perishable_token_expires_at based on the existing column name _perishable_token.

IMO, we should change the name of _perishable_token to _password_reset_token
I was also requesting @bhaskaryasa, if he could change the name of the new column to _password_reset_token_expires_at.

@bhaskaryasa 's concern was that we might invalidate some of the password reset links when a developer upgrades to this new version of the column name i.e., from _perishable_token to _password_reset_token.

I think the risk is pretty low, as the user can always request to have their password reset again. The new password reset token will be created in the _password_reset_token column instead of the _perishable_token.

WDYT?

@facebook-github-bot
Copy link

@bhaskaryasa updated the pull request - view changes

@bhaskaryasa
Copy link
Contributor Author

Rebased and fixed the issue. It should be good now.

@@ -7,6 +7,7 @@ node_js:
services:
- postgresql
- redis-server
- docker
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bhaskaryasa
The above changes don't appear to be yours but somehow showed up in your push.

Looks like you are pulling in changes from some other branch
or
you might have missed a rebase step

I see there are some other similar changes.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes the rebase step seems to be off...

@facebook-github-bot
Copy link

@bhaskaryasa updated the pull request - view changes

@flovilmart
Copy link
Contributor

I've tapped the update button, seems to be good, waiting for travis to finish and merging

@flovilmart
Copy link
Contributor

flovilmart commented Nov 11, 2016

There seems to be an issue with postgres with invalid links. We can't merge it as is, otherwise we'll loose our ability to generate latest branches upon merges.

Can you have a look? I'm pretty sure this is trivial. https://travis-ci.org/ParsePlatform/parse-server/jobs/175176642#L1719

Copy link
Contributor

@flovilmart flovilmart left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Failing with Postgres, can you please have a look? Seems that the invalid link expiration is not working correctly.

you can also use it_exclude_dbs(['postgres']) to mark the test as not run for postgres

@facebook-github-bot
Copy link

@bhaskaryasa updated the pull request - view changes

@bhaskaryasa
Copy link
Contributor Author

@flovilmart Fixed the issue with postgres. I see that all tests are through now.

@flovilmart
Copy link
Contributor

Fixes: #2734

@bhaskaryasa
Copy link
Contributor Author

@flovilmart - I am working on additional settings to password-policy. Do you think this PR can be merged so that I can base my new work on top of it?

@flovilmart
Copy link
Contributor

@cherukumilli , please merge when you think it's good!

@cherukumilli
Copy link
Contributor

@flovilmart
I reviewed it and it looks good. I will go ahead and merge it.

@cherukumilli cherukumilli merged commit cf6ce5b into parse-community:master Nov 17, 2016
rsouzas pushed a commit to back4app/parse-server that referenced this pull request Dec 3, 2016
* adds resetTokenValidityDuration setting

* adds a validator to validate password that can be used to enforce strong
passwords

* adds unit tests for passwordPolicy.validator

* adds unit tests to to fail reset password function if password is not in a valid format

* updates README.md for passwordPolicy

* prevents duplicate check for password validator in updateUserPassword

* adds optional setting to disallow username in password

* updates test cases to use fdescribe instead of describe

* updates test cases to use request-promise instead of request

* adds ability to use a RegExp or Callback function or both for a passwordPolicy.validator

* expect username parameter in redirect to password_reset_success

* adds support for _perishable_token_expires_at in postgres
@bhaskaryasa bhaskaryasa deleted the password-policy branch December 11, 2016 01:18
Jcarlosjunior pushed a commit to back4app/parse-server that referenced this pull request Dec 13, 2016
* adds resetTokenValidityDuration setting

* adds a validator to validate password that can be used to enforce strong
passwords

* adds unit tests for passwordPolicy.validator

* adds unit tests to to fail reset password function if password is not in a valid format

* updates README.md for passwordPolicy

* prevents duplicate check for password validator in updateUserPassword

* adds optional setting to disallow username in password

* updates test cases to use fdescribe instead of describe

* updates test cases to use request-promise instead of request

* adds ability to use a RegExp or Callback function or both for a passwordPolicy.validator

* expect username parameter in redirect to password_reset_success

* adds support for _perishable_token_expires_at in postgres
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants