X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?p=controller.git;a=blobdiff_plain;f=opendaylight%2Fusermanager%2Fapi%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fcontroller%2Fusermanager%2FUserConfig.java;h=07c814adf14c7b2d35f9991a9bd3e5875296f922;hp=0e6a48ab52b32bdc66eb114ad26021ef07aa56ad;hb=ae69cd76093e444e0b8f5b3ca10302af36024c86;hpb=54f0b73b9c5b40ef74a13fd38de9c8e1370bcb24 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 0e6a48ab52..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 @@ -9,6 +9,9 @@ package org.opendaylight.controller.usermanager; import java.io.Serializable; +import java.nio.charset.Charset; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Iterator; import java.util.List; @@ -16,6 +19,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import org.opendaylight.controller.sal.authorization.AuthResultEnum; +import org.opendaylight.controller.sal.utils.HexEncode; import org.opendaylight.controller.sal.utils.Status; import org.opendaylight.controller.sal.utils.StatusCode; import org.opendaylight.controller.usermanager.AuthResponse; @@ -27,27 +31,48 @@ import org.opendaylight.controller.usermanager.AuthResponse; public class UserConfig implements Serializable { private static final long serialVersionUID = 1L; - /* - * Clear text password as we are moving to some MD5 digest for when saving - * configurations - */ 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; - private static final Pattern INVALID_USERNAME_CHARACTERS = Pattern - .compile("([/\\s\\.\\?#%;\\\\]+)"); + 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 { + try { + UserConfig.oneWayFunction = MessageDigest.getInstance("SHA-1"); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } + } public UserConfig() { } + /** + * Construct a UserConfig object and takes care of hashing the user password + * + * @param user + * the user name + * @param password + * the plain text password + * @param roles + * the list of roles + */ public UserConfig(String user, String password, List roles) { this.user = user; - this.password = password; - this.roles = (roles == null) ? new ArrayList() - : new ArrayList(roles); + + /* + * 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); } public String getUser() { @@ -75,28 +100,37 @@ public class UserConfig implements Serializable { @Override public boolean equals(Object obj) { - if (this == obj) + if (this == obj) { return true; - if (obj == null) + } + if (obj == null) { return false; - if (getClass() != obj.getClass()) + } + if (getClass() != obj.getClass()) { return false; + } UserConfig other = (UserConfig) obj; if (password == null) { - if (other.password != null) + if (other.password != null) { return false; - } else if (!password.equals(other.password)) + } + } else if (!password.equals(other.password)) { return false; + } if (roles == null) { - if (other.roles != null) + if (other.roles != null) { return false; - } else if (!roles.equals(other.roles)) + } + } else if (!roles.equals(other.roles)) { return false; + } if (user == null) { - if (other.user != null) + if (other.user != null) { return false; - } else if (!user.equals(other.user)) + } + } else if (!user.equals(other.user)) { return false; + } return true; } @@ -106,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; } @@ -122,8 +159,7 @@ public class UserConfig implements Serializable { } Matcher mUser = UserConfig.INVALID_USERNAME_CHARACTERS.matcher(user); - if (user.length() > UserConfig.USERNAME_MAXLENGTH - || mUser.find() == true) { + if (user.length() > UserConfig.USERNAME_MAXLENGTH || mUser.find() == true) { return new Status(StatusCode.BADREQUEST, "Username can have 1-32 non-whitespace " + "alphanumeric characters and any special " @@ -133,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); } @@ -153,20 +189,19 @@ public class UserConfig implements Serializable { return new Status(StatusCode.SUCCESS); } - public Status update(String currentPassword, String newPassword, - List newRoles) { + public Status update(String currentPassword, String newPassword, List newRoles) { + // To make any changes to a user configured profile, current password // must always be provided - if (!this.password.equals(currentPassword)) { - return new Status(StatusCode.BADREQUEST, - "Current password is incorrect"); + if (!this.password.equals(hash(currentPassword))) { + return new Status(StatusCode.BADREQUEST, "Current password is incorrect"); } // Create a new object with the proposed modifications UserConfig proposed = new UserConfig(); proposed.user = this.user; - proposed.password = (newPassword != null)? newPassword : this.password; - proposed.roles = (newRoles != null)? newRoles : this.roles; + proposed.password = (newPassword == null)? this.password : hash(newPassword); + proposed.roles = (newRoles == null)? this.roles : newRoles; // Validate it Status status = proposed.validate(); @@ -184,7 +219,7 @@ public class UserConfig implements Serializable { public AuthResponse authenticate(String clearTextPass) { AuthResponse locResponse = new AuthResponse(); - if (password.equals(clearTextPass)) { + if (password.equals(hash(clearTextPass))) { locResponse.setStatus(AuthResultEnum.AUTH_ACCEPT_LOC); locResponse.addData(getRolesString()); } else { @@ -205,4 +240,33 @@ public class UserConfig implements Serializable { } return buffer.toString(); } + + public static String hash(String message) { + if (message == null) { + return message; + } + 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; + } }