ImportDatabaseUserService.java
/*
* Copyright (c) 2002-2021, City of Paris
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright notice
* and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice
* and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* 3. Neither the name of 'Mairie de Paris' nor 'Lutece' nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* License 1.0
*/
package fr.paris.lutece.plugins.mylutece.modules.database.authentication.service;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import fr.paris.lutece.plugins.mylutece.business.attribute.AttributeHome;
import fr.paris.lutece.plugins.mylutece.business.attribute.IAttribute;
import fr.paris.lutece.plugins.mylutece.business.attribute.MyLuteceUserField;
import fr.paris.lutece.plugins.mylutece.business.attribute.MyLuteceUserFieldHome;
import fr.paris.lutece.plugins.mylutece.modules.database.authentication.business.DatabaseHome;
import fr.paris.lutece.plugins.mylutece.modules.database.authentication.business.DatabaseUser;
import fr.paris.lutece.plugins.mylutece.modules.database.authentication.business.DatabaseUserHome;
import fr.paris.lutece.plugins.mylutece.modules.database.authentication.service.parameter.DatabaseUserParameterService;
import fr.paris.lutece.plugins.mylutece.service.MyLutecePlugin;
import fr.paris.lutece.plugins.mylutece.service.attribute.MyLuteceUserFieldListenerService;
import fr.paris.lutece.plugins.mylutece.service.attribute.MyLuteceUserFieldService;
import fr.paris.lutece.plugins.mylutece.util.SecurityUtils;
import fr.paris.lutece.portal.service.csv.CSVMessageDescriptor;
import fr.paris.lutece.portal.service.csv.CSVMessageLevel;
import fr.paris.lutece.portal.service.csv.CSVReaderService;
import fr.paris.lutece.portal.service.i18n.I18nService;
import fr.paris.lutece.portal.service.mail.MailService;
import fr.paris.lutece.portal.service.plugin.Plugin;
import fr.paris.lutece.portal.service.plugin.PluginService;
import fr.paris.lutece.portal.service.spring.SpringContextService;
import fr.paris.lutece.portal.service.template.AppTemplateService;
import fr.paris.lutece.portal.service.util.AppLogService;
import fr.paris.lutece.portal.service.util.AppPathService;
import fr.paris.lutece.portal.service.util.AppPropertiesService;
import fr.paris.lutece.util.html.HtmlTemplate;
/**
* Import database users from a CSV file
*/
public class ImportDatabaseUserService extends CSVReaderService
{
private static final String MESSAGE_NO_STATUS = "module.mylutece.database.import_users_from_file.importNoStatus";
private static final String MESSAGE_ACCESS_CODE_ALREADY_USED = "module.mylutece.database.message.user_exist";
private static final String MESSAGE_EMAIL_ALREADY_USED = "module.mylutece.database.message.user_exist";
private static final String MESSAGE_USERS_IMPORTED = "module.mylutece.database.import_users_from_file.usersImported";
private static final String MESSAGE_ERROR_MIN_NUMBER_COLUMNS = "module.mylutece.database.import_users_from_file.messageErrorMinColumnNumber";
private static final String MESSAGE_ACCOUNT_IMPORTED_MAIL_SUBJECT = "module.mylutece.database.import_users_from_file.email.mailSubject";
private static final String MESSAGE_ERROR_IMPORTING_ATTRIBUTES = "module.mylutece.database.import_users_from_file.errorImportingAttributes";
private static final String PROPERTY_NO_REPLY_EMAIL = "mail.noreply.email";
private static final String PROPERTY_IMPORT_EXPORT_USER_SEPARATOR = "lutece.importExportUser.defaultSeparator";
private static final String PROPERTY_SITE_NAME = "lutece.name";
private static final String TEMPLATE_MAIL_USER_IMPORTED = "admin/plugins/mylutece/modules/database/mail_user_imported.html";
private static final String MARK_SITE_NAME = "site_name";
private static final String MARK_USER = "user";
private static final String MARK_SITE_LINK = "site_link";
private static final String MARK_PASSWORD = "password";
private static final String CONSTANT_DEFAULT_IMPORT_EXPORT_USER_SEPARATOR = ":";
private static final String CONSTANT_ROLE = "role";
private static final String CONSTANT_GROUP = "group";
private static final int CONSTANT_MINIMUM_COLUMNS_PER_LINE = 7;
private Character _strAttributesSeparator;
private boolean _bUpdateExistingUsers;
private DatabaseUserParameterService _userParamService = DatabaseUserParameterService.getService( );
/**
* {@inheritDoc}
*/
@Override
protected List<CSVMessageDescriptor> readLineOfCSVFile( String [ ] strLineDataArray, int nLineNumber, Locale locale, String strBaseUrl )
{
Plugin databasePlugin = PluginService.getPlugin( DatabasePlugin.PLUGIN_NAME );
Plugin mylutecePlugin = PluginService.getPlugin( MyLutecePlugin.PLUGIN_NAME );
List<CSVMessageDescriptor> listMessages = new ArrayList<>( );
int nIndex = 0;
String strAccessCode = strLineDataArray [nIndex++];
String strLastName = strLineDataArray [nIndex++];
String strFirstName = strLineDataArray [nIndex++];
String strEmail = strLineDataArray [nIndex++];
boolean bUpdateUser = getUpdateExistingUsers( );
int nUserId = 0;
if ( bUpdateUser )
{
int nAccessCodeUserId = DatabaseUserHome.findDatabaseUserIdFromLogin( strAccessCode, databasePlugin );
if ( nAccessCodeUserId > 0 )
{
nUserId = nAccessCodeUserId;
}
bUpdateUser = nUserId > 0;
}
String strStatus = strLineDataArray [nIndex++];
int nStatus = 0;
if ( StringUtils.isNotEmpty( strStatus ) && StringUtils.isNumeric( strStatus ) )
{
nStatus = Integer.parseInt( strStatus );
}
else
{
Object [ ] args = {
strLastName, strFirstName, nStatus
};
String strMessage = I18nService.getLocalizedString( MESSAGE_NO_STATUS, args, locale );
CSVMessageDescriptor message = new CSVMessageDescriptor( CSVMessageLevel.INFO, nLineNumber, strMessage );
listMessages.add( message );
}
// We ignore the password max valid date attribute because we changed the password.
nIndex++;
// We ignore the account max valid date attribute
nIndex++;
DatabaseUser user = new DatabaseUser( );
user.setLogin( strAccessCode );
user.setLastName( strLastName );
user.setFirstName( strFirstName );
user.setEmail( strEmail );
user.setStatus( nStatus );
if ( bUpdateUser )
{
user.setUserId( nUserId );
// We update the user
DatabaseService.getService( ).doUpdateUser( user, databasePlugin );
}
else
{
// We create the user
String strPassword = SecurityUtils.makePassword( _userParamService, databasePlugin );
DatabaseService.getService( ).doCreateUser( user, strPassword, databasePlugin );
notifyUserAccountCreated( user, strPassword, locale, AppPathService.getProdUrl( strBaseUrl ) );
}
// We remove old roles, groups and attributes of the user
DatabaseHome.removeRolesForUser( user.getUserId( ), databasePlugin );
DatabaseHome.removeGroupsForUser( user.getUserId( ), databasePlugin );
MyLuteceUserFieldService.doRemoveUserFields( user.getUserId( ), locale );
// We get every attributes, roles and groups of the user
Map<Integer, List<String>> mapAttributesValues = new HashMap<>( );
List<String> listRoles = new ArrayList<>( );
List<String> listGroups = new ArrayList<>( );
while ( nIndex < strLineDataArray.length )
{
String strValue = strLineDataArray [nIndex];
if ( StringUtils.isNotBlank( strValue ) && ( strValue.indexOf( getAttributesSeparator( ) ) > 0 ) )
{
int nSeparatorIndex = strValue.indexOf( getAttributesSeparator( ) );
String strLineId = strValue.substring( 0, nSeparatorIndex );
if ( StringUtils.isNotBlank( strLineId ) )
{
if ( StringUtils.equalsIgnoreCase( strLineId, CONSTANT_ROLE ) )
{
listRoles.add( strValue.substring( nSeparatorIndex + 1 ) );
}
else
if ( StringUtils.equalsIgnoreCase( strLineId, CONSTANT_GROUP ) )
{
listGroups.add( strValue.substring( nSeparatorIndex + 1 ) );
}
else
{
int nAttributeId = Integer.parseInt( strLineId );
String strAttributeValue = strValue.substring( nSeparatorIndex + 1 );
List<String> listValues = mapAttributesValues.get( nAttributeId );
if ( listValues == null )
{
listValues = new ArrayList<>( );
}
listValues.add( strAttributeValue );
mapAttributesValues.put( nAttributeId, listValues );
}
}
}
nIndex++;
}
// We create roles
for ( String strRole : listRoles )
{
DatabaseHome.addRoleForUser( user.getUserId( ), strRole, databasePlugin );
}
// We create groups
for ( String strGoup : listGroups )
{
DatabaseHome.addGroupForUser( user.getUserId( ), strGoup, databasePlugin );
}
// We save the attributes found
List<IAttribute> listAttributes = AttributeHome.findAll( locale, mylutecePlugin );
for ( IAttribute attribute : listAttributes )
{
List<String> listValues = mapAttributesValues.get( attribute.getIdAttribute( ) );
if ( CollectionUtils.isNotEmpty( listValues ) )
{
int nIdField = 0;
boolean bMyLuteceAttribute = ( attribute.getPlugin( ) == null )
|| StringUtils.equals( attribute.getPlugin( ).getName( ), MyLutecePlugin.PLUGIN_NAME );
for ( String strValue : listValues )
{
int nSeparatorIndex = strValue.indexOf( getAttributesSeparator( ) );
if ( nSeparatorIndex >= 0 )
{
nIdField = 0;
try
{
nIdField = Integer.parseInt( strValue.substring( 0, nSeparatorIndex ) );
}
catch( NumberFormatException e )
{
nIdField = 0;
}
strValue = strValue.substring( nSeparatorIndex + 1 );
}
else
{
nIdField = 0;
}
String [ ] strValues = {
strValue
};
try
{
List<MyLuteceUserField> listUserFields = attribute.getUserFieldsData( strValues, user.getUserId( ) );
for ( MyLuteceUserField userField : listUserFields )
{
if ( userField != null )
{
userField.getAttributeField( ).setIdField( nIdField );
MyLuteceUserFieldHome.create( userField, mylutecePlugin );
}
}
if ( !bMyLuteceAttribute )
{
for ( MyLuteceUserFieldListenerService myLuteceUserFieldListenerService : SpringContextService
.getBeansOfType( MyLuteceUserFieldListenerService.class ) )
{
myLuteceUserFieldListenerService.doCreateUserFields( user.getUserId( ), listUserFields, locale );
}
}
}
catch( Exception e )
{
AppLogService.error( e.getMessage( ), e );
String strErrorMessage = I18nService.getLocalizedString( MESSAGE_ERROR_IMPORTING_ATTRIBUTES, locale );
CSVMessageDescriptor error = new CSVMessageDescriptor( CSVMessageLevel.ERROR, nLineNumber, strErrorMessage );
listMessages.add( error );
}
}
}
}
return listMessages;
}
/**
* {@inheritDoc}
*/
@Override
protected List<CSVMessageDescriptor> checkLineOfCSVFile( String [ ] strLineDataArray, int nLineNumber, Locale locale )
{
int nMinColumnNumber = CONSTANT_MINIMUM_COLUMNS_PER_LINE;
Plugin databasePlugin = PluginService.getPlugin( DatabasePlugin.PLUGIN_NAME );
List<CSVMessageDescriptor> listMessages = new ArrayList<>( );
if ( ( strLineDataArray == null ) || ( strLineDataArray.length < nMinColumnNumber ) )
{
int nNbCol;
if ( strLineDataArray == null )
{
nNbCol = 0;
}
else
{
nNbCol = strLineDataArray.length;
}
Object [ ] args = {
nNbCol, nMinColumnNumber
};
String strErrorMessage = I18nService.getLocalizedString( MESSAGE_ERROR_MIN_NUMBER_COLUMNS, args, locale );
CSVMessageDescriptor error = new CSVMessageDescriptor( CSVMessageLevel.ERROR, nLineNumber, strErrorMessage );
listMessages.add( error );
return listMessages;
}
if ( !getUpdateExistingUsers( ) )
{
String strAccessCode = strLineDataArray [0];
String strEmail = strLineDataArray [3];
if ( DatabaseUserHome.findDatabaseUserIdFromLogin( strAccessCode, databasePlugin ) > 0 )
{
String strMessage = I18nService.getLocalizedString( MESSAGE_ACCESS_CODE_ALREADY_USED, locale );
CSVMessageDescriptor error = new CSVMessageDescriptor( CSVMessageLevel.ERROR, nLineNumber, strMessage );
listMessages.add( error );
}
else
{
Collection<DatabaseUser> listUsers = DatabaseUserHome.findDatabaseUsersListForEmail( strEmail, databasePlugin );
if ( CollectionUtils.isNotEmpty( listUsers ) )
{
String strMessage = I18nService.getLocalizedString( MESSAGE_EMAIL_ALREADY_USED, locale );
CSVMessageDescriptor error = new CSVMessageDescriptor( CSVMessageLevel.ERROR, nLineNumber, strMessage );
listMessages.add( error );
}
}
}
return listMessages;
}
/**
* {@inheritDoc}
*/
@Override
protected List<CSVMessageDescriptor> getEndOfProcessMessages( int nNbLineParses, int nNbLinesWithoutErrors, Locale locale )
{
List<CSVMessageDescriptor> listMessages = new ArrayList<>( );
Object [ ] args = {
nNbLineParses, nNbLinesWithoutErrors
};
String strMessageContent = I18nService.getLocalizedString( MESSAGE_USERS_IMPORTED, args, locale );
CSVMessageDescriptor message = new CSVMessageDescriptor( CSVMessageLevel.INFO, 0, strMessageContent );
listMessages.add( message );
return listMessages;
}
/**
* Notify a user of the creation of his account and give him his credentials
*
* @param user
* the user to notify
* @param strPassword
* The password of the user
* @param locale
* The locale
* @param strProdUrl
* The prod URL
*/
private void notifyUserAccountCreated( DatabaseUser user, String strPassword, Locale locale, String strProdUrl )
{
String strSenderEmail = AppPropertiesService.getProperty( PROPERTY_NO_REPLY_EMAIL );
String strSiteName = AppPropertiesService.getProperty( PROPERTY_SITE_NAME );
String strEmailSubject = I18nService.getLocalizedString( MESSAGE_ACCOUNT_IMPORTED_MAIL_SUBJECT, new String [ ] {
strSiteName
}, locale );
String strBaseURL = strProdUrl;
Map<String, Object> model = new HashMap<>( );
model.put( MARK_USER, user );
model.put( MARK_SITE_NAME, strSiteName );
model.put( MARK_SITE_LINK, MailService.getSiteLink( strBaseURL, true ) );
model.put( MARK_PASSWORD, strPassword );
HtmlTemplate template = AppTemplateService.getTemplate( TEMPLATE_MAIL_USER_IMPORTED, locale, model );
MailService.sendMailHtml( user.getEmail( ), strSenderEmail, strSenderEmail, strEmailSubject, template.getHtml( ) );
}
/**
* Get the separator used for attributes of admin users.
*
* @return The separator
*/
public Character getAttributesSeparator( )
{
if ( _strAttributesSeparator == null )
{
_strAttributesSeparator = AppPropertiesService.getProperty( PROPERTY_IMPORT_EXPORT_USER_SEPARATOR, CONSTANT_DEFAULT_IMPORT_EXPORT_USER_SEPARATOR )
.charAt( 0 );
}
return _strAttributesSeparator;
}
/**
* Get the update users flag
*
* @return True if existing users should be updated, false if they should be ignored.
*/
public boolean getUpdateExistingUsers( )
{
return _bUpdateExistingUsers;
}
/**
* Set the update users flag
*
* @param bUpdateExistingUsers
* True if existing users should be updated, false if they should be ignored.
*/
public void setUpdateExistingUsers( boolean bUpdateExistingUsers )
{
this._bUpdateExistingUsers = bUpdateExistingUsers;
}
}