From: Alessandro Boch Date: Wed, 18 Sep 2013 07:13:49 +0000 (-0700) Subject: Add strong password check for users X-Git-Tag: releasepom-0.1.0~60^2 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?p=controller.git;a=commitdiff_plain;h=ae69cd76093e444e0b8f5b3ca10302af36024c86 Add strong password check for users - User password must be at least 8 characters long, contain upper and lower case letters, at least one number and one non-alphanumeric character. Max lenght 256. - Disabled by default, can be enabled through config.ini Change-Id: I4696801dc60379292a3d6a687795f33971bd85c8 Signed-off-by: Alessandro Boch --- diff --git a/opendaylight/distribution/opendaylight/src/main/resources/configuration/config.ini b/opendaylight/distribution/opendaylight/src/main/resources/configuration/config.ini index 970929f794..12f18ff645 100644 --- a/opendaylight/distribution/opendaylight/src/main/resources/configuration/config.ini +++ b/opendaylight/distribution/opendaylight/src/main/resources/configuration/config.ini @@ -94,3 +94,6 @@ controllerKeyStore= controllerKeyStorePassword= controllerTrustStore= controllerTrustStorePassword= + +# User Manager configurations +enableStrongPasswordCheck = false diff --git a/opendaylight/usermanager/api/src/main/java/org/opendaylight/controller/usermanager/UserConfig.java b/opendaylight/usermanager/api/src/main/java/org/opendaylight/controller/usermanager/UserConfig.java index cca194e953..07c814adf1 100644 --- a/opendaylight/usermanager/api/src/main/java/org/opendaylight/controller/usermanager/UserConfig.java +++ b/opendaylight/usermanager/api/src/main/java/org/opendaylight/controller/usermanager/UserConfig.java @@ -34,9 +34,11 @@ public class UserConfig implements Serializable { protected String user; protected List roles; private String password; + + private static final boolean strongPasswordCheck = Boolean.getBoolean("enableStrongPasswordCheck"); + private static final String BAD_PASSWORD = "Bad Password"; private static final int USERNAME_MAXLENGTH = 32; - private static final int PASSWORD_MINLENGTH = 5; - private static final int PASSWORD_MAXLENGTH = 256; + protected static final String PASSWORD_REGEX = "(?=.*[^\\w])(?=.*\\d)(?=.*[a-z])(?=.*[A-Z]).{8,256}$"; private static final Pattern INVALID_USERNAME_CHARACTERS = Pattern.compile("([/\\s\\.\\?#%;\\\\]+)"); private static MessageDigest oneWayFunction = null; static { @@ -63,16 +65,12 @@ public class UserConfig implements Serializable { public UserConfig(String user, String password, List roles) { this.user = user; - this.password = password; - if (this.validatePassword().isSuccess()) { - /* - * Only if the password is a valid one, hash it. So in case it is not - * valid, when UserConfig.validate() is called, the proper - * validation error will be returned to the caller. If we hashed a - * priori instead, the mis-configuration would be masked - */ - this.password = hash(this.password); - } + /* + * Password validation to be done on clear text password. If fails, mark + * the password with a well known label, so that object validation can + * report the proper error. Only if password is a valid one, hash it. + */ + this.password = (validatePassword(password).isSuccess()) ? hash(password) : BAD_PASSWORD; this.roles = (roles == null) ? new ArrayList() : new ArrayList(roles); } @@ -142,12 +140,15 @@ public class UserConfig implements Serializable { } public Status validate() { - Status validCheck = validateRoles(); + Status validCheck = validateUsername(); if (validCheck.isSuccess()) { - validCheck = validateUsername(); + validCheck = (!password.equals(BAD_PASSWORD)) ? new Status(StatusCode.SUCCESS) : new Status( + StatusCode.BADREQUEST, + "Password should be 8 to 256 characters long, contain both upper and lower case letters, " + + "at least one number and at least one non alphanumeric character"); } if (validCheck.isSuccess()) { - validCheck = validatePassword(); + validCheck = validateRoles(); } return validCheck; } @@ -168,15 +169,15 @@ public class UserConfig implements Serializable { return new Status(StatusCode.SUCCESS); } - private Status validatePassword() { + private Status validatePassword(String password) { if (password == null || password.isEmpty()) { return new Status(StatusCode.BADREQUEST, "Password cannot be empty"); } - if (password.length() < UserConfig.PASSWORD_MINLENGTH - || password.length() > UserConfig.PASSWORD_MAXLENGTH) { - return new Status(StatusCode.BADREQUEST, - "Password should have 5-256 characters"); + if (strongPasswordCheck && !password.matches(UserConfig.PASSWORD_REGEX)) { + return new Status(StatusCode.BADREQUEST, "Password should be 8 to 256 characters long, " + + "contain both upper and lower case letters, at least one number " + + "and at least one non alphanumeric character"); } return new Status(StatusCode.SUCCESS); } @@ -247,4 +248,25 @@ public class UserConfig implements Serializable { UserConfig.oneWayFunction.reset(); return HexEncode.bytesToHexString(UserConfig.oneWayFunction.digest(message.getBytes(Charset.defaultCharset()))); } + + /** + * Returns UserConfig instance populated with the passed parameters. It does + * not run any checks on the passed parameters. + * + * @param userName + * the user name + * @param password + * the plain text password + * @param roles + * the list of roles + * @return the UserConfig object populated with the passed parameters. No + * validity check is run on the input parameters. + */ + public static UserConfig getUncheckedUserConfig(String userName, String password, List roles) { + UserConfig config = new UserConfig(); + config.user = userName; + config.password = hash(password); + config.roles = roles; + return config; + } } diff --git a/opendaylight/usermanager/api/src/test/java/org/opendaylight/controller/usermanager/AuthorizationUserConfigTest.java b/opendaylight/usermanager/api/src/test/java/org/opendaylight/controller/usermanager/AuthorizationUserConfigTest.java index 4c2a19e426..8c029a7488 100644 --- a/opendaylight/usermanager/api/src/test/java/org/opendaylight/controller/usermanager/AuthorizationUserConfigTest.java +++ b/opendaylight/usermanager/api/src/test/java/org/opendaylight/controller/usermanager/AuthorizationUserConfigTest.java @@ -117,4 +117,41 @@ public class AuthorizationUserConfigTest { UserConfig userConfig2 = new UserConfig("uname", "ciscocisco", roles); assertEquals(userConfig, userConfig2); } + + @Test + public void userConfigPasswordTest() { + + String regex = UserConfig.PASSWORD_REGEX; + String password = null; + + // Good password + password = "aBc@eF#h9"; + assertTrue(password.matches(regex)); + password = "^aBc@eF#h9$88ad*o&"; + assertTrue(password.matches(regex)); + password = "_^aBc@\":eF#h;9$\\8|8ad*o&-(){}/,.> roles = new ArrayList(1); roles.add(DEFAULT_ADMIN_ROLE); - localUserConfigList.put(DEFAULT_ADMIN, new UserConfig(DEFAULT_ADMIN, DEFAULT_ADMIN_PASSWORD, roles)); + // Need to skip the strong password check for the default admin + UserConfig defaultAdmin = UserConfig.getUncheckedUserConfig(UserManager.DEFAULT_ADMIN, + UserManager.DEFAULT_ADMIN_PASSWORD, roles); + localUserConfigList.put(UserManager.DEFAULT_ADMIN, defaultAdmin); } }