/** * <a href="http://www.openolat.org"> * OpenOLAT - Online Learning and Training</a><br> * <p> * Licensed under the Apache License, Version 2.0 (the "License"); <br> * you may not use this file except in compliance with the License.<br> * You may obtain a copy of the License at the * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a> * <p> * Unless required by applicable law or agreed to in writing,<br> * software distributed under the License is distributed on an "AS IS" BASIS, <br> * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br> * See the License for the specific language governing permissions and <br> * limitations under the License. * <p> * Initial code contributed and copyrighted by<br> * frentix GmbH, http://www.frentix.com * <p> */ package org.olat.user; import java.io.Serializable; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import javax.annotation.PostConstruct; import javax.persistence.EntityManager; import javax.persistence.TypedQuery; import org.olat.basesecurity.BaseSecurity; import org.olat.basesecurity.IdentityImpl; import org.olat.basesecurity.IdentityNames; import org.olat.basesecurity.IdentityRef; import org.olat.basesecurity.IdentityShort; import org.olat.core.commons.persistence.DB; import org.olat.core.commons.persistence.DBFactory; import org.olat.core.commons.persistence.DBQuery; import org.olat.core.helpers.Settings; import org.olat.core.id.Identity; import org.olat.core.id.Preferences; import org.olat.core.id.User; import org.olat.core.id.UserConstants; import org.olat.core.logging.AssertException; import org.olat.core.util.WebappHelper; import org.olat.core.util.cache.CacheWrapper; import org.olat.core.util.coordinate.CoordinatorManager; import org.olat.core.util.i18n.I18nModule; import org.olat.core.util.mail.MailHelper; import org.olat.properties.Property; import org.olat.properties.PropertyManager; import org.springframework.beans.factory.annotation.Autowired; /** * <h3>Description:</h3> * This implementation of the user manager manipulates user objects based on a * hibernate implementation * <p> * Initial Date: 31.07.2007 <br> * * @author Florian Gnaegi, frentix GmbH, http://www.frentix.com */ public class UserManagerImpl extends UserManager { // used to save user data in the properties table private static final String CHARSET = "charset"; private UserDisplayNameCreator userDisplayNameCreator; @Autowired private DB dbInstance; @Autowired private BaseSecurity securityManager; @Autowired private CoordinatorManager coordinatorManager; private CacheWrapper<Serializable,String> userToFullnameCache; private CacheWrapper<Long,String> userToNameCache; /** * Use UserManager.getInstance(), this is a spring factory method to load the * correct user manager */ private UserManagerImpl() { INSTANCE = this; } @PostConstruct public void init() { userToFullnameCache = coordinatorManager.getCoordinator().getCacher() .getCache(UserManager.class.getSimpleName(), "userfullname"); userToNameCache = coordinatorManager.getCoordinator().getCacher() .getCache(UserManager.class.getSimpleName(), "username"); } /** * @see org.olat.user.UserManager#createUser(java.lang.String, java.lang.String, java.lang.String) */ public User createUser(String firstName, String lastName, String eMail) { UserImpl newUser = new UserImpl(); newUser.setFirstName(firstName); newUser.setLastName(lastName); newUser.setEmail(eMail); newUser.setCreationDate(new Date()); Preferences prefs = newUser.getPreferences(); Locale loc; // for junit test case: use German Locale if (Settings.isJUnitTest()) { loc = Locale.GERMAN; } else { loc = I18nModule.getDefaultLocale(); } //Locale loc prefs.setLanguage(loc.toString()); prefs.setFontsize("normal"); prefs.setPresenceMessagesPublic(false); prefs.setInformSessionTimeout(false); return newUser; } @Override public boolean isEmailInUse(String email) { DB db = DBFactory.getInstance(); String[] emailProperties = {UserConstants.EMAIL, UserConstants.INSTITUTIONALEMAIL}; for(String emailProperty:emailProperties) { StringBuilder sb = new StringBuilder(); sb.append("select count(user) from org.olat.core.id.User user where ") .append("user.") .append(emailProperty) .append("=:email_value"); String query = sb.toString(); DBQuery dbq = db.createQuery(query); dbq.setString("email_value", email); Number countEmail = (Number)dbq.uniqueResult(); if(countEmail.intValue() > 0) { return true; } } return false; } @Override public List<Long> findUserKeyWithProperty(String propName, String propValue) { StringBuilder sb = new StringBuilder(); sb.append("select user.key from ").append(UserImpl.class.getName()).append(" user ") .append(" where user.").append(propName).append("=:propValue"); return dbInstance.getCurrentEntityManager() .createQuery(sb.toString(), Long.class) .setParameter("propValue", propValue) .getResultList(); } @Override public Identity findIdentityKeyWithProperty(String propName, String propValue) { StringBuilder sb = new StringBuilder("select identity from ").append(IdentityImpl.class.getName()).append(" identity ") .append(" inner join fetch identity.user user ") .append(" where user.").append(propName).append("=:propValue"); List<Identity> userKeys = dbInstance.getCurrentEntityManager() .createQuery(sb.toString(), Identity.class) .setParameter("propValue", propValue).getResultList(); if(userKeys.isEmpty()) { return null; } return userKeys.get(0); } /** * @see org.olat.user.UserManager#findIdentityByEmail(java.lang.String) */ public Identity findIdentityByEmail(String email) { if (!MailHelper.isValidEmailAddress(email)) { throw new AssertException("Identity cannot be searched by email, if email is not valid. Used address: " + email); } StringBuilder sb = new StringBuilder("select identity from ").append(IdentityImpl.class.getName()).append(" identity ") .append(" inner join fetch identity.user user ") .append(" where "); boolean mysql = "mysql".equals(dbInstance.getDbVendor()); //search email StringBuilder emailSb = new StringBuilder(sb); if(mysql) { emailSb.append(" user.").append(UserConstants.EMAIL).append("=:email"); } else { emailSb.append(" lower(user.").append(UserConstants.EMAIL).append(") = lower(:email)"); } List<Identity> identities = dbInstance.getCurrentEntityManager() .createQuery(emailSb.toString(), Identity.class) .setParameter("email", email).getResultList(); if (identities.size() > 1) { throw new AssertException("more than one identity found with email::" + email); } //search institutional email StringBuilder institutionalSb = new StringBuilder(sb); if(mysql) { institutionalSb.append(" user.").append(UserConstants.INSTITUTIONALEMAIL).append("=:email"); } else { institutionalSb.append(" lower(user.").append(UserConstants.INSTITUTIONALEMAIL).append(") = lower(:email)"); } List<Identity> instIdentities = dbInstance.getCurrentEntityManager() .createQuery(institutionalSb.toString(), Identity.class) .setParameter("email", email).getResultList(); if (instIdentities.size() > 1) { throw new AssertException("more than one identity found with institutional-email::" + email); } // check if email found in both fields && identity is not the same if ( (identities.size() > 0) && (instIdentities.size() > 0) && ( identities.get(0) != instIdentities.get(0) ) ) { throw new AssertException("found two identites with same email::" + email + " identity1=" + identities.get(0) + " identity2=" + instIdentities.get(0)); } if (identities.size() == 1) { return identities.get(0); } if (instIdentities.size() == 1) { return instIdentities.get(0); } return null; } @Override public List<Identity> findIdentitiesByEmail(List<String> emailList) { List<String> emails = new ArrayList<String>(emailList); for (int i=0; i<emails.size(); i++) { String email = emails.get(i).toLowerCase(); if (!MailHelper.isValidEmailAddress(email)) { emails.remove(i); logWarn("Invalid email address: " + email, null); } else { emails.set(i, email); } } if(emails.isEmpty()) { return Collections.emptyList(); } StringBuilder sb = new StringBuilder("select identity from ").append(IdentityImpl.class.getName()).append(" identity ") .append(" inner join fetch identity.user user ") .append(" where "); boolean mysql = "mysql".equals(dbInstance.getDbVendor()); //search email StringBuilder emailSb = new StringBuilder(sb); if(mysql) { emailSb.append(" user.").append(UserConstants.EMAIL).append(" in (:emails) "); } else { emailSb.append(" lower(user.").append(UserConstants.EMAIL).append(") in (:emails)"); } List<Identity> identities = dbInstance.getCurrentEntityManager() .createQuery(emailSb.toString(), Identity.class) .setParameter("emails", emails).getResultList(); //search institutional email StringBuilder institutionalSb = new StringBuilder(sb); if(mysql) { institutionalSb.append(" user.").append(UserConstants.INSTITUTIONALEMAIL).append(" in (:emails) "); } else { institutionalSb.append(" lower(user.").append(UserConstants.INSTITUTIONALEMAIL).append(") in (:emails)"); } if(!identities.isEmpty()) { institutionalSb.append(" and identity not in (:identities) "); } TypedQuery<Identity> institutionalQuery = dbInstance.getCurrentEntityManager() .createQuery(institutionalSb.toString(), Identity.class) .setParameter("emails", emails); if(!identities.isEmpty()) { institutionalQuery.setParameter("identities", identities); } List<Identity> instIdentities = institutionalQuery.getResultList(); identities.addAll(instIdentities); return identities; } /** * @see org.olat.user.UserManager#findUserByEmail(java.lang.String) */ public User findUserByEmail(String email) { if (isLogDebugEnabled()){ logDebug("Trying to find user with email '" + email + "'"); } Identity ident = findIdentityByEmail(email); // if no user found return null if (ident == null) { if (isLogDebugEnabled()){ logDebug("Could not find user '" + email + "'"); } return null; } return ident.getUser(); } public boolean userExist(String email) { StringBuilder sb = new StringBuilder("select distinct count(user) from ").append(UserImpl.class.getName()).append(" user where "); boolean mysql = "mysql".equals(dbInstance.getDbVendor()); //search email StringBuilder emailSb = new StringBuilder(sb); if(mysql) { emailSb.append(" user.").append(UserConstants.EMAIL).append("=:email"); } else { emailSb.append(" lower(user.").append(UserConstants.EMAIL).append(") = lower(:email)"); } Number count = dbInstance.getCurrentEntityManager() .createQuery(emailSb.toString(), Number.class) .setParameter("email", email) .getSingleResult(); if(count.intValue() > 0) { return true; } //search institutional email StringBuilder institutionalSb = new StringBuilder(sb); if(mysql) { institutionalSb.append(" user.").append(UserConstants.INSTITUTIONALEMAIL).append(" =:email"); } else { institutionalSb.append(" lower(user.").append(UserConstants.INSTITUTIONALEMAIL).append(") = lower(:email)"); } count = dbInstance.getCurrentEntityManager() .createQuery(institutionalSb.toString(), Number.class) .setParameter("email", email) .getSingleResult(); return count.intValue() > 0; } /** * @see org.olat.user.UserManager#loadUserByKey(java.lang.Long) */ public User loadUserByKey(Long key) { return DBFactory.getInstance().loadObject(UserImpl.class, key); // User not loaded yet (lazy initialization). Need to access // a field first to really load user from database. } /** * @see org.olat.user.UserManager#updateUser(org.olat.core.id.User) */ @Override public User updateUser(User usr) { if (usr == null) throw new AssertException("User object is null!"); return dbInstance.getCurrentEntityManager().merge(usr); } /** * @see org.olat.user.UserManager#updateUserFromIdentity(org.olat.core.id.Identity) */ @Override public boolean updateUserFromIdentity(Identity identity) { try { String fullName = getUserDisplayName(identity); updateUsernameCache(identity.getKey(), identity.getName(), fullName); } catch (Exception e) { logWarn("Error update usernames cache", e); } User user = updateUser(identity.getUser()); ((IdentityImpl)identity).setUser(user); return true; } /** * @see org.olat.user.UserManager#setUserCharset(org.olat.core.id.Identity, java.lang.String) */ public void setUserCharset(Identity identity, String charset){ PropertyManager pm = PropertyManager.getInstance(); Property p = pm.findProperty(identity, null, null, null, CHARSET); if(p != null){ p.setStringValue(charset); pm.updateProperty(p); } else { Property newP = pm.createUserPropertyInstance(identity, null, CHARSET, null, null, charset, null); pm.saveProperty(newP); } } /** * @see org.olat.user.UserManager#getUserCharset(org.olat.core.id.Identity) */ public String getUserCharset(Identity identity){ String charset; charset = WebappHelper.getDefaultCharset(); PropertyManager pm = PropertyManager.getInstance(); Property p = pm.findProperty(identity, null, null, null, CHARSET); if(p != null){ charset = p.getStringValue(); // if after migration the system does not support the charset choosen by a // user // (a rather rare case) if(!Charset.isSupported(charset)){ charset = WebappHelper.getDefaultCharset(); } } else { charset = WebappHelper.getDefaultCharset(); } return charset; } @Override public int warmUp() { EntityManager em = dbInstance.getCurrentEntityManager(); int batchSize = 5000; TypedQuery<IdentityShort> query = em .createNamedQuery("selectAllIdentitiesShortUnordered", IdentityShort.class) .setMaxResults(batchSize); int count = 0; List<IdentityShort> identities; do { identities = query.setFirstResult(count).getResultList(); em.clear(); for(IdentityShort identity:identities) { if(identity.getStatus() < Identity.STATUS_DELETED) { getUserDisplayName(identity); } } count += identities.size(); } while(identities.size() >= batchSize); return count; } @Override public String getUsername(Long identityKey) { if(identityKey == null || identityKey.longValue() <= 0) { return null; } String username = userToNameCache.get(identityKey); if(username == null) { IdentityShort identity = securityManager.loadIdentityShortByKey(identityKey); getUserDisplayName(identity);//fill the cache username = identity.getName(); } return username; } @Override public String getUserDisplayName(String username) { if(username == null) return null; String fullName = userToFullnameCache.get(username); if(fullName == null) { List<IdentityShort> identities = securityManager.findShortIdentitiesByName(Collections.singletonList(username)); for(IdentityShort identity:identities) { fullName = getUserDisplayName(identity); } } return fullName; } @Override public String getUserDisplayName(Long identityKey) { if(identityKey == null || identityKey.longValue() <= 0) { return ""; } String fullName = userToFullnameCache.get(identityKey); if(fullName == null) { IdentityShort identity = securityManager.loadIdentityShortByKey(identityKey); fullName = getUserDisplayName(identity); } return fullName; } @Override public Map<String, String> getUserDisplayNamesByUserName(Collection<String> usernames) { if(usernames == null | usernames.isEmpty()) { return Collections.emptyMap(); } Map<String, String> fullNames = new HashMap<String,String>(); List<String> newUsernames = new ArrayList<String>(); for(String username:usernames) { String fullName = userToFullnameCache.get(username); if(fullName != null) { fullNames.put(username, fullName); } else { newUsernames.add(username); } } List<IdentityShort> identities = securityManager.findShortIdentitiesByName(newUsernames); for(IdentityShort identity:identities) { String fullName = getUserDisplayName(identity); fullNames.put(identity.getName(), fullName); newUsernames.remove(identity.getName()); } //not found for(String notFound:newUsernames) { userToFullnameCache.put(notFound, notFound); } return fullNames; } @Override public String getUserDisplayName(IdentityRef identity) { if(identity instanceof Identity) { return getUserDisplayName((Identity)identity); } return getUserDisplayName(identity.getKey()); } @Override public String getUserDisplayName(Identity identity) { if (userDisplayNameCreator == null || identity == null) return ""; String fullName = getUserDisplayName(identity.getUser()); updateUsernameCache(identity.getKey(), identity.getName(), fullName); return fullName; } /** * @see org.olat.user.UserManager#getUserDisplayName(org.olat.core.id.User) */ @Override public String getUserDisplayName(User user) { if (userDisplayNameCreator == null || user == null) return ""; return userDisplayNameCreator.getUserDisplayName(user); } /** * @see org.olat.user.UserManager#getUserDisplayName(org.olat.core.id.IdentityShort) */ @Override public String getUserDisplayName(IdentityNames identity) { if (userDisplayNameCreator == null || identity == null) return ""; String fullName = userDisplayNameCreator.getUserDisplayName(identity); updateUsernameCache(identity.getKey(), identity.getName(), fullName); return fullName; } @Override public String getUserDisplayName(String firstName, String lastName) { return userDisplayNameCreator.getDisplayName(firstName, lastName); } @Override public Map<Long, String> getUserDisplayNamesByKey(Collection<Long> identityKeys) { if(identityKeys == null | identityKeys.isEmpty()) { return Collections.emptyMap(); } Map<Long, String> fullNames = new HashMap<Long,String>(); List<Long> newIdentityKeys = new ArrayList<Long>(); for(Long identityKey:identityKeys) { String fullName = userToFullnameCache.get(identityKey); if(fullName != null) { fullNames.put(identityKey, fullName); } else { newIdentityKeys.add(identityKey); } } List<IdentityShort> identities = securityManager.loadIdentityShortByKeys(newIdentityKeys); for(IdentityShort identity:identities) { String fullName = getUserDisplayName(identity); updateUsernameCache(identity.getKey(), identity.getName(), fullName); fullNames.put(identity.getKey(), fullName); } return fullNames; } private void updateUsernameCache(Long identityKey, String username, String fullName) { if(fullName == null) return; if(identityKey != null) { userToFullnameCache.put(identityKey, fullName); } if(username != null) { userToFullnameCache.put(username, fullName); } if(username != null && identityKey != null) { userToNameCache.put(identityKey, username); } } /** * Sping setter method * @param userDisplayNameCreator the userDisplayNameCreator to set */ public void setUserDisplayNameCreator(UserDisplayNameCreator userDisplayNameCreator) { this.userDisplayNameCreator = userDisplayNameCreator; } }