feature: implement password policies to avoid weak passwords#4008
feature: implement password policies to avoid weak passwords#4008kezhenxu94 merged 2 commits intoapolloconfig:masterfrom
Conversation
Codecov Report
@@ Coverage Diff @@
## master #4008 +/- ##
============================================
+ Coverage 51.31% 51.46% +0.15%
- Complexity 2504 2524 +20
============================================
Files 482 484 +2
Lines 14772 14798 +26
Branches 1528 1532 +4
============================================
+ Hits 7580 7616 +36
+ Misses 6662 6652 -10
Partials 530 530
Continue to review full report at Codecov.
|
...lo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/UserInfoController.java
Outdated
Show resolved
Hide resolved
apollo-portal/src/main/resources/static/scripts/controller/UserController.js
Outdated
Show resolved
Hide resolved
| user.setUsername("username"); | ||
| user.setPassword("9sivg8hg3wjz8"); | ||
| userInfoController.createOrUpdateUser(user); | ||
| Assert.assertTrue(true); |
There was a problem hiding this comment.
Done, this test case has been removed. Thanks
There was a problem hiding this comment.
Hi @WillardHu , I don't mean to remove the case, I mean the assertTrue(true) is pointless, removing this line should be enough
...-portal/src/test/java/com/ctrip/framework/apollo/portal/util/CommonlyUsedPwdCheckerTest.java
Outdated
Show resolved
Hide resolved
70f1d0c to
edc6de5
Compare
edc6de5 to
cc1cb83
Compare
| } | ||
|
|
||
| /** | ||
| * @return The passwrod contains code fragment or is blank. |
There was a problem hiding this comment.
| * @return The passwrod contains code fragment or is blank. | |
| * @return The password contains code fragment or is blank. |
| public static boolean notMatchRegex(String password) { | ||
| return !PWD_PATTERN.matcher(password).matches(); | ||
| } |
There was a problem hiding this comment.
| public static boolean notMatchRegex(String password) { | |
| return !PWD_PATTERN.matcher(password).matches(); | |
| } | |
| public static boolean isWeakPassword(String password) { | |
| return !PWD_PATTERN.matcher(password).matches() || isCommonlyUsed(password); | |
| } |
apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/util/UserPasswordChecker.java
Outdated
Show resolved
Hide resolved
| var alpha_chars =[ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', | ||
| 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']; | ||
|
|
||
| function randomAlphanumeric(len) { |
There was a problem hiding this comment.
I'm not sure whether we should provide this functionality, because most modern browsers (like Safari) or password manager (like 1password) has this ability to generate strong random password, this function defined here is rather naive IMO
There was a problem hiding this comment.
I agree with you if it's self-registration, but in this scenario, it is the administrator manage the account. This is just a convenience for administrators to generate default passwords for members if he wanted. And the browser's password generation extension may only work for the password component. If you don't want it, I'll remove it. Thanks.
There was a problem hiding this comment.
I would remove that functionality since strong password generation can be done by the browser.
Additionally a custom password generator would be harder to maintain (for example if the password policy changes etc) with little to none benefit.
There was a problem hiding this comment.
I would also suggest we don't provide this functionality as it is not a core function of apollo and there are many plugins or browsers doing this job much better. What we need to do though is to make sure the password form is compatible with those plugins or browsers.
There was a problem hiding this comment.
Ok, the frontend modifications have been rollback.
cc1cb83 to
f9312ba
Compare
f9312ba to
a732f6c
Compare
DiegoKrupitza
left a comment
There was a problem hiding this comment.
The rest looks good to me.
| if (UserPasswordChecker.isWeakPassword(user.getPassword())) { | ||
| throw new BadRequestException( | ||
| "Password needs a number and lowercase letter and between 8~20 characters. " | ||
| + "And cannot be a weak password."); |
There was a problem hiding this comment.
I think you should adapt the message. Makes it more useable and clear to the user what the real reason is.
It can be not matching to the pattern, weak password or both.
If it is just one and you display both reasons in the error message the user might not understand it why you get both.
PS: you should also explain what a weak password is in the exception
There was a problem hiding this comment.
Ok, I've split the error message, and wrap it with CheckResult.
|
|
||
| public class UserPasswordChecker { | ||
|
|
||
| private static final Pattern PWD_PATTERN = Pattern |
There was a problem hiding this comment.
Would be nice if you could write few test cases that check if this pattern really works as intended. Regex is often a reason for bugs.
| "1q2w", "2w3e", "3e4r", "5t6y", "abcd", "qwer", "asdf", "zxcv" | ||
| ); | ||
|
|
||
| public static boolean isWeakPassword(String password) { |
There was a problem hiding this comment.
I would suggest we use instance method instead of static method so that we could mock this method easily.
There was a problem hiding this comment.
Ok, I used Spring IOC to register the bean instance of UserPasswordChecker interface’s implementation class AuthUserPasswordChecker.
| /** | ||
| * @return The password contains code fragment or is blank. | ||
| */ | ||
| private static boolean isCommonlyUsed(String password) { |
There was a problem hiding this comment.
I would suggest we use instance method instead of static method so that we could mock this method easily.
| try { | ||
| userInfoController.createOrUpdateUser(user); | ||
| } catch (BadRequestException e) { | ||
| Assert.assertEquals( |
There was a problem hiding this comment.
It's better we mock password checker here instead of testing the password logic here, as it is not core logic of UserInfoController.
| import org.junit.Assert; | ||
| import org.junit.Test; | ||
|
|
||
| public class CommonlyUsedPwdCheckerTest { |
There was a problem hiding this comment.
This test should be renamed as something like UserPasswordCheckerTest so that we will test all kinds of weak password scenarios.
a732f6c to
465491c
Compare
| CheckResult pwdCheckRes; | ||
| if (!(pwdCheckRes = passwordChecker.checkWeakPassword(user.getPassword())).isSuccess()) { | ||
| throw new BadRequestException(pwdCheckRes.getMessage()); | ||
| } |
There was a problem hiding this comment.
Is there a reason why you assign pwdCheckRes inside the if and not before when you declare the var pwdCheckRes?
Otherwise to make the code more readable I would suggest to change it to
CheckResult pwdCheckRes = passwordChecker.checkWeakPassword(user.getPassword());
if (!pwdCheckRes.isSuccess()) {
throw new BadRequestException(pwdCheckRes.getMessage());
}Signed-off-by: WillardHu <[email protected]>
465491c to
9b9281e
Compare
|
Good work! 👍🏽:shipit: |
Signed-off-by: WillardHu [email protected]
What's the purpose of this PR
Closes #3948
Which issue(s) this PR fixes:
UserInfoController.createOrUpdateUser(..)method;Brief changelog
Implement password policies to avoid weak passwords
Follow this checklist to help us incorporate your contribution quickly and easily:
mvn clean testto make sure this pull request doesn't break anything.CHANGESlog.