/**
* The contents of this file are subject to the OpenMRS Public License
* Version 1.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://license.openmrs.org
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
* License for the specific language governing rights and limitations
* under the License.
*
* Copyright (C) OpenMRS, LLC. All Rights Reserved.
*/
package org.openmrs.api.db.hibernate;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Vector;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Expression;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Restrictions;
import org.openmrs.Person;
import org.openmrs.Privilege;
import org.openmrs.Role;
import org.openmrs.User;
import org.openmrs.api.context.Context;
import org.openmrs.api.db.DAOException;
import org.openmrs.api.db.LoginCredential;
import org.openmrs.api.db.UserDAO;
import org.openmrs.patient.impl.LuhnIdentifierValidator;
import org.openmrs.util.Security;
import org.openmrs.util.UserByNameComparator;
/**
* Hibernate specific database methods for the UserService
*
* @see org.openmrs.api.context.Context
* @see org.openmrs.api.db.UserDAO
* @see org.openmrs.api.UserService
*/
public class HibernateUserDAO implements UserDAO {
protected final Log log = LogFactory.getLog(getClass());
/**
* Hibernate session factory
*/
private SessionFactory sessionFactory;
/**
* Set session factory
*
* @param sessionFactory
*/
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
/**
* @see org.openmrs.api.UserService#saveUser(org.openmrs.User, java.lang.String)
*/
public User saveUser(User user, String password) {
// only change the user's password when creating a new user
boolean isNewUser = user.getUserId() == null;
sessionFactory.getCurrentSession().saveOrUpdate(user);
if (isNewUser && password != null) {
//update the new user with the password
String salt = Security.getRandomToken();
String hashedPassword = Security.encodeString(password + salt);
updateUserPassword(hashedPassword, salt, Context.getAuthenticatedUser().getUserId(), new Date(), user
.getUserId());
}
return user;
}
/**
* @see org.openmrs.api.UserService#getUserByUsername(java.lang.String)
*/
@SuppressWarnings("unchecked")
public User getUserByUsername(String username) {
Query query = sessionFactory.getCurrentSession().createQuery(
"from User u where u.retired = 0 and (u.username = ? or u.systemId = ?)");
query.setString(0, username);
query.setString(1, username);
List<User> users = query.list();
if (users == null || users.size() == 0) {
log.warn("request for username '" + username + "' not found");
return null;
}
return users.get(0);
}
/**
* @see org.openmrs.api.UserService#hasDuplicateUsername(org.openmrs.User)
*/
public boolean hasDuplicateUsername(String username, String systemId, Integer userId) {
if (username == null || username.length() == 0)
username = "-";
if (systemId == null || systemId.length() == 0)
systemId = "-";
if (userId == null)
userId = new Integer(-1);
String usernameWithCheckDigit = username;
try {
//Hardcoding in Luhn since past user IDs used this validator.
usernameWithCheckDigit = new LuhnIdentifierValidator().getValidIdentifier(username);
}
catch (Exception e) {}
Query query = sessionFactory
.getCurrentSession()
.createQuery(
"select count(*) from User u where (u.username = :uname1 or u.systemId = :uname2 or u.username = :sysid1 or u.systemId = :sysid2 or u.systemId = :uname3) and u.userId <> :uid");
query.setString("uname1", username);
query.setString("uname2", username);
query.setString("sysid1", systemId);
query.setString("sysid2", systemId);
query.setString("uname3", usernameWithCheckDigit);
query.setInteger("uid", userId);
Long count = (Long) query.uniqueResult();
log.debug("# users found: " + count);
return (count != null && count != 0);
}
/**
* @see org.openmrs.api.UserService#getUser(java.lang.Integer)
*/
public User getUser(Integer userId) {
User user = (User) sessionFactory.getCurrentSession().get(User.class, userId);
return user;
}
/**
* @see org.openmrs.api.UserService#getAllUsers()
*/
@SuppressWarnings("unchecked")
public List<User> getAllUsers() throws DAOException {
return sessionFactory.getCurrentSession().createQuery("from User u order by u.userId").list();
}
/**
* @see org.openmrs.api.UserService#deleteUser(org.openmrs.User)
*/
public void deleteUser(User user) {
sessionFactory.getCurrentSession().delete(user);
}
/**
* @see org.openmrs.api.UserService#getUsersByRole(org.openmrs.Role)
*/
@SuppressWarnings("unchecked")
public List<User> getUsersByRole(Role role) throws DAOException {
List<User> users = sessionFactory.getCurrentSession().createCriteria(User.class, "u").createCriteria("roles", "r")
.add(Expression.like("r.role", role.getRole())).addOrder(Order.asc("u.username")).list();
return users;
}
/**
* @see org.openmrs.api.UserService#getAllPrivileges()
*/
@SuppressWarnings("unchecked")
public List<Privilege> getAllPrivileges() throws DAOException {
return sessionFactory.getCurrentSession().createQuery("from Privilege p order by p.privilege").list();
}
/**
* @see org.openmrs.api.UserService#getPrivilege(String)
*/
public Privilege getPrivilege(String p) throws DAOException {
return (Privilege) sessionFactory.getCurrentSession().get(Privilege.class, p);
}
/**
* @see org.openmrs.api.db.UserDAO#deletePrivilege(org.openmrs.Privilege)
*/
public void deletePrivilege(Privilege privilege) throws DAOException {
sessionFactory.getCurrentSession().delete(privilege);
}
/**
* @see org.openmrs.api.db.UserDAO#savePrivilege(org.openmrs.Privilege)
*/
public Privilege savePrivilege(Privilege privilege) throws DAOException {
sessionFactory.getCurrentSession().saveOrUpdate(privilege);
return privilege;
}
/**
* @see org.openmrs.api.UserService#purgeRole(org.openmrs.Role)
*/
public void deleteRole(Role role) throws DAOException {
sessionFactory.getCurrentSession().delete(role);
}
/**
* @see org.openmrs.api.UserService#saveRole(org.openmrs.Role)
*/
public Role saveRole(Role role) throws DAOException {
sessionFactory.getCurrentSession().saveOrUpdate(role);
return role;
}
/**
* @see org.openmrs.api.UserService#getAllRoles()
*/
@SuppressWarnings("unchecked")
public List<Role> getAllRoles() throws DAOException {
return sessionFactory.getCurrentSession().createQuery("from Role r order by r.role").list();
}
/**
* @see org.openmrs.api.UserService#getRole(String)
*/
public Role getRole(String r) throws DAOException {
return (Role) sessionFactory.getCurrentSession().get(Role.class, r);
}
/**
* @see org.openmrs.api.db.UserDAO#changePassword(org.openmrs.User, java.lang.String)
*/
public void changePassword(User u, String pw) throws DAOException {
User authUser = Context.getAuthenticatedUser();
if (authUser == null)
authUser = u;
log.debug("updating password");
//update the user with the new password
String salt = Security.getRandomToken();
String newHashedPassword = Security.encodeString(pw + salt);
updateUserPassword(newHashedPassword, salt, authUser.getUserId(), new Date(), u.getUserId());
}
/**
* @see org.openmrs.api.db.UserDAO#changeHashedPassword(User, String, String)
*/
public void changeHashedPassword(User user, String hashedPassword, String salt) throws DAOException {
User authUser = Context.getAuthenticatedUser();
updateUserPassword(hashedPassword, salt, authUser.getUserId(), new Date(), user.getUserId());
}
/**
* @param newHashedPassword
* @param salt
* @param userId
* @param date
* @param userId2
*/
private void updateUserPassword(String newHashedPassword, String salt, Integer changedBy, Date dateChanged,
Integer userIdToChange) {
User changeForUser = getUser(userIdToChange);
if (changeForUser == null)
throw new DAOException("Couldn't find user to set password for userId=" + userIdToChange);
User changedByUser = getUser(changedBy);
LoginCredential credentials = new LoginCredential();
credentials.setUserId(userIdToChange);
credentials.setHashedPassword(newHashedPassword);
credentials.setSalt(salt);
credentials.setChangedBy(changedByUser);
credentials.setDateChanged(dateChanged);
credentials.setUuid(changeForUser.getUuid());
sessionFactory.getCurrentSession().merge(credentials);
}
/**
* @see org.openmrs.api.UserService#changePassword(java.lang.String, java.lang.String)
*/
public void changePassword(String pw, String pw2) throws DAOException {
User u = Context.getAuthenticatedUser();
LoginCredential credentials = getLoginCredential(u);
if (!credentials.checkPassword(pw)) {
log.error("Passwords don't match");
throw new DAOException("Passwords don't match");
}
log.info("updating password for " + u.getUsername());
// update the user with the new password
String salt = Security.getRandomToken();
String newHashedPassword = Security.encodeString(pw2 + salt);
updateUserPassword(newHashedPassword, salt, u.getUserId(), new Date(), u.getUserId());
}
/**
* @see org.openmrs.api.UserService#changeQuestionAnswer(java.lang.String, java.lang.String,
* java.lang.String)
*/
public void changeQuestionAnswer(String pw, String question, String answer) throws DAOException {
User u = Context.getAuthenticatedUser();
LoginCredential credentials = getLoginCredential(u);
if (!credentials.checkPassword(pw)) {
log.error("Passwords don't match");
throw new DAOException("Passwords don't match");
}
changeQuestionAnswer(u, question, answer);
}
/**
* @see org.openmrs.api.UserService#changeQuestionAnswer(User, String, String)
*/
public void changeQuestionAnswer(User u, String question, String answer) throws DAOException {
log.info("Updating secret question and answer for " + u.getUsername());
LoginCredential credentials = getLoginCredential(u);
credentials.setSecretQuestion(question);
credentials.setSecretAnswer(answer);
credentials.setDateChanged(new Date());
credentials.setChangedBy(u);
updateLoginCredential(credentials);
}
/**
* @see org.openmrs.api.UserService#isSecretAnswer(User, java.lang.String)
*/
public boolean isSecretAnswer(User u, String answer) throws DAOException {
if (answer == null || answer.equals(""))
return false;
String answerOnRecord = getLoginCredential(u).getSecretAnswer();
return (answer.equals(answerOnRecord));
}
/**
* @see UserDAO#getUsers(String, List, boolean, Integer, Integer)
*/
@SuppressWarnings("unchecked")
public List<User> getUsers(String name, List<Role> roles, boolean includeRetired, Integer start, Integer length) {
String hqlSelectStart = "select distinct user from User as user inner join user.person.names as name ";
Query query = createUserSearchQuery(name, roles, includeRetired, hqlSelectStart);
if (start != null)
query.setFirstResult(start);
if (length != null && length > 0)
query.setMaxResults(length);
List<User> returnList = query.list();
if (!CollectionUtils.isEmpty(returnList))
Collections.sort(returnList, new UserByNameComparator());
return returnList;
}
/**
* @see org.openmrs.api.UserService#generateSystemId()
*/
public Integer generateSystemId() {
// TODO this algorithm will fail if someone deletes a user that is not the last one.
String sql = "select count(user_id) as user_id from users";
Query query = sessionFactory.getCurrentSession().createSQLQuery(sql);
Object object = query.uniqueResult();
Integer id = null;
if (object instanceof BigInteger)
id = ((BigInteger) query.uniqueResult()).intValue() + 1;
else if (object instanceof Integer)
id = ((Integer) query.uniqueResult()).intValue() + 1;
else {
log.warn("What is being returned here? Definitely nothing expected object value: '" + object + "' of class: "
+ object.getClass());
id = 1;
}
return id;
}
/**
* @see org.openmrs.api.UserService#getUsersByName(java.lang.String, java.lang.String, boolean)
*/
@SuppressWarnings("unchecked")
public List<User> getUsersByName(String givenName, String familyName, boolean includeRetired) {
List<User> users = new Vector<User>();
Criteria crit = sessionFactory.getCurrentSession().createCriteria(User.class);
crit.createAlias("person", "person");
crit.createAlias("person.names", "names");
crit.add(Expression.eq("names.givenName", givenName));
crit.add(Expression.eq("names.familyName", familyName));
crit.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
if (!includeRetired)
crit.add(Expression.eq("retired", false));
for (User u : (List<User>) crit.list()) {
users.add(u);
}
return users;
}
/**
* @see org.openmrs.api.db.UserDAO#getPrivilegeByUuid(java.lang.String)
*/
public Privilege getPrivilegeByUuid(String uuid) {
return (Privilege) sessionFactory.getCurrentSession().createQuery("from Privilege p where p.uuid = :uuid")
.setString("uuid", uuid).uniqueResult();
}
/**
* @see org.openmrs.api.db.UserDAO#getRoleByUuid(java.lang.String)
*/
public Role getRoleByUuid(String uuid) {
return (Role) sessionFactory.getCurrentSession().createQuery("from Role r where r.uuid = :uuid").setString("uuid",
uuid).uniqueResult();
}
/**
* @see org.openmrs.api.db.UserDAO#getUserByUuid(java.lang.String)
*/
public User getUserByUuid(String uuid) {
User ret = null;
if (uuid != null) {
uuid = uuid.trim();
ret = (User) sessionFactory.getCurrentSession().createQuery("from User u where u.uuid = :uuid").setString(
"uuid", uuid).uniqueResult();
}
return ret;
}
/**
* @see org.openmrs.api.db.UserDAO#getLoginCredential(org.openmrs.User)
*/
public LoginCredential getLoginCredential(User user) {
return (LoginCredential) sessionFactory.getCurrentSession().get(LoginCredential.class, user.getUserId());
}
/**
* @see org.openmrs.api.db.UserDAO#getLoginCredential(org.openmrs.User)
*/
public LoginCredential getLoginCredentialByUuid(String uuid) {
if (uuid == null)
return null;
else
return (LoginCredential) sessionFactory.getCurrentSession().createQuery(
"from LoginCredential where uuid = :uuid").setString("uuid", uuid.trim()).uniqueResult();
}
/**
* @see org.openmrs.api.db.UserDAO#updateLoginCredential(org.openmrs.LoginCredential)
*/
public void updateLoginCredential(LoginCredential credential) {
sessionFactory.getCurrentSession().update(credential);
}
/**
* @see org.openmrs.api.db.UserDAO#getUsersByPerson(org.openmrs.Person, boolean)
*/
@SuppressWarnings("unchecked")
public List<User> getUsersByPerson(Person person, boolean includeRetired) {
Criteria crit = sessionFactory.getCurrentSession().createCriteria(User.class);
if (person != null)
crit.add(Restrictions.eq("person", person));
if (!includeRetired)
crit.add(Restrictions.eq("retired", false));
return (List<User>) crit.list();
}
/**
* @see org.openmrs.api.db.UserDAO#getCountOfUsers(String, List, boolean)
*/
@Override
public Integer getCountOfUsers(String name, List<Role> roles, boolean includeRetired) {
String hqlSelectStart = "select count(distinct user) from User as user inner join user.person.names as name ";
Query query = createUserSearchQuery(name, roles, includeRetired, hqlSelectStart);
return ((Long) query.uniqueResult()).intValue();
}
/**
* Utility methods that creates a hibernate query object from the specified arguments
*
* @param name The name of the user to search against
* @param roles the roles to match against
* @param includeRetired Specifies if retired users should be included or not
* @param hqlSelectStart The starting phrase of the select statement that includes the joined
* tables
* @return the created hibernate query object
*/
private Query createUserSearchQuery(String name, List<Role> roles, boolean includeRetired, String hqlSelectStart) {
log.debug("name: " + name);
name = HibernateUtil.escapeSqlWildcards(name, sessionFactory);
// Create an HQL query like this:
// select distinct user
// from User as user inner join user.person.names as name inner join user.roles as role
// where (user.username like :name1 or ...and for systemId givenName familyName familyName2...)
// and (user.username like :name2 or ...and for systemId givenName familyName familyName2...)
// ...repeat for all name fragments...
// and role in :roleList
// and user.retired = false
// order by username asc
List<String> criteria = new ArrayList<String>();
int counter = 0;
Map<String, String> namesMap = new HashMap<String, String>();
if (name != null) {
name = name.replace(", ", " ");
String[] names = name.split(" ");
for (String n : names) {
if (n != null && n.length() > 0) {
// compare each fragment of the query against username, systemId, given, middle, family, and family2
String key = "name" + ++counter;
String value = n + "%";
namesMap.put(key, value);
criteria.add("(user.username like :" + key + " or user.systemId like :" + key
+ " or name.givenName like :" + key + " or name.middleName like :" + key
+ " or name.familyName like :" + key + " or name.familyName2 like :" + key + ")");
}
}
}
if (includeRetired == false)
criteria.add("user.retired = false");
// build the hql query
String hql = hqlSelectStart;
boolean searchOnRoles = false;
if (CollectionUtils.isNotEmpty(roles)) {
hql += "inner join user.roles as role ";
searchOnRoles = true;
}
if (criteria.size() > 0)
hql += "where ";
for (Iterator<String> i = criteria.iterator(); i.hasNext();) {
hql += i.next() + " ";
if (i.hasNext())
hql += "and ";
}
//Match against the specified roles
if (searchOnRoles)
hql += " and role in (:roleList)";
Query query = sessionFactory.getCurrentSession().createQuery(hql);
for (Map.Entry<String, String> e : namesMap.entrySet())
query.setString(e.getKey(), e.getValue());
if (searchOnRoles) {
query.setParameterList("roleList", roles);
}
return query;
}
}