Java tutorial
/* * Data Hub Service (DHuS) - For Space data distribution. * Copyright (C) 2013,2014,2015 GAEL Systems * * This file is part of DHuS software sources. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package fr.gael.dhus.service; import java.security.MessageDigest; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.regex.Pattern; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.hibernate.Criteria; import org.hibernate.FetchMode; import org.hibernate.criterion.DetachedCriteria; import org.hibernate.criterion.Projections; import org.quartz.SchedulerException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.Cache; import org.springframework.cache.CacheManager; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.Cacheable; import org.springframework.cache.annotation.Caching; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.crypto.codec.Hex; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import com.google.gson.ExclusionStrategy; import com.google.gson.FieldAttributes; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import fr.gael.dhus.database.dao.AccessRestrictionDao; import fr.gael.dhus.database.dao.CollectionDao; import fr.gael.dhus.database.dao.CountryDao; import fr.gael.dhus.database.dao.ProductDao; import fr.gael.dhus.database.dao.SearchDao; import fr.gael.dhus.database.dao.UserDao; import fr.gael.dhus.database.object.Collection; import fr.gael.dhus.database.object.Country; import fr.gael.dhus.database.object.Product; import fr.gael.dhus.database.object.Role; import fr.gael.dhus.database.object.Search; import fr.gael.dhus.database.object.User; import fr.gael.dhus.database.object.User.PasswordEncryption; import fr.gael.dhus.database.object.restriction.AccessRestriction; import fr.gael.dhus.messaging.mail.MailServer; import fr.gael.dhus.service.exception.EmailNotSentException; import fr.gael.dhus.service.exception.MalformedEmailException; import fr.gael.dhus.service.exception.ProductNotExistingException; import fr.gael.dhus.service.exception.RequiredFieldMissingException; import fr.gael.dhus.service.exception.RootNotModifiableException; import fr.gael.dhus.service.exception.UserBadEncryptionException; import fr.gael.dhus.service.exception.UserBadOldPasswordException; import fr.gael.dhus.service.exception.UserNotExistingException; import fr.gael.dhus.service.exception.UsernameBadCharacterException; import fr.gael.dhus.service.job.JobScheduler; import fr.gael.dhus.spring.context.SecurityContextProvider; import fr.gael.dhus.system.config.ConfigurationManager; /** * User Service provides connected clients with a set of method to interact with * it. */ @Service public class UserService extends WebService { private static final Logger LOGGER = LogManager.getLogger(UserService.class); @Autowired private SearchDao searchDao; @Autowired private CountryDao countryDao; @Autowired private UserDao userDao; @Autowired private CollectionDao collectionDao; @Autowired private ProductDao productDao; @Autowired private AccessRestrictionDao accessRestrictionDao; @Autowired private ConfigurationManager cfgManager; @Autowired private MailServer mailer; @Autowired private JobScheduler scheduler; @Autowired private SecurityService securityService; @Autowired private CacheManager cacheManager; /** * Pattern for username checking */ private static Pattern USERNAME_PATTERN = Pattern.compile("^[a-zA-Z0-9\\._\\-]+$"); /** * Pattern for email checking * Note: This pattern contains all the possible characters in an e-mail. * DHuS shall restrict these mail characters to enhance mailing security... * As far as mail servers already avoid a large part of possible mailing * hacks, no security breach is expected even if all the character are * authorized in DHuS... */ private static Pattern EMAIL_PATTERN = Pattern.compile("^[a-zA-Z0-9!#$%\\x26'*+/=?^_`{|}~-]+" + "(?:\\.[a-zA-Z0-9!#$%\\x26'*+/=?^_`{|}~-]+)*" + "@" + "(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\\.)+" + "[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?$"); /** * Return user corresponding to given id. * * @param id User id. * @throws RootNotModifiableException */ @PreAuthorize("hasAnyRole('ROLE_USER_MANAGER','ROLE_DATA_MANAGER','ROLE_SYSTEM_MANAGER')") @Transactional(readOnly = true, propagation = Propagation.REQUIRED) @Cacheable(value = "user", key = "#id") public User getUser(String id) throws RootNotModifiableException { User u = userDao.read(id); checkRoot(u); return u; } @Transactional(readOnly = true, propagation = Propagation.REQUIRED) public User getUserNoCache(String id) { User u = userDao.read(id); return u; } /** * Return user corresponding to given user name. * * @param name User name. * @throws RootNotModifiableException */ @PreAuthorize("hasAnyRole('ROLE_USER_MANAGER','ROLE_DATA_MANAGER','ROLE_SYSTEM_MANAGER')") @Transactional(readOnly = true, propagation = Propagation.REQUIRED) @Cacheable(value = "userByName", key = "#name?.toLowerCase()") public User getUserByName(String name) throws RootNotModifiableException { User u = this.getUserNoCheck(name); checkRoot(u); return u; } @Transactional(readOnly = true, propagation = Propagation.REQUIRED) @Cacheable(value = "userByName", key = "#name?.toLowerCase()") public User getUserNoCheck(String name) { return userDao.getByName(name); } /** * Get all users corresponding to given filter. * * @param filter * @return All users corresponding to given filter. */ @PreAuthorize("hasAnyRole('ROLE_USER_MANAGER','ROLE_SYSTEM_MANAGER')") @Transactional(readOnly = true, propagation = Propagation.REQUIRED) public Iterator<User> getUsers(String filter, int skip) { return userDao.scrollNotDeleted(filter, skip); } /** * Retrieves corresponding users at the given criteria. * * @param criteria criteria contains filter and order of required collection. * @param skip number of skipped valid results. * @param top max of valid results. * @return a list of {@link User} */ @Transactional(readOnly = true) public List<User> getUsers(DetachedCriteria criteria, int skip, int top) { if (criteria == null) { criteria = DetachedCriteria.forClass(User.class); } criteria.setFetchMode("roles", FetchMode.SELECT); criteria.setFetchMode("restrictions", FetchMode.SELECT); List<User> result = userDao.listCriteria(criteria, skip, top); return result; } /** * Counts corresponding users at the given criteria. * * @param criteria criteria contains filter of required collection. * @return number of corresponding users. */ @Transactional(readOnly = true) public int countUsers(DetachedCriteria criteria) { if (criteria == null) { criteria = DetachedCriteria.forClass(User.class); } criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY); criteria.setProjection(Projections.rowCount()); return userDao.count(criteria); } @PreAuthorize("hasRole('ROLE_STATS')") @Transactional(readOnly = true, propagation = Propagation.REQUIRED) public Iterator<User> getAllUsers(String filter, int skip) { return userDao.scrollAll(filter, skip); } @PreAuthorize("hasRole('ROLE_USER_MANAGER')") @Transactional(readOnly = true, propagation = Propagation.REQUIRED) public Iterator<User> getUsersForDataRight(String filter, int skip) { return userDao.scrollForDataRight(filter, skip); } /** * Create given User, after checking required fields. * * @param user * @throws RequiredFieldMissingException * @throws RootNotModifiableException */ @PreAuthorize("hasRole('ROLE_USER_MANAGER')") @Transactional(readOnly = false, propagation = Propagation.REQUIRED) public void createUser(User user) throws RequiredFieldMissingException, RootNotModifiableException, EmailNotSentException { systemCreateUser(user); } /** * Create given User, after checking required fields. * No @PreAuthorize. * * @param user * @throws RequiredFieldMissingException * @throws RootNotModifiableException */ @Transactional(readOnly = false) @CacheEvict(value = "userByName", key = "#user?.getUsername().toLowerCase()") public void systemCreateUser(User user) throws RequiredFieldMissingException, RootNotModifiableException, EmailNotSentException { checkRequiredFields(user); checkRoot(user); userDao.create(user); } /** * Create given User as temporary User, after checking required fields. * * @param user * @throws RequiredFieldMissingException * @throws RootNotModifiableException */ @Transactional(readOnly = false, propagation = Propagation.REQUIRED) public void createTmpUser(User user) throws RequiredFieldMissingException, RootNotModifiableException, EmailNotSentException { checkRequiredFields(user); checkRoot(user); userDao.createTmpUser(user); } @Transactional(readOnly = true, propagation = Propagation.REQUIRED) public Country getCountry(long id) { return countryDao.read(id); } @Transactional(readOnly = false, propagation = Propagation.REQUIRED) public void validateTmpUser(String code) { User u = userDao.getUserFromUserCode(code); if (u != null && userDao.isTmpUser(u)) { userDao.registerTmpUser(u); // update cache entries Cache cache = cacheManager.getCache("user"); if (cache != null) { synchronized (cache) { if (cache.get(u.getUUID()) != null) { cache.put(u.getUUID(), u); } } } cache = cacheManager.getCache("userByName"); if (cache != null) { synchronized (cache) { if (cache.get(u.getUsername()) != null) { cache.put(u.getUsername(), u); } } } } } @Transactional(readOnly = true, propagation = Propagation.REQUIRED) public boolean checkUserCodeForPasswordReset(String code) { return userDao.getUserFromUserCode(code) != null; } @Transactional(readOnly = false, propagation = Propagation.REQUIRED) @Caching(evict = { @CacheEvict(value = "user", allEntries = true), @CacheEvict(value = "userByName", allEntries = true), @CacheEvict(value = "json_user", allEntries = true) }) public void resetPassword(String code, String new_password) throws RootNotModifiableException, RequiredFieldMissingException, EmailNotSentException { User u = userDao.getUserFromUserCode(code); if (u == null) { throw new UserNotExistingException(); } checkRoot(u); u.setPassword(new_password); checkRequiredFields(u); userDao.update(u); } /** * Update given User, after checking required fields. * * @param user * @throws RootNotModifiableException * @throws RequiredFieldMissingException */ @PreAuthorize("hasRole('ROLE_USER_MANAGER')") @Transactional(readOnly = false, propagation = Propagation.REQUIRED) @Caching(evict = { @CacheEvict(value = "user", key = "#user?.getUUID ()"), @CacheEvict(value = "userByName", key = "#user?.username.toLowerCase()"), @CacheEvict(value = "json_user", key = "#user") }) public void updateUser(User user) throws RootNotModifiableException, RequiredFieldMissingException { User u = userDao.read(user.getUUID()); boolean updateRoles = user.getRoles().size() != u.getRoles().size(); if (!updateRoles) { int roleFound = 0; for (Role r : u.getRoles()) { if (user.getRoles().contains(r)) { roleFound++; } } updateRoles = roleFound != user.getRoles().size(); } checkRoot(u); u.setUsername(user.getUsername()); u.setFirstname(user.getFirstname()); u.setLastname(user.getLastname()); u.setAddress(user.getAddress()); u.setCountry(user.getCountry()); u.setEmail(user.getEmail()); u.setPhone(user.getPhone()); u.setRoles(user.getRoles()); u.setUsage(user.getUsage()); u.setSubUsage(user.getSubUsage()); u.setDomain(user.getDomain()); u.setSubDomain(user.getSubDomain()); if (user.getPassword() != null) { // If password is null, it means client forgot to set it up. // it should never been set to null. u.setEncryptedPassword(user.getPassword(), user.getPasswordEncryption()); } Set<AccessRestriction> restrictions = user.getRestrictions(); Set<AccessRestriction> restrictionsToDelete = u.getRestrictions(); if (u.getRestrictions() != null && user.getRestrictions() != null) { for (AccessRestriction oldOne : u.getRestrictions()) { for (AccessRestriction newOne : user.getRestrictions()) { if (oldOne.getBlockingReason().equals(newOne.getBlockingReason())) { restrictions.remove(newOne); restrictions.add(oldOne); restrictionsToDelete.remove(oldOne); } continue; } } } u.setRestrictions(restrictions); checkRequiredFields(u); userDao.update(u); if ((restrictions != null && !restrictions.isEmpty()) || updateRoles) { SecurityContextProvider.forceLogout(u.getUsername()); } if (restrictionsToDelete != null) { for (AccessRestriction restriction : restrictionsToDelete) { accessRestrictionDao.delete(restriction); } } // Fix to mail user when admin updates his account // Temp : to move in mail class after LOGGER.debug("User " + u.getUsername() + " Updated."); if (cfgManager.getMailConfiguration().isOnUserUpdate()) { String email = u.getEmail(); // Do not send mail to system admin : never used if (cfgManager.getAdministratorConfiguration().getName().equals(u.getUsername()) && (email == null)) email = "dhus@gael.fr"; LOGGER.debug("Sending email to " + email); if (email == null) throw new UnsupportedOperationException("Missing Email in configuration: " + "Cannot inform modified user \"" + u.getUsername() + "."); String message = new String("Dear " + getUserWelcome(u) + ",\n\nYour account on " + cfgManager.getNameConfiguration().getShortName() + " has been updated by an administrator:\n" + u.toString() + "\n" + "For help requests please write to: " + cfgManager.getSupportConfiguration().getMail() + "\n\n" + "Kind regards,\n" + cfgManager.getSupportConfiguration().getName() + ".\n" + cfgManager.getServerConfiguration().getExternalUrl()); String subject = new String("Account " + u.getUsername() + " updated"); try { mailer.send(email, null, null, subject, message); } catch (Exception e) { throw new EmailNotSentException("Cannot send email to " + email, e); } LOGGER.debug("email sent."); } } /** * Update given User, after checking required fields. * @param user * @throws RootNotModifiableException * @throws RequiredFieldMissingException */ @Transactional(readOnly = false) public void systemUpdateUser(User user) throws RootNotModifiableException, RequiredFieldMissingException { checkRoot(user); userDao.update(user); // FIXME reproduce updateUser()? } /** * Delete user corresponding to given id. * * @param uuid User id. * @throws RootNotModifiableException */ @PreAuthorize("hasRole('ROLE_USER_MANAGER')") @Transactional(readOnly = false, propagation = Propagation.REQUIRED) @Caching(evict = { @CacheEvict(value = "user", allEntries = true), @CacheEvict(value = "userByName", allEntries = true), @CacheEvict(value = "json_user", allEntries = true) }) public void deleteUser(String uuid) throws RootNotModifiableException, EmailNotSentException { User u = userDao.read(uuid); checkRoot(u); SecurityContextProvider.forceLogout(u.getUsername()); userDao.removeUser(u); } /** * Cout number of users corresponding to filter. * * @param filter * @return Number of users corresponding to filter. */ @PreAuthorize("hasAnyRole('ROLE_USER_MANAGER','ROLE_DATA_MANAGER','ROLE_SYSTEM_MANAGER')") @Transactional(readOnly = true, propagation = Propagation.REQUIRED) public int count(String filter) { return userDao.countNotDeleted(filter); } @PreAuthorize("hasRole('ROLE_STATS')") @Transactional(readOnly = true, propagation = Propagation.REQUIRED) public int countAll(String filter) { return userDao.countAll(filter); } @PreAuthorize("hasAnyRole('ROLE_USER_MANAGER','ROLE_DATA_MANAGER')") @Transactional(readOnly = true, propagation = Propagation.REQUIRED) public int countForDataRight(String filter) { return userDao.countForDataRight(filter); } @PreAuthorize("isAuthenticated ()") @Transactional(readOnly = true, propagation = Propagation.REQUIRED) public List<AccessRestriction> getRestrictions(String user_uuid) { return new ArrayList<>(userDao.read(user_uuid).getRestrictions()); } /** * THIS METHOD IS NOT SAFE: IT MUST BE REMOVED. * TODO: manage access by page. * @param user_uuid * @return */ @PreAuthorize("hasRole('ROLE_DATA_MANAGER')") @Transactional(readOnly = true, propagation = Propagation.REQUIRED) public List<Long> getAuthorizedProducts(String user_uuid) { return productDao.getAuthorizedProducts(user_uuid); } @PreAuthorize("hasRole('ROLE_DATA_MANAGER')") @Transactional(readOnly = true, propagation = Propagation.REQUIRED) public List<String> getAuthorizedCollections(String user_uuid) { return collectionDao.getAuthorizedCollections(user_uuid); } @PreAuthorize("hasRole('ROLE_DATA_MANAGER')") @Transactional(readOnly = false, propagation = Propagation.REQUIRED) public void addAccessToCollections(String user_uuid, List<String> collection_uuids) throws RootNotModifiableException { User user = userDao.read(user_uuid); checkRoot(user); // database for (String collectionUUID : collection_uuids) { Collection collection = collectionDao.read(collectionUUID); userDao.addAccessToCollection(user, collection); } } @PreAuthorize("hasRole('ROLE_DATA_MANAGER')") @Transactional(readOnly = false, propagation = Propagation.REQUIRED) public void removeAccessToCollections(String user_uuid, List<String> collection_uuids) throws RootNotModifiableException { User user = userDao.read(user_uuid); checkRoot(user); for (String collectionUUID : collection_uuids) { Collection collection = collectionDao.read(collectionUUID); userDao.removeAccessToCollection(user.getUUID(), collection); } } @PreAuthorize("hasRole('ROLE_DATA_MANAGER')") public void addAccessToProducts(Long user_id, List<Long> product_ids) throws RootNotModifiableException { // TODO to remove } @PreAuthorize("hasRole('ROLE_DATA_MANAGER')") public void removeAccessToProducts(Long user_id, List<Long> product_ids) throws RootNotModifiableException { // TODO to remove } /** * Update given User, after checking required fields. * * @param user * @throws RootNotModifiableException * @throws RequiredFieldMissingException */ @PreAuthorize("isAuthenticated ()") @Transactional(readOnly = false, propagation = Propagation.REQUIRED) @Caching(evict = { @CacheEvict(value = "user", key = "#user.getUUID ()"), @CacheEvict(value = "userByName", key = "#user.username.toLowerCase()"), @CacheEvict(value = "json_user", key = "#user") }) public void selfUpdateUser(User user) throws RootNotModifiableException, RequiredFieldMissingException, EmailNotSentException { User u = userDao.read(user.getUUID()); checkRoot(u); u.setEmail(user.getEmail()); u.setFirstname(user.getFirstname()); u.setLastname(user.getLastname()); u.setAddress(user.getAddress()); u.setPhone(user.getPhone()); u.setCountry(user.getCountry()); u.setUsage(user.getUsage()); u.setSubUsage(user.getSubUsage()); u.setDomain(user.getDomain()); u.setSubDomain(user.getSubDomain()); checkRequiredFields(u); userDao.update(u); } @PreAuthorize("isAuthenticated ()") @Transactional(readOnly = false, propagation = Propagation.REQUIRED) @Caching(evict = { @CacheEvict(value = "user", allEntries = true), @CacheEvict(value = "userByName", allEntries = true), @CacheEvict(value = "json_user", allEntries = true) }) public void selfChangePassword(String uuid, String old_password, String new_password) throws RootNotModifiableException, RequiredFieldMissingException, EmailNotSentException, UserBadOldPasswordException { User u = userDao.read(uuid); checkRoot(u); //encrypt old password to compare PasswordEncryption encryption = u.getPasswordEncryption(); if (encryption != PasswordEncryption.NONE) // when configurable { try { MessageDigest md = MessageDigest.getInstance(encryption.getAlgorithmKey()); old_password = new String(Hex.encode(md.digest(old_password.getBytes("UTF-8")))); } catch (Exception e) { throw new UserBadEncryptionException( "There was an error while encrypting password of user " + u.getUsername(), e); } } if (!u.getPassword().equals(old_password)) { throw new UserBadOldPasswordException("Old password is not correct."); } u.setPassword(new_password); checkRequiredFields(u); userDao.update(u); } @PreAuthorize("hasRole('ROLE_SEARCH')") @Transactional(readOnly = false, propagation = Propagation.REQUIRED) public void storeUserSearch(String uuid, String search, String footprint, HashMap<String, String> advanced, String complete) { User u = userDao.read(uuid); if (u == null) { throw new UserNotExistingException(); } for (Search s : u.getPreferences().getSearches()) { if (s.getComplete().equals(complete)) { return; } } userDao.storeUserSearch(u, search, footprint, advanced, complete); } @PreAuthorize("hasRole('ROLE_SEARCH')") @Transactional(readOnly = false, propagation = Propagation.REQUIRED) public void removeUserSearch(String u_uuid, String uuid) { User u = userDao.read(u_uuid); if (u == null) { throw new UserNotExistingException(); } userDao.removeUserSearch(u, uuid); } @PreAuthorize("hasRole('ROLE_SEARCH')") @Transactional(readOnly = false, propagation = Propagation.REQUIRED) public void activateUserSearchNotification(String uuid, boolean notify) { userDao.activateUserSearchNotification(uuid, notify); } @PreAuthorize("hasRole('ROLE_SEARCH')") @Transactional(readOnly = true, propagation = Propagation.REQUIRED) public int countUserSearches(String uuid) { User u = userDao.read(uuid); if (u == null) { throw new UserNotExistingException(); } List<Search> searches = userDao.getUserSearches(u); return searches != null ? searches.size() : 0; } @PreAuthorize("hasRole('ROLE_SEARCH')") @Transactional(readOnly = true, propagation = Propagation.REQUIRED) public int countUploadedProducts(String uuid) { User u = userDao.read(uuid); if (u == null) { throw new UserNotExistingException(); } List<Product> uploadeds = productDao.getUploadedProducts(u); return uploadeds != null ? uploadeds.size() : 0; } @PreAuthorize("hasRole('ROLE_SEARCH')") @Transactional(readOnly = false, propagation = Propagation.REQUIRED) public void clearSavedSearches(String uuid) { User u = userDao.read(uuid); if (u == null) { throw new UserNotExistingException(); } userDao.clearUserSearches(u); } @PreAuthorize("hasRole('ROLE_SEARCH')") @Transactional(readOnly = true, propagation = Propagation.REQUIRED) public List<Search> getAllUserSearches(String uuid) { User u = userDao.read(uuid); if (u == null) { throw new UserNotExistingException(); } return userDao.getUserSearches(u); } @PreAuthorize("hasRole('ROLE_SEARCH')") public Date getNextScheduleSearch() throws SchedulerException { return scheduler.getNextSearchesJobSchedule(); } @PreAuthorize("hasRole('ROLE_SEARCH')") @Transactional(readOnly = true, propagation = Propagation.REQUIRED) public List<Search> scrollSearchesOfUser(String uuid, int skip, int top) { User u = userDao.read(uuid); if (u == null) { throw new UserNotExistingException(); } return searchDao.scrollSearchesOfUser(u, skip, top); } @PreAuthorize("hasRole('ROLE_UPLOAD')") @Transactional(readOnly = true, propagation = Propagation.REQUIRED) public List<Product> getUploadedProducts(String uuid, int skip, int top) throws UserNotExistingException, ProductNotExistingException { User user = userDao.read(uuid); if (user == null) { throw new UserNotExistingException(); } return productDao.scrollUploadedProducts(user, skip, top); } @PreAuthorize("hasRole('ROLE_UPLOAD')") @Transactional(readOnly = true, propagation = Propagation.REQUIRED) public Set<String> getUploadedProductsIdentifiers(String uuid) throws UserNotExistingException, ProductNotExistingException { User user = userDao.read(uuid); if (user == null) { throw new UserNotExistingException(); } List<Product> products = productDao.getUploadedProducts(user); Set<String> prods = new HashSet<String>(); for (Product p : products) { prods.add(p.getIdentifier()); } return prods; } @Transactional(readOnly = true, propagation = Propagation.REQUIRED) public void forgotPassword(User user, String baseuri) throws UserNotExistingException, RootNotModifiableException, EmailNotSentException { checkRoot(user); User checked = userDao.getByName(user.getUsername()); if (checked == null || !checked.getEmail().toLowerCase().equals(user.getEmail().toLowerCase())) { throw new UserNotExistingException("No user can be found for this " + "username/mail combination"); } String message = "Dear " + getUserWelcome(checked) + ",\n\n" + "Please follow this link to set a new password in the " + cfgManager.getNameConfiguration().getShortName() + " system:\n" + cfgManager.getServerConfiguration().getExternalUrl() + baseuri + userDao.computeUserCode(checked) + "\n\n" + "For help requests please write to: " + cfgManager.getSupportConfiguration().getMail() + "\n\n" + "Kind regards.\n" + cfgManager.getSupportConfiguration().getName() + ".\n" + cfgManager.getServerConfiguration().getExternalUrl(); String subject = "User password reset"; try { mailer.send(checked.getEmail(), null, null, subject, message); } catch (Exception e) { throw new EmailNotSentException("Cannot send email to " + checked.getEmail(), e); } } @Transactional(readOnly = true, propagation = Propagation.REQUIRED) private void checkRoot(User user) throws RootNotModifiableException { if (user == null) return; if (userDao.isRootUser(user)) { throw new RootNotModifiableException("Root cannot be modified"); } } @Transactional(readOnly = true, propagation = Propagation.REQUIRED) private void checkRequiredFields(User user) throws RequiredFieldMissingException, UsernameBadCharacterException, MalformedEmailException { if (user.getUsername() == null || user.getUsername().trim().isEmpty() || user.getPassword() == null || user.getPassword().trim().isEmpty() || user.getEmail() == null || user.getEmail().trim().isEmpty()) { throw new RequiredFieldMissingException("At least one required field is empty."); } // Test username allowed chars [a-zA-Z0-9] if (!USERNAME_PATTERN.matcher(user.getUsername()).find()) { throw new UsernameBadCharacterException( "At least one forbidden character has been detected in username."); } // Test email field if (!EMAIL_PATTERN.matcher(user.getEmail()).find()) { throw new MalformedEmailException("Email is not well formed."); } } @Transactional(readOnly = true, propagation = Propagation.REQUIRED) private String getUserWelcome(User u) { String firstname = u.getUsername(); String lastname = ""; if (u.getFirstname() != null && !u.getFirstname().trim().isEmpty()) { firstname = u.getFirstname(); if (u.getLastname() != null && !u.getLastname().trim().isEmpty()) lastname = " " + u.getLastname(); } return firstname + lastname; } @Transactional(readOnly = true, propagation = Propagation.REQUIRED) public String getPublicDataUserUUID() { return userDao.getPublicData().getUUID(); } @Transactional(readOnly = true, propagation = Propagation.REQUIRED) public List<Country> getCountries() { return countryDao.readAll(); } @PreAuthorize("isAuthenticated ()") @Transactional(readOnly = true, propagation = Propagation.REQUIRED) public User getCurrentUserInformation() throws RootNotModifiableException { User u = securityService.getCurrentUser(); if (u == null) return null; return getUserByName(u.getUUID()); } /** * Facility method to easily provide user content with resolved lazy fields * to be able to serialize. The method takes care of the possible cycles * such as "users->pref->filescanners->collections->users" ... * It also removes possible huge product list from collections. * * @param u the user to resolve. * @return the resolved user. */ @Transactional(readOnly = true, propagation = Propagation.REQUIRED) @Cacheable(value = "json_user", key = "#u") public User resolveUser(User u) { u = userDao.read(u.getUUID()); Gson gson = new GsonBuilder().setExclusionStrategies(new ExclusionStrategy() { public boolean shouldSkipClass(Class<?> clazz) { // Avoid huge number of products in collection return clazz == Product.class; } /** * Custom field exclusion goes here */ public boolean shouldSkipField(FieldAttributes f) { // Avoid cycles caused by collection tree and user/auth users... return f.getName().equals("authorizedUsers") || f.getName().equals("parent") || f.getName().equals("subCollections"); } }).serializeNulls().create(); String users_string = gson.toJson(u); return gson.fromJson(users_string, User.class); } /* * Get all non deleted users corresponding to given filter from the specified offset and limit. * @param filter * @param skip * @param top * @return */ @PreAuthorize("hasRole('ROLE_USER_MANAGER')") @Transactional(readOnly = true, propagation = Propagation.REQUIRED) public Iterator<User> getUsersByFilter(String filter, int skip) { return userDao.scrollNotDeletedByFilter(filter, skip); } /** * Cout number of users corresponding to filter. * * @param filter * @return Number of users corresponding to filter. */ @PreAuthorize("hasAnyRole('ROLE_USER_MANAGER','ROLE_DATA_MANAGER')") @Transactional(readOnly = true, propagation = Propagation.REQUIRED) public int countByFilter(String filter) { return userDao.countNotDeletedByFilter(filter); } /** * Finds a referenced country in ISO norm. * @param country name, alpha2 or alpha3 of country. * @return true, if country name, alpha2 or alpha is referenced in ISO norme. */ @Transactional(readOnly = true) public Country getCountry(String country) { switch (country.length()) { case 2: return countryDao.getCountryByAlpha2(country); case 3: return countryDao.getCountryByAlpha3(country); default: return countryDao.getCountryByName(country); } } }