View Javadoc
1   /*
2    * Copyright (c) 2002-2022, 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.portal.service.admin;
35  
36  import java.sql.Timestamp;
37  import java.text.DateFormat;
38  import java.text.ParseException;
39  import java.text.SimpleDateFormat;
40  import java.util.ArrayList;
41  import java.util.Date;
42  import java.util.HashMap;
43  import java.util.List;
44  import java.util.Locale;
45  import java.util.Map;
46  import java.util.stream.Collectors;
47  
48  import org.apache.commons.collections.CollectionUtils;
49  import org.apache.commons.lang3.StringUtils;
50  
51  import fr.paris.lutece.portal.business.user.AdminUser;
52  import fr.paris.lutece.portal.business.user.AdminUserHome;
53  import fr.paris.lutece.portal.business.user.attribute.AdminUserField;
54  import fr.paris.lutece.portal.business.user.attribute.AdminUserFieldFilter;
55  import fr.paris.lutece.portal.business.user.attribute.AdminUserFieldHome;
56  import fr.paris.lutece.portal.business.user.attribute.IAttribute;
57  import fr.paris.lutece.portal.business.user.attribute.ISimpleValuesAttributes;
58  import fr.paris.lutece.portal.business.user.authentication.LuteceDefaultAdminUser;
59  import fr.paris.lutece.portal.business.workgroup.AdminWorkgroupHome;
60  import fr.paris.lutece.portal.service.csv.CSVMessageDescriptor;
61  import fr.paris.lutece.portal.service.csv.CSVMessageLevel;
62  import fr.paris.lutece.portal.service.i18n.I18nService;
63  import fr.paris.lutece.portal.service.plugin.Plugin;
64  import fr.paris.lutece.portal.service.plugin.PluginService;
65  import fr.paris.lutece.portal.service.spring.SpringContextService;
66  import fr.paris.lutece.portal.service.user.attribute.AdminUserFieldListenerService;
67  import fr.paris.lutece.portal.service.user.attribute.AttributeService;
68  import fr.paris.lutece.portal.service.util.AppLogService;
69  import fr.paris.lutece.portal.service.util.AppPathService;
70  
71  /**
72   * Class to import Admin Users from CSV files.
73   */
74  public class DefaultImportAdminUserService extends ImportAdminUserService
75  {
76  
77      private static final String CONSTANT_RIGHT = "right";
78      private static final String CONSTANT_ROLE = "role";
79      private static final String CONSTANT_WORKGROUP = "workgroup";
80      private static final String PROPERTY_MESSAGE_EMAIL_SUBJECT_NOTIFY_USER = "portal.users.notify_user.email.subject";
81      private static final String TEMPLATE_NOTIFY_USER = "admin/user/notify_user_account_created.html";
82      private static final String MESSAGE_ERROR_IMPORTING_ATTRIBUTES = "portal.users.import_users_from_file.errorImportingAttributes";
83      private static final String MESSAGE_NO_LEVEL = "portal.users.import_users_from_file.importNoLevel";
84      private static final String MESSAGE_NO_STATUS = "portal.users.import_users_from_file.importNoStatus";
85      private static final int CONSTANT_MINIMUM_COLUMNS_PER_LINE = 12;
86  
87      // Template
88      private static final String TEMPLATE_DEFAULT_IMPORT_USERS_FROM_FILE = "admin/user/import_users_from_file.html";
89      private static final AttributeService _attributeService = AttributeService.getInstance( );
90  
91      /**
92       * {@inheritDoc}
93       */
94      @Override
95      protected List<CSVMessageDescriptor> readLineOfCSVFile( String [ ] strLineDataArray, int nLineNumber, Locale locale, String strBaseUrl )
96      {
97          List<CSVMessageDescriptor> listMessages = new ArrayList<>( );
98          int nIndex = 0;
99  
100         String strAccessCode = strLineDataArray [nIndex++];
101         String strLastName = strLineDataArray [nIndex++];
102         String strFirstName = strLineDataArray [nIndex++];
103         String strEmail = strLineDataArray [nIndex++];
104 
105         boolean bUpdateUser = getUpdateExistingUsers( );
106         int nUserId = 0;
107 
108         if ( bUpdateUser )
109         {
110             int nAccessCodeUserId = AdminUserHome.checkAccessCodeAlreadyInUse( strAccessCode );
111             int nEmailUserId = AdminUserHome.checkEmailAlreadyInUse( strEmail );
112 
113             if ( nAccessCodeUserId > 0 )
114             {
115                 nUserId = nAccessCodeUserId;
116             }
117             else
118                 if ( nEmailUserId > 0 )
119                 {
120                     nUserId = nEmailUserId;
121                 }
122             bUpdateUser = nUserId > 0;
123         }
124 
125         String strStatus = strLineDataArray [nIndex++];
126         int nStatus = getStatus( strStatus, strLastName, strFirstName, nLineNumber, listMessages, locale );
127 
128         String strLocale = strLineDataArray [nIndex++];
129         String strLevelUser = strLineDataArray [nIndex++];
130         int nLevelUser = getLevel( strLevelUser, strLastName, strFirstName, nLineNumber, listMessages, locale );
131 
132         nIndex++;
133 
134         String strAccessibilityMode = strLineDataArray [nIndex++];
135         boolean bAccessibilityMode = Boolean.parseBoolean( strAccessibilityMode );
136         nIndex++;
137         nIndex++;
138 
139         AdminUser user = null;
140 
141         if ( bUpdateUser )
142         {
143             user = AdminUserHome.findByPrimaryKey( nUserId );
144         }
145         else
146         {
147             user = new LuteceDefaultAdminUser( );
148         }
149 
150         user.setAccessCode( strAccessCode );
151         user.setLastName( strLastName );
152         user.setFirstName( strFirstName );
153         user.setEmail( strEmail );
154         user.setStatus( nStatus );
155         user.setUserLevel( nLevelUser );
156         user.setLocale( new Locale( strLocale ) );
157         user.setAccessibilityMode( bAccessibilityMode );
158 
159         String strDateLastLogin = strLineDataArray [nIndex++];
160         user = saveOrUpdateUser( user, bUpdateUser, nUserId, strDateLastLogin, strBaseUrl );
161 
162         // We remove any previous right, roles, workgroup and attributes of the user
163         AdminUserHome.removeAllRightsForUser( user.getUserId( ) );
164         AdminUserHome.removeAllRolesForUser( user.getUserId( ) );
165 
166         AdminUserFieldFilterute/AdminUserFieldFilter.html#AdminUserFieldFilter">AdminUserFieldFilter auFieldFilter = new AdminUserFieldFilter( );
167         auFieldFilter.setIdUser( user.getUserId( ) );
168         AdminUserFieldHome.removeByFilter( auFieldFilter );
169 
170         // We get every attribute, role, right and workgroup of the user
171         Map<Integer, List<String>> mapAttributesValues = new HashMap<>( );
172         List<String> listAdminRights = new ArrayList<>( );
173         List<String> listAdminRoles = new ArrayList<>( );
174         List<String> listAdminWorkgroups = new ArrayList<>( );
175 
176         while ( nIndex < strLineDataArray.length )
177         {
178             String strValue = strLineDataArray [nIndex];
179             readAttribute( strValue, listAdminRights, listAdminRoles, listAdminWorkgroups, mapAttributesValues );
180             nIndex++;
181         }
182 
183         // We create rights
184         for ( String strRight : listAdminRights )
185         {
186             AdminUserHome.createRightForUser( user.getUserId( ), strRight );
187         }
188 
189         // We create roles
190         for ( String strRole : listAdminRoles )
191         {
192             AdminUserHome.createRoleForUser( user.getUserId( ), strRole );
193         }
194 
195         // We create workgroups
196         for ( String strWorkgoup : listAdminWorkgroups )
197         {
198             AdminWorkgroupHome.addUserForWorkgroup( user, strWorkgoup );
199         }
200 
201         List<IAttribute> listAttributes = _attributeService.getAllAttributesWithoutFields( locale );
202 
203         // We save the attributes found
204         saveAttributes( listAttributes, user, nLineNumber, mapAttributesValues, listMessages, locale );
205 
206         return listMessages;
207     }
208 
209     private int getLevel( String strLevelUser, String strLastName, String strFirstName, int nLineNumber, List<CSVMessageDescriptor> listMessages,
210             Locale locale )
211     {
212         int nLevelUser = 3;
213 
214         if ( StringUtils.isNotEmpty( strLevelUser ) && StringUtils.isNumeric( strLevelUser ) )
215         {
216             nLevelUser = Integer.parseInt( strLevelUser );
217         }
218         else
219         {
220             Object [ ] args = {
221                     strLastName, strFirstName, nLevelUser
222             };
223             String strMessage = I18nService.getLocalizedString( MESSAGE_NO_LEVEL, args, locale );
224             CSVMessageDescriptorVMessageDescriptor.html#CSVMessageDescriptor">CSVMessageDescriptor message = new CSVMessageDescriptor( CSVMessageLevel.INFO, nLineNumber, strMessage );
225             listMessages.add( message );
226         }
227         return nLevelUser;
228     }
229 
230     private int getStatus( String strStatus, String strLastName, String strFirstName, int nLineNumber, List<CSVMessageDescriptor> listMessages, Locale locale )
231     {
232         int nStatus = 0;
233 
234         if ( StringUtils.isNotEmpty( strStatus ) && StringUtils.isNumeric( strStatus ) )
235         {
236             nStatus = Integer.parseInt( strStatus );
237         }
238         else
239         {
240             Object [ ] args = {
241                     strLastName, strFirstName, nStatus
242             };
243             String strMessage = I18nService.getLocalizedString( MESSAGE_NO_STATUS, args, locale );
244             CSVMessageDescriptorVMessageDescriptor.html#CSVMessageDescriptor">CSVMessageDescriptor message = new CSVMessageDescriptor( CSVMessageLevel.INFO, nLineNumber, strMessage );
245             listMessages.add( message );
246         }
247         return nStatus;
248     }
249 
250     private void readAttribute( String strValue, List<String> listAdminRights, List<String> listAdminRoles, List<String> listAdminWorkgroups,
251             Map<Integer, List<String>> mapAttributesValues )
252     {
253         if ( StringUtils.isBlank( strValue ) )
254         {
255             return;
256         }
257         int nSeparatorIndex = strValue.indexOf( getAttributesSeparator( ) );
258         if ( nSeparatorIndex >= 0 )
259         {
260             String strLineId = strValue.substring( 0, nSeparatorIndex );
261             if ( StringUtils.isNotBlank( strLineId ) )
262             {
263                 if ( StringUtils.equalsIgnoreCase( strLineId, CONSTANT_RIGHT ) )
264                 {
265                     listAdminRights.add( strValue.substring( nSeparatorIndex + 1 ) );
266                 }
267                 else
268                     if ( StringUtils.equalsIgnoreCase( strLineId, CONSTANT_ROLE ) )
269                     {
270                         listAdminRoles.add( strValue.substring( nSeparatorIndex + 1 ) );
271                     }
272                     else
273                         if ( StringUtils.equalsIgnoreCase( strLineId, CONSTANT_WORKGROUP ) )
274                         {
275                             listAdminWorkgroups.add( strValue.substring( nSeparatorIndex + 1 ) );
276                         }
277                         else
278                         {
279                             int nAttributeId = Integer.parseInt( strLineId );
280 
281                             String strAttributeValue = strValue.substring( nSeparatorIndex + 1 );
282                             List<String> listValues = mapAttributesValues.get( nAttributeId );
283 
284                             if ( listValues == null )
285                             {
286                                 listValues = new ArrayList<>( );
287                             }
288 
289                             listValues.add( strAttributeValue );
290                             mapAttributesValues.put( nAttributeId, listValues );
291                         }
292             }
293         }
294     }
295 
296     private void saveAttributes( List<IAttribute> listAttributes, AdminUser user, int nLineNumber, Map<Integer, List<String>> mapAttributesValues,
297             List<CSVMessageDescriptor> listMessages, Locale locale )
298     {
299         listAttributes = listAttributes.stream( ).filter( a -> a instanceof ISimpleValuesAttributes ).collect( Collectors.toList( ) );
300         for ( IAttribute attribute : listAttributes )
301         {
302             List<String> listValues = mapAttributesValues.get( attribute.getIdAttribute( ) );
303 
304             if ( CollectionUtils.isEmpty( listValues ) )
305             {
306                 continue;
307             }
308 
309             int nIdField = 0;
310             for ( String strValue : listValues )
311             {
312                 int nSeparatorIndex = strValue.indexOf( getAttributesSeparator( ) );
313 
314                 if ( nSeparatorIndex >= 0 )
315                 {
316                     nIdField = 0;
317 
318                     try
319                     {
320                         nIdField = Integer.parseInt( strValue.substring( 0, nSeparatorIndex ) );
321                     }
322                     catch( NumberFormatException e )
323                     {
324                         nIdField = 0;
325                     }
326 
327                     strValue = strValue.substring( nSeparatorIndex + 1 );
328                 }
329                 else
330                 {
331                     nIdField = 0;
332                 }
333 
334                 createFields( attribute, user, strValue, nIdField, nLineNumber, listMessages, locale );
335             }
336         }
337     }
338 
339     private void createFields( IAttribute attribute, AdminUser user, String strValue, int nIdField, int nLineNumber, List<CSVMessageDescriptor> listMessages,
340             Locale locale )
341     {
342         Plugin pluginCore = PluginService.getCore( );
343         boolean bCoreAttribute = ( attribute.getPlugin( ) == null ) || StringUtils.equals( pluginCore.getName( ), attribute.getPlugin( ).getName( ) );
344         try
345         {
346             List<AdminUserField> listUserFields = ( (ISimpleValuesAttributes) attribute ).getUserFieldsData( new String [ ] {
347                     strValue
348             }, user );
349 
350             for ( AdminUserField userField : listUserFields )
351             {
352                 if ( userField != null )
353                 {
354                     userField.getAttributeField( ).setIdField( nIdField );
355                     AdminUserFieldHome.create( userField );
356                 }
357             }
358 
359             if ( !bCoreAttribute )
360             {
361                 for ( AdminUserFieldListenerService adminUserFieldListenerService : SpringContextService.getBeansOfType( AdminUserFieldListenerService.class ) )
362                 {
363                     adminUserFieldListenerService.doCreateUserFields( user, listUserFields, locale );
364                 }
365             }
366         }
367         catch( Exception e )
368         {
369             AppLogService.error( e.getMessage( ), e );
370 
371             String strErrorMessage = I18nService.getLocalizedString( MESSAGE_ERROR_IMPORTING_ATTRIBUTES, locale );
372             CSVMessageDescriptorCSVMessageDescriptor.html#CSVMessageDescriptor">CSVMessageDescriptor error = new CSVMessageDescriptor( CSVMessageLevel.ERROR, nLineNumber, strErrorMessage );
373             listMessages.add( error );
374         }
375     }
376 
377     private AdminUser/../../fr/paris/lutece/portal/business/user/AdminUser.html#AdminUser">AdminUser saveOrUpdateUser( AdminUser user, boolean bUpdateUser, int nUserId, String strDateLastLogin, String strBaseUrl )
378     {
379         if ( bUpdateUser )
380         {
381             user.setUserId( nUserId );
382             // We update the user
383             AdminUserHome.update( user );
384         }
385         else
386         {
387             Timestamp dateLastLogin = getDateLastLogin( strDateLastLogin );
388             // We create the user
389             user.setPasswordReset( true );
390             user.setPasswordMaxValidDate( null );
391             user.setAccountMaxValidDate( AdminUserService.getAccountMaxValidDate( ) );
392             user.setDateLastLogin( dateLastLogin );
393 
394             if ( AdminAuthenticationService.getInstance( ).isDefaultModuleUsed( ) )
395             {
396                 LuteceDefaultAdminUser/lutece/portal/business/user/authentication/LuteceDefaultAdminUser.html#LuteceDefaultAdminUser">LuteceDefaultAdminUser defaultAdminUser = (LuteceDefaultAdminUser) user;
397                 String strPassword = AdminUserService.makePassword( );
398                 defaultAdminUser.setPassword( AdminUserService.encryptPassword( strPassword ) );
399                 AdminUserHome.create( defaultAdminUser );
400                 AdminUserService.notifyUser( AppPathService.getProdUrl( strBaseUrl ), user, strPassword, PROPERTY_MESSAGE_EMAIL_SUBJECT_NOTIFY_USER,
401                         TEMPLATE_NOTIFY_USER );
402             }
403             else
404             {
405                 AdminUserHome.create( user );
406             }
407         }
408         return user;
409     }
410 
411     private Timestamp getDateLastLogin( String strDateLastLogin )
412     {
413         Timestamp dateLastLogin = AdminUser.getDefaultDateLastLogin( );
414 
415         if ( StringUtils.isNotBlank( strDateLastLogin ) )
416         {
417             DateFormat dateFormat = new SimpleDateFormat( );
418             Date dateParsed;
419 
420             try
421             {
422                 dateParsed = dateFormat.parse( strDateLastLogin );
423             }
424             catch( ParseException e )
425             {
426                 AppLogService.error( e.getMessage( ), e );
427                 dateParsed = null;
428             }
429 
430             if ( dateParsed != null )
431             {
432                 dateLastLogin = new Timestamp( dateParsed.getTime( ) );
433             }
434         }
435         return dateLastLogin;
436     }
437 
438     /**
439      * {@inheritDoc}
440      */
441     @Override
442     public String getImportFromFileTemplate( )
443     {
444         return TEMPLATE_DEFAULT_IMPORT_USERS_FROM_FILE;
445     }
446 
447     /**
448      * {@inheritDoc}
449      */
450     @Override
451     public int getNbMinColumns( )
452     {
453         return CONSTANT_MINIMUM_COLUMNS_PER_LINE;
454     }
455 
456     /**
457      * {@inheritDoc}
458      */
459     @Override
460     public String getAccessCode( String [ ] strLineDataArray )
461     {
462         return strLineDataArray [0];
463     }
464 
465     /**
466      * {@inheritDoc}
467      */
468     @Override
469     public String getEmail( String [ ] strLineDataArray )
470     {
471         return strLineDataArray [3];
472     }
473 }