// // Copyright 2010 Cinch Logic Pty Ltd. // // http://www.chililog.com // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package org.chililog.server.data; import java.io.Serializable; import java.util.ArrayList; import java.util.regex.Pattern; import org.apache.commons.lang.StringUtils; import org.chililog.server.common.ChiliLogException; import org.chililog.server.common.CryptoUtils; import com.mongodb.DBObject; /** * <p> * The User Business Object encapsulate user details used for authentication and authorization: * <ul> * <li>Username</li> * <li>Password</li> * <li>Roles</li> * </ul> * </p> * * @author vibul * */ public class UserBO extends BO implements Serializable { private static final long serialVersionUID = 1L; public static final String SYSTEM_ADMINISTRATOR_ROLE_NAME = "system.administrator"; public static final String REPOSITORY_ROLE_PREFIX = "repo."; public static final String REPOSITORY_ADMINISTRATOR_ROLE_SUFFIX = ".administrator"; public static final String REPOSITORY_ADMINISTRATOR_ROLE_TEMPLATE = REPOSITORY_ROLE_PREFIX + "%s" + REPOSITORY_ADMINISTRATOR_ROLE_SUFFIX; public static final String REPOSITORY_WORKBENCH_ROLE_SUFFIX = ".workbench"; public static final String REPOSITORY_WORKBENCH_ROLE_TEMPLATE = REPOSITORY_ROLE_PREFIX + "%s" + REPOSITORY_WORKBENCH_ROLE_SUFFIX; public static final String REPOSITORY_PUBLISHER_ROLE_SUFFIX = ".publisher"; public static final String REPOSITORY_PUBLISHER_ROLE_TEMPLATE = REPOSITORY_ROLE_PREFIX + "%s" + REPOSITORY_PUBLISHER_ROLE_SUFFIX; public static final String REPOSITORY_SUBSCRIBER_ROLE_SUFFIX = ".subscriber"; public static final String REPOSITORY_SUBSCRIBER_ROLE_TEMPLATE = REPOSITORY_ROLE_PREFIX + "%s" + REPOSITORY_SUBSCRIBER_ROLE_SUFFIX; // Pattern thanks to http://www.regular-expressions.info/email.html private static Pattern _emailAddressPattern = Pattern .compile("^[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\\.)+[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?$"); private String _username; private String _password; private ArrayList<String> _roles = new ArrayList<String>(); private Status _status = Status.ENABLED; private String _displayName; private String _emailAddress; static final String USERNAME_FIELD_NAME = "username"; static final String PASSWORD_FIELD_NAME = "password"; static final String ROLES_FIELD_NAME = "roles"; static final String STATUS_FIELD_NAME = "status"; static final String DISPLAY_NAME_FIELD_NAME = "display_name"; static final String EMAIL_ADDRESS_FIELD_NAME = "email_address"; /** * Basic constructor */ public UserBO() { return; } /** * Constructor that loads our properties retrieved from the mongoDB dbObject * * @param dbObject * database object as retrieved from mongoDB * @throws ChiliLogException */ public UserBO(DBObject dbObject) throws ChiliLogException { super(dbObject); _username = MongoUtils.getString(dbObject, USERNAME_FIELD_NAME, true); _password = MongoUtils.getString(dbObject, PASSWORD_FIELD_NAME, false); _roles = MongoUtils.getStringArrayList(dbObject, ROLES_FIELD_NAME, false); _status = Status.valueOf(MongoUtils.getString(dbObject, STATUS_FIELD_NAME, true)); _displayName = MongoUtils.getString(dbObject, DISPLAY_NAME_FIELD_NAME, false); _emailAddress = MongoUtils.getString(dbObject, EMAIL_ADDRESS_FIELD_NAME, false); return; } /** * Puts our properties into the mongoDB object so that it can be saved * * @param dbObject * mongoDB database object that can be used for saving * @throws ChiliLogException */ @Override protected void savePropertiesToDBObject(DBObject dbObject) throws ChiliLogException { MongoUtils.setString(dbObject, USERNAME_FIELD_NAME, _username, true); MongoUtils.setString(dbObject, PASSWORD_FIELD_NAME, _password, true); MongoUtils.setStringArrayList(dbObject, ROLES_FIELD_NAME, _roles, false); MongoUtils.setString(dbObject, STATUS_FIELD_NAME, _status.toString(), true); MongoUtils.setString(dbObject, DISPLAY_NAME_FIELD_NAME, _displayName, false); MongoUtils.setString(dbObject, EMAIL_ADDRESS_FIELD_NAME, _emailAddress, false); if (!StringUtils.isBlank(_emailAddress) && !_emailAddressPattern.matcher(_emailAddress).matches()) { throw new ChiliLogException(Strings.USER_EMAIL_ADDRESS_FORMAT_ERROR, _emailAddress); } } /** * Returns the username (or unique code) for this user */ public String getUsername() { return _username; } /** * Assigns a unique code to this user * * @param username */ public void setUsername(String username) { _username = username; } /** * Returns a hashed password */ public String getPassword() { return _password; } /** * Assigns a password to this user. If plain text, then it is hashed before saving * * @param password * Password * @param isPlainText * indicates if the password is plain text or hashed. * @throws ChiliLogException * if there is an error during hashing */ public void setPassword(String password, boolean isPlainText) throws ChiliLogException { if (isPlainText) { _password = CryptoUtils.createSHA512Hash(password, null); } else { _password = password; } } /** * Validates the plain text password against the hash password stored with this user record * * @param plainTextPassword * the plain text password to validate * @return true if <code>plainTextPassword</code> matches the stored hashed password, false if it does not match. * @throws ChiliLogException * if there was an error during password verification */ public boolean validatePassword(String plainTextPassword) throws ChiliLogException { return CryptoUtils.verifyHash(plainTextPassword, _password); } /** * Add this user to the role * * @param role * name of role */ public void addRole(String role) { if (!hasRole(role)) { _roles.add(role); } } /** * Removes the user from the role * * @param role */ public void removeRole(String role) { _roles.remove(role); } /** * Removes the user from all roles */ public void removeAllRoles() { _roles.clear(); } /** * Does the user have this role? * * @param role * Name of role to check * @return true if the user has the role, false if not */ public boolean hasRole(String role) { return _roles.contains(role); } /** * Returns an array of roles to which the user belongs. Modifying the array will NOT modify the user's role. Use * <code>addRole</code> and <code>removeRole</code> instead. */ public String[] getRoles() { return _roles.toArray(new String[] {}); } /** * Returns the status of this user account */ public Status getStatus() { return _status; } /** * Sets the status of this user account * * @param status */ public void setStatus(Status status) { _status = status; } /** * Returns the name of the user to display on the UI */ public String getDisplayName() { return _displayName; } /** * Sets the name of the user to display on the UI */ public void setDisplayName(String displayName) { _displayName = displayName; } /** * Returns the email address of the user */ public String getEmailAddress() { return _emailAddress; } public void setEmailAddress(String emailAddress) { _emailAddress = emailAddress; } /** * Returns if the user is a system administrator or not */ public boolean isSystemAdministrator() { return hasRole(SYSTEM_ADMINISTRATOR_ROLE_NAME); } /** * Creates the role name for users who can manage the repository. Administrators have access to the repository in * the workbench, can publish to the repository, can subscribe to the repository as well configure the repository. * * @param repositoryName * name of the repository * @return Role name that grants the user permission to administer the named repository */ public static String createRepositoryAdministratorRoleName(String repositoryName) { return String.format(REPOSITORY_ADMINISTRATOR_ROLE_TEMPLATE, repositoryName); } /** * Creates the role name for users who can access the named repository from the workbench. Workbench users cannot * publish but they can subscribe to log entries (for streaming) * * @param repositoryName * name of the repository * @return Role name that grants the user permission to access the named repository from the workbench. */ public static String createRepositoryWorkbenchRoleName(String repositoryName) { return String.format(REPOSITORY_WORKBENCH_ROLE_TEMPLATE, repositoryName); } /** * Creates the role name for users who can publish log entries to this repository. * * @param repositoryName * name of the repository * @return Role name that grants the user permission to publish log entries to this repository. */ public static String createRepositoryPublisherRoleName(String repositoryName) { return String.format(REPOSITORY_PUBLISHER_ROLE_TEMPLATE, repositoryName); } /** * Creates the role name for users who can subscribe to log entries from this repository * * @param repositoryName * name of the repository * @return Role name that grants the user permission subscribe to log entries from this repository */ public static String createRepositorySubscriberRoleName(String repositoryName) { return String.format(REPOSITORY_SUBSCRIBER_ROLE_TEMPLATE, repositoryName); } /** * <p> * Given a role name, return the repository to which the role provides permission. Assumes repository roles are in * the format: <code>repo.[repository name].[role type]</code> * </p> * <p> * This provides a quick short cut way to find out the repositories to which a user has access * </p> * * @param role * Role name. For example repo.xxx.administrator * @return The repository name (xxx in the above example). Null if not repository access. */ public static String extractRepositoryNameFromRole(String role) { if (StringUtils.isBlank(role)) { return null; } if (!role.startsWith(REPOSITORY_ROLE_PREFIX)) { return null; } if (role.endsWith(REPOSITORY_ADMINISTRATOR_ROLE_SUFFIX) || role.endsWith(REPOSITORY_WORKBENCH_ROLE_SUFFIX) || role.endsWith(REPOSITORY_PUBLISHER_ROLE_SUFFIX) || role.endsWith(REPOSITORY_SUBSCRIBER_ROLE_SUFFIX)) { return role.substring(5, role.lastIndexOf('.')); } return null; } /** * User status * * @author vibul * */ public static enum Status { /** * User can login */ ENABLED, /** * User cannot login */ DISABLED, /** * User cannot login due to too many failed login attempts */ LOCKED } }