View Javadoc
1   /*
2    * Copyright (c) 2002-2021, City of Paris
3    * All rights reserved.
4    *
5    * Redistribution and use in source and binary forms, with or without
6    * modification, are permitted provided that the following conditions
7    * are met:
8    *
9    *  1. Redistributions of source code must retain the above copyright notice
10   *     and the following disclaimer.
11   *
12   *  2. Redistributions in binary form must reproduce the above copyright notice
13   *     and the following disclaimer in the documentation and/or other materials
14   *     provided with the distribution.
15   *
16   *  3. Neither the name of 'Mairie de Paris' nor 'Lutece' nor the names of its
17   *     contributors may be used to endorse or promote products derived from
18   *     this software without specific prior written permission.
19   *
20   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21   * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
24   * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27   * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28   * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30   * POSSIBILITY OF SUCH DAMAGE.
31   *
32   * License 1.0
33   */
34  package fr.paris.lutece.plugins.mylutece.modules.database.authentication;
35  
36  import java.sql.Timestamp;
37  import java.util.ArrayList;
38  import java.util.Arrays;
39  import java.util.Collection;
40  import java.util.HashMap;
41  import java.util.HashSet;
42  import java.util.List;
43  import java.util.Locale;
44  import java.util.Map;
45  import java.util.Set;
46  import java.util.stream.Collectors;
47  
48  import javax.security.auth.login.FailedLoginException;
49  import javax.security.auth.login.LoginException;
50  import javax.servlet.http.HttpServletRequest;
51  
52  import org.apache.commons.lang3.StringUtils;
53  
54  import fr.paris.lutece.plugins.mylutece.authentication.PortalAuthentication;
55  import fr.paris.lutece.plugins.mylutece.authentication.logs.ConnectionLog;
56  import fr.paris.lutece.plugins.mylutece.authentication.logs.ConnectionLogHome;
57  import fr.paris.lutece.plugins.mylutece.business.LuteceUserAttributeDescription;
58  import fr.paris.lutece.plugins.mylutece.business.LuteceUserRoleDescription;
59  import fr.paris.lutece.plugins.mylutece.business.attribute.AttributeField;
60  import fr.paris.lutece.plugins.mylutece.business.attribute.AttributeFieldHome;
61  import fr.paris.lutece.plugins.mylutece.business.attribute.AttributeHome;
62  import fr.paris.lutece.plugins.mylutece.business.attribute.IAttribute;
63  import fr.paris.lutece.plugins.mylutece.business.attribute.MyLuteceUserField;
64  import fr.paris.lutece.plugins.mylutece.business.attribute.MyLuteceUserFieldHome;
65  import fr.paris.lutece.plugins.mylutece.modules.database.authentication.business.DatabaseHome;
66  import fr.paris.lutece.plugins.mylutece.modules.database.authentication.business.DatabaseUserHome;
67  import fr.paris.lutece.plugins.mylutece.modules.database.authentication.business.GroupRoleHome;
68  import fr.paris.lutece.plugins.mylutece.modules.database.authentication.business.parameter.DatabaseUserParameterHome;
69  import fr.paris.lutece.plugins.mylutece.modules.database.authentication.service.DatabaseAccountLifeTimeService;
70  import fr.paris.lutece.plugins.mylutece.modules.database.authentication.service.DatabasePlugin;
71  import fr.paris.lutece.plugins.mylutece.modules.database.authentication.service.DatabaseService;
72  import fr.paris.lutece.plugins.mylutece.modules.database.authentication.web.MyLuteceDatabaseApp;
73  import fr.paris.lutece.plugins.mylutece.service.MyLutecePlugin;
74  import fr.paris.lutece.plugins.mylutece.util.SecurityUtils;
75  import fr.paris.lutece.portal.business.role.RoleHome;
76  import fr.paris.lutece.portal.business.template.DatabaseTemplateHome;
77  import fr.paris.lutece.portal.service.i18n.I18nService;
78  import fr.paris.lutece.portal.service.mail.MailService;
79  import fr.paris.lutece.portal.service.plugin.Plugin;
80  import fr.paris.lutece.portal.service.plugin.PluginService;
81  import fr.paris.lutece.portal.service.security.FailedLoginCaptchaException;
82  import fr.paris.lutece.portal.service.security.LoginRedirectException;
83  import fr.paris.lutece.portal.service.security.LuteceUser;
84  import fr.paris.lutece.portal.service.template.AppTemplateService;
85  import fr.paris.lutece.portal.service.util.AppException;
86  import fr.paris.lutece.portal.service.util.AppLogService;
87  import fr.paris.lutece.portal.service.util.AppPathService;
88  import fr.paris.lutece.portal.service.util.AppPropertiesService;
89  import fr.paris.lutece.util.ReferenceItem;
90  import fr.paris.lutece.util.html.HtmlTemplate;
91  import fr.paris.lutece.util.http.SecurityUtil;
92  
93  /**
94   * The Class provides an implementation of the inherited abstract class PortalAuthentication based on a database.
95   *
96   * @author Mairie de Paris
97   * @version 2.0.0
98   *
99   * @since Lutece v2.0.0
100  */
101 public class BaseAuthentication extends PortalAuthentication
102 {
103     // //////////////////////////////////////////////////////////////////////////////////////////////
104     // Constants
105     private static final String AUTH_SERVICE_NAME = AppPropertiesService.getProperty( "mylutece-database.service.name" );
106     private static final String PLUGIN_JCAPTCHA = "jcaptcha";
107 
108     // PROPERTIES
109     private static final String PROPERTY_MAX_ACCESS_FAILED = "access_failures_max";
110     private static final String PROPERTY_ACCESS_FAILED_CAPTCHA = "access_failures_captcha";
111     private static final String PROPERTY_INTERVAL_MINUTES = "access_failures_interval";
112     private static final String PROPERTY_UNBLOCK_USER = "mylutece_database_unblock_user";
113     private static final String PROPERTY_TOO_MANY_FAILURES = "mylutece.ip.labelTooManyLoginTrials";
114 
115     // PARAMETERS
116     private static final String PARAMETER_UNBLOCK_USER_MAIL_SENDER = "unblock_user_mail_sender";
117     private static final String PARAMETER_UNBLOCK_USER_MAIL_SUBJECT = "unblock_user_mail_subject";
118     private static final String PARAMETER_ENABLE_UNBLOCK_IP = "enable_unblock_ip";
119 
120     // MARK
121     private static final String MARK_URL = "url";
122     private static final String MARK_SITE_LINK = "site_link";
123 
124     // Messages properties
125     private static final String PROPERTY_MESSAGE_USER_NOT_FOUND_DATABASE = "module.mylutece.database.message.userNotFoundDatabase";
126 
127     // constants
128     private static final String CONSTANT_PATH_ICON = "images/local/skin/plugins/mylutece/modules/database/mylutece-database.png";
129     private static final String BEAN_USER_ATTRIBUTES_SERVICE = "mylutece.myLuteceUserAttributesService";
130 
131     /**
132      * Constructor
133      *
134      */
135     public BaseAuthentication( )
136     {
137         super( );
138     }
139 
140     /**
141      * Gets the Authentification service name
142      * 
143      * @return The name of the authentication service
144      */
145     @Override
146     public String getAuthServiceName( )
147     {
148         return AUTH_SERVICE_NAME;
149     }
150 
151     /**
152      * Gets the Authentification type
153      * 
154      * @param request
155      *            The HTTP request
156      * @return The type of authentication
157      */
158     @Override
159     public String getAuthType( HttpServletRequest request )
160     {
161         return HttpServletRequest.BASIC_AUTH;
162     }
163 
164     /**
165      * This methods checks the login info in the database.
166      *
167      * Unlike its parent, it does not throw LoginRedirectException.
168      *
169      * @param strUserName
170      *            The username
171      * @param strUserPassword
172      *            The password
173      * @param request
174      *            The HttpServletRequest
175      * @return A LuteceUser object corresponding to the login
176      * @throws LoginException
177      *             The LoginException
178      */
179     @Override
180     public LuteceUser login( String strUserName, String strUserPassword, HttpServletRequest request ) throws LoginException
181     {
182         try
183         {
184             return super.login( strUserName, strUserPassword, request );
185         }
186         catch( LoginRedirectException lre )
187         {
188             throw new AppException( "Mylutece-database: impossible. this code should never be reached.", lre );
189         }
190     }
191 
192     /**
193      * This methods checks the login info in the database
194      *
195      * @param strUserName
196      *            The username
197      * @param strUserPassword
198      *            The password
199      * @param request
200      *            The HttpServletRequest
201      * @return A LuteceUser object corresponding to the login
202      * @throws LoginException
203      *             The LoginException
204      */
205     @Override
206     public LuteceUser processLogin( String strUserName, String strUserPassword, HttpServletRequest request ) throws LoginException
207     {
208         DatabaseService databaseService = DatabaseService.getService( );
209 
210         Plugin pluginMyLutece = PluginService.getPlugin( MyLutecePlugin.PLUGIN_NAME );
211         Plugin plugin = PluginService.getPlugin( DatabasePlugin.PLUGIN_NAME );
212 
213         // Creating a record of connections log
214         ConnectionLog connectionLog = new ConnectionLog( );
215         connectionLog.setIpAddress( SecurityUtil.getRealIp( request ) );
216         connectionLog.setDateLogin( new java.sql.Timestamp( new java.util.Date( ).getTime( ) ) );
217 
218         // Test the number of errors during an interval of minutes
219         int nMaxFailed = DatabaseUserParameterHome.getIntegerSecurityParameter( PROPERTY_MAX_ACCESS_FAILED, plugin );
220         int nMaxFailedCaptcha = 0;
221         int nIntervalMinutes = DatabaseUserParameterHome.getIntegerSecurityParameter( PROPERTY_INTERVAL_MINUTES, plugin );
222         boolean bEnableCaptcha = false;
223 
224         if ( PluginService.isPluginEnable( PLUGIN_JCAPTCHA ) )
225         {
226             nMaxFailedCaptcha = DatabaseUserParameterHome.getIntegerSecurityParameter( PROPERTY_ACCESS_FAILED_CAPTCHA, plugin );
227         }
228 
229         Locale locale = request.getLocale( );
230 
231         if ( ( ( nMaxFailed > 0 ) || ( nMaxFailedCaptcha > 0 ) ) && ( nIntervalMinutes > 0 ) )
232         {
233             int nNbFailed = ConnectionLogHome.getLoginErrors( connectionLog, nIntervalMinutes, pluginMyLutece );
234 
235             if ( ( nMaxFailedCaptcha > 0 ) && ( nNbFailed >= nMaxFailedCaptcha ) )
236             {
237                 bEnableCaptcha = true;
238             }
239 
240             if ( ( nMaxFailed > 0 ) && ( nNbFailed >= nMaxFailed ) )
241             {
242                 if ( nNbFailed == nMaxFailed )
243                 {
244                     ReferenceItem item = DatabaseUserParameterHome.findByKey( PARAMETER_ENABLE_UNBLOCK_IP, plugin );
245 
246                     if ( ( item != null ) && item.isChecked( ) )
247                     {
248                         sendUnlockLinkToUser( strUserName, nIntervalMinutes, request, plugin );
249                     }
250                 }
251 
252                 Object [ ] args = {
253                         Integer.toString( nIntervalMinutes )
254                 };
255                 String strMessage = I18nService.getLocalizedString( PROPERTY_TOO_MANY_FAILURES, args, locale );
256 
257                 if ( bEnableCaptcha )
258                 {
259                     throw new FailedLoginCaptchaException( strMessage, bEnableCaptcha );
260                 }
261                 else
262                 {
263                     throw new FailedLoginException( strMessage );
264                 }
265             }
266         }
267 
268         BaseUser user = DatabaseHome.findLuteceUserByLogin( strUserName, plugin, this );
269 
270         // Unable to find the user
271         if ( ( user == null ) || !databaseService.isUserActive( strUserName, plugin ) )
272         {
273             AppLogService.info( "Unable to find user in the database : " + strUserName );
274 
275             if ( bEnableCaptcha )
276             {
277                 throw new FailedLoginCaptchaException( I18nService.getLocalizedString( PROPERTY_MESSAGE_USER_NOT_FOUND_DATABASE, locale ), bEnableCaptcha );
278             }
279             else
280             {
281                 throw new FailedLoginException( I18nService.getLocalizedString( PROPERTY_MESSAGE_USER_NOT_FOUND_DATABASE, locale ) );
282             }
283         }
284 
285         // Check password
286         if ( !databaseService.checkPassword( strUserName, strUserPassword, plugin ) )
287         {
288             AppLogService.info( "User login : Incorrect login or password" + strUserName );
289 
290             if ( bEnableCaptcha )
291             {
292                 throw new FailedLoginCaptchaException( I18nService.getLocalizedString( PROPERTY_MESSAGE_USER_NOT_FOUND_DATABASE, locale ), bEnableCaptcha );
293             }
294             else
295             {
296                 throw new FailedLoginException( I18nService.getLocalizedString( PROPERTY_MESSAGE_USER_NOT_FOUND_DATABASE, locale ) );
297             }
298         }
299 
300         // Get database roles
301         List<String> arrayRoles = DatabaseHome.findUserRolesFromLogin( strUserName, plugin );
302 
303         if ( !arrayRoles.isEmpty( ) )
304         {
305             user.addRoles( arrayRoles );
306         }
307 
308         // Get groups
309         List<String> arrayGroups = DatabaseHome.findUserGroupsFromLogin( strUserName, plugin );
310 
311         if ( !arrayGroups.isEmpty( ) )
312         {
313             user.setGroups( arrayGroups );
314         }
315 
316         // set local database user attributes
317         setLocalDatabaseUserAttributes( locale, user );
318 
319         // We update the status of the user if his password has become obsolete
320         Timestamp passwordMaxValidDate = DatabaseHome.findPasswordMaxValideDateFromLogin( strUserName, plugin );
321 
322         if ( ( passwordMaxValidDate != null ) && ( passwordMaxValidDate.getTime( ) < new java.util.Date( ).getTime( ) ) )
323         {
324             DatabaseHome.updateResetPasswordFromLogin( strUserName, Boolean.TRUE, plugin );
325         }
326 
327         int nUserId = DatabaseHome.findUserIdFromLogin( strUserName, plugin );
328         databaseService.updateUserExpirationDate( nUserId, plugin );
329 
330         return user;
331     }
332 
333     /**
334      * This methods logout the user
335      * 
336      * @param user
337      *            The user
338      */
339     @Override
340     public void logout( LuteceUser user )
341     {
342         // Nothing
343     }
344 
345     /**
346      * Find users by login
347      * 
348      * @param request
349      *            The request
350      * @param strLogin
351      *            the login
352      * @return DatabaseUser the user corresponding to the login
353      */
354     @Override
355     public boolean findResetPassword( HttpServletRequest request, String strLogin )
356     {
357         Plugin plugin = PluginService.getPlugin( DatabasePlugin.PLUGIN_NAME );
358 
359         return DatabaseHome.findResetPasswordFromLogin( strLogin, plugin );
360     }
361 
362     /**
363      * This method returns an anonymous Lutece user
364      *
365      * @return An anonymous Lutece user
366      */
367     @Override
368     public LuteceUser getAnonymousUser( )
369     {
370         return new BaseUser( LuteceUser.ANONYMOUS_USERNAME, this );
371     }
372 
373     /**
374      * Checks that the current user is associated to a given role
375      * 
376      * @param user
377      *            The user
378      * @param request
379      *            The HTTP request
380      * @param strRole
381      *            The role name
382      * @return Returns true if the user is associated to the role, otherwise false
383      */
384     @Override
385     public boolean isUserInRole( LuteceUser user, HttpServletRequest request, String strRole )
386     {
387         String [ ] roles = getRolesByUser( user );
388 
389         if ( ( roles != null ) && ( strRole != null ) )
390         {
391             for ( String role : roles )
392             {
393                 if ( strRole.equals( role ) )
394                 {
395                     return true;
396                 }
397             }
398         }
399 
400         return false;
401     }
402 
403     /**
404      * Returns the View account page URL of the Authentication Service
405      * 
406      * @return The URL
407      */
408     @Override
409     public String getViewAccountPageUrl( )
410     {
411         return MyLuteceDatabaseApp.getViewAccountUrl( );
412     }
413 
414     /**
415      * Returns the New account page URL of the Authentication Service
416      * 
417      * @return The URL
418      */
419     @Override
420     public String getNewAccountPageUrl( )
421     {
422         return MyLuteceDatabaseApp.getNewAccountUrl( );
423     }
424 
425     /**
426      * Returns the Change password page URL of the Authentication Service
427      * 
428      * @return The URL
429      */
430     public String getChangePasswordPageUrl( )
431     {
432         return MyLuteceDatabaseApp.getChangePasswordUrl( );
433     }
434 
435     /**
436      * Returns the lost password URL of the Authentication Service
437      * 
438      * @return The URL
439      */
440     @Override
441     public String getLostPasswordPageUrl( )
442     {
443         return MyLuteceDatabaseApp.getLostPasswordUrl( );
444     }
445 
446     /**
447      * {@inheritDoc}
448      * 
449      * @return
450      */
451     @Override
452     public String getLostLoginPageUrl( )
453     {
454         return MyLuteceDatabaseApp.getLostLoginUrl( );
455     }
456 
457     /**
458      * {@inheritDoc}
459      */
460     @Override
461     public String getResetPasswordPageUrl( HttpServletRequest request )
462     {
463         return AppPathService.getBaseUrl( request ) + MyLuteceDatabaseApp.getMessageResetPasswordUrl( );
464     }
465 
466     /**
467      * Returns all users managed by the authentication service if this feature is available.
468      * 
469      * @return A collection of Lutece users or null if the service doesn't provide a users list
470      */
471     @Override
472     public Collection<LuteceUser> getUsers( )
473     {
474         Plugin plugin = PluginService.getPlugin( DatabasePlugin.PLUGIN_NAME );
475 
476         Collection<BaseUser> baseUsers = DatabaseHome.findDatabaseUsersList( plugin, this );
477         Collection<LuteceUser> luteceUsers = new ArrayList<>( );
478 
479         for ( BaseUser user : baseUsers )
480         {
481             luteceUsers.add( user );
482         }
483 
484         return luteceUsers;
485     }
486 
487     /**
488      * Returns the user managed by the authentication service if this feature is available.
489      * 
490      * @param userLogin
491      *            the user login
492      * @return A Lutece users or null if the service doesn't provide a user
493      */
494     @Override
495     public LuteceUser getUser( String userLogin )
496     {
497         Plugin plugin = PluginService.getPlugin( DatabasePlugin.PLUGIN_NAME );
498 
499         return DatabaseHome.findLuteceUserByLogin( userLogin, plugin, this );
500     }
501 
502     /**
503      * get all roles for this user : - user's roles - user's groups roles
504      *
505      * @param user
506      *            The user
507      * @return Array of roles
508      */
509     @Override
510     public String [ ] getRolesByUser( LuteceUser user )
511     {
512         Plugin plugin = PluginService.getPlugin( DatabasePlugin.PLUGIN_NAME );
513         Set<String> setRoles = new HashSet<>( );
514         String [ ] strGroups = user.getGroups( );
515         String [ ] strRoles = user.getRoles( );
516 
517         if ( strRoles != null )
518         {
519             setRoles.addAll( Arrays.asList( strRoles ) );
520         }
521 
522         if ( strGroups != null )
523         {
524             for ( String strGroupKey : strGroups )
525             {
526                 Collection<String> arrayRolesGroup = GroupRoleHome.findGroupRoles( strGroupKey, plugin );
527 
528                 for ( String strRole : arrayRolesGroup )
529                 {
530                     setRoles.add( strRole );
531                 }
532             }
533         }
534 
535         String [ ] strReturnRoles = new String [ setRoles.size( )];
536         setRoles.toArray( strReturnRoles );
537 
538         return strReturnRoles;
539     }
540 
541     /**
542      *
543      * {@inheritDoc}
544      */
545     @Override
546     public String getIconUrl( )
547     {
548         return CONSTANT_PATH_ICON;
549     }
550 
551     /**
552      *
553      * Returns {@link DatabasePlugin#PLUGIN_NAME}.
554      * 
555      * @return {@link DatabasePlugin#PLUGIN_NAME}
556      */
557     @Override
558     public String getName( )
559     {
560         return DatabasePlugin.PLUGIN_NAME;
561     }
562 
563     /**
564      * {@inheritDoc}
565      */
566     @Override
567     public String getPluginName( )
568     {
569         return DatabasePlugin.PLUGIN_NAME;
570     }
571 
572     /**
573      * {@inheritDoc}
574      */
575     @Override
576     public void updateDateLastLogin( LuteceUser user, HttpServletRequest request )
577     {
578         DatabaseService databaseService = DatabaseService.getService( );
579         Plugin plugin = PluginService.getPlugin( DatabasePlugin.PLUGIN_NAME );
580         databaseService.updateUserLastLoginDate( user.getName( ), plugin );
581     }
582     
583     
584     /**
585      * {@inheritDoc}
586      */
587     @Override
588     public List<LuteceUserRoleDescription> getLuteceUserRolesProvided(Locale locale)
589     {
590     	return RoleHome.findAll().stream().map(x->new  LuteceUserRoleDescription(x, LuteceUserRoleDescription.TYPE_MANUAL_ASSIGNMENT, null)).collect(Collectors.toList());
591     	
592     }
593     
594     /**
595      * {@inheritDoc}
596      */
597     @Override
598    public List<LuteceUserAttributeDescription> getLuteceUserAttributesProvided(Locale locale)
599     {
600     	
601     	List<LuteceUserAttributeDescription> listUserDescription=  new ArrayList<LuteceUserAttributeDescription>();
602     	listUserDescription.add(new LuteceUserAttributeDescription( LuteceUser.NAME_FAMILY, "databaseUser.getLastName()", ""));
603      	listUserDescription.add(new LuteceUserAttributeDescription( LuteceUser.NAME_GIVEN, "databaseUser.getFirstName()", ""));
604      	listUserDescription.add(new LuteceUserAttributeDescription( LuteceUser.BUSINESS_INFO_ONLINE_EMAIL, "databaseUser.getEmail()", ""));
605      	listUserDescription.add(new LuteceUserAttributeDescription( LuteceUser.DATE_LAST_LOGIN, "databaseUser.getLastLogin()", ""));
606 
607      	Plugin myLutecePlugin = PluginService.getPlugin( MyLutecePlugin.PLUGIN_NAME );
608      	//Add User Attributes
609         listUserDescription.addAll(AttributeHome.findAll( locale, myLutecePlugin ).stream().map(x->new  LuteceUserAttributeDescription(x.getTitle(),x.getTitle(), x.getHelpMessage())).collect(Collectors.toList()));
610     	
611         return listUserDescription;
612     }
613     
614 
615     private void sendUnlockLinkToUser( String strLogin, int nIntervalMinutes, HttpServletRequest request, Plugin plugin )
616     {
617         int nIdUser = DatabaseUserHome.findDatabaseUserIdFromLogin( strLogin, plugin );
618 
619         if ( nIdUser > 0 )
620         {
621             ReferenceItem referenceItem = DatabaseUserParameterHome.findByKey( PARAMETER_UNBLOCK_USER_MAIL_SENDER, plugin );
622             String strSender = ( referenceItem == null ) ? StringUtils.EMPTY : referenceItem.getName( );
623 
624             referenceItem = DatabaseUserParameterHome.findByKey( PARAMETER_UNBLOCK_USER_MAIL_SUBJECT, plugin );
625 
626             String strSubject = ( referenceItem == null ) ? StringUtils.EMPTY : referenceItem.getName( );
627 
628             String strLink = SecurityUtils.buildResetConnectionLogUrl( nIntervalMinutes, request );
629 
630             Map<String, Object> model = new HashMap<>( );
631             model.put( MARK_URL, strLink );
632             model.put( MARK_SITE_LINK, MailService.getSiteLink( AppPathService.getBaseUrl( request ), true ) );
633 
634             String strTemplate = DatabaseTemplateHome.getTemplateFromKey( PROPERTY_UNBLOCK_USER );
635             HtmlTemplate template = AppTemplateService.getTemplateFromStringFtl( strTemplate, request.getLocale( ), model );
636 
637             DatabaseAccountLifeTimeServicetication/service/DatabaseAccountLifeTimeService.html#DatabaseAccountLifeTimeService">DatabaseAccountLifeTimeService accountLifeTimeService = new DatabaseAccountLifeTimeService( );
638 
639             String strUserMail = accountLifeTimeService.getUserMainEmail( nIdUser );
640 
641             if ( ( strUserMail != null ) && StringUtils.isNotBlank( strUserMail ) )
642             {
643                 MailService.sendMailHtml( strUserMail, strSender, strSender, strSubject, template.getHtml( ) );
644             }
645         }
646     }
647 
648     /**
649      * set user attributes stored in local database
650      * 
651      * @param locale
652      * @param user
653      * @return the user attributes
654      */
655     private void setLocalDatabaseUserAttributes( Locale locale, BaseUser user )
656     {
657 
658         // Specific attributes
659         Plugin myLutecePlugin = PluginService.getPlugin( MyLutecePlugin.PLUGIN_NAME );
660         int idUser = DatabaseHome.findUserIdFromLogin( user.getAccessCode( ), myLutecePlugin );
661 
662         List<IAttribute> listAttributes = AttributeHome.findAll( locale, myLutecePlugin );
663 
664         for ( IAttribute attribute : listAttributes )
665         {
666             List<AttributeField> listAttributeFields = AttributeFieldHome.selectAttributeFieldsByIdAttribute( attribute.getIdAttribute( ), myLutecePlugin );
667             attribute.setListAttributeFields( listAttributeFields );
668 
669             List<MyLuteceUserField> listUserFields = MyLuteceUserFieldHome.selectUserFieldsByIdUserIdAttribute( idUser, attribute.getIdAttribute( ),
670                     myLutecePlugin );
671 
672             if ( listUserFields.size( ) == 1 )
673             {
674                 user.setUserInfo( attribute.getTitle( ), listUserFields.get( 0 ).getValue( ) );
675             }
676             else
677                 if ( listUserFields.size( ) > 0 )
678                 {
679                     for ( MyLuteceUserField userField : listUserFields )
680                     {
681                         user.setUserInfo( attribute.getTitle( ) + "_" + userField.getAttributeField( ).getTitle( ), userField.getValue( ) );
682                     }
683                 }
684                 else
685                 {
686                     user.setUserInfo( String.valueOf( attribute.getTitle( ) ), StringUtils.EMPTY );
687                 }
688         }
689     }
690 }