/** * Copyright (c) 2009--2015 Red Hat, Inc. * * This software is licensed to you under the GNU General Public License, * version 2 (GPLv2). There is NO WARRANTY for this software, express or * implied, including the implied warranties of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2 * along with this software; if not, see * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. * * Red Hat trademarks are not licensed under GPLv2. No permission is * granted to use or replicate Red Hat trademarks that are incorporated * in this software or its documentation. */ package com.redhat.rhn.manager.user; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.SortedSet; import java.util.regex.Pattern; import javax.mail.internet.AddressException; import javax.mail.internet.InternetAddress; import org.apache.commons.lang.StringUtils; import com.redhat.rhn.common.conf.UserDefaults; import com.redhat.rhn.common.db.ResetPasswordFactory; import com.redhat.rhn.common.hibernate.LookupException; import com.redhat.rhn.common.localization.LocalizationService; import com.redhat.rhn.common.messaging.MessageQueue; import com.redhat.rhn.common.validator.ParsedConstraint; import com.redhat.rhn.common.validator.ValidatorError; import com.redhat.rhn.domain.common.ResetPassword; import com.redhat.rhn.domain.org.Org; import com.redhat.rhn.domain.role.Role; import com.redhat.rhn.domain.role.RoleFactory; import com.redhat.rhn.domain.server.ManagedServerGroup; import com.redhat.rhn.domain.server.ServerGroup; import com.redhat.rhn.domain.server.ServerGroupFactory; import com.redhat.rhn.domain.user.Address; import com.redhat.rhn.domain.user.User; import com.redhat.rhn.domain.user.UserFactory; import com.redhat.rhn.frontend.events.NewUserEvent; /** * A command to create or edit users * @version $Rev$ */ public class CreateUserCommand { private User user; private Org org; private Address addr; private boolean makeOrgAdmin; private boolean makeSatAdmin; private Set<Role> temporaryRoles = new HashSet<Role>(); private Set<ServerGroup> serverGroups = new HashSet<ServerGroup>(); private List<ValidatorError> errors; private List<ValidatorError> passwordErrors; /* * Why we need passwordErrors. * The setPassword method on UserImpl encrypts passwords (If configured to do so). * If we don't validate the password before we set it, we will only ever be able * to get to the encrypted password back which will always be valid. */ /** * Constructor... creates an empty user object */ public CreateUserCommand() { user = UserFactory.createUser(); } /** * Validates the user object. Checks login and email attributes. * @return an Object array of ValidatorErrors. */ public ValidatorError[] validate() { errors = new ArrayList(); //clear validation errors if (passwordErrors != null) { errors.addAll(passwordErrors); //add any password validation errors } validateEmail(); validateLogin(); validatePrefix(); return errors.toArray(new ValidatorError[0]); } /** * Publishes a new user event to the message queue * @param accountCreator The person whom created the user * @param admins Org admins * @param domain The servername for this server (used to build url). * @param password The user's password. It must be explicitly passed in * seperate from the user because the password associated with the User object * might be encrypted, thus useless for this method */ public void publishNewUserEvent(User accountCreator, List admins, String domain, String password) { NewUserEvent userevt = new NewUserEvent(); userevt.setAccountCreator(accountCreator); userevt.setAdmins(admins); ResetPassword rp = ResetPasswordFactory.createNewEntryFor(this.user); String link = ResetPasswordFactory.generateLink(rp); userevt.setLink(link); userevt.setDomain(domain); userevt.setUser(this.user); MessageQueue.publish(userevt); } /** * Saves the new user object (along with the Org and Address). */ public void storeNewUser() { /* * Ok, this is a bloody ugly hack, but since the pl/sql used by * UserFactory.saveNewUser() is shared and the use pam authentication seems to be * the only thing affected by it, we are going to work around it here. * * The Create_New_User function in the db creates an entry in rhnUserInfo with the * default values. This means that anything stored in User.personalInfo gets * reset. We need to be able to update the use_pam_authentication column in this * table, so save the value, save the user, then set the attribute back to what it * was before we called UserManager.createUser(). This will ensure that what was * selected on the form is what gets stored with the user (since hibernate will * then be taking care of the db values). * * We really need to a) divorce ourselves from www and oracle apps b) get rid of the * application/business logic stored in pl/sql functions in the db and c) clean up * the dirty hacks like this that are throughout our code. We shouldn't have to work * around the db in our code. */ boolean usePam = user.getUsePamAuthentication(); //save what we got from the form user = UserManager.createUser(user, org, addr); if (this.makeOrgAdmin) { user.addPermanentRole(RoleFactory.ORG_ADMIN); } if (this.makeSatAdmin) { user.addPermanentRole(RoleFactory.SAT_ADMIN); } user.setUsePamAuthentication(usePam); //set it back UserManager.resetTemporaryRoles(user, temporaryRoles); if (org.getOrgConfig().isCreateDefaultSg()) { ManagedServerGroup sg = ServerGroupFactory.lookupByNameAndOrg( user.getLogin(), user.getOrg()); if (sg == null) { // create default system group for the user sg = ServerGroupFactory.create(user.getLogin(), user.getLogin() + " default system group", user.getOrg()); } UserManager.grantServerGroupPermission(user, sg.getId()); user.setDefaultSystemGroupIds(new HashSet<Long>(Arrays.asList(sg.getId()))); } // assign server groups permissions for (ServerGroup sg : serverGroups) { UserManager.grantServerGroupPermission(user, sg.getId()); } UserManager.storeUser(user); //save the user via hibernate } /** * Private helper method to validate the user's email address. Puts errors into the * errors list. */ private void validateEmail() { // Make sure user and email are not null if (user == null || user.getEmail() == null) { errors.add(new ValidatorError("error.addr_invalid", "null")); return; } // Make email is not over the max length if (user.getEmail().length() > UserDefaults.get().getMaxEmailLength()) { errors.add(new ValidatorError("error.maxemail")); return; } // Make sure set email is valid try { new InternetAddress(user.getEmail()).validate(); } catch (AddressException e) { errors.add(new ValidatorError("error.addr_invalid", user.getEmail())); } } /** * Private helper method to validate the user's login. Puts errors into the errors List. */ private void validateLogin() { int max = UserDefaults.get().getMaxUserLength(); if (user == null) { errors.add(new ValidatorError("error.minlogin", "null")); return; } String login = StringUtils.defaultString(user.getLogin()); /* * Check for login minimum length * Since login.getBytes().length >= login.length(), just check for min length */ if (login.length() < UserDefaults.get().getMinUserLength()) { errors.add(new ValidatorError("error.minlogin", UserDefaults.get().getMinUserLength())); return; } /* * Check for login maximum length * Since we are allowing utf8 input, but not supporting it in the db, we need to * check the length of the bytes here as well. * TODO: Do better error checking here once the db and code is fully localized and * we are supporting it on logins */ else if (login.length() > max || login.getBytes().length > max) { errors.add(new ValidatorError("error.maxlogin", login)); return; } // validate using the webui code. I must say that RequiredConstraint // is a stupid place for hiding username validation. But nevertheless // I will continue to propogate this crap until we want to revisit // validation. ParsedConstraint rc = new ParsedConstraint("CreateUserCommand"); if (!rc.isValidUserName(login)) { errors.add(new ValidatorError("errors.username", login)); return; } // Make sure desiredLogin isn't taken already try { UserFactory.lookupByLogin(login); errors.add(new ValidatorError("error.login_already_taken", login)); } catch (LookupException le) { // User is not taken // so we can coolly add him. } } /** * Private helper method to validate the user's prefix. Puts errors into the * errors list. */ private void validatePrefix() { String prefix = user.getPrefix(); if (prefix != null) { // Make sure whether prefix is valid, if it is set SortedSet validPrefixes = LocalizationService.getInstance().availablePrefixes(); if (prefix.isEmpty()) { user.setPrefix(" "); } if (!validPrefixes.contains(user.getPrefix())) { errors.add(new ValidatorError("error.user_invalid_prefix", prefix, validPrefixes.toString())); } } } /** * Private helper method to validate the password. This happens when the setPassword * method of this class is called. Puts errors into the passwordErrors list. * @param passwordIn The password to check. */ private void validatePassword(String passwordIn) { if (passwordIn == null || passwordIn.length() < UserDefaults.get().getMinPasswordLength()) { passwordErrors.add(new ValidatorError("error.minpassword", UserDefaults.get().getMinPasswordLength())); } // Newlines and tab characters can slip through the API much easier than the UI: if (Pattern.compile("[\\t\\n]").matcher(passwordIn).find()) { passwordErrors.add(new ValidatorError("error.invalidpasswordcharacters")); } else if (passwordIn.length() > UserDefaults.get().getMaxPasswordLength()) { passwordErrors.add(new ValidatorError("error.maxpassword", user.getPassword())); } } /***** User accessors *****/ /** * @param companyIn The company sent */ public void setCompany(String companyIn) { user.setCompany(companyIn); } /** * @param emailIn The email to set */ public void setEmail(String emailIn) { user.setEmail(emailIn); } /** * @param loginIn The login to set */ public void setLogin(String loginIn) { user.setLogin(loginIn); } /** * @param passwordIn The raw password to set */ public void setRawPassword(String passwordIn) { passwordErrors = new ArrayList(); //init password errors list user.setRawPassword(passwordIn); } /** * @param passwordIn The password to set */ public void setPassword(String passwordIn) { passwordErrors = new ArrayList(); //init password errors list validatePassword(passwordIn); user.setPassword(passwordIn); } /** * @param prefixIn The prefix to set */ public void setPrefix(String prefixIn) { user.setPrefix(prefixIn); } /** * @param firstNamesIn The first names to set */ public void setFirstNames(String firstNamesIn) { user.setFirstNames(firstNamesIn); } /** * @param lastNameIn The last name to set */ public void setLastName(String lastNameIn) { user.setLastName(lastNameIn); } /** * @param phoneIn The phone to set */ public void setPhone(String phoneIn) { user.setPhone(phoneIn); } /** * @param faxIn The fax to set */ public void setFax(String faxIn) { user.setFax(faxIn); } /** * @param val Should this user use pam authentication? */ public void setUsePamAuthentication(boolean val) { user.setUsePamAuthentication(val); } /** * Setter for the org object. * @param orgIn the org to set */ public void setOrg(Org orgIn) { this.org = orgIn; } /** * Sets the Address object * @param addrIn The address to set */ public void setAddress(Address addrIn) { this.addr = addrIn; } /** * @param val Should this user be an org admin? */ public void setMakeOrgAdmin(boolean val) { this.makeOrgAdmin = val; } /** * @return The user object */ public User getUser() { return this.user; } /** * * @param val Should this user be a sat admin? */ public void setMakeSatAdmin(boolean val) { this.makeSatAdmin = val; } /** * @return Returns the roles. */ public Set<Role> getTemporaryRoles() { return temporaryRoles; } /** * @param rolesIn The roles to set. */ public void setTemporaryRoles(Set<Role> rolesIn) { temporaryRoles = rolesIn; } /** * @return Returns the Server Groups. */ public Set<ServerGroup> getServerGroups() { return serverGroups; } /** * @param sgsIn The Server Groups to set. */ public void setServerGroups(Set<ServerGroup> sgsIn) { serverGroups = sgsIn; } }