/*
* 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);
}
}
}