View Javadoc
1   /*
2    * Copyright (c) 2002-2014, Mairie de 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.wssodatabase.authentication.service;
35  
36  import fr.paris.lutece.plugins.mylutece.modules.wssodatabase.authentication.business.IdxWSSODatabaseHome;
37  import fr.paris.lutece.plugins.mylutece.modules.wssodatabase.authentication.business.WssoProfil;
38  import fr.paris.lutece.plugins.mylutece.modules.wssodatabase.authentication.business.WssoProfilHome;
39  import fr.paris.lutece.plugins.mylutece.modules.wssodatabase.authentication.business.WssoUser;
40  import fr.paris.lutece.plugins.mylutece.modules.wssodatabase.authentication.business.WssoUserHome;
41  import fr.paris.lutece.plugins.mylutece.modules.wssodatabase.authentication.business.WssoUserRoleHome;
42  import fr.paris.lutece.plugins.mylutece.service.attribute.MyLuteceUserFieldService;
43  import fr.paris.lutece.portal.business.role.Role;
44  import fr.paris.lutece.portal.business.role.RoleHome;
45  import fr.paris.lutece.portal.service.csv.CSVMessageDescriptor;
46  import fr.paris.lutece.portal.service.csv.CSVMessageLevel;
47  import fr.paris.lutece.portal.service.csv.CSVReaderService;
48  import fr.paris.lutece.portal.service.i18n.I18nService;
49  import fr.paris.lutece.portal.service.mail.MailService;
50  import fr.paris.lutece.portal.service.plugin.Plugin;
51  import fr.paris.lutece.portal.service.plugin.PluginService;
52  import fr.paris.lutece.portal.service.template.AppTemplateService;
53  import fr.paris.lutece.portal.service.util.AppPathService;
54  import fr.paris.lutece.portal.service.util.AppPropertiesService;
55  import fr.paris.lutece.util.html.HtmlTemplate;
56  
57  import java.util.ArrayList;
58  import java.util.Collection;
59  import java.util.HashMap;
60  import java.util.List;
61  import java.util.Locale;
62  import java.util.Map;
63  import java.util.regex.Pattern;
64  
65  import org.apache.commons.collections.CollectionUtils;
66  import org.apache.commons.lang.StringUtils;
67  
68  
69  /**
70   * Import database users from a CSV file
71   */
72  public class ImportWssoDatabaseUserService extends CSVReaderService
73  {
74      private static final String MESSAGE_ACCESS_CODE_ALREADY_USED = "module.mylutece.wssodatabase.message.user_exist";
75      private static final String MESSAGE_EMAIL_ALREADY_USED = "module.mylutece.wssodatabase.message.user_exist";
76      private static final String MESSAGE_USERS_IMPORTED = "module.mylutece.wssodatabase.import_users_from_file.usersImported";
77      private static final String MESSAGE_ERROR_MIN_NUMBER_COLUMNS = "module.mylutece.wssodatabase.import_users_from_file.messageErrorMinColumnNumber";
78      private static final String MESSAGE_ACCOUNT_IMPORTED_MAIL_SUBJECT = "module.mylutece.wssodatabase.import_users_from_file.email.mailSubject";
79      private static final String MESSAGE_ROLE_UNKNOWN = "module.mylutece.wssodatabase.message.import_user_role_unknown";
80  
81      //    private static final String MESSAGE_GROUP_UNKNOWN = "module.mylutece.wssodatabase.message.import_user_group_unknown";
82      private static final String MESSAGE_PROFIL_UNKNOWN = "module.mylutece.wssodatabase.message.import_user_profil_unknown";
83      private static final String MESSAGE_GUID_EMPTY = "module.mylutece.wssodatabase.message.import_user_guid_empty";
84      private static final String MESSAGE_FIRST_NAME_EMPTY = "module.mylutece.wssodatabase.message.import_user_first_name_empty";
85      private static final String MESSAGE_LAST_NAME_EMPTY = "module.mylutece.wssodatabase.message.import_user_last_name_empty";
86      private static final String MESSAGE_EMAIL_EMPTY = "module.mylutece.wssodatabase.message.import_user_email_empty";
87      private static final String MESSAGE_EMAIL_INVALID = "module.mylutece.wssodatabase.message.import_user_email_invalid";
88      private static final String PROPERTY_NO_REPLY_EMAIL = "mail.noreply.email";
89      private static final String PROPERTY_IMPORT_EXPORT_USER_SEPARATOR = "lutece.importExportUser.defaultSeparator";
90      private static final String PROPERTY_SITE_NAME = "lutece.name";
91      private static final String TEMPLATE_MAIL_USER_IMPORTED = "admin/plugins/mylutece/modules/wssodatabase/mail_user_imported.html";
92      private static final String MARK_SITE_NAME = "site_name";
93      private static final String MARK_USER = "user";
94      private static final String MARK_SITE_LINK = "site_link";
95      private static final String CONSTANT_DEFAULT_IMPORT_EXPORT_USER_SEPARATOR = ":";
96      private static final String CONSTANT_ROLE = "role";
97      private static final int CONSTANT_MINIMUM_COLUMNS_PER_LINE = 4;
98  
99      /**
100      * Format d'un e-mail (texte@domain.extension)
101      */
102     private static final String CONSTANT_EMAIL = "(^([a-zA-Z0-9]+(([\\.\\-\\_]?[a-zA-Z0-9]+)+)?)\\@(([a-zA-Z0-9]+[\\.\\-\\_])+[a-zA-Z]{2,4})$)|(^$)";
103     private Character _strAttributesSeparator;
104     private boolean _bUpdateExistingUsers;
105 
106     /**
107      * {@inheritDoc}
108      */
109     @Override
110     protected List<CSVMessageDescriptor> readLineOfCSVFile( String[] strLineDataArray, int nLineNumber, Locale locale,
111             String strBaseUrl )
112     {
113         Plugin databasePlugin = PluginService.getPlugin( WssoDatabasePlugin.PLUGIN_NAME );
114         List<CSVMessageDescriptor> listMessages = new ArrayList<CSVMessageDescriptor>( );
115         int nIndex = 0;
116 
117         String strGuid = strLineDataArray[nIndex++];
118 
119         if ( StringUtils.isBlank( strGuid ) )
120         {
121             String strErrorMessage = I18nService.getLocalizedString( MESSAGE_GUID_EMPTY, null, locale );
122             CSVMessageDescriptor error = new CSVMessageDescriptor( CSVMessageLevel.ERROR, nLineNumber, strErrorMessage );
123             listMessages.add( error );
124         }
125 
126         String strLastName = strLineDataArray[nIndex++];
127 
128         if ( StringUtils.isBlank( strLastName ) )
129         {
130             String strErrorMessage = I18nService.getLocalizedString( MESSAGE_LAST_NAME_EMPTY, null, locale );
131             CSVMessageDescriptor error = new CSVMessageDescriptor( CSVMessageLevel.ERROR, nLineNumber, strErrorMessage );
132             listMessages.add( error );
133         }
134 
135         String strFirstName = strLineDataArray[nIndex++];
136 
137         if ( StringUtils.isBlank( strFirstName ) )
138         {
139             String strErrorMessage = I18nService.getLocalizedString( MESSAGE_FIRST_NAME_EMPTY, null, locale );
140             CSVMessageDescriptor error = new CSVMessageDescriptor( CSVMessageLevel.ERROR, nLineNumber, strErrorMessage );
141             listMessages.add( error );
142         }
143 
144         String strEmail = strLineDataArray[nIndex++];
145 
146         if ( StringUtils.isBlank( strEmail ) )
147         {
148             String strErrorMessage = I18nService.getLocalizedString( MESSAGE_EMAIL_EMPTY, null, locale );
149             CSVMessageDescriptor error = new CSVMessageDescriptor( CSVMessageLevel.ERROR, nLineNumber, strErrorMessage );
150             listMessages.add( error );
151         }
152         else
153         {
154             if ( !Pattern.matches( CONSTANT_EMAIL, strEmail ) )
155             {
156                 String strErrorMessage = I18nService.getLocalizedString( MESSAGE_EMAIL_INVALID, null, locale );
157                 CSVMessageDescriptor error = new CSVMessageDescriptor( CSVMessageLevel.ERROR, nLineNumber,
158                         strErrorMessage );
159                 listMessages.add( error );
160             }
161         }
162 
163         if ( CollectionUtils.isNotEmpty( listMessages ) )
164         {
165             return listMessages;
166         }
167 
168         boolean bUpdateUser = getUpdateExistingUsers( );
169         int nUserId = 0;
170 
171         if ( bUpdateUser )
172         {
173             int nAccessCodeUserId = WssoUserHome.findDatabaseUserIdFromGuid( strGuid, databasePlugin );
174 
175             if ( nAccessCodeUserId > 0 )
176             {
177                 nUserId = nAccessCodeUserId;
178             }
179 
180             bUpdateUser = nUserId > 0;
181         }
182 
183         WssoUser wssoUser = new WssoUser( );
184 
185         wssoUser.setGuid( strGuid );
186         wssoUser.setLastName( strLastName );
187         wssoUser.setFirstName( strFirstName );
188         wssoUser.setEmail( strEmail );
189 
190         if ( bUpdateUser )
191         {
192             wssoUser.setMyluteceWssoUserId( nUserId );
193             // We update the user
194             WssoUserHome.update( wssoUser, databasePlugin );
195         }
196         else
197         {
198             // We create the user
199             WssoUserHome.create( wssoUser, databasePlugin );
200             notifyUserAccountCreated( wssoUser, locale, AppPathService.getProdUrl( strBaseUrl ) );
201         }
202 
203         // We remove old roles, groups and attributes of the user
204         WssoUserRoleHome.deleteRolesForUser( wssoUser.getMyluteceWssoUserId( ), databasePlugin );
205         //        DatabaseHome.removeGroupsForUser( user.getUserId( ), databasePlugin );
206         IdxWSSODatabaseHome.removeProfilsForUser( wssoUser.getMyluteceWssoUserId( ), databasePlugin );
207         MyLuteceUserFieldService.doRemoveUserFields( wssoUser.getMyluteceWssoUserId( ), locale );
208 
209         // We get every attributes, roles and groups of the user
210         List<String> listRoles = new ArrayList<String>( );
211 
212         //        List<String> listGroups = new ArrayList<String>( );
213         List<String> listProfils = new ArrayList<String>( );
214 
215         while ( nIndex < strLineDataArray.length )
216         {
217             String strValue = strLineDataArray[nIndex];
218 
219             if ( StringUtils.isNotBlank( strValue ) && ( strValue.indexOf( getAttributesSeparator( ) ) > 0 ) )
220             {
221                 int nSeparatorIndex = strValue.indexOf( getAttributesSeparator( ) );
222                 String strLineId = strValue.substring( 0, nSeparatorIndex );
223 
224                 if ( StringUtils.isNotBlank( strLineId ) )
225                 {
226                     if ( StringUtils.equalsIgnoreCase( strLineId, CONSTANT_ROLE ) )
227                     {
228                         String strRole = strValue.substring( nSeparatorIndex + 1 );
229                         Role role = RoleHome.findByPrimaryKey( strRole );
230 
231                         if ( role == null )
232                         {
233                             Object[] args = { strRole };
234                             String strErrorMessage = I18nService
235                                     .getLocalizedString( MESSAGE_ROLE_UNKNOWN, args, locale );
236                             CSVMessageDescriptor error = new CSVMessageDescriptor( CSVMessageLevel.ERROR, nLineNumber,
237                                     strErrorMessage );
238                             listMessages.add( error );
239                         }
240                         else
241                         {
242                             listRoles.add( strRole );
243                         }
244                     }
245 
246                     //                    else if ( StringUtils.equalsIgnoreCase( strLineId, CONSTANT_GROUP ) )
247                     //                    {
248                     //                    	String strGroup = strValue.substring( nSeparatorIndex + 1 );
249                     //                    	Group group = GroupHome.findByPrimaryKey(strGroup);
250                     //                    	if(group == null) {
251                     //                        	Object[] args = { strGroup };
252                     //                            String strErrorMessage = I18nService.getLocalizedString( MESSAGE_GROUP_UNKNOWN, args, locale );
253                     //                            CSVMessageDescriptor error = new CSVMessageDescriptor( CSVMessageLevel.ERROR, nLineNumber, strErrorMessage );
254                     //                            listMessages.add( error );
255                     //                    	}
256                     //                    	else{
257                     //                    		listGroups.add( strGroup );
258                     //                    	}
259                     //                    }
260                     else
261                     {
262                         String strProfil = strValue.substring( nSeparatorIndex + 1 );
263                         WssoProfil profil = WssoProfilHome.findWssoProfilByCode( strProfil, databasePlugin );
264 
265                         if ( profil == null )
266                         {
267                             Object[] args = { strProfil };
268                             String strErrorMessage = I18nService.getLocalizedString( MESSAGE_PROFIL_UNKNOWN, args,
269                                     locale );
270                             CSVMessageDescriptor error = new CSVMessageDescriptor( CSVMessageLevel.ERROR, nLineNumber,
271                                     strErrorMessage );
272                             listMessages.add( error );
273                         }
274                         else
275                         {
276                             listProfils.add( strProfil );
277                         }
278                     }
279                 }
280             }
281 
282             nIndex++;
283         }
284 
285         // We create roles
286         if ( CollectionUtils.isNotEmpty( listRoles ) )
287         {
288             for ( String strRole : listRoles )
289             {
290                 WssoUserRoleHome.createRoleForUser( wssoUser.getMyluteceWssoUserId( ), strRole, databasePlugin );
291             }
292         }
293 
294         // We create groups
295         //        for ( String strGoup : listGroups )
296         //        {
297         //            DatabaseHome.addGroupForUser( user.getUserId( ), strGoup, databasePlugin );
298         //        }
299         if ( CollectionUtils.isNotEmpty( listProfils ) )
300         {
301             for ( String profil : listProfils )
302             {
303                 IdxWSSODatabaseHome.addUserForProfil( wssoUser.getMyluteceWssoUserId( ), profil, databasePlugin );
304             }
305         }
306 
307         return listMessages;
308     }
309 
310     /**
311      * {@inheritDoc}
312      */
313     @Override
314     protected List<CSVMessageDescriptor> checkLineOfCSVFile( String[] strLineDataArray, int nLineNumber, Locale locale )
315     {
316         int nMinColumnNumber = CONSTANT_MINIMUM_COLUMNS_PER_LINE;
317         Plugin databasePlugin = PluginService.getPlugin( WssoDatabasePlugin.PLUGIN_NAME );
318         List<CSVMessageDescriptor> listMessages = new ArrayList<CSVMessageDescriptor>( );
319 
320         if ( ( strLineDataArray == null ) || ( strLineDataArray.length < nMinColumnNumber ) )
321         {
322             int nNbCol;
323 
324             if ( strLineDataArray == null )
325             {
326                 nNbCol = 0;
327             }
328             else
329             {
330                 nNbCol = strLineDataArray.length;
331             }
332 
333             Object[] args = { nNbCol, nMinColumnNumber };
334             String strErrorMessage = I18nService.getLocalizedString( MESSAGE_ERROR_MIN_NUMBER_COLUMNS, args, locale );
335             CSVMessageDescriptor error = new CSVMessageDescriptor( CSVMessageLevel.ERROR, nLineNumber, strErrorMessage );
336             listMessages.add( error );
337 
338             return listMessages;
339         }
340 
341         if ( !getUpdateExistingUsers( ) )
342         {
343             String strAccessCode = strLineDataArray[0];
344             String strEmail = strLineDataArray[3];
345 
346             if ( WssoUserHome.findDatabaseUserIdFromGuid( strAccessCode, databasePlugin ) > 0 )
347             {
348                 String strMessage = I18nService.getLocalizedString( MESSAGE_ACCESS_CODE_ALREADY_USED, locale );
349                 CSVMessageDescriptor error = new CSVMessageDescriptor( CSVMessageLevel.ERROR, nLineNumber, strMessage );
350                 listMessages.add( error );
351             }
352             else
353             {
354                 Collection<WssoUser> listUsers = WssoUserHome.findWssoUserssByLastNameOrFirtNameOrEmailByProfil( null,
355                         null, null, strEmail, databasePlugin );
356 
357                 if ( ( listUsers != null ) && ( listUsers.size( ) > 0 ) )
358                 {
359                     String strMessage = I18nService.getLocalizedString( MESSAGE_EMAIL_ALREADY_USED, locale );
360                     CSVMessageDescriptor error = new CSVMessageDescriptor( CSVMessageLevel.ERROR, nLineNumber,
361                             strMessage );
362                     listMessages.add( error );
363                 }
364             }
365         }
366 
367         return listMessages;
368     }
369 
370     /**
371      * {@inheritDoc}
372      */
373     @Override
374     protected List<CSVMessageDescriptor> getEndOfProcessMessages( int nNbLineParses, int nNbLinesWithoutErrors,
375             Locale locale )
376     {
377         List<CSVMessageDescriptor> listMessages = new ArrayList<CSVMessageDescriptor>( );
378         Object[] args = { nNbLineParses, nNbLinesWithoutErrors };
379         String strMessageContent = I18nService.getLocalizedString( MESSAGE_USERS_IMPORTED, args, locale );
380         CSVMessageDescriptor message = new CSVMessageDescriptor( CSVMessageLevel.INFO, 0, strMessageContent );
381         listMessages.add( message );
382 
383         return listMessages;
384     }
385 
386     /**
387      * Notify a user of the creation of his account and give him his credentials
388      * @param user the user to notify
389      * @param locale The locale
390      * @param strProdUrl The prod URL
391      */
392     private void notifyUserAccountCreated( WssoUser user, Locale locale, String strProdUrl )
393     {
394         String strSenderEmail = AppPropertiesService.getProperty( PROPERTY_NO_REPLY_EMAIL );
395         String strSiteName = AppPropertiesService.getProperty( PROPERTY_SITE_NAME );
396 
397         String strEmailSubject = I18nService.getLocalizedString( MESSAGE_ACCOUNT_IMPORTED_MAIL_SUBJECT,
398                 new String[] { strSiteName }, locale );
399         String strBaseURL = strProdUrl;
400         Map<String, Object> model = new HashMap<String, Object>( );
401         model.put( MARK_USER, user );
402         model.put( MARK_SITE_NAME, strSiteName );
403         model.put( MARK_SITE_LINK, MailService.getSiteLink( strBaseURL, true ) );
404 
405         HtmlTemplate template = AppTemplateService.getTemplate( TEMPLATE_MAIL_USER_IMPORTED, locale, model );
406 
407         MailService
408                 .sendMailHtml( user.getEmail( ), strSenderEmail, strSenderEmail, strEmailSubject, template.getHtml( ) );
409     }
410 
411     /**
412      * Get the separator used for attributes of admin users.
413      * @return The separator
414      */
415     public Character getAttributesSeparator( )
416     {
417         if ( _strAttributesSeparator == null )
418         {
419             _strAttributesSeparator = AppPropertiesService.getProperty( PROPERTY_IMPORT_EXPORT_USER_SEPARATOR,
420                     CONSTANT_DEFAULT_IMPORT_EXPORT_USER_SEPARATOR ).charAt( 0 );
421         }
422 
423         return _strAttributesSeparator;
424     }
425 
426     /**
427      * Get the update users flag
428      * @return True if existing users should be updated, false if they should be
429      *         ignored.
430      */
431     public boolean getUpdateExistingUsers( )
432     {
433         return _bUpdateExistingUsers;
434     }
435 
436     /**
437      * Set the update users flag
438      * @param bUpdateExistingUsers True if existing users should be updated,
439      *            false if they should be ignored.
440      */
441     public void setUpdateExistingUsers( boolean bUpdateExistingUsers )
442     {
443         this._bUpdateExistingUsers = bUpdateExistingUsers;
444     }
445 }