-
-
Notifications
You must be signed in to change notification settings - Fork 4.8k
Adds password history support to passwordPolicy #3102
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
Merged
cherukumilli
merged 4 commits into
parse-community:master
from
bhaskaryasa:password-policy--history
Nov 29, 2016
Merged
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
cc57c6b
password history support in passwordPolicy
bhaskaryasa 9f5bac3
fix eslint issues
bhaskaryasa 75d28f6
- renamed "passwordHistory" setting to "maxPasswordHistory"
bhaskaryasa 11b6bcd
Merge branch 'master' into password-policy--history
cherukumilli File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1010,4 +1010,242 @@ describe("Password Policy: ", () => { | |
}); | ||
}); | ||
|
||
it('should fail if passwordPolicy.maxPasswordHistory is not a number', done => { | ||
reconfigureServer({ | ||
appName: 'passwordPolicy', | ||
passwordPolicy: { | ||
maxPasswordHistory: "not a number" | ||
}, | ||
publicServerURL: "http://localhost:8378/1" | ||
}).then(() => { | ||
fail('passwordPolicy.maxPasswordHistory "not a number" test failed'); | ||
done(); | ||
}).catch(err => { | ||
expect(err).toEqual('passwordPolicy.maxPasswordHistory must be an integer ranging 0 - 20'); | ||
done(); | ||
}); | ||
}); | ||
|
||
it('should fail if passwordPolicy.maxPasswordHistory is a negative number', done => { | ||
reconfigureServer({ | ||
appName: 'passwordPolicy', | ||
passwordPolicy: { | ||
maxPasswordHistory: -10 | ||
}, | ||
publicServerURL: "http://localhost:8378/1" | ||
}).then(() => { | ||
fail('passwordPolicy.maxPasswordHistory negative number test failed'); | ||
done(); | ||
}).catch(err => { | ||
expect(err).toEqual('passwordPolicy.maxPasswordHistory must be an integer ranging 0 - 20'); | ||
done(); | ||
}); | ||
}); | ||
|
||
it('should fail if passwordPolicy.maxPasswordHistory is greater than 20', done => { | ||
reconfigureServer({ | ||
appName: 'passwordPolicy', | ||
passwordPolicy: { | ||
maxPasswordHistory: 21 | ||
}, | ||
publicServerURL: "http://localhost:8378/1" | ||
}).then(() => { | ||
fail('passwordPolicy.maxPasswordHistory negative number test failed'); | ||
done(); | ||
}).catch(err => { | ||
expect(err).toEqual('passwordPolicy.maxPasswordHistory must be an integer ranging 0 - 20'); | ||
done(); | ||
}); | ||
}); | ||
|
||
it('should fail to reset if the new password is same as the last password', done => { | ||
const user = new Parse.User(); | ||
const emailAdapter = { | ||
sendVerificationEmail: () => Promise.resolve(), | ||
sendPasswordResetEmail: options => { | ||
requestp.get({ | ||
uri: options.link, | ||
followRedirect: false, | ||
simple: false, | ||
resolveWithFullResponse: true | ||
}).then(response => { | ||
expect(response.statusCode).toEqual(302); | ||
const re = /http:\/\/localhost:8378\/1\/apps\/choose_password\?token=([a-zA-Z0-9]+)\&id=test\&username=user1/; | ||
const match = response.body.match(re); | ||
if (!match) { | ||
fail("should have a token"); | ||
return Promise.reject("Invalid password link"); | ||
} | ||
return Promise.resolve(match[1]); // token | ||
}).then(token => { | ||
return new Promise((resolve, reject) => { | ||
requestp.post({ | ||
uri: "http://localhost:8378/1/apps/test/request_password_reset", | ||
body: `new_password=user1&token=${token}&username=user1`, | ||
headers: { | ||
'Content-Type': 'application/x-www-form-urlencoded' | ||
}, | ||
followRedirect: false, | ||
simple: false, | ||
resolveWithFullResponse: true | ||
}).then(response => { | ||
resolve([response, token]); | ||
}).catch(error => { | ||
reject(error); | ||
}); | ||
}); | ||
}).then(data => { | ||
const response = data[0]; | ||
const token = data[1]; | ||
expect(response.statusCode).toEqual(302); | ||
expect(response.body).toEqual(`Found. Redirecting to http://localhost:8378/1/apps/choose_password?username=user1&token=${token}&id=test&error=New%20password%20should%20not%20be%20the%20same%20as%20last%201%20passwords.&app=passwordPolicy`); | ||
done(); | ||
return Promise.resolve(); | ||
}).catch(error => { | ||
jfail(error); | ||
fail("Repeat password test failed"); | ||
done(); | ||
}); | ||
}, | ||
sendMail: () => { | ||
} | ||
}; | ||
reconfigureServer({ | ||
appName: 'passwordPolicy', | ||
verifyUserEmails: false, | ||
emailAdapter: emailAdapter, | ||
passwordPolicy: { | ||
maxPasswordHistory: 1 | ||
}, | ||
publicServerURL: "http://localhost:8378/1" | ||
}).then(() => { | ||
user.setUsername("user1"); | ||
user.setPassword("user1"); | ||
user.set('email', '[email protected]'); | ||
user.signUp().then(() => { | ||
return Parse.User.logOut(); | ||
}).then(() => { | ||
return Parse.User.requestPasswordReset('[email protected]'); | ||
}).catch(error => { | ||
jfail(error); | ||
fail("SignUp or reset request failed"); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
|
||
|
||
it('should fail if the new password is same as the previous one', done => { | ||
const user = new Parse.User(); | ||
|
||
reconfigureServer({ | ||
appName: 'passwordPolicy', | ||
verifyUserEmails: false, | ||
passwordPolicy: { | ||
maxPasswordHistory: 5 | ||
}, | ||
publicServerURL: "http://localhost:8378/1" | ||
}).then(() => { | ||
user.setUsername("user1"); | ||
user.setPassword("user1"); | ||
user.set('email', '[email protected]'); | ||
user.signUp().then(() => { | ||
// try to set the same password as the previous one | ||
user.setPassword('user1'); | ||
return user.save(); | ||
}).then(() => { | ||
fail("should have failed because the new password is same as the old"); | ||
done(); | ||
}).catch(error => { | ||
expect(error.message).toEqual('New password should not be the same as last 5 passwords.'); | ||
expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
|
||
it('should fail if the new password is same as the 5th oldest one and policy does not allow the previous 5', done => { | ||
const user = new Parse.User(); | ||
|
||
reconfigureServer({ | ||
appName: 'passwordPolicy', | ||
verifyUserEmails: false, | ||
passwordPolicy: { | ||
maxPasswordHistory: 5 | ||
}, | ||
publicServerURL: "http://localhost:8378/1" | ||
}).then(() => { | ||
user.setUsername("user1"); | ||
user.setPassword("user1"); | ||
user.set('email', '[email protected]'); | ||
user.signUp().then(() => { | ||
// build history | ||
user.setPassword('user2'); | ||
return user.save(); | ||
}).then(() => { | ||
user.setPassword('user3'); | ||
return user.save(); | ||
}).then(() => { | ||
user.setPassword('user4'); | ||
return user.save(); | ||
}).then(() => { | ||
user.setPassword('user5'); | ||
return user.save(); | ||
}).then(() => { | ||
// set the same password as the initial one | ||
user.setPassword('user1'); | ||
return user.save(); | ||
}).then(() => { | ||
fail("should have failed because the new password is same as the old"); | ||
done(); | ||
}).catch(error => { | ||
expect(error.message).toEqual('New password should not be the same as last 5 passwords.'); | ||
expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
|
||
it('should succeed if the new password is same as the 6th oldest one and policy does not allow only previous 5', done => { | ||
const user = new Parse.User(); | ||
|
||
reconfigureServer({ | ||
appName: 'passwordPolicy', | ||
verifyUserEmails: false, | ||
passwordPolicy: { | ||
maxPasswordHistory: 5 | ||
}, | ||
publicServerURL: "http://localhost:8378/1" | ||
}).then(() => { | ||
user.setUsername("user1"); | ||
user.setPassword("user1"); | ||
user.set('email', '[email protected]'); | ||
user.signUp().then(() => { | ||
// build history | ||
user.setPassword('user2'); | ||
return user.save(); | ||
}).then(() => { | ||
user.setPassword('user3'); | ||
return user.save(); | ||
}).then(() => { | ||
user.setPassword('user4'); | ||
return user.save(); | ||
}).then(() => { | ||
user.setPassword('user5'); | ||
return user.save(); | ||
}).then(() => { | ||
user.setPassword('user6'); // this pushes initial password out of history | ||
return user.save(); | ||
}).then(() => { | ||
// set the same password as the initial one | ||
user.setPassword('user1'); | ||
return user.save(); | ||
}).then(() => { | ||
done(); | ||
}).catch(() => { | ||
fail("should have succeeded because the new password is not in history"); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
}) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@bhaskaryasa
Do we really need an email adapter here? I think this test case can be simplified to something like:
WDYT?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, you are right. Somehow I was thinking of only resetPassword as the way to change password in which case you will need to go through emailAdapter. It skipped my mind that password can be changed by the user after signing in. I will change the test cases.