package org.multibit.mbm.core.model; import com.google.common.collect.Lists; import org.jasypt.util.password.PasswordEncryptor; import org.jasypt.util.password.StrongPasswordEncryptor; import org.jasypt.util.password.rfc2307.RFC2307SHAPasswordEncryptor; import java.util.List; import java.util.UUID; /** *  <p>Builder to provide the following to {@link User}:</p> *  <ul> *  <li>Provide a fluent interface to facilitate building the entity</li> *  </ul> * * @since 0.0.1 *   */ public class UserBuilder { private final PasswordEncryptor weakPasswordEncryptor = new RFC2307SHAPasswordEncryptor(); private final PasswordEncryptor strongPasswordEncryptor = new StrongPasswordEncryptor(); private String apiKey = null; private String secretKey = null; private List<AddContactMethod> addContactMethods = Lists.newArrayList(); private List<AddRole> addRoles = Lists.newArrayList(); private String username; private String plainPassword; private Customer customer; private Supplier supplier; private boolean isBuilt = false; /** * Indicates that repeatable test data should be used * This is because it is a nightmare to fully mock the behaviour * of this class and this presents a simple alternative */ public static boolean isTestMode = false; /** * @return A new instance of the builder */ public static UserBuilder newInstance() { return new UserBuilder(); } public UserBuilder() { } /** * Handles the building process. No further configuration is possible after this. */ public User build() { validateState(); // User is a DTO and so requires a default constructor User user = new User(); // The API key should be a UUID but represented as a String // to ease persistence if (apiKey == null) { apiKey = createApiKey(); } user.setApiKey(apiKey); if (secretKey == null) { secretKey = createSecretKey(); } user.setSecretKey(secretKey); user.setUsername(username); // TODO Use Mockito for this if (plainPassword != null) { // Make a digest of the plain password since that is what will be received // during authentication requests user.setPasswordDigest(weakPasswordEncryptor.encryptPassword(plainPassword)); // In test mode we don't use the strong multi-pass digest if (!isTestMode) { // In production the digest is used to seed the strong multi-pass digest user.setPasswordDigest(strongPasswordEncryptor.encryptPassword(user.getPasswordDigest())); } } // Bi-directional relationship if (customer != null) { user.setCustomer(customer); customer.setUser(user); } if (supplier != null) { user.setSupplier(supplier); supplier.setUser(user); } for (AddRole addRole : addRoles) { addRole.applyTo(user); } for (AddContactMethod addContactMethod : addContactMethods) { addContactMethod.applyTo(user); } isBuilt = true; return user; } private void validateState() { if (isBuilt) { throw new IllegalStateException("The entity has been built"); } } /** * @return A suitable API key */ private String createApiKey() { return UUID.randomUUID().toString(); } /** * @return A suitable secret key */ private String createSecretKey() { return createApiKey() + createApiKey(); } /** * @param apiKey The public API key (e.g. "1234-5678") * * @return The builder */ public UserBuilder withApiKey(String apiKey) { this.apiKey = apiKey; return this; } /** * @param secretKey The secretKey (base64 encoded) * * @return The builder */ public UserBuilder withSecretKey(String secretKey) { this.secretKey = secretKey; return this; } public UserBuilder withContactMethod(ContactMethod contactMethod, String detail) { addContactMethods.add(new AddContactMethod(contactMethod, detail)); return this; } public UserBuilder withRole(Role role) { addRoles.add(new AddRole(role)); return this; } public UserBuilder withRoles(List<Role> roles) { for (Role role : roles) { addRoles.add(new AddRole(role)); } return this; } public UserBuilder withUsername(String username) { this.username = username; return this; } public UserBuilder withPassword(String password) { this.plainPassword = password; return this; } /** * Add the Customer to the User (one permitted) * * @return The builder */ public UserBuilder withCustomer(Customer customer) { this.customer = customer; return this; } /** * Add the Supplied to the User (one permitted) * * @return The builder */ public UserBuilder withSupplier(Supplier supplier) { this.supplier = supplier; return this; } /** * Handles adding a new contact method to the user */ private class AddContactMethod { private final ContactMethod contactMethod; private final String detail; private AddContactMethod(ContactMethod contactMethod, String detail) { this.contactMethod = contactMethod; this.detail = detail; } void applyTo(User user) { ContactMethodDetail contactMethodDetail = new ContactMethodDetail(); contactMethodDetail.setPrimaryDetail(detail); user.setContactMethodDetail(contactMethod, contactMethodDetail); } } /** * Handles adding a new contact method to the user */ private class AddRole { private final Role role; private AddRole(Role role) { this.role = role; } void applyTo(User user) { UserRole userRole = new UserRole(); UserRole.UserRolePk userRolePk = new UserRole.UserRolePk(); userRolePk.setUser(user); userRolePk.setRole(role); userRole.setPrimaryKey(userRolePk); user.getUserRoles().add(userRole); } } }