/**
* Copyright (c) 2009--2016 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.domain.user;
import java.sql.Types;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.hibernate.Query;
import org.hibernate.Session;
import com.redhat.rhn.common.db.datasource.CallableMode;
import com.redhat.rhn.common.db.datasource.DataResult;
import com.redhat.rhn.common.db.datasource.ModeFactory;
import com.redhat.rhn.common.db.datasource.SelectMode;
import com.redhat.rhn.common.hibernate.HibernateFactory;
import com.redhat.rhn.common.hibernate.LookupException;
import com.redhat.rhn.common.localization.LocalizationService;
import com.redhat.rhn.domain.org.Org;
import com.redhat.rhn.domain.org.OrgFactory;
import com.redhat.rhn.domain.role.Role;
import com.redhat.rhn.domain.role.RoleFactory;
import com.redhat.rhn.domain.server.Server;
import com.redhat.rhn.domain.user.legacy.UserImpl;
import com.redhat.rhn.manager.session.SessionManager;
/**
* UserFactory - the singleton class used to fetch and store
* com.redhat.rhn.domain.user.User objects from the
* database.
* @version $Rev$
*/
public class UserFactory extends HibernateFactory {
private static final String USER_ID = "user_id";
private static final String LOGIN_UC = "loginUc";
private static final UserFactory SINGLETON = new UserFactory();
protected static final Logger LOG = Logger.getLogger(UserFactory.class);
private static List timeZoneList;
private static final Role[] IMPLIEDROLESARRAY = { RoleFactory.CHANNEL_ADMIN,
RoleFactory.CONFIG_ADMIN, RoleFactory.SYSTEM_GROUP_ADMIN,
RoleFactory.ACTIVATION_KEY_ADMIN };
/** List of Role objects that are applied if you are an Org_admin */
public static final List <Role> IMPLIEDROLES = Arrays.asList(IMPLIEDROLESARRAY);
public static final State ENABLED = loadState("enabled");
public static final State DISABLED = loadState("disabled");
protected UserFactory() {
super();
}
/**
* Helper method to load a user state by label. Should only be used to init the
* static member vars of this class.
* @param label The label of the state to lookup
* @return Returns the appropriate state (or null).
*/
private static State loadState(String label) {
Session session = HibernateFactory.getSession();
State state = (State) session.getNamedQuery("UserState.lookupByLabel")
.setParameter("label", label)
//Retrieve from cache if there
.setCacheable(true)
.uniqueResult();
return state;
}
/**
* Returns the responsible user (first org admin) of the org.
* @param orgId Org id
* @param r Role to search for (ORG_ADMIN)
* @return the responsible user (first org admin) of the org.
*/
public static User findResponsibleUser(Long orgId, Role r) {
Session session = HibernateFactory.getSession();
Iterator itr = session.getNamedQuery("User.findResponsibleUser")
.setParameter("org_id", orgId)
.setParameter("type_id", r.getId())
//Retrieve from cache if there
.list().iterator();
if (itr.hasNext()) {
// only care about the first one
Object[] row = (Object[])itr.next();
User u = createUser();
u.setId((Long) row[0]);
u.setLogin((String)row[1]);
return u;
}
return null;
}
/**
* Returns user (first org admin) of the org.
* @param orgIn Org id
* @return the user (first org admin) of the org.
*/
public static User findRandomOrgAdmin(Org orgIn) {
Role r = RoleFactory.ORG_ADMIN;
Session session = HibernateFactory.getSession();
Iterator<Long> itr = session.getNamedQuery("User.findRandomOrgAdmin")
.setParameter("org_id", orgIn.getId())
.setParameter("type_id", r.getId())
//Retrieve from cache if there
.list().iterator();
if (itr.hasNext()) {
// only care about the first one
return UserFactory.lookupById(itr.next());
}
return null;
}
/** Get the Logger for the derived class so log messages
* show up on the correct class
*/
@Override
protected Logger getLogger() {
return LOG;
}
/**
* Create a new user from scratch
* @return the user created
*/
public static User createUser() {
return new UserImpl();
}
/**
* Create a new address instance.
* @return Address the address created
*/
public static Address createAddress() {
AddressImpl addr = new AddressImpl();
addr.setPrivType(Address.TYPE_MARKETING);
return addr;
}
/**
* Save this instance of address
* @param addr the address to save.
*/
public static void saveAddress(Address addr) {
getInstance().saveObject(addr);
}
/**
* Lookup a user by their id
* @param id the id to search for
* @return the user found
*/
public static User lookupById(Long id) {
Session session = HibernateFactory.getSession();
User u = (User)session.get(UserImpl.class, id);
return u;
}
/**
* Get users by their ids.
*
* If the incoming list has more than 1000 entries, we'll chop it up and run several
* queries, re-assembling the results in application code. This is to accommodate
* Oracle's ORA-01795 error "maximum number of expressions in a list is 1000".
*
* @param ids the ids to lookup for
* @return the list of com.redhat.rhn.domain.User objects found
*/
public static List<User> lookupByIds(Collection<Long> ids) {
if (ids.size() < 1000) {
return realLookupByIds(ids);
}
List<User> results = new LinkedList<User>();
List<Long> blockOfIds = new LinkedList<Long>();
for (Long uid : ids) {
blockOfIds.add(uid);
if (blockOfIds.size() == 999) {
results.addAll(realLookupByIds(blockOfIds));
blockOfIds = new LinkedList<Long>();
}
}
// Deal with the remainder:
if (blockOfIds.size() > 0) {
results.addAll(realLookupByIds(blockOfIds));
}
return results;
}
private static List<User> realLookupByIds(Collection<Long> ids) {
Session session = HibernateFactory.getSession();
Query query = session.getNamedQuery("User.findByIds")
.setParameterList("userIds", ids);
return query.list();
}
/**
* Lookup a user by their id, assuming that they are in the same Org as
* the user doing the search.
* @param user the user doing the search
* @param id the id to search for
* @return the user found
*/
public static User lookupById(User user, Long id) {
Map<String, Object> params = new HashMap<String, Object>();
params.put("uid", id);
params.put("orgId", user.getOrg().getId());
User returnedUser = (User)getInstance().lookupObjectByNamedQuery(
"User.findByIdandOrgId", params);
if (returnedUser == null || !user.getOrg().equals(returnedUser.getOrg())) {
LocalizationService ls = LocalizationService.getInstance();
LookupException e = new LookupException("Could not find user " + id);
e.setLocalizedTitle(ls.getMessage("lookup.jsp.title.user"));
e.setLocalizedReason1(ls.getMessage("lookup.jsp.reason1.user"));
e.setLocalizedReason2(ls.getMessage("lookup.jsp.reason2.user"));
throw e;
}
return returnedUser;
}
/**
* Lookup a user by their login
* @param login the login to search by
* @return the User found
*/
public static User lookupByLogin(String login) {
Map<String, Object> params = new HashMap<String, Object>();
params.put(LOGIN_UC, login.toUpperCase());
User user = (User)getInstance()
.lookupObjectByNamedQuery("User.findByLogin", params);
if (user == null) {
LocalizationService ls = LocalizationService.getInstance();
LookupException e = new LookupException("Could not find user " + login);
e.setLocalizedTitle(ls.getMessage("lookup.jsp.title.user"));
e.setLocalizedReason1(ls.getMessage("lookup.jsp.reason1.user"));
e.setLocalizedReason2(ls.getMessage("lookup.jsp.reason2.user"));
throw e;
}
return user;
}
/**
* Lookup a user by their login
* @param user the user doing the search
* @param login the login to search by
* @return the User found
*/
public static User lookupByLogin(User user, String login) {
Map<String, Object> params = new HashMap<String, Object>();
params.put(LOGIN_UC, login.toUpperCase());
params.put("orgId", user.getOrg().getId());
User returnedUser = (User)getInstance().lookupObjectByNamedQuery(
"User.findByLoginAndOrgId", params);
if (returnedUser == null) {
LocalizationService ls = LocalizationService.getInstance();
LookupException e = new LookupException("Could not find user " + login);
e.setLocalizedTitle(ls.getMessage("lookup.jsp.title.user"));
e.setLocalizedReason1(ls.getMessage("lookup.jsp.reason1.user"));
e.setLocalizedReason2(ls.getMessage("lookup.jsp.reason2.user"));
throw e;
}
return returnedUser;
}
/**
* Gets a long value from the dataresult
* @param dr The DataResult object containing the output
* @param key The key for the output value
* @return the long value
*/
private static long getLongValue(DataResult dr, String key) {
Long id = (Long)((Map)dr.get(0)).get(key);
return id.longValue();
}
/**
* Insert a new user. Invalid to call this when updating a user
* TODO: mmccune fill out the other fields in the user object.
* @param usr The object we are commiting.
* @param addr The address to add to the User
* @param orgId Org this new user is a member of
* @return User The freshly commited user.
*/
public static User saveNewUser(User usr, Address addr, Long orgId) {
return getInstance().addNewUser(usr, addr, orgId);
}
/**
* Convenience method to determine whether a user is disabled
* or not
* @param user to check on...
* @return Returns true if the user is disabled
*/
public static boolean isDisabled(User user) {
Map<String, Object> params = new HashMap<String, Object>();
params.put("user", user);
List <StateChange> changes = getInstance().
listObjectsByNamedQuery("StateChanges.lookupByUserId", params);
return changes != null && !changes.isEmpty() &&
DISABLED.equals(changes.get(0).getState());
}
/**
* Insert a new user. Invalid to call this when updating a user
* TODO: mmccune fill out the other fields in the user object.
* @param usr The object we are commiting.
* @param addr The address to add to the User
* @param orgId Org this new user is a member of
* @return User The freshly commited user.
*/
protected User addNewUser(User usr, Address addr, Long orgId) {
LOG.debug("Starting addNewUser");
if (addr != null) {
usr.setAddress1(addr.getAddress1());
usr.setAddress2(addr.getAddress2());
usr.setCity(addr.getCity());
usr.setCountry(addr.getCountry());
usr.setFax(addr.getFax());
usr.setIsPoBox(addr.getIsPoBox());
usr.setPhone(addr.getPhone());
usr.setState(addr.getState());
usr.setZip(addr.getZip());
}
// save the user
CallableMode m = ModeFactory.getCallableMode("User_queries", "create_new_user");
Map<String, Object> inParams = new HashMap<String, Object>();
Map<String, Integer> outParams = new HashMap<String, Integer>();
// Can't add the orgId to the object until the User has been
// successfully added to the DB. Doing so will mean that if
// there are problems, the user won't be rolled back properly.
inParams.put("orgId", orgId);
inParams.put("login", usr.getLogin());
inParams.put("password", usr.getPassword());
inParams.put("contactId", null);
inParams.put("prefix", StringUtils.defaultString(usr.getPrefix(), " "));
inParams.put("fname", StringUtils.defaultString(usr.getFirstNames(), null));
inParams.put("lname", StringUtils.defaultString(usr.getLastName(), null));
inParams.put("genqual", null);
inParams.put("parentCompany", StringUtils.defaultIfEmpty(usr.getCompany(), null));
inParams.put("company", StringUtils.defaultIfEmpty(usr.getCompany(), null));
inParams.put("title", StringUtils.defaultIfEmpty(usr.getTitle(), null));
inParams.put("phone", StringUtils.defaultIfEmpty(usr.getPhone(), null));
inParams.put("fax", StringUtils.defaultIfEmpty(usr.getFax(), null));
inParams.put("email", StringUtils.defaultIfEmpty(usr.getEmail(), null));
inParams.put("pin", new Integer(0));
inParams.put("fnameOl", " ");
inParams.put("lnameOl", " ");
inParams.put("addr1", StringUtils.defaultIfEmpty(usr.getAddress1(), null));
inParams.put("addr2", StringUtils.defaultIfEmpty(usr.getAddress2(), null));
inParams.put("addr3", " ");
inParams.put("city", StringUtils.defaultIfEmpty(usr.getCity(), null));
inParams.put("state", StringUtils.defaultIfEmpty(usr.getState(), null));
inParams.put("zip", StringUtils.defaultIfEmpty(usr.getZip(), null));
inParams.put("country", StringUtils.defaultIfEmpty(usr.getCountry(), null));
inParams.put("altFnames", null);
inParams.put("altLnames", null);
inParams.put("contCall", "N");
inParams.put("contMail", "N");
inParams.put("contFax", "N");
inParams.put("contEmail", "N");
outParams.put("userId", new Integer(Types.NUMERIC));
Map<String, Object> result = m.execute(inParams, outParams);
Org org = OrgFactory.lookupById(orgId);
if (org != null) {
usr.setOrg(org);
}
long userId = ((Long) result.get("userId")).longValue();
// We need to lookup the User to make sure that the Address in the
// User object has an Id and that the User has an org_id.
User retval = lookupById(new Long(userId));
saveObject(retval);
return retval;
}
/**
* Insert or Update a user
* @param user The object we are committing.
*/
public static void save(User user) {
getInstance().saveUser(user);
}
/**
* Insert or Update a user
* @param user The object we are committing.
*/
protected void saveUser(User user) {
LOG.debug("*********STARTING SAVE USER*********\n\n\n\n\n\n\n\n");
if (user.getId() == null) {
// New org, gotta use the stored procedure.
throw new IllegalArgumentException("Only use commit for" +
" existing users");
}
saveObject(user);
syncUserPerms(user);
}
/**
* Syncs the user permissions with server group info..
* @param usr the user to sync
*/
protected void syncUserPerms(User usr) {
// Here we are replacing the functionality in add/remove_from_usergroup
// and update_perms_for_user stored procedures
UserImpl uimpl = (UserImpl) usr;
boolean orgAdminChanged = false;
Boolean wasOrgAdmin = uimpl.wasOrgAdmin();
if (wasOrgAdmin != null) {
orgAdminChanged =
usr.hasRole(RoleFactory.ORG_ADMIN) != wasOrgAdmin.booleanValue();
}
if (orgAdminChanged) {
syncServerGroupPerms(usr);
}
uimpl.resetWasOrgAdmin();
}
/**
* Syncs the user permissions with server group info..
* @param usr User to be synchronized.
*/
public void syncServerGroupPerms(User usr) {
CallableMode m = ModeFactory.getCallableMode("User_queries",
"update_perms_for_user");
Map<String, Object> inParams = new HashMap<String, Object>();
inParams.put(USER_ID, usr.getId());
m.execute(inParams, new HashMap<String, Integer>());
}
/**
* Get the timezone by ID
* @param id ID number for timezone
* @return TimeZone the requested time zone
*/
public static RhnTimeZone getTimeZone(int id) {
Session session = HibernateFactory.getSession();
return (RhnTimeZone) session.getNamedQuery("RhnTimeZone.loadTimeZoneById")
.setInteger("tid", id)
//Retrieve from cache if there
.setCacheable(true)
.uniqueResult();
}
/**
* Get the timezone by olson name
* @param olsonName olson name for timezone
* @return TimeZone the requested time zone
*/
public static RhnTimeZone getTimeZone(String olsonName) {
Session session = HibernateFactory.getSession();
return (RhnTimeZone) session
.getNamedQuery("RhnTimeZone.loadTimeZoneByOlsonName")
.setString("ton", olsonName)
//Retrieve from cache if there
.setCacheable(true)
.uniqueResult();
}
/**
* Gets the default time zone
* @return US Eastern Time Zone
*/
public static RhnTimeZone getDefaultTimeZone() {
RhnTimeZone sysDefault = getTimeZone(TimeZone.getDefault().getID());
if (sysDefault != null) {
return sysDefault;
}
Session session = HibernateFactory.getSession();
List<RhnTimeZone> allTimeZones =
session.getNamedQuery("RhnTimeZone.loadAll").list();
for (RhnTimeZone tz : allTimeZones) {
if (TimeZone.getDefault().getRawOffset() == TimeZone.getTimeZone(
tz.getOlsonName()).getRawOffset()) {
return tz;
}
}
// This should not happen unless the timezone table is incomplete
return getTimeZone("America/New_York");
}
/**
* Get all timezones in apropriate order
* @return List a list of timezones
*/
public static List lookupAllTimeZones() {
//timeZoneList is manually cached because instance variable is properly sorted
//whereas the database is not.
if (timeZoneList == null) {
List timeZones = null; //temporary holding place until sorted
Session session = HibernateFactory.getSession();
timeZones = session.getNamedQuery("RhnTimeZone.loadAll").list();
//Now sort the timezones, GMT+0000 at top, then East-to-West
if (timeZones != null) {
Collections.sort(timeZones, new Comparator() {
@Override
public int compare(Object o1, Object o2) {
RhnTimeZone t1 = (RhnTimeZone) o1;
RhnTimeZone t2 = (RhnTimeZone) o2;
Integer offSet1 = t1.getTimeZone().getRawOffset();
Integer offSet2 = t2.getTimeZone().getRawOffset();
// Make sure GMT+0000 is first
if (offSet1 == 0 && offSet2 != 0) {
// first one GMT
return -1;
}
if (offSet1 != 0 && offSet2 == 0) {
// second one GMT
return 1;
}
// Make sure negative offsets 'win' over positive
if (offSet1 < 0 && offSet2 > 0) {
return -1;
}
if (offSet1 > 0 && offSet2 < 0) {
return 1;
}
if (offSet2.equals(offSet1)) {
return t2.getOlsonName().compareTo(t1.getOlsonName());
}
return offSet2.compareTo(offSet1);
}
});
}
timeZoneList = timeZones;
}
return timeZoneList;
}
/**
* Disable a user
* @param userToDisable The user to disable
* @param disabledBy The user committing the act
*/
public void disable(User userToDisable, User disabledBy) {
createStateChange(userToDisable, disabledBy, DISABLED);
SessionManager.purgeUserSessions(userToDisable);
}
/**
* Enable a user
* @param userToEnable The user to enable
* @param enabledBy The user committing the act
*/
public void enable(User userToEnable, User enabledBy) {
createStateChange(userToEnable, enabledBy, ENABLED);
}
/**
* Helper method to do the work of disabling/enabling a user
* @param victim The user to change
* @param changer The user doing the changing
* @param newState The state to change the user to
*/
private void createStateChange(User victim, User changer, State newState) {
//Create state change
StateChange change = new StateChange();
change.setUser(victim);
change.setChangedBy(changer);
change.setState(newState);
//Add change to victim
victim.addChange(change);
save(victim);
}
/**
* Method to determine whether a satellite has any users. Returns
* true if satellite has one or more users, false otherwise. Also
* returns false if this method is called on a hosted installation.
* @return true if satellite has one or more users, false otherwise.
*/
public static boolean satelliteHasUsers() {
SelectMode m = ModeFactory.getMode("User_queries", "user_count");
DataResult dr = m.execute(new HashMap<String, Object>());
Map row = (Map) dr.get(0);
Long count = (Long) row.get("user_count");
return (count.longValue() > 0);
}
/**
*
* @return an instance of user Factory
*/
public static UserFactory getInstance() {
return SINGLETON;
}
/**
* Looks up the UserServerPreference corresponding to the given
* user, server, and preference label
* @param user user who the preference corresponds to
* @param server server that preference corresponds to
* @param name preference label we are looking for
* @return UserServerPreference that corresponds to the parameters
*/
public UserServerPreference lookupServerPreferenceByUserServerAndName(User user,
Server server,
String name) {
UserServerPreferenceId id = new UserServerPreferenceId(user, server, name);
Session session = HibernateFactory.getSession();
return (UserServerPreference) session.get(UserServerPreference.class, id);
}
/**
* Sets a UserServerPreference to true or false
* @param user User whose preference will be set
* @param server Server we are setting the perference on
* @param preferenceName the name of the preference
* @see com.redhat.rhn.domain.user.UserServerPreferenceId
* @param value true if the preference should be true, false otherwise
*/
public void setUserServerPreferenceValue(User user,
Server server,
String preferenceName,
boolean value) {
Session session = HibernateFactory.getSession();
UserServerPreferenceId id = new UserServerPreferenceId(user,
server,
preferenceName);
UserServerPreference usp = (UserServerPreference)
session.get(UserServerPreference.class, id);
/* Here, we delete the preference's entry if it should be true.
* We would hopefully be ok setting the value to "1," but I'm emulating
* the Perl side here just to be safe
*/
if (value) {
if (usp != null) {
session.delete(usp);
}
}
else {
if (usp == null) {
id = new UserServerPreferenceId(user, server, preferenceName);
usp = new UserServerPreference();
usp.setId(id);
usp.setValue("0");
session.save(usp);
}
}
}
/**
* Return a list of all Users who has the given email.
*
* @param email String to find users for.
* @return list of users.
*/
public static List<User> lookupByEmail(String email) {
Session session = HibernateFactory.getSession();
Query query = session.getNamedQuery("User.findByEmail")
.setParameter("userEmail", email);
return query.list();
}
/**
* Return a list of all User's who are in the given org.
*
* @param inOrg Org to find users for.
* @return list of users.
*/
public List<User> findAllUsers(Org inOrg) {
Session session = HibernateFactory.getSession();
Map<String, Object> params = new HashMap<String, Object>();
params.put("org_id", inOrg.getId());
return listObjectsByNamedQuery("User.findAllUsersByOrg", params);
}
/**
* Return a list of all User's who are org admins in the given org.
*
* @param inOrg Org to find administrators for.
* @return list of users.
*/
public List<User> findAllOrgAdmins(Org inOrg) {
Session session = HibernateFactory.getSession();
Map<String, Object> params = new HashMap<String, Object>();
params.put("org_id", inOrg.getId());
return listObjectsByNamedQuery("User.findAllOrgAdmins", params);
}
/**
* @param userId the user id
*/
public static void deleteUser(Long userId) {
CallableMode m = ModeFactory.getCallableMode("User_queries",
"delete_user");
Map<String, Object> inParams = new HashMap<String, Object>();
Map<String, Integer> outParams = new HashMap<String, Integer>();
inParams.put(USER_ID, userId);
m.execute(inParams, outParams);
}
}