blob: e7d92ce462f1595681d5fb25cd2a2129389513e7 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.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://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.openmeetings.db.dao.user;
import static org.apache.openmeetings.db.util.UserHelper.getMinLoginLength;
import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_DEFAULT_LANG_KEY;
import static org.apache.openmeetings.util.OpenmeetingsVariables.webAppRootKey;
import java.security.NoSuchAlgorithmException;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TimeZone;
import java.util.UUID;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.PersistenceContext;
import javax.persistence.TypedQuery;
import org.apache.commons.lang3.StringUtils;
import org.apache.openjpa.persistence.OpenJPAEntityManager;
import org.apache.openjpa.persistence.OpenJPAPersistence;
import org.apache.openjpa.persistence.OpenJPAQuery;
import org.apache.openmeetings.db.dao.IDataProviderDao;
import org.apache.openmeetings.db.dao.basic.ConfigurationDao;
import org.apache.openmeetings.db.entity.user.Address;
import org.apache.openmeetings.db.entity.user.Organisation_Users;
import org.apache.openmeetings.db.entity.user.User;
import org.apache.openmeetings.db.entity.user.User.Right;
import org.apache.openmeetings.db.entity.user.User.Type;
import org.apache.openmeetings.db.util.TimezoneUtil;
import org.apache.openmeetings.db.util.UserHelper;
import org.apache.openmeetings.util.AuthLevelUtil;
import org.apache.openmeetings.util.DaoHelper;
import org.apache.openmeetings.util.OmException;
import org.apache.openmeetings.util.crypt.ManageCryptStyle;
import org.apache.wicket.util.string.Strings;
import org.red5.logging.Red5LoggerFactory;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
/**
* CRUD operations for {@link User}
*
* @author swagner, solomax, vasya
*
*/
@Transactional
public class UserDao implements IDataProviderDao<User> {
private static final Logger log = Red5LoggerFactory.getLogger(UserDao.class, webAppRootKey);
public final static String[] searchFields = {"lastname", "firstname", "login", "adresses.email", "adresses.town"};
@PersistenceContext
private EntityManager em;
@Autowired
private ConfigurationDao cfgDao;
@Autowired
private StateDao stateDao;
@Autowired
private TimezoneUtil timezoneUtil;
public static Set<Right> getDefaultRights() {
Set<Right> rights = new HashSet<User.Right>();
rights.add(Right.Login);
rights.add(Right.Dashboard);
rights.add(Right.Room);
return rights;
}
/**
* Get a new instance of the {@link User} entity, with all default values
* set
*
* @param currentUser - the user to copy time zone from
* @return new User instance
*/
public User getNewUserInstance(User currentUser) {
User user = new User();
user.setSalutations_id(1L); // TODO: Fix default selection to be configurable
user.setRights(getDefaultRights());
user.setLanguage_id(cfgDao.getConfValue(CONFIG_DEFAULT_LANG_KEY, Long.class, "1"));
user.setTimeZoneId(timezoneUtil.getTimeZone(currentUser).getID());
user.setForceTimeZoneCheck(false);
user.setSendSMS(false);
user.setAge(new Date());
Address adresses = new Address();
adresses.setStates(stateDao.getStateById(1L));
user.setAdresses(adresses);
user.setShowContactData(false);
user.setShowContactDataToContacts(false);
return user;
}
public List<User> get(int first, int count) {
TypedQuery<User> q = em.createNamedQuery("getNondeletedUsers", User.class);
q.setFirstResult(first);
q.setMaxResults(count);
return q.getResultList();
}
private String getAdditionalJoin(boolean filterContacts) {
return filterContacts ? "LEFT JOIN u.organisation_users ou" : null;
}
private String getAdditionalWhere(boolean excludeContacts, Map<String, Object> params) {
if (excludeContacts) {
params.put("contact", Type.contact);
return "u.type <> :contact";
}
return null;
}
private String getAdditionalWhere(boolean filterContacts, Long ownerId, Map<String, Object> params) {
if (filterContacts) {
params.put("ownerId", ownerId);
params.put("contact", Type.contact);
return "((u.type <> :contact AND ou.organisation.organisation_id IN (SELECT ou.organisation.organisation_id FROM Organisation_Users ou WHERE ou.user.user_id = :ownerId)) "
+ "OR (u.type = :contact AND u.ownerId = :ownerId))";
}
return null;
}
private void setAdditionalParams(TypedQuery<?> q, Map<String, Object> params) {
for (Map.Entry<String, Object> me: params.entrySet()) {
q.setParameter(me.getKey(), me.getValue());
}
}
public List<User> get(String search, int start, int count, String sort, boolean filterContacts, long currentUserId) {
Map<String, Object> params = new HashMap<String, Object>();
TypedQuery<User> q = em.createQuery(DaoHelper.getSearchQuery("User", "u", getAdditionalJoin(filterContacts), search, true, true, false
, getAdditionalWhere(filterContacts, currentUserId, params), sort, searchFields), User.class);
q.setFirstResult(start);
q.setMaxResults(count);
setAdditionalParams(q, params);
return q.getResultList();
}
public long count() {
// get all users
TypedQuery<Long> q = em.createNamedQuery("countNondeletedUsers", Long.class);
return q.getSingleResult();
}
public long count(String search) {
return count(search, false, -1);
}
public long count(String search, long currentUserId) {
return count(search, false, currentUserId);
}
public long count(String search, boolean filterContacts, long currentUserId) {
Map<String, Object> params = new HashMap<String, Object>();
TypedQuery<Long> q = em.createQuery(DaoHelper.getSearchQuery("User", "u", getAdditionalJoin(filterContacts), search, true, true, true
, getAdditionalWhere(filterContacts, currentUserId, params), null, searchFields), Long.class);
setAdditionalParams(q, params);
return q.getSingleResult();
}
//This is AdminDao method
public List<User> get(String search, boolean excludeContacts, int first, int count) {
Map<String, Object> params = new HashMap<String, Object>();
TypedQuery<User> q = em.createQuery(DaoHelper.getSearchQuery("User", "u", null, search, true, true, false
, getAdditionalWhere(excludeContacts, params), null, searchFields), User.class);
setAdditionalParams(q, params);
q.setFirstResult(first);
q.setMaxResults(count);
return q.getResultList();
}
public List<User> get(String search, boolean filterContacts, long currentUserId) {
Map<String, Object> params = new HashMap<String, Object>();
TypedQuery<User> q = em.createQuery(DaoHelper.getSearchQuery("User", "u", getAdditionalJoin(filterContacts), search, true, true, false
, getAdditionalWhere(filterContacts, currentUserId, params), null, searchFields), User.class);
setAdditionalParams(q, params);
return q.getResultList();
}
public User update(User u, Long userId) {
if (u.getOrganisation_users() != null) {
for (Organisation_Users ou : u.getOrganisation_users()) {
ou.setUser(u);
}
}
if (u.getUser_id() == null) {
if (u.getRegdate() == null) {
u.setRegdate(new Date());
}
u.setStarttime(new Date());
em.persist(u);
} else {
u.setUpdatetime(new Date());
u = em.merge(u);
}
//this is necessary due to organisation details are lost on update
for (Organisation_Users ou : u.getOrganisation_users()) {
em.refresh(ou);
}
return u;
}
// TODO: Why the password field is not set via the Model is because its
// FetchType is Lazy, this extra hook here might be not needed with a
// different mechanism to protect the password from being read
// sebawagner, 01.10.2012
public User update(User user, String password, long updatedBy) throws NoSuchAlgorithmException {
User u = update(user, updatedBy);
if (password != null && !password.isEmpty()) {
//OpenJPA is not allowing to set fields not being fetched before
User u1 = get(u.getUser_id(), true);
u1.updatePassword(cfgDao, password);
update(u1, updatedBy);
}
return u;
}
public void delete(User u, Long userId) {
deleteUserID(u.getUser_id());
}
public User get(long user_id) {
return get(user_id, false);
}
private User get(long user_id, boolean force) {
User u = null;
if (user_id > 0) {
OpenJPAEntityManager oem = OpenJPAPersistence.cast(em);
boolean qrce = oem.getFetchPlan().getQueryResultCacheEnabled();
oem.getFetchPlan().setQueryResultCacheEnabled(false); //FIXME update in cache during update
TypedQuery<User> q = oem.createNamedQuery("getUserById", User.class).setParameter("id", user_id);
@SuppressWarnings("unchecked")
OpenJPAQuery<User> kq = OpenJPAPersistence.cast(q);
kq.getFetchPlan().addFetchGroup("orgUsers");
if (force) {
kq.getFetchPlan().addFetchGroup("backupexport");
}
try {
u = kq.getSingleResult();
} catch (NoResultException ne) {
//no-op
}
oem.getFetchPlan().setQueryResultCacheEnabled(qrce);
} else {
log.info("[get] " + "Info: No USER_ID given");
}
return u;
}
public Long deleteUserID(long userId) {
try {
if (userId != 0) {
User us = get(userId);
for (Organisation_Users ou : us.getOrganisation_users()){
em.remove(ou);
}
us.setOrganisation_users(null);
us.setDeleted(true);
us.setUpdatetime(new Date());
us.setSipUser(null);
Address adr = us.getAdresses();
if (adr != null) {
adr.setDeleted(true);
}
if (us.getUser_id() == null) {
em.persist(us);
} else {
if (!em.contains(us)) {
em.merge(us);
}
}
return us.getUser_id();
}
} catch (Exception ex2) {
log.error("[deleteUserID]", ex2);
}
return null;
}
public List<User> get(Collection<Long> ids) {
return em.createNamedQuery("getUsersByIds", User.class).setParameter("ids", ids).getResultList();
}
public List<User> getAllUsers() {
TypedQuery<User> q = em.createNamedQuery("getNondeletedUsers", User.class);
return q.getResultList();
}
public List<User> getAllBackupUsers() {
try {
TypedQuery<User> q = em.createNamedQuery("getAllUsers", User.class);
@SuppressWarnings("unchecked")
OpenJPAQuery<User> kq = OpenJPAPersistence.cast(q);
kq.getFetchPlan().addFetchGroups("backupexport", "orgUsers");
return kq.getResultList();
} catch (Exception ex2) {
log.error("[getAllUsersDeleted] ", ex2);
}
return null;
}
/**
* check for duplicates
*
* @param login
* @param type
* @param domainId
* @param id
* @return
*/
public boolean checkLogin(String login, Type type, Long domainId, Long id) {
try {
User u = getByLogin(login, type, domainId);
return u == null || u.getUser_id().equals(id);
} catch (Exception e) {
//exception is thrown in case of non-unique result
return false;
}
}
/**
* Checks if a mail is already taken by someone else
*
* @param email
* @param type
* @param domainId
* @param id
* @return
*/
public boolean checkEmail(String email, Type type, Long domainId, Long id) {
log.debug("checkEmail: email = {}, id = {}", email, id);
try {
User u = getByEmail(email, type, domainId);
return u == null || u.getUser_id().equals(id);
} catch (Exception e) {
//exception is thrown in case of non-unique result
return false;
}
}
public boolean validLogin(String login) {
return !Strings.isEmpty(login) && login.length() >= UserHelper.getMinLoginLength(cfgDao);
}
public User getByLogin(String login, Type type, Long domainId) {
User u = null;
try {
u = em.createNamedQuery("getUserByLogin", User.class)
.setParameter("login", login)
.setParameter("type", type)
.setParameter("domainId", domainId == null ? 0 : domainId)
.getSingleResult();
} catch (NoResultException ex) {
}
return u;
}
public User getByEmail(String email) {
return getByEmail(email, User.Type.user, null);
}
public User getByEmail(String email, User.Type type, Long domainId) {
User u = null;
try {
u = em.createNamedQuery("getUserByEmail", User.class)
.setParameter("email", email)
.setParameter("type", type)
.setParameter("domainId", domainId == null ? 0 : domainId)
.getSingleResult();
} catch (NoResultException ex) {
}
return u;
}
public Object getUserByHash(String hash) {
if (hash.length() == 0) {
return new Long(-5);
}
User us = null;
try {
us = em.createNamedQuery("getUserByHash", User.class)
.setParameter("resethash", hash)
.setParameter("type", User.Type.user)
.getSingleResult();
} catch (NoResultException ex) {
} catch (Exception e) {
log.error("[getUserByHash]", e);
}
if (us != null) {
return us;
} else {
return new Long(-5);
}
}
/**
* @param search
* @return
*/
public Long selectMaxFromUsersWithSearch(String search) {
try {
// get all users
TypedQuery<Long> query = em.createNamedQuery("selectMaxFromUsersWithSearch", Long.class);
query.setParameter("search", StringUtils.lowerCase(search));
List<Long> ll = query.getResultList();
log.info("selectMaxFromUsers" + ll.get(0));
return ll.get(0);
} catch (Exception ex2) {
log.error("[selectMaxFromUsers] ", ex2);
}
return null;
}
/**
* Returns true if the password is correct
*
* @param userId
* @param password
* @return
*/
public boolean verifyPassword(Long userId, String password) {
TypedQuery<Long> query = em.createNamedQuery("checkPassword", Long.class);
query.setParameter("userId", userId);
query.setParameter("password", ManageCryptStyle.getInstanceOfCrypt().createPassPhrase(password));
return query.getResultList().get(0) == 1;
}
public User getContact(String email, long ownerId) {
return getContact(email, "", "", ownerId);
}
public User getContact(String email, User owner) {
return getContact(email, "", "", null, null, owner);
}
public User getContact(String email, String firstName, String lastName, long ownerId) {
return getContact(email, firstName, lastName, null, null, get(ownerId));
}
public User getContact(String email, String firstName, String lastName, Long langId, String tzId, long ownerId) {
return getContact(email, firstName, lastName, langId, tzId, get(ownerId));
}
public User getContact(String email, String firstName, String lastName, Long langId, String tzId, User owner) {
User to = null;
try {
to = em.createNamedQuery("getContactByEmailAndUser", User.class)
.setParameter("email", email).setParameter("type", User.Type.contact).setParameter("ownerId", owner.getUser_id()).getSingleResult();
} catch (Exception e) {
//no-op
}
if (to == null) {
to = new User();
to.setType(Type.contact);
String login = owner.getUser_id() + "_" + email; //UserId prefix is used to ensure unique login
to.setLogin(login.length() < getMinLoginLength(cfgDao) ? UUID.randomUUID().toString() : login);
to.setFirstname(firstName);
to.setLastname(lastName);
to.setLanguage_id(null == langId ? owner.getLanguage_id() : langId);
to.setOwnerId(owner.getUser_id());
to.setAdresses(new Address());
to.getAdresses().setEmail(email);
to.setTimeZoneId(null == tzId ? owner.getTimeZoneId() : tzId);
}
return to;
}
/**
* @param hash
* @return
*/
public User getUserByActivationHash(String hash) {
TypedQuery<User> query = em.createQuery("SELECT u FROM User as u WHERE u.activatehash = :activatehash"
+ " AND u.deleted = false", User.class);
query.setParameter("activatehash", hash);
User u = null;
try {
u = query.getSingleResult();
} catch (NoResultException e) {
// u=null}
}
return u;
}
private <T> TypedQuery<T> getUserProfileQuery(Class<T> clazz, long userId, String text, String offers, String search, String orderBy, boolean asc) {
Map<String, Object> params = new HashMap<String, Object>();
boolean filterContacts = true;
boolean count = clazz.isAssignableFrom(Long.class);
StringBuilder sb = new StringBuilder("SELECT ");
sb.append(count ? "COUNT(" : "").append("u").append(count ? ") " : " ")
.append("FROM User u ").append(getAdditionalJoin(filterContacts)).append(" WHERE u.deleted = false AND ")
.append(getAdditionalWhere(filterContacts, userId, params));
if (!Strings.isEmpty(offers)) {
sb.append(" AND (LOWER(u.userOffers) LIKE :userOffers) ");
params.put("userOffers", getStringParam(offers));
}
if (!Strings.isEmpty(search)) {
sb.append(" AND (LOWER(u.userSearchs) LIKE :userSearchs) ");
params.put("userSearchs", getStringParam(search));
}
if (!Strings.isEmpty(text)) {
sb.append(" AND (LOWER(u.login) LIKE :search ")
.append("OR LOWER(u.firstname) LIKE :search ")
.append("OR LOWER(u.lastname) LIKE :search ")
.append("OR LOWER(u.adresses.email) LIKE :search ")
.append("OR LOWER(u.adresses.town) LIKE :search " + ") ");
params.put("search", getStringParam(text));
}
if (!count && !Strings.isEmpty(orderBy)) {
sb.append(" ORDER BY ").append(orderBy).append(asc ? " ASC" : " DESC");
}
TypedQuery<T> query = em.createQuery(sb.toString(), clazz);
setAdditionalParams(query, params);
return query;
}
private String getStringParam(String param) {
return param == null ? "%" : "%" + StringUtils.lowerCase(param) + "%";
}
public List<User> searchUserProfile(long userId, String text, String offers, String search, String orderBy, int start, int max, boolean asc) {
return getUserProfileQuery(User.class, userId, text, offers, search, orderBy, asc).setFirstResult(start).setMaxResults(max).getResultList();
}
public Long searchCountUserProfile(long userId, String text, String offers, String search) {
return getUserProfileQuery(Long.class, userId, text, offers, search, null, false).getSingleResult();
}
public User getExternalUser(String extId, String extType) {
User u = null;
try {
u = em.createNamedQuery("getExternalUser", User.class)
.setParameter("externalId", extId)
.setParameter("externalType", extType)
.getSingleResult();
} catch (NoResultException ex) {
}
return u;
}
public List<User> get(String search, int start, int count, String order) {
return get(search, start, count, order, false, -1);
}
public Set<Right> getRights(Long id) {
Set<Right> rights = new HashSet<Right>();
if (id == null) {
return rights;
}
// For direct access of linked users
if (id < 0) {
rights.add(Right.Room);
return rights;
}
User u = get(id);
if (u != null) {
return u.getRights();
}
return rights;
}
/**
* login logic
*
* @param userOrEmail: login or email of the user being tested
* @param userpass: password of the user being tested
* @return User object in case of successful login
* @throws OmException in case of any issue
*/
public User login(String userOrEmail, String userpass) throws OmException {
List<User> users = em.createNamedQuery("getUserByLoginOrEmail", User.class)
.setParameter("userOrEmail", userOrEmail)
.setParameter("type", Type.user)
.getResultList();
log.debug("debug SIZE: " + users.size());
if (users.size() == 0) {
throw new OmException(-10L);
}
User u = users.get(0);
if (!verifyPassword(u.getUser_id(), userpass)) {
throw new OmException(-11L);
}
// Check if activated
if (!AuthLevelUtil.hasLoginLevel(u.getRights())) {
throw new OmException(-41L);
}
log.debug("loginUser " + u.getOrganisation_users());
if (u.getOrganisation_users().isEmpty()) {
throw new OmException("No Organization assigned to user");
}
u.setLastlogin(new Date());
return update(u, u.getUser_id());
}
public Address getAddress(String street, String zip, String town, long states_id, String additionalname, String fax, String phone, String email) {
Address a = new Address();
a.setStreet(street);
a.setZip(zip);
a.setTown(town);
a.setStates(stateDao.getStateById(states_id));
a.setAdditionalname(additionalname);
a.setComment("");
a.setFax(fax);
a.setPhone(phone);
a.setEmail(email);
return a;
}
public User addUser(Set<Right> rights, String firstname, String login, String lastname, long language_id,
String userpass, Address adress, boolean sendSMS, Date age, String hash, TimeZone timezone,
Boolean forceTimeZoneCheck, String userOffers, String userSearchs, Boolean showContactData,
Boolean showContactDataToContacts, String externalId, String externalType, List<Organisation_Users> orgList, String pictureuri) throws NoSuchAlgorithmException {
User u = new User();
u.setFirstname(firstname);
u.setLogin(login);
u.setLastname(lastname);
u.setAge(age);
u.setAdresses(adress);
u.setSendSMS(sendSMS);
u.setRights(rights);
u.setLastlogin(new Date());
u.setLasttrans(new Long(0));
u.setSalutations_id(1L);
u.setStarttime(new Date());
u.setActivatehash(hash);
u.setTimeZoneId(timezone.getID());
u.setForceTimeZoneCheck(forceTimeZoneCheck);
u.setExternalUserId(externalId);
u.setExternalUserType(externalType);
if (!Strings.isEmpty(u.getExternalUserType())) {
u.setType(Type.external);
}
u.setUserOffers(userOffers);
u.setUserSearchs(userSearchs);
u.setShowContactData(showContactData);
u.setShowContactDataToContacts(showContactDataToContacts);
// this is needed cause the language is not a needed data at registering
u.setLanguage_id(language_id != 0 ? language_id : null);
if (!Strings.isEmpty(userpass)) {
u.updatePassword(cfgDao, userpass);
}
u.setRegdate(new Date());
u.setDeleted(false);
u.setPictureuri(pictureuri);
u.setOrganisation_users(orgList);
return update(u, null);
}
}