001/*
002 *  Copyright 2012 Anyware Services
003 *
004 *  Licensed under the Apache License, Version 2.0 (the "License");
005 *  you may not use this file except in compliance with the License.
006 *  You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 *  Unless required by applicable law or agreed to in writing, software
011 *  distributed under the License is distributed on an "AS IS" BASIS,
012 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *  See the License for the specific language governing permissions and
014 *  limitations under the License.
015 */
016package org.ametys.web.usermanagement;
017
018import java.io.IOException;
019import java.sql.Timestamp;
020import java.time.LocalDate;
021import java.time.ZoneId;
022import java.time.ZonedDateTime;
023import java.util.Arrays;
024import java.util.Date;
025import java.util.HashMap;
026import java.util.Iterator;
027import java.util.List;
028import java.util.Map;
029import java.util.Optional;
030import java.util.Set;
031import java.util.UUID;
032
033import org.apache.avalon.framework.configuration.Configuration;
034import org.apache.avalon.framework.configuration.ConfigurationException;
035import org.apache.avalon.framework.service.ServiceException;
036import org.apache.avalon.framework.service.ServiceManager;
037import org.apache.commons.lang.StringUtils;
038import org.apache.commons.lang3.RandomStringUtils;
039import org.apache.ibatis.session.SqlSession;
040
041import org.ametys.cms.repository.Content;
042import org.ametys.cms.transformation.URIResolver;
043import org.ametys.cms.transformation.URIResolverExtensionPoint;
044import org.ametys.core.datasource.AbstractMyBatisDAO;
045import org.ametys.core.user.InvalidModificationException;
046import org.ametys.core.user.User;
047import org.ametys.core.user.UserManager;
048import org.ametys.core.user.directory.ModifiableUserDirectory;
049import org.ametys.core.user.directory.NotUniqueUserException;
050import org.ametys.core.user.directory.UserDirectory;
051import org.ametys.core.user.population.PopulationContextHelper;
052import org.ametys.core.user.population.UserPopulation;
053import org.ametys.core.user.population.UserPopulationDAO;
054import org.ametys.core.util.I18nUtils;
055import org.ametys.core.util.URIUtils;
056import org.ametys.core.util.mail.SendMailHelper;
057import org.ametys.plugins.repository.AmetysObjectIterable;
058import org.ametys.plugins.repository.AmetysObjectResolver;
059import org.ametys.plugins.repository.AmetysRepositoryException;
060import org.ametys.plugins.repository.query.expression.Expression;
061import org.ametys.plugins.repository.query.expression.Expression.Operator;
062import org.ametys.runtime.i18n.I18nizableText;
063import org.ametys.runtime.i18n.I18nizableTextParameter;
064import org.ametys.runtime.parameter.Errors;
065import org.ametys.web.repository.page.Page;
066import org.ametys.web.repository.page.PageQueryHelper;
067import org.ametys.web.repository.page.ZoneItem;
068import org.ametys.web.repository.site.Site;
069import org.ametys.web.repository.site.SiteManager;
070import org.ametys.web.site.SiteConfigurationExtensionPoint;
071import org.ametys.web.tags.TagExpression;
072
073import com.google.common.collect.Multimap;
074
075import jakarta.mail.MessagingException;
076
077/**
078 * Manages registration and password recovery of users.
079 */
080public class UserSignupManager extends AbstractMyBatisDAO
081{
082
083    /** The component role. */
084    public static final String ROLE = UserSignupManager.class.getName();
085
086    /** Return code which indicates no error. */
087    public static final int SIGNUP_NO_ERROR = 0;
088
089    /** Temporary signup return code: a user tried to sign-up but the e-mail already exists in the temporary table. */
090    public static final int SIGNUP_ERROR_TEMP_EMAIL_ALREADY_EXISTS = 1;
091
092    /** Temporary signup return code: a user tried to sign-up but the FO UsersManager already possesses a user with this e-mail as login. */
093    public static final int SIGNUP_ERROR_USER_ALREADY_EXISTS = 2;
094
095    /** Token return code: a user provided a token, but it doesn't exist. */
096    public static final int SIGNUP_TOKEN_UNKNOWN = 3;
097
098    /** Token return code: a user provided a token, but it isn't valid anymore. */
099    public static final int SIGNUP_TOKEN_EXPIRED = 4;
100    
101    /** Token return code: undefined error. */
102    public static final int SIGNUP_ERROR = 5;
103    
104    /** Reset token return code: a user asked for a new token, but no subscription request can be found with this e-mail. */
105    public static final int SIGNUP_RESET_ERROR_EMAIL_UNKNOWN = 5;
106
107    /** The user manager. */
108    protected UserManager _userManager;
109    
110    /** The DAO for user populations */
111    protected UserPopulationDAO _userPopulationDAO;
112
113    /** The site manager. */
114    protected SiteManager _siteManager;
115
116    /** The site configuration extension point. */
117    protected SiteConfigurationExtensionPoint _siteConf;
118
119    /** The ametys object resolver. */
120    protected AmetysObjectResolver _resolver;
121
122    /** The ametys object resolver. */
123    protected URIResolverExtensionPoint _uriResolverEP;
124
125    /** The page URI resolver. */
126    protected URIResolver _pageUriResolver;
127
128    /** The i18n utils. */
129    protected I18nUtils _i18nUtils;
130
131    /** The user sign up configuration */
132    protected UserSignUpConfiguration _userSignUpConfiguration;
133    
134    /** The temporary users table. */
135    protected String _tempUsersTable;
136
137    /** The password change table. */
138    protected String _pwdChangeTable;
139
140    /** The population context helper. */
141    protected PopulationContextHelper _populationContextHelper;
142
143    /** Enumeration for different cases of lost password errors */
144    public enum LostPasswordError
145    {
146        /** User not connected */
147        NOT_CONNECTED,
148        /** Lost password return code: the login or e-mail the user provided doesn't correspond to a population. */
149        USER_UNKNOWN,
150        /** Lost password return code: the population the user provided doesn't correspond to a user. */
151        POPULATION_UNKNOWN,
152        /** Lost password return code: the user provided belongs to an unmodifiable user directory */
153        UNMODIFIABLE_USER_DIRECTORY,
154        /** Lost password return code: the user provided have an empty email */
155        EMPTY_EMAIL,
156        /** Lost password return code: the informations the user provided match several users */
157        SEVERAL_USERS,
158        /** Token return code: a user provided a token, but it doesn't exist. */
159        TOKEN_UNKNOWN,
160        /** Token return code: a user provided a token, but it isn't valid anymore. */
161        TOKEN_EXPIRED;
162    }
163    
164    @Override
165    public void service(ServiceManager serviceManager) throws ServiceException
166    {
167        super.service(serviceManager);
168        
169        _userManager = (UserManager) serviceManager.lookup(UserManager.ROLE);
170        _userPopulationDAO = (UserPopulationDAO) serviceManager.lookup(UserPopulationDAO.ROLE);
171        _siteManager = (SiteManager) serviceManager.lookup(SiteManager.ROLE);
172        _siteConf = (SiteConfigurationExtensionPoint) serviceManager.lookup(SiteConfigurationExtensionPoint.ROLE);
173        _resolver = (AmetysObjectResolver) serviceManager.lookup(AmetysObjectResolver.ROLE);
174        _uriResolverEP = (URIResolverExtensionPoint) serviceManager.lookup(URIResolverExtensionPoint.ROLE);
175        _i18nUtils = (I18nUtils) serviceManager.lookup(I18nUtils.ROLE);
176        _userSignUpConfiguration = (UserSignUpConfiguration) serviceManager.lookup(UserSignUpConfiguration.ROLE);
177        _populationContextHelper = (PopulationContextHelper) serviceManager.lookup(PopulationContextHelper.ROLE);
178    }
179
180    @Override
181    public void configure(Configuration configuration) throws ConfigurationException
182    {
183        super.configure(configuration);
184        
185        // Configure pool and tables.
186        _tempUsersTable = configuration.getChild("temp-users-table").getValue();
187        _pwdChangeTable = configuration.getChild("pwd-change-table").getValue();
188    }
189
190    /**
191     * Test if public signup is allowed in a site.
192     * @param siteName the site to test.
193     * @return true if public signup is allowed, false otherwise.
194     */
195    public boolean isPublicSignupAllowed(String siteName)
196    {
197        Site site = _siteManager.getSite(siteName);
198        return site.getValue("public-signup", false, false);
199    }
200
201    /**
202     * Get the sign-up page in a given site and sitemap.<br>
203     * If more than one page are tagged "sign-up", return the first.
204     * @param siteName the site name.
205     * @param language the sitemap name.
206     * @return the sign-up page or null if not found.
207     */
208    public Page getSignupPage(String siteName, String language)
209    {
210        Page page = null;
211
212        try (AmetysObjectIterable<Page> pages = getSignupPages(siteName, language);)
213        {
214            Iterator<Page> it = pages.iterator();
215            if (it.hasNext())
216            {
217                page = it.next();
218            }
219        }
220
221        return page;
222    }
223
224    /**
225     * Get all the pages tagged "sign-up".
226     * @param siteName the site name.
227     * @param language the sitemap name.
228     * @return an iterable on all the pages tagged "sign-up".
229     */
230    public AmetysObjectIterable<Page> getSignupPages(String siteName, String language)
231    {
232        Expression expression = new TagExpression(Operator.EQ, "USER_SIGNUP");
233        String query = PageQueryHelper.getPageXPathQuery(siteName, language, null, expression, null);
234
235        return _resolver.query(query);
236    }
237
238    /**
239     * Get the password change page in a given site and sitemap.
240     * If more than one page are tagged "password change", return the first.
241     * @param siteName the site name.
242     * @param language the sitemap name.
243     * @return the password change page or null if not found.
244     */
245    public Page getPwdChangePage(String siteName, String language)
246    {
247        Page page = null;
248
249        try (AmetysObjectIterable<Page> pages = getPwdChangePages(siteName, language);)
250        {
251            Iterator<Page> it = pages.iterator();
252            if (it.hasNext())
253            {
254                page = it.next();
255            }
256        }
257
258        return page;
259    }
260    
261    /**
262     * Get the GTU page
263     * Returns null if not found or empty
264     * @param zoneItem the zone item
265     * @return the GTU page or null.
266     */
267    public Page getGTUPage(ZoneItem zoneItem)
268    {
269        Page page = null;
270
271        try
272        {
273            String tosMode = zoneItem.getServiceParameters().getValue("terms-of-service-mode");
274            if ("PAGE".equals(tosMode))
275            {
276                String tosPageId = zoneItem.getServiceParameters().getValue("terms-of-service-page");
277                if (StringUtils.isNotEmpty(tosPageId))
278                {
279                    page = _resolver.resolveById(tosPageId);
280                }
281            }
282        }
283        catch (AmetysRepositoryException e)
284        {
285            // Nothing
286        }
287
288        return page;
289    }
290    
291    /**
292     * Get the GTU content
293     * Returns null if not found or empty
294     * @param zoneItem the zone item
295     * @return the GTU content or null.
296     */
297    public Content getGTUContent(ZoneItem zoneItem)
298    {
299        Content content = null;
300
301        try
302        {
303            String tosMode = zoneItem.getServiceParameters().getValue("terms-of-service-mode");
304            if ("CONTENT".equals(tosMode))
305            {
306                String tosContentId = zoneItem.getServiceParameters().getValue("terms-of-service-content");
307                if (StringUtils.isNotEmpty(tosContentId))
308                {
309                    content = _resolver.resolveById(tosContentId);
310                }
311            }
312            
313        }
314        catch (AmetysRepositoryException e)
315        {
316            // Nothing
317        }
318
319        return content;
320    }
321    
322    /**
323     * Get the GTU page
324     * Returns null if not found or empty
325     * @param zoneItem the zone item
326     * @return the success page or null.
327     */
328    public Page getSuccessPage(ZoneItem zoneItem)
329    {
330        Page page = null;
331
332        try
333        {
334            String successMode = zoneItem.getServiceParameters().getValue("success-mode");
335            if ("PAGE".equals(successMode))
336            {
337                String successPageId = zoneItem.getServiceParameters().getValue("success-page");
338                if (StringUtils.isNotEmpty(successPageId))
339                {
340                    page = _resolver.resolveById(successPageId);
341                }
342            }
343        }
344        catch (AmetysRepositoryException e)
345        {
346            // Nothing
347        }
348
349        return page;
350    }
351    
352    /**
353     * Get the success content
354     * Returns null if not found or empty
355     * @param zoneItem the zone item
356     * @return the success content or null.
357     */
358    public Content getSuccessContent(ZoneItem zoneItem)
359    {
360        Content content = null;
361
362        try
363        {
364            String successMode = zoneItem.getServiceParameters().getValue("success-mode");
365            if ("CONTENT".equals(successMode))
366            {
367                String successContentId = zoneItem.getServiceParameters().getValue("success-content");
368                if (StringUtils.isNotEmpty(successContentId))
369                {
370                    content = _resolver.resolveById(successContentId);
371                }
372            }
373        }
374        catch (AmetysRepositoryException e)
375        {
376            // Nothing
377        }
378
379        return content;
380    }
381
382    /**
383     * Get all the pages tagged "password change".
384     * @param siteName the site name.
385     * @param language the sitemap name.
386     * @return an iterable on all the pages tagged "password change".
387     */
388    public AmetysObjectIterable<Page> getPwdChangePages(String siteName, String language)
389    {
390        Expression expression = new TagExpression(Operator.EQ, "USER_PASSWORD_CHANGE");
391        String query = PageQueryHelper.getPageXPathQuery(siteName, language, null, expression, null);
392
393        return _resolver.query(query);
394    }
395    
396    /**
397     * Get the user from email if he/she exists in the populations 
398     * @param email the e-mail to test.
399     * @param siteName The site name
400     * @return the user if the user exists, empty otherwise.
401     * @throws UserManagementException if an error occurs.
402     * @throws NotUniqueUserException if several user respond to the email
403     */
404    public Optional<User> getUserIfHeExists(String email, String siteName) throws UserManagementException, NotUniqueUserException
405    {
406        Set<String> populationIds = _populationContextHelper.getUserPopulationsOnContexts(Arrays.asList("/sites/" + siteName, "/sites-fo/" + siteName), false, false);
407        for (String population : populationIds)
408        {
409            User user = _userManager.getUser(population, email);
410            if (user != null)
411            {
412                return Optional.of(user);
413            }
414            else
415            {
416                User userByEmail = _userManager.getUserByEmail(population, email);
417                if (userByEmail != null)
418                {
419                    return Optional.of(userByEmail);
420                }
421            }
422        }
423        
424        return Optional.empty();
425    }
426    
427    /**
428     * Tests if the user already exists in the populations 
429     * @param email the e-mail to test.
430     * @param siteName The site name
431     * @return true if the user exists, false otherwise.
432     * @throws UserManagementException if an error occurs.
433     */
434    public boolean userExists(String email, String siteName) throws UserManagementException
435    {
436        try
437        {
438            return getUserIfHeExists(email, siteName).isPresent();
439        }
440        catch (NotUniqueUserException e)
441        {
442            return true;
443        }
444    }
445
446    /**
447     * Validate the user subscription data.
448     * @param siteName the site name.
449     * @param email the user e-mail.
450     * @param additionalValues the additional user values.
451     * @return a Map of the Errors by field.
452     * @throws UserManagementException if an error occurs.
453     */
454    public Map<String, Errors> validate(String siteName, String email, Map<String, String> additionalValues) throws UserManagementException
455    {
456        Map<String, String> userInfos = new HashMap<>();
457        
458        userInfos.putAll(additionalValues);
459        
460        // Standard info for user (provide a dummy password, as we do not know it yet).
461        userInfos.put("login", email);
462        userInfos.put("email", email);
463        userInfos.put("password", "password");
464
465        Map<String, Errors> usersManagerErrors = new HashMap<>(); //foUsersManager.validate(userInfos);
466        Map<String, Errors> errors = new HashMap<>(usersManagerErrors);
467
468        // If there are errors on the login, do not return it except if
469        if (errors.containsKey("login"))
470        {
471            if (!errors.containsKey("email"))
472            {
473                errors.put("email", errors.get("login"));
474            }
475            errors.remove("login");
476        }
477
478        return errors;
479    }
480
481    /**
482     * Validate the user password.
483     * @param siteName the site name.
484     * @param password the password to validate.
485     * @param login the login of the user
486     * @param population The id of the population
487     * @return a Map of the Errors by field.
488     * @throws UserManagementException if an error occurs.
489     */
490    public Map<String, Errors> validatePassword(String siteName, String password, String login, String population) throws UserManagementException
491    {
492        Map<String, String> userInfos = new HashMap<>();
493
494        userInfos.put("password", password);
495
496        UserDirectory userDirectory = _userManager.getUserDirectory(population, login);
497        if (!(userDirectory instanceof ModifiableUserDirectory))
498        {
499            throw new UserManagementException("The user subscription feature can't be used, as the UserDirectory is not modifiable.");
500        }
501        Map<String, Errors> usersManagerErrors = ((ModifiableUserDirectory) userDirectory).validate(userInfos);
502        Map<String, Errors> errors = new HashMap<>();
503
504        // Keep only errors related to the password field.
505        if (usersManagerErrors.containsKey("password"))
506        {
507            errors.put("password", usersManagerErrors.get("password"));
508        }
509
510        return errors;
511    }
512
513    /**
514     * Validate and store a sign-up request and send a confirmation e-mail.
515     * @param siteName the site name.
516     * @param language the sitemap name.
517     * @param email the user e-mail address.
518     * @param population the population
519     * @param userDirectoryId the id of the user directory of the population
520     * @return a status code indicating success or error.
521     * @throws UserManagementException if an error occurs.
522     */
523    public int temporarySignup(String siteName, String language, String email, String population, String userDirectoryId) throws UserManagementException
524    {
525        return temporarySignup(siteName, language, email, population, userDirectoryId, true);
526    }
527    
528    /**
529     * Validate and store a sign-up request and send a confirmation e-mail.
530     * @param siteName the site name.
531     * @param language the sitemap name.
532     * @param email the user e-mail address.
533     * @param population the population
534     * @param userDirectoryId the id of the user directory of the population
535     * @param sendMail Set to false to not send mail at end of process
536     * @return a status code indicating success or error.
537     * @throws UserManagementException if an error occurs.
538     */
539    public int temporarySignup(String siteName, String language, String email, String population, String userDirectoryId, boolean sendMail) throws UserManagementException
540    {
541        // Verify that public sign-up is allowed and throw an exception otherwise.
542        checkPublicSignup(siteName);
543
544        if (getLogger().isDebugEnabled())
545        {
546            getLogger().debug("A user is requesting a sign-up: " + email);
547        }
548
549        // Generate unique token.
550        String token = UUID.randomUUID().toString().replace("-", "");
551
552        int status = addTemporaryUser(siteName, email, token, population, userDirectoryId);
553
554        // Send validation e-mail with token.
555        if (status == SIGNUP_NO_ERROR && sendMail)
556        {
557            sendSignupConfirmMail(siteName, language, email, token);
558        }
559
560        return status;
561    }
562    
563    /**
564     * Get a token for temp user
565     * @param siteName the site name.
566     * @param email the user e-mail address.
567     * @param population The id of the population
568     * @param userDirectoryId The id of the user directory of the population
569     * @return the user token or null if not found
570     * @throws UserManagementException if an error occurs.
571     */
572    public String getToken(String siteName, String email, String population, String userDirectoryId) throws UserManagementException
573    {
574        TempUser tempUser = getTempUser(siteName, email, population, userDirectoryId);
575        if (tempUser != null)
576        {
577            return tempUser.getToken();
578        }
579        
580        return null;
581    }
582
583    /**
584     * Reset a sign-up request: generate a new token and re-send the confirmation e-mail.
585     * @param siteName the site name.
586     * @param language the sitemap name.
587     * @param email the user e-mail address.
588     * @param population The id of the population
589     * @param userDirectoryId The id of the user directory of the population
590     * @return a status code indicating success or error.
591     * @throws UserManagementException if an error occurs.
592     */
593    public int resetTempSignup(String siteName, String language, String email, String population, String userDirectoryId) throws UserManagementException
594    {
595        // Verify that public sign-up is allowed and throw an exception otherwise.
596        checkPublicSignup(siteName);
597
598        if (getLogger().isDebugEnabled())
599        {
600            getLogger().debug("Resetting temporary signup for email: " + email + " in site " + siteName);
601        }
602
603        // Generate a new token.
604        String newToken = UUID.randomUUID().toString().replace("-", "");
605
606        // Test if the subscription request really exists.
607        TempUser tempUser = getTempUser(siteName, email, population, userDirectoryId);
608
609        if (tempUser == null)
610        {
611            // No subscription request with this e-mail.
612            return SIGNUP_RESET_ERROR_EMAIL_UNKNOWN;
613        }
614
615        updateTempToken(siteName, email, newToken, population, userDirectoryId);
616
617        sendSignupConfirmMail(siteName, language, email, newToken);
618
619        return SIGNUP_NO_ERROR;
620    }
621
622
623    /**
624     * Create the user in the FO UsersManager from his temporary request.
625     * @param siteName the site name.
626     * @param language the current language
627     * @param firstname the user firstname
628     * @param lastname the user lastname
629     * @param email the user e-mail address.
630     * @param token the request token.
631     * @param password the user password.
632     * @param population The id of the population
633     * @param userDirectoryId The id of the user directory of the population
634     * @param errors the errors
635     * @return a status code indicating success or error.
636     * @throws UserManagementException if an error occurs.
637     */
638    public int signup(String siteName, String language, String firstname, String lastname, String email, String token, String password, String population, String userDirectoryId, Multimap<String, I18nizableText> errors) throws UserManagementException
639    {
640        // Verify that public sign-up is allowed and throw an exception otherwise.
641        checkPublicSignup(siteName);
642
643        Map<String, String> userInfos = new HashMap<>();
644
645        TempUser tempUser = getTempUser(siteName, email, token, population, userDirectoryId);
646
647        if (tempUser == null)
648        {
649            return SIGNUP_TOKEN_UNKNOWN;
650        }
651        
652        // Generate login
653        String login = RandomStringUtils.randomNumeric(10);
654
655        // Standard info for user.
656        userInfos.put("login", login);
657        userInfos.put("email", email);
658        userInfos.put("firstname", firstname);
659        userInfos.put("lastname", lastname);
660        userInfos.put("password", password);
661
662        try
663        {
664            validationBeforeSignup(errors);
665            
666            if (errors.isEmpty())
667            {
668                // Add the user.
669                ModifiableUserDirectory userDirectory = (ModifiableUserDirectory) _userPopulationDAO.getUserPopulation(population).getUserDirectory(userDirectoryId);
670                userDirectory.add(userInfos);
671
672                // Remove the temporary user.
673                removeTempUser(siteName, email, token, population, userDirectoryId);
674                
675                User createdUser = userDirectory.getUser(login);
676                
677                // Do additional operations after signup
678                additionalSignupOperations(createdUser, errors);
679                
680                if (errors.isEmpty())
681                {
682                    sendSignupValidatedMail(siteName, language, createdUser);
683                    return SIGNUP_NO_ERROR;
684                }
685            }
686            
687            return SIGNUP_ERROR;
688        }
689        catch (InvalidModificationException e)
690        {
691            throw new UserManagementException("An error occurred signing up the user.", e);
692        }
693    }
694    
695    /**
696     * Do some validation before signup
697     * @param errors the map of errors to fill in cause of errors during validation
698     */
699    public void validationBeforeSignup(Multimap<String, I18nizableText> errors)
700    {
701        // Nothing
702    }
703    
704    /**
705     * Process additional operations after creation of user
706     * @param createdUser the created user
707     * @param errors the map of errors to fill in case of errors during additional operations
708     * @throws UserManagementException if an error occurs.
709     */
710    public void additionalSignupOperations(User createdUser, Multimap<String, I18nizableText> errors) throws UserManagementException
711    {
712        // Nothing
713    }
714    
715
716    /**
717     * Create a reset password request and send a confirmation e-mail.
718     * @param siteName the site name.
719     * @param language the sitemap name.
720     * @param loginOrEmail the user login or email.
721     * @param populationId the population
722     * @return a status code indicating success or error.
723     * @throws UserManagementException if an error occurs.
724     */
725    public LostPasswordError resetPassword(String siteName, String language, String loginOrEmail, String populationId) throws UserManagementException
726    {
727        // Check if the population exists
728        UserPopulation population = _userPopulationDAO.getUserPopulation(populationId);
729        if (population == null)
730        {
731            return LostPasswordError.POPULATION_UNKNOWN;
732        }
733        
734        // Check if the user exists and get it
735        User user = _userManager.getUser(populationId, loginOrEmail);
736        if (user == null)
737        {
738            try
739            {
740                user = _userManager.getUserByEmail(populationId, loginOrEmail);
741                if (user == null)
742                {
743                    // No user with this e-mail or login.
744                    return LostPasswordError.USER_UNKNOWN;
745                }
746            }
747            catch (NotUniqueUserException e)
748            {
749                return LostPasswordError.SEVERAL_USERS;
750            }
751        }
752        
753        // Check if the user directory is modifiable
754        if (!(user.getUserDirectory() instanceof ModifiableUserDirectory))
755        {
756            return LostPasswordError.UNMODIFIABLE_USER_DIRECTORY;
757        }
758        
759        if (StringUtils.isEmpty(user.getEmail()))
760        {
761            return LostPasswordError.EMPTY_EMAIL;
762        }
763
764        // Generate a new token.
765        String token = UUID.randomUUID().toString().replace("-", "");
766
767        // Insert the token in the database.
768        addPasswordToken(siteName, user.getIdentity().getLogin(), token, populationId);
769
770        // Send the e-mail.
771        sendResetPasswordMail(siteName, language, user, token);
772
773        return null;
774    }
775
776    /**
777     * Change the user password.
778     * @param siteName the site name.
779     * @param login the user login.
780     * @param token the password change request token.
781     * @param newPassword the new password.
782     * @param population the population
783     * @return a status code indicating success or error.
784     * @throws UserManagementException if an error occurs.
785     */
786    public int changeUserPassword(String siteName, String login, String token, String newPassword, String population) throws UserManagementException
787    {
788        int tokenStatus = checkPasswordToken(siteName, login, token, population);
789
790        if (tokenStatus != SIGNUP_NO_ERROR)
791        {
792            return tokenStatus;
793        }
794
795        Map<String, String> userInfos = new HashMap<>();
796
797        userInfos.put("login", login);
798        userInfos.put("password", newPassword);
799
800        try
801        {
802            UserDirectory userDirectory = _userManager.getUserDirectory(population, login);
803            if (!(userDirectory instanceof ModifiableUserDirectory))
804            {
805                throw new UserManagementException("The user's password can't be changed, as the UserDirectory is not modifiable.");
806            }
807            ((ModifiableUserDirectory) userDirectory).update(userInfos);
808
809            removePasswordToken(siteName, login, token, population);
810
811            return SIGNUP_NO_ERROR;
812        }
813        catch (InvalidModificationException e)
814        {
815            throw new UserManagementException("An error occurred signing up the user.", e);
816        }
817    }
818    
819    /**
820     * Check the sign-up request token.
821     * @param siteName the site name.
822     * @param email the user e-mail.
823     * @param token the sign-up request token.
824     * @param population The id of the population
825     * @param userDirectoryId The id of the user directory of the population
826     * @return a status code indicating success or error.
827     * @throws UserManagementException if an error occurs.
828     */
829    public int checkToken(String siteName, String email, String token, String population, String userDirectoryId) throws UserManagementException
830    {
831        removeExpiredTokens();
832
833        try (SqlSession sqlSession = getSession())
834        {
835            String stmtId = "UserSignupManager.getSubscriptionDate";
836            
837            Map<String, Object> params = new HashMap<>();
838            params.put("tempUsersTable", _tempUsersTable);
839            
840            params.put("site", siteName);
841            params.put("email", email);
842            params.put("token", token);
843            params.put("population", population);
844            params.put("userDirectory", userDirectoryId);
845            
846            Date rawSubscriptionDate = sqlSession.selectOne(stmtId, params);
847
848            // Date verification.
849            if (rawSubscriptionDate == null)
850            {
851                return SIGNUP_TOKEN_UNKNOWN;
852            }
853            
854            // The validity limit
855            ZonedDateTime limit = LocalDate.now().atStartOfDay(ZoneId.systemDefault()).minusDays(_userSignUpConfiguration.getTokenValidity());
856            ZonedDateTime subscriptionDate = rawSubscriptionDate.toInstant().atZone(ZoneId.systemDefault());
857
858            if (subscriptionDate.isBefore(limit))
859            {
860                return SIGNUP_TOKEN_EXPIRED;
861            }
862
863            return SIGNUP_NO_ERROR;
864        }
865        catch (Exception e)
866        {
867            throw new UserManagementException("Database error while testing the token for user [" + email + "]", e);
868        }
869    }
870
871    /**
872     * Check the password change request token.
873     * @param siteName the site name.
874     * @param login the user login.
875     * @param token the password change request token.
876     * @param population the population
877     * @return a status code indicating success or error.
878     * @throws UserManagementException if an error occurs.
879     */
880    public int checkPasswordToken(String siteName, String login, String token, String population) throws UserManagementException
881    {
882        removeExpiredPasswordTokens();
883
884        try (SqlSession sqlSession = getSession())
885        {
886            String stmtId = "UserSignupManager.getRequestDate";
887            
888            Map<String, Object> params = new HashMap<>();
889            params.put("pwdChangeTable", _pwdChangeTable);
890            
891            params.put("site", siteName);
892            params.put("login", login);
893            params.put("token", token);
894            params.put("population", population);
895            
896            Date rawRequestDate = sqlSession.selectOne(stmtId, params);
897
898            // Date verification.
899            if (rawRequestDate == null)
900            {
901                return SIGNUP_TOKEN_UNKNOWN;
902            }
903
904            // Check the validity.
905            ZonedDateTime limit = LocalDate.now().atStartOfDay(ZoneId.systemDefault()).minusDays(_userSignUpConfiguration.getTokenValidity());
906            ZonedDateTime requestDate = rawRequestDate.toInstant().atZone(ZoneId.systemDefault());
907
908            if (requestDate.isBefore(limit))
909            {
910                return SIGNUP_TOKEN_EXPIRED;
911            }
912
913            return SIGNUP_NO_ERROR;
914        }
915        catch (Exception e)
916        {
917            throw new UserManagementException("Database error while testing the password token for user " + login, e);
918        }
919    }
920
921    /**
922     * Remove the expired sign-up request tokens.
923     * @throws UserManagementException if an error occurs.
924     */
925    public void removeExpiredTokens() throws UserManagementException
926    {
927        try (SqlSession sqlSession = getSession())
928        {
929            String stmtId = "UserSignupManager.deleteExpiredTokens";
930            
931            Map<String, Object> params = new HashMap<>();
932            params.put("tempUsersTable", _tempUsersTable);
933            
934            ZonedDateTime limit = LocalDate.now().atStartOfDay(ZoneId.systemDefault()).minusDays(_userSignUpConfiguration.getTokenValidity());
935            Timestamp limitTimestamp = Timestamp.from(limit.toInstant());
936            params.put("threshold", limitTimestamp);
937            
938            sqlSession.delete(stmtId, params);
939            sqlSession.commit();
940        }
941        catch (Exception e)
942        {
943            throw new UserManagementException("Database error while removing the expired tokens.", e);
944        }
945    }
946
947    /**
948     * Remove the expired change password request tokens.
949     * @throws UserManagementException if an error occurs.
950     */
951    public void removeExpiredPasswordTokens() throws UserManagementException
952    {
953        try (SqlSession sqlSession = getSession())
954        {
955            String stmtId = "UserSignupManager.deleteExpiredPasswordTokens";
956            
957            Map<String, Object> params = new HashMap<>();
958            params.put("pwdChangeTable", _pwdChangeTable);
959            
960            ZonedDateTime limit = LocalDate.now().atStartOfDay(ZoneId.systemDefault()).minusDays(_userSignUpConfiguration.getTokenValidity());
961            Timestamp limitTimestamp = Timestamp.from(limit.toInstant());
962            params.put("threshold", limitTimestamp);
963            
964            sqlSession.delete(stmtId, params);
965            sqlSession.commit();
966        }
967        catch (Exception e)
968        {
969            throw new UserManagementException("Database error while removing the expired tokens.", e);
970        }
971    }
972
973    /**
974     * Verify that public sign-up is allowed. If not, throw an exception.
975     * @param siteName the site name.
976     * @throws UserManagementException if public sign-up is not enabled.
977     */
978    protected void checkPublicSignup(String siteName) throws UserManagementException
979    {
980        if (!isPublicSignupAllowed(siteName))
981        {
982            throw new UserManagementException("Public signup is disabled for this site.");
983        }
984    }
985
986    /**
987     * Create a user sign-up request ("temporary" user) in the database.
988     * @param siteName the site name.
989     * @param email the user e-mail.
990     * @param token the generated token.
991     * @param population the population
992     * @param userDirectoryId the id of the user directory of the population
993     * @return a status code indicating success or error.
994     * @throws UserManagementException if an error occurs.
995     */
996    protected int addTemporaryUser(String siteName, String email, String token, String population, String userDirectoryId) throws UserManagementException
997    {
998        try (SqlSession sqlSession = getSession())
999        {
1000            // Does this email already exists
1001            String stmtId = "UserSignupManager.tempEmailExists";
1002            
1003            Map<String, Object> params = new HashMap<>();
1004            params.put("tempUsersTable", _tempUsersTable);
1005            
1006            params.put("site", siteName);
1007            params.put("email", email);
1008            params.put("population", population);
1009            params.put("userDirectory", userDirectoryId);
1010            
1011            List<String> emails = sqlSession.selectList(stmtId, params);
1012            
1013            if (!emails.isEmpty())
1014            {
1015                return SIGNUP_ERROR_TEMP_EMAIL_ALREADY_EXISTS;
1016            }
1017            
1018            // Add temp user
1019            stmtId = "UserSignupManager.addTempUser";
1020            params = new HashMap<>();
1021            params.put("tempUsersTable", _tempUsersTable);
1022            
1023            Timestamp now = new Timestamp(System.currentTimeMillis());
1024            
1025            params.put("site", siteName);
1026            params.put("email", email);
1027            params.put("population", population);
1028            params.put("userDirectory", userDirectoryId);
1029            params.put("subscription_date", now);
1030            params.put("token", token);
1031            
1032            sqlSession.insert(stmtId, params);
1033            sqlSession.commit();
1034            
1035            return SIGNUP_NO_ERROR;
1036        }
1037        catch (Exception e)
1038        {
1039            throw new UserManagementException("Database error while signing up a new user [" + email + "]", e);
1040        }
1041    }
1042
1043    /**
1044     * Send a sign-up confirmation link by e-mail.
1045     * @param siteName the site name.
1046     * @param language the e-mail language.
1047     * @param email the user e-mail.
1048     * @param token the generated token.
1049     * @throws UserManagementException if an error occurs.
1050     */
1051    protected void sendSignupConfirmMail(String siteName, String language, String email, String token) throws UserManagementException
1052    {
1053        if (getLogger().isDebugEnabled())
1054        {
1055            getLogger().debug("Sending signup confirmation e-mail to " + email);
1056        }
1057
1058        Site site = _siteManager.getSite(siteName);
1059        String from = site.getValue("site-mail-from");
1060
1061        // Prepare mail.
1062        Map<String, I18nizableTextParameter> i18nParams = new HashMap<>();
1063        i18nParams.put("siteName", new I18nizableText(siteName));
1064        i18nParams.put("email", new I18nizableText(email));
1065        i18nParams.put("token", new I18nizableText(token));
1066
1067        Page signupPage = getSignupPage(siteName, language);
1068        if (signupPage != null)
1069        {
1070            if (_pageUriResolver == null)
1071            {
1072                _pageUriResolver = _uriResolverEP.getResolverForType("page");
1073            }
1074
1075            String encodedEmail = URIUtils.encodeParameter(email);
1076
1077            // Compute the confirmation URI and add it to the parameters.
1078            String confirmUri = _pageUriResolver.resolve(signupPage.getId(), false, true, false);
1079            confirmUri = confirmUri + "?email=" + encodedEmail + "&token=" + token;
1080
1081            i18nParams.put("confirmUri", new I18nizableText(confirmUri));
1082        }
1083
1084        // Add site information in the parameters.
1085        i18nParams.put("siteTitle", new I18nizableText(site.getTitle()));
1086        i18nParams.put("siteUrl", new I18nizableText(site.getUrl()));
1087
1088        String subject = _userSignUpConfiguration.getSubjectForSignUpEmail(i18nParams, language);
1089        String textBody = _userSignUpConfiguration.getTextBodyForSignUpEmail(i18nParams, language);
1090        String htmlBody = _userSignUpConfiguration.getHtmlBodyForSignUpEmail(i18nParams, language);
1091
1092        try
1093        {
1094            // Send the e-mail.
1095            SendMailHelper.newMail()
1096                          .withSubject(subject)
1097                          .withHTMLBody(htmlBody)
1098                          .withTextBody(textBody)
1099                          .withSender(from)
1100                          .withRecipient(email)
1101                          .sendMail();
1102        }
1103        catch (MessagingException | IOException e)
1104        {
1105            throw new UserManagementException("Error sending the sign-up confirmation mail.", e);
1106        }
1107    }
1108    
1109    /**
1110     * Send a sign-up confirmation link by e-mail.
1111     * @param siteName the site name.
1112     * @param language the e-mail language.
1113     * @param user the created user
1114     * @throws UserManagementException if an error occurs.
1115     */
1116    protected void sendSignupValidatedMail(String siteName, String language, User user) throws UserManagementException
1117    {
1118        Site site = _siteManager.getSite(siteName);
1119        String from = site.getValue("site-mail-from");
1120        String email = user.getEmail();
1121
1122        if (getLogger().isDebugEnabled())
1123        {
1124            getLogger().debug("Sending signup validation e-mail to " + email);
1125        }
1126        
1127        // Prepare mail.
1128        Map<String, I18nizableTextParameter> i18nParams = new HashMap<>();
1129        i18nParams.put("siteName", new I18nizableText(siteName));
1130        i18nParams.put("fullName", new I18nizableText(user.getFullName()));
1131        i18nParams.put("email", new I18nizableText(user.getEmail()));
1132        i18nParams.put("login", new I18nizableText(user.getIdentity().getLogin()));
1133
1134        // Add site information in the parameters.
1135        i18nParams.put("siteTitle", new I18nizableText(site.getTitle()));
1136        i18nParams.put("siteUrl", new I18nizableText(site.getUrl()));
1137
1138        String subject = _userSignUpConfiguration.getSubjectForSignUpValidatedEmail(i18nParams, language);
1139        String textBody = _userSignUpConfiguration.getTextBodyForSignUpValidatedEmail(i18nParams, language);
1140        String htmlBody = _userSignUpConfiguration.getHtmlBodyForSignUpValidatedEmail(i18nParams, language);
1141
1142        try
1143        {
1144            // Send the e-mail.
1145            SendMailHelper.newMail()
1146                          .withSubject(subject)
1147                          .withHTMLBody(htmlBody)
1148                          .withTextBody(textBody)
1149                          .withSender(from)
1150                          .withRecipient(email)
1151                          .sendMail();
1152        }
1153        catch (MessagingException | IOException e)
1154        {
1155            throw new UserManagementException("Error sending the sign-up validation mail.", e);
1156        }
1157    }
1158
1159    /**
1160     * Update the sign-up request token: reset the date and set a new token.
1161     * @param siteName the site name.
1162     * @param email the user e-mail.
1163     * @param newToken the new token.
1164     * @param population The id of the population
1165     * @param userDirectoryId The id of the user directory of the population
1166     * @throws UserManagementException if an error occurs.
1167     */
1168    protected void updateTempToken(String siteName, String email, String newToken, String population, String userDirectoryId) throws UserManagementException
1169    {
1170        try (SqlSession sqlSession = getSession())
1171        {
1172            String stmtId = "UserSignupManager.updateTempToken";
1173            
1174            Map<String, Object> params = new HashMap<>();
1175            params.put("tempUsersTable", _tempUsersTable);
1176            
1177            Timestamp now = new Timestamp(System.currentTimeMillis());
1178            params.put("subscription_date", now);
1179            params.put("token", newToken);
1180            params.put("site", siteName);
1181            params.put("email", email);
1182            params.put("population", population);
1183            params.put("userDirectory", userDirectoryId);
1184            
1185            sqlSession.update(stmtId, params);
1186            sqlSession.commit();
1187        }
1188        catch (Exception e)
1189        {
1190            throw new UserManagementException("Database error while resetting the subscription for user [" + email + "]", e);
1191        }
1192    }
1193
1194    /**
1195     * Create a user password change request in the database.
1196     * @param siteName the site name.
1197     * @param login the user login.
1198     * @param token the generated token.
1199     * @param population the population
1200     * @throws UserManagementException if an error occurs.
1201     */
1202    protected void addPasswordToken(String siteName, String login, String token, String population) throws UserManagementException
1203    {
1204        try (SqlSession sqlSession = getSession())
1205        {
1206            String stmtId = "UserSignupManager.hasToken";
1207            
1208            Map<String, Object> params = new HashMap<>();
1209            params.put("pwdChangeTable", _pwdChangeTable);
1210            
1211            params.put("site", siteName);
1212            params.put("login", login);
1213            params.put("population", population);
1214            
1215            List<Object> pwdEntries = sqlSession.selectList(stmtId, params);
1216            
1217            if (pwdEntries.isEmpty())
1218            {
1219                // Insert a new token.
1220                stmtId = "UserSignupManager.addPasswordToken";
1221                params = new HashMap<>();
1222                params.put("pwdChangeTable", _pwdChangeTable);
1223                
1224                Timestamp now = new Timestamp(System.currentTimeMillis());
1225                params.put("site", siteName);
1226                params.put("login", login);
1227                params.put("request_date", now);
1228                params.put("token", token);
1229                params.put("population", population);
1230                
1231                sqlSession.insert(stmtId, params);
1232            }
1233            else
1234            {
1235                // Update the existing token.
1236                stmtId = "UserSignupManager.updatePasswordToken";
1237                params = new HashMap<>();
1238                params.put("pwdChangeTable", _pwdChangeTable);
1239                
1240                Timestamp now = new Timestamp(System.currentTimeMillis());
1241                params.put("request_date", now);
1242                params.put("token", token);
1243                params.put("site", siteName);
1244                params.put("login", login);
1245                params.put("population", population);
1246                
1247                sqlSession.update(stmtId, params);
1248            }
1249            
1250            // commit insert or update
1251            sqlSession.commit();
1252        }
1253        catch (Exception e)
1254        {
1255            throw new UserManagementException("Database error while inserting a password change token for " + login, e);
1256        }
1257    }
1258
1259    /**
1260     * Get a temporary user from his site name and e-mail.
1261     * @param siteName the site name.
1262     * @param email The temporary user e-mail. Cannot be null.
1263     * @param population the population
1264     * @param userDirectoryId the id of the user directory of the population
1265     * @return the temporary user or null if not found.
1266     * @throws UserManagementException if an error occurs.
1267     */
1268    protected TempUser getTempUser(String siteName, String email, String population, String userDirectoryId) throws UserManagementException
1269    {
1270        return getTempUser(siteName, email, null, population, userDirectoryId);
1271    }
1272
1273    /**
1274     * Get a temporary user from his site name, e-mail and/or token.
1275     * At least one of e-mail and token must be provided.
1276     * @param siteName the site name.
1277     * @param email The temporary user e-mail. Can be null.
1278     * @param token The temporary user token. Can be null.
1279     * @param population the population. Must be not null if email is not null
1280     * @param userDirectoryId the id of the user directory of the population. Must be not null if email is not null
1281     * @return the temporary user or null if not found.
1282     * @throws UserManagementException if an error occurs.
1283     */
1284    protected TempUser getTempUser(String siteName, String email, String token, String population, String userDirectoryId) throws UserManagementException
1285    {
1286        if (StringUtils.isEmpty(email) && StringUtils.isEmpty(token))
1287        {
1288            throw new UserManagementException("Either e-mail or token must be provided.");
1289        }
1290        
1291        try (SqlSession sqlSession = getSession())
1292        {
1293            // Does this email already exists
1294            String stmtId = "UserSignupManager.getTempUser";
1295            
1296            Map<String, Object> params = new HashMap<>();
1297            params.put("tempUsersTable", _tempUsersTable);
1298            
1299            params.put("site", siteName);
1300            if (StringUtils.isNotEmpty(email))
1301            {
1302                params.put("email", email);
1303                params.put("population", population);
1304                params.put("userDirectory", userDirectoryId);
1305            }
1306            if (StringUtils.isNotEmpty(token))
1307            {
1308                params.put("token", token);
1309            }
1310            
1311            return sqlSession.selectOne(stmtId, params);
1312        }
1313        catch (Exception e)
1314        {
1315            throw new UserManagementException("Database error while getting the request for user [" + email + "] and token [" + token + "]", e);
1316        }
1317    }
1318
1319    /**
1320     * Remove the temporary .
1321     * @param siteName the site name.
1322     * @param email the user e-mail address.
1323     * @param token the request token.
1324     * @param population the population
1325     * @param userDirectoryId the id of the user directory of the population
1326     * @throws UserManagementException if an error occurs.
1327     */
1328    protected void removeTempUser(String siteName, String email, String token, String population, String userDirectoryId) throws UserManagementException
1329    {
1330        try (SqlSession sqlSession = getSession())
1331        {
1332            String stmtId = "UserSignupManager.removeTempUser";
1333            
1334            Map<String, Object> params = new HashMap<>();
1335            params.put("tempUsersTable", _tempUsersTable);
1336            
1337            params.put("site", siteName);
1338            params.put("email", email);
1339            params.put("token", token);
1340            params.put("population", population);
1341            params.put("userDirectory", userDirectoryId);
1342            
1343            sqlSession.delete(stmtId, params);
1344            sqlSession.commit();
1345        }
1346        catch (Exception e)
1347        {
1348            throw new UserManagementException("Database error while removing the token of user " + email, e);
1349        }
1350    }
1351
1352    /**
1353     * Remove the password change request.
1354     * @param siteName the site name.
1355     * @param login the user login.
1356     * @param token the request token.
1357     * @param population the population
1358     * @throws UserManagementException if an error occurs.
1359     */
1360    protected void removePasswordToken(String siteName, String login, String token, String population) throws UserManagementException
1361    {
1362        try (SqlSession sqlSession = getSession())
1363        {
1364            String stmtId = "UserSignupManager.removePasswordToken";
1365            
1366            Map<String, Object> params = new HashMap<>();
1367            params.put("pwdChangeTable", _pwdChangeTable);
1368            
1369            params.put("site", siteName);
1370            params.put("login", login);
1371            params.put("token", token);
1372            params.put("population", population);
1373            
1374            sqlSession.delete(stmtId, params);
1375            sqlSession.commit();
1376        }
1377        catch (Exception e)
1378        {
1379            throw new UserManagementException("Database error while removing the token of user " + login, e);
1380        }
1381    }
1382
1383    /**
1384     * Send a sign-up confirmation link by e-mail.
1385     * @param siteName the site name.
1386     * @param language the e-mail language.
1387     * @param user the user object.
1388     * @param token the generated token.
1389     * @throws UserManagementException if an error occurs.
1390     */
1391    protected void sendResetPasswordMail(String siteName, String language, User user, String token) throws UserManagementException
1392    {
1393        String login = user.getIdentity().getLogin();
1394        String population = user.getIdentity().getPopulationId();
1395
1396        if (getLogger().isDebugEnabled())
1397        {
1398            getLogger().debug("Sending reset password e-mail to " + login);
1399        }
1400
1401        Site site = _siteManager.getSite(siteName);
1402        String from = site.getValue("site-mail-from");
1403
1404        // Prepare mail
1405        Map<String, I18nizableTextParameter> i18nParams = new HashMap<>();
1406        i18nParams.put("siteName", new I18nizableText(siteName));
1407        i18nParams.put("login", new I18nizableText(login));
1408        i18nParams.put("email", new I18nizableText(user.getEmail()));
1409        i18nParams.put("fullName", new I18nizableText(user.getFullName()));
1410        i18nParams.put("token", new I18nizableText(token));
1411
1412        Page passwordPage = getPwdChangePage(siteName, language);
1413        if (passwordPage != null)
1414        {
1415            if (_pageUriResolver == null)
1416            {
1417                _pageUriResolver = _uriResolverEP.getResolverForType("page");
1418            }
1419
1420            String encodedLogin = URIUtils.encodeParameter(login);
1421            String encodedPopulation = URIUtils.encodeParameter(population);
1422
1423            // Compute the confirmation URI and add it to the parameters.
1424            String confirmUri = _pageUriResolver.resolve(passwordPage.getId(), false, true, false);
1425            confirmUri = confirmUri + "?login=" + encodedLogin + "&population=" + encodedPopulation + "&token=" + token;
1426
1427            i18nParams.put("confirmUri", new I18nizableText(confirmUri));
1428        }
1429
1430        // Add site information in the parameters.
1431        i18nParams.put("siteTitle", new I18nizableText(site.getTitle()));
1432        i18nParams.put("siteUrl", new I18nizableText(site.getUrl()));
1433
1434        String subject = _userSignUpConfiguration.getSubjectForResetPwdEmail(i18nParams, language);
1435        String textBody = _userSignUpConfiguration.getTextBodyForResetPwdEmail(i18nParams, language);
1436        String htmlBody = _userSignUpConfiguration.getHtmlBodyForResetPwdEmail(i18nParams, language);
1437
1438        try
1439        {
1440            // Send the e-mail.
1441            SendMailHelper.newMail()
1442                          .withSubject(subject)
1443                          .withHTMLBody(htmlBody)
1444                          .withTextBody(textBody)
1445                          .withSender(from)
1446                          .withRecipient(user.getEmail())
1447                          .sendMail();
1448        }
1449        catch (MessagingException | IOException e)
1450        {
1451            throw new UserManagementException("Error sending a password reset e-mail.", e);
1452        }
1453    }
1454
1455    /**
1456     * Bean representing a user sign-up request.
1457     */
1458    public static class TempUser
1459    {
1460        /** The site name. */
1461        protected String _site;
1462        
1463        /** The user e-mail. */
1464        protected String _email;
1465
1466        /** The user subscription date. */
1467        protected Date _subscriptionDate;
1468
1469        /** The request token. */
1470        protected String _token;
1471
1472        /** The id of the population */
1473        protected String _population;
1474
1475        /** The id of the user directory of the population */
1476        protected String _userDirectoryId;
1477
1478        /**
1479         * Constructor.
1480         * @param site the site
1481         * @param email the user's email
1482         * @param subscriptionDate the date of subscription
1483         * @param token the user's token
1484         * @param population The id of the population
1485         * @param userDirectoryId The id of the user directory of the population
1486         */
1487        public TempUser(String site, String email, Date subscriptionDate, String token, String population, String userDirectoryId)
1488        {
1489            this._site = site;
1490            this._email = email;
1491            this._subscriptionDate = subscriptionDate;
1492            this._token = token;
1493            this._population = population;
1494            this._userDirectoryId = userDirectoryId;
1495        }
1496        /**
1497         * Get the site.
1498         * @return the site
1499         */
1500        public String getSite()
1501        {
1502            return _site;
1503        }
1504        /**
1505         * Set the site.
1506         * @param site the site to set
1507         */
1508        public void setSite(String site)
1509        {
1510            this._site = site;
1511        }
1512        /**
1513         * Get the email.
1514         * @return _the email
1515         */
1516        public String getEmail()
1517        {
1518            return _email;
1519        }
1520        /**
1521         * Set the email.
1522         * @param email the email to set
1523         */
1524        public void setEmail(String email)
1525        {
1526            this._email = email;
1527        }
1528        /**
1529         * Get the subscriptionDate.
1530         * @return _the subscriptionDate
1531         */
1532        public Date getSubscriptionDate()
1533        {
1534            return _subscriptionDate;
1535        }
1536        /**
1537         * Set the subscriptionDate.
1538         * @param subscriptionDate the subscriptionDate to set
1539         */
1540        public void setSubscriptionDate(Date subscriptionDate)
1541        {
1542            this._subscriptionDate = subscriptionDate;
1543        }
1544        /**
1545         * Get the token.
1546         * @return _the token
1547         */
1548        public String getToken()
1549        {
1550            return _token;
1551        }
1552        /**
1553         * Set the token.
1554         * @param token the token to set
1555         */
1556        public void setToken(String token)
1557        {
1558            this._token = token;
1559        }
1560        /**
1561         * Get the population.
1562         * @return the population
1563         */
1564        public String getPopulation()
1565        {
1566            return _population;
1567        }
1568        /**
1569         * Set the population.
1570         * @param population the population to set
1571         */
1572        public void setPopulation(String population)
1573        {
1574            this._population = population;
1575        }
1576        /**
1577         * Get the user directory id.
1578         * @return the user directory id
1579         */
1580        public String getUserDirectoryId()
1581        {
1582            return _userDirectoryId;
1583        }
1584        /**
1585         * Set the user directory index.
1586         * @param userDirectoryId the user directory id to set
1587         */
1588        public void setUserDirectoryIndex(String userDirectoryId)
1589        {
1590            this._userDirectoryId = userDirectoryId;
1591        }
1592
1593    }
1594
1595}