Clarify Responsibility
Example - Builder
import org.apache.commons.lang.StringEscapeUtils;
import org.apache.commons.validator.routines.EmailValidator;
import org.mongodb.morphia.Datastore;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* Allows easy creation of valid, unique users in a given database.
* <p>
* This user will have a set ID based on the database is it intended for. The password will also be encrypted based on the PasswordService's encryptPassword function. All fields besides password and username will be Javascript escaped for Mongo. The username will be validated as an email address.
*
* @since June 2013
*/
public final class UserBuilder {
/**
* User builder instance to be returned.
*/
private static final UserBuilder USER_INSTANCE = new UserBuilder();
/**
* The user to be CREATED.
*/
private static UserDTO sUser = new UserDTO();
/**
* The plaintext version of the user's password.
*/
private static String sPlaintextPassword = null;
/**
* The datastore which the user will be intended for.
*/
private static Datastore sDatastore = null;
/**
* Private constructor to prevent instances being CREATED.
*/
private UserBuilder() {
// hide the constructor
}
/**
* Reset the builder to its initial state. Mainly for testing.
*/
protected static void reset() {
sUser = new UserDTO();
sDatastore = null;
sPlaintextPassword = null;
}
/**
* Returns the CREATED user after validating its fields for the datastore.
* <p>
* The user will have its password encrypted, and its ID will be set based on the provided datastore.
*
* @return The CREATED User object.
*/
public static UserDTO create() throws RuntimeException {
checkAllFieldsExist();
if (sDatastore == null) {
throwRuntimeException("Must provide datastore.");
}
sUser.setID(generateID());
validatePassword();
sUser.setCredentials(encryptPassword(sPlaintextPassword));
validateUserEmail();
final UserDTO toReturn = sUser;
reset();
return toReturn;
}
/**
* Returns the CREATED user after validating its fields for the datastore.
* <p>
* The user will have its password encrypted, but no ID will be set and the datastore (if provided) will be ignored.
*
* @return The CREATED User object.
*/
protected static UserDTO createWithoutDatastore() throws RuntimeException {
checkAllFieldsExist();
validatePassword();
sUser.setCredentials(encryptPassword(sPlaintextPassword));
validateUserEmail();
final UserDTO toReturn = sUser;
reset();
return toReturn;
}
/**
* The user will validated and its ID will be set for the given datastore.
*
* @param ds The database this user will be intended for after creation.
* @return The UserBuilder instance.
*/
public static UserBuilder forDatabase(final Datastore ds) {
sDatastore = ds;
return USER_INSTANCE;
}
/**
* Set the username, which should be a valid email address.
*
* @param username The email and username for this user.
* @return The UserBuilder instance.
*/
public static UserBuilder withEmailAsUsername(final String username) {
sUser.setUsername(username);
sUser.setEmail(username);
return USER_INSTANCE;
}
/**
* Set the password, which will be encrypted.
* <p>
* The password should meet the following criteria: -8 characters or longer -Contains at least three of these character types: -lowercase letter -uppercase letter -special character -number
*
* @param password The user's password.
* @return The UserBuilder instance.
*/
public static UserBuilder withPassword(final String password) {
sPlaintextPassword = password;
return USER_INSTANCE;
}
/**
* Set the username, which should be a valid email address.
*
* @param firstName The user's first name.
* @param lastName The user's last name.
* @return The UserBuilder instance.
*/
public static UserBuilder withName(final String firstName, final String lastName) {
sUser.setFirstName(StringEscapeUtils.escapeJavaScript(firstName));
sUser.setLastName(StringEscapeUtils.escapeJavaScript(lastName));
return USER_INSTANCE;
}
/**
* Set user's type.
*
* @param type The users type.
* @return The UserBuilder instance.
*/
public static UserBuilder withType(final String type) {
sUser.setType(StringEscapeUtils.escapeJavaScript(type));
return USER_INSTANCE;
}
/**
* Set the user's roles. These will be used with Shiro's authentication.
*
* @param roles The roles for this user.
* @return The UserBuilder instance.
*/
public static UserBuilder withRoles(final String[] roles) {
final Set<String> roleSet = new HashSet<>();
for (final String r : roles) {
roleSet.add(StringEscapeUtils.escapeJavaScript(r));
}
sUser.setRoles(roleSet);
return USER_INSTANCE;
}
public static UserBuilder withAuthorizedGroups(final List<BatchGroup> authorizedGroups) {
sUser.setAuthorizedGroups(authorizedGroups);
return USER_INSTANCE;
}
private static void checkAllFieldsExist() throws RuntimeException {
if (fieldInvalid(sUser.getUsername())) {
throwRuntimeException("Must provide username.");
}
if (fieldInvalid(sPlaintextPassword)) {
throwRuntimeException("Must provide password.");
}
if (fieldInvalid(sUser.getFirstName())) {
throwRuntimeException("Must provide first name.");
}
if (fieldInvalid(sUser.getLastName())) {
throwRuntimeException("Must provide last name.");
}
if (fieldInvalid(sUser.getType())) {
throwRuntimeException("Must provide type.");
}
final Set roles = sUser.getRoles();
if (roles == null || roles.size() == 0) {
throwRuntimeException("Must provide roles.");
}
}
private static boolean fieldInvalid(final String str) {
return str == null || str.length() < 1;
}
private static void validateUserEmail() throws RuntimeException {
final EmailValidator ev = EmailValidator.getInstance();
if (!(ev.isValid(sUser.getEmail()) && ev.isValid(sUser.getUsername()))) {
throwRuntimeException("Username was not a valid email address.");
}
}
/*
* Ensures that the password meets the following criteria: -8 characters or longer -Contains at least three of these character types: -lowercase letter -uppercase letter -special character -number
*/
private static void validatePassword() throws RuntimeException {
validatePassword(sPlaintextPassword);
}
public static void validatePassword(final String plainTextPassword) {
if (plainTextPassword.length() < 8) {
throwRuntimeException("Password is shorter than 8 characters.");
}
short hasNumber = 0;
short hasLower = 0;
short hasUpper = 0;
short hasSpecial = 0;
for (int i = 0; i < plainTextPassword.length(); ++i) {
final char c = plainTextPassword.charAt(i);
if ('a' <= c && c <= 'z') {
hasLower = 1;
} else if ('A' <= c && c <= 'Z') {
hasUpper = 1;
} else if ('0' <= c && c <= '9') {
hasNumber = 1;
} else {
hasSpecial = 1;
}
}
if (hasLower + hasNumber + hasUpper + hasSpecial >= 3) {
return;
}
throwRuntimeException("Password must contain 3 of: lowercase, uppercase, numbers, special chars.");
}
public static void withDefaultProficiency(final String proficiency) {
sUser.setProficiency(proficiency);
}
private static String encryptPassword(final String cleartext) {
final PasswordService qps = new PasswordService();
return qps.encryptPassword(cleartext);
}
private static String generateID() {
final IdDAO IdDAO = new IdDAO(sDatastore);
return IdDAO.generateId(User.class).toString();
}
private static void throwRuntimeException(final String message) throws RuntimeException {
reset();
throw new RuntimeException(message);
}
public static String getPassword(final String plainTextPassword) {
validatePassword(plainTextPassword);
return encryptPassword(plainTextPassword);
}
}Last updated