From 5d625f0d208d4d8736278ee31facba012459e367 Mon Sep 17 00:00:00 2001 From: seboo <25958061+seboo@users.noreply.github.com> Date: Wed, 16 Apr 2025 17:38:33 +0200 Subject: [PATCH] LUT-29770 : FileService migration --- run.sh | 1 + .../portal/service/file/FileService.java | 88 ++----- .../service/file/FileServiceException.java | 17 +- .../service/file/IFileStoreService.java | 59 +++++ .../file/IFileStoreServiceProvider.java | 12 +- .../DefaultFileDownloadService.java | 40 +-- .../DefaultFileNoRBACService.java | 2 + .../DefaultFileRBACService.java | 16 +- .../DefaultFileServiceProducer.java | 32 +++ .../DefaultFileStoreServiceProvider.java | 203 +++++++++++++++ .../LocalDatabaseFileService.java | 227 ++--------------- .../portal/service/file/FileServiceTest.java | 234 +++++++++++------- webapp/WEB-INF/conf/config.properties | 9 + 13 files changed, 534 insertions(+), 406 deletions(-) create mode 100755 run.sh create mode 100644 src/java/fr/paris/lutece/portal/service/file/IFileStoreService.java create mode 100644 src/java/fr/paris/lutece/portal/service/file/implementation/DefaultFileServiceProducer.java create mode 100644 src/java/fr/paris/lutece/portal/service/file/implementation/DefaultFileStoreServiceProvider.java diff --git a/run.sh b/run.sh new file mode 100755 index 000000000..56396ce76 --- /dev/null +++ b/run.sh @@ -0,0 +1 @@ +JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64 ~/tools/mvn388/apache-maven-3.8.8/bin/mvn clean lutece:exploded antrun:run -Dlutece-test-hsql test -Dtest=FileServiceTest diff --git a/src/java/fr/paris/lutece/portal/service/file/FileService.java b/src/java/fr/paris/lutece/portal/service/file/FileService.java index 14d9e5b86..14879ee56 100644 --- a/src/java/fr/paris/lutece/portal/service/file/FileService.java +++ b/src/java/fr/paris/lutece/portal/service/file/FileService.java @@ -33,13 +33,12 @@ */ package fr.paris.lutece.portal.service.file; +import org.apache.commons.lang3.StringUtils; + import fr.paris.lutece.portal.service.util.AppException; -import jakarta.annotation.PostConstruct; import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.inject.Instance; import jakarta.enterprise.inject.spi.CDI; -import jakarta.inject.Inject; - /** * @@ -48,8 +47,6 @@ @ApplicationScoped public class FileService { - - // parameters public static final String PARAMETER_FILE_ID = "file_id"; public static final String PARAMETER_RESOURCE_ID = "resource_id"; public static final String PARAMETER_RESOURCE_TYPE = "resource_type"; @@ -58,39 +55,20 @@ public class FileService public static final String PARAMETER_BO = "is_bo"; public static final String PARAMETER_PROVIDER = "provider"; - // constants - public static final String PERMISSION_VIEW = "VIEW"; - // messages private static final String MSG_NO_FILE_SERVICE = "No file service Available"; - - @Inject - private IFileStoreServiceProvider _defaulFileStoreServiceProvider; - @Inject - private Instance _fileStoreServiceProviderList; - - - /** - * init - */ - @PostConstruct - void initFileService( ) - { - _defaulFileStoreServiceProvider = getDefaultServiceProvider( ); - } - /** * Returns the unique instance of the {@link FileService} service. * - *

This method is deprecated and is provided for backward compatibility only. - * For new code, use dependency injection with {@code @Inject} to obtain the - * {@link FileService} instance instead.

+ *

+ * This method is deprecated and is provided for backward compatibility only. For new code, use dependency injection with {@code @Inject} to obtain the + * {@link FileService} instance instead. + *

* * @return The unique instance of {@link FileService}. * - * @deprecated Use {@code @Inject} to obtain the {@link FileService} - * instance. This method will be removed in future versions. + * @deprecated Use {@code @Inject} to obtain the {@link FileService} instance. This method will be removed in future versions. */ @Deprecated( since = "8.0", forRemoval = true ) public static FileService getInstance( ) @@ -98,14 +76,14 @@ public static FileService getInstance( ) return CDI.current( ).select( FileService.class ).get( ); } - /** + /** * get the current FileStoreService provider * * @return the current FileStoreService provider */ public IFileStoreServiceProvider getFileStoreServiceProvider( ) { - return getFileStoreServiceProvider( null) ; + return getFileStoreServiceProvider( null ); } /** @@ -116,46 +94,24 @@ public IFileStoreServiceProvider getFileStoreServiceProvider( ) */ public IFileStoreServiceProvider getFileStoreServiceProvider( String strFileStoreServiceProviderName ) { + Instance fileServiceProviders = CDI.current( ).select( IFileStoreServiceProvider.class ); - if ( strFileStoreServiceProviderName == null && _defaulFileStoreServiceProvider != null ) - { - return _defaulFileStoreServiceProvider; - } - - // search file service - if ( !_fileStoreServiceProviderList.isUnsatisfied() && strFileStoreServiceProviderName != null ) + if ( fileServiceProviders.stream( ).count( ) == 0 ) { - for ( IFileStoreServiceProvider fss : _fileStoreServiceProviderList ) - { - if ( strFileStoreServiceProviderName.equals( fss.getName( ) ) ) - { - return fss; - } - } - return _fileStoreServiceProviderList.stream() - .filter(fss -> strFileStoreServiceProviderName.equals(fss.getName())) - .findFirst().orElseThrow(()-> new AppException( MSG_NO_FILE_SERVICE )); + throw new AppException( MSG_NO_FILE_SERVICE ); } + ; - // otherwise - throw new AppException( MSG_NO_FILE_SERVICE ); - } - /** get default File Store Service Provider - * - * @return the provider - */ - private IFileStoreServiceProvider getDefaultServiceProvider( ) - { - // search default file service - if ( !_fileStoreServiceProviderList.isUnsatisfied() ) + if ( StringUtils.isNotEmpty( strFileStoreServiceProviderName ) ) + { + return fileServiceProviders.stream( ).filter( fss -> strFileStoreServiceProviderName.equals( fss.getName( ) ) ).findFirst( ) + .orElseThrow( ( ) -> new AppException( MSG_NO_FILE_SERVICE ) ); + } + else { - return _fileStoreServiceProviderList.stream() - .filter(fss -> fss.isDefault()) - .findFirst() - .orElseGet(() -> _fileStoreServiceProviderList.stream().findFirst() - .orElseThrow(() -> new AppException(MSG_NO_FILE_SERVICE))); + // search default file service provider + return fileServiceProviders.stream( ).filter( IFileStoreServiceProvider::isDefault ).findFirst( ) + .orElseThrow( ( ) -> new AppException( MSG_NO_FILE_SERVICE ) ); } - // otherwise - throw new AppException( MSG_NO_FILE_SERVICE ); } } diff --git a/src/java/fr/paris/lutece/portal/service/file/FileServiceException.java b/src/java/fr/paris/lutece/portal/service/file/FileServiceException.java index 56c7e489c..1aa690f11 100644 --- a/src/java/fr/paris/lutece/portal/service/file/FileServiceException.java +++ b/src/java/fr/paris/lutece/portal/service/file/FileServiceException.java @@ -38,12 +38,12 @@ public class FileServiceException extends Exception private static final long serialVersionUID = -4788782240985061911L; - // The response code status + // The response code status private Integer _nResponseCodeStatus; - + // The response I18N message key private String _strI18nMessageKey; - + /** * Creates a new instance of ExpiredLinkException * @@ -54,7 +54,7 @@ public FileServiceException( String strMessage ) { super( strMessage ); } - + /** * Creates a new instance of HttpAccessException. * @@ -83,7 +83,7 @@ public FileServiceException( String strMessage, Integer nResponseCodeStatus, Exc super( strMessage, e ); _nResponseCodeStatus = nResponseCodeStatus; } - + /** * the response code (based on http codes) * @@ -99,7 +99,8 @@ public Integer getResponseCode( ) * * @return the key */ - public String getI18nMessageKey() { - return _strI18nMessageKey; - } + public String getI18nMessageKey( ) + { + return _strI18nMessageKey; + } } diff --git a/src/java/fr/paris/lutece/portal/service/file/IFileStoreService.java b/src/java/fr/paris/lutece/portal/service/file/IFileStoreService.java new file mode 100644 index 000000000..6ebd1b7b4 --- /dev/null +++ b/src/java/fr/paris/lutece/portal/service/file/IFileStoreService.java @@ -0,0 +1,59 @@ +package fr.paris.lutece.portal.service.file; + +import java.io.InputStream; +import fr.paris.lutece.portal.business.file.File; + +import fr.paris.lutece.portal.service.upload.MultipartItem; +import jakarta.servlet.http.HttpServletRequest; + +public interface IFileStoreService +{ + + public String getName( ); + + public default boolean healthCheck( ) + { + // default + return true; + } + + /** + * {@inheritDoc} + */ + String storeFile( File file ) throws FileServiceException; + + /** + * {@inheritDoc} + */ + File getFile( String strKey ) throws FileServiceException; + + /** + * {@inheritDoc} + */ + void delete( String strKey ); + + /** + * {@inheritDoc} + */ + fr.paris.lutece.portal.business.file.File getFileMetaData( String strKey ); + + /** + * {@inheritDoc} + */ + String storeBytes( byte [ ] blob ); + + /** + * {@inheritDoc} + */ + String storeInputStream( InputStream inputStream ); + + /** + * {@inheritDoc} + */ + String storeFileItem( MultipartItem fileItem ); + + /** + * {@inheritDoc} + */ + InputStream getInputStream( String strKey ); +} diff --git a/src/java/fr/paris/lutece/portal/service/file/IFileStoreServiceProvider.java b/src/java/fr/paris/lutece/portal/service/file/IFileStoreServiceProvider.java index f8520c4e0..faeba4dc9 100755 --- a/src/java/fr/paris/lutece/portal/service/file/IFileStoreServiceProvider.java +++ b/src/java/fr/paris/lutece/portal/service/file/IFileStoreServiceProvider.java @@ -64,19 +64,18 @@ public interface IFileStoreServiceProvider extends Serializable * @return true if default */ public boolean isDefault( ); - + /** - * health check + * health check * * @return true if available */ public default boolean healthCheck( ) { - // default + // default return true; } - /** * Stores a file Lutece File * @@ -93,7 +92,7 @@ public default boolean healthCheck( ) * the fileItem * @return The key of the blob */ - String storeFileItem( MultipartItem fileItem )throws FileServiceException; + String storeFileItem( MultipartItem fileItem ) throws FileServiceException; /** * Stores an input stream @@ -123,8 +122,7 @@ public default boolean healthCheck( ) File getFile( String strKey ) throws FileServiceException; /** - * Get a file object only filled with the meta data (name, size ...) - * without the physical file content + * Get a file object only filled with the meta data (name, size ...) without the physical file content * * @param strKey * The key of the file diff --git a/src/java/fr/paris/lutece/portal/service/file/implementation/DefaultFileDownloadService.java b/src/java/fr/paris/lutece/portal/service/file/implementation/DefaultFileDownloadService.java index 06e72d2ce..83dfe1877 100644 --- a/src/java/fr/paris/lutece/portal/service/file/implementation/DefaultFileDownloadService.java +++ b/src/java/fr/paris/lutece/portal/service/file/implementation/DefaultFileDownloadService.java @@ -34,8 +34,6 @@ package fr.paris.lutece.portal.service.file.implementation; import fr.paris.lutece.portal.service.file.ExpiredLinkException; -import fr.paris.lutece.portal.service.file.FileService; -import static fr.paris.lutece.portal.service.file.FileService.PARAMETER_VALIDITY_TIME; import fr.paris.lutece.portal.service.file.IFileDownloadUrlService; import fr.paris.lutece.portal.service.security.RsaService; import fr.paris.lutece.portal.service.util.AppLogService; @@ -48,6 +46,7 @@ import java.util.Map; import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Named; import jakarta.servlet.http.HttpServletRequest; import org.apache.commons.lang3.StringUtils; @@ -57,16 +56,25 @@ * */ @ApplicationScoped +@Named( "defaultFileDownloadService" ) public class DefaultFileDownloadService implements IFileDownloadUrlService { private static final long serialVersionUID = 1L; + // parameters + public static final String PARAMETER_FILE_ID = "file_id"; + public static final String PARAMETER_RESOURCE_ID = "resource_id"; + public static final String PARAMETER_RESOURCE_TYPE = "resource_type"; + public static final String PARAMETER_VALIDITY_TIME = "validity_time"; + public static final String PARAMETER_DATA = "data"; + public static final String PARAMETER_BO = "is_bo"; + public static final String PARAMETER_PROVIDER = "provider"; + // constants protected static final String URL_FO = "jsp/site/file/download"; protected static final String URL_BO = "jsp/admin/file/download"; private static final String SERVICE_NAME = "DefaultFileDownloadService"; private static final String DEFAULT_SEPARATOR = "/"; - private String _separator = DEFAULT_SEPARATOR; // Keys @@ -94,9 +102,9 @@ public static Map buildAdditionnalDatas( String strFileId, Strin { Map map = new HashMap<>( ); - map.put( FileService.PARAMETER_FILE_ID, strFileId ); - map.put( FileService.PARAMETER_RESOURCE_ID, strResourceId ); - map.put( FileService.PARAMETER_RESOURCE_TYPE, strResourceType ); + map.put( PARAMETER_FILE_ID, strFileId ); + map.put( PARAMETER_RESOURCE_ID, strResourceId ); + map.put( PARAMETER_RESOURCE_TYPE, strResourceType ); return map; } @@ -126,7 +134,7 @@ public String getFileDownloadUrlFO( String strFileKey, Map addit { additionnalData = new HashMap<>( ); } - additionnalData.put( FileService.PARAMETER_FILE_ID, strFileKey ); + additionnalData.put( PARAMETER_FILE_ID, strFileKey ); return getEncryptedUrl( sbUrl.toString( ), getDataToEncrypt( additionnalData ), strFileStorageServiceProviderName ); } @@ -156,7 +164,7 @@ public String getFileDownloadUrlBO( String strFileKey, Map addit { additionnalData = new HashMap<>( ); } - additionnalData.put( FileService.PARAMETER_FILE_ID, strFileKey ); + additionnalData.put( PARAMETER_FILE_ID, strFileKey ); return getEncryptedUrl( sbUrl.toString( ), getDataToEncrypt( additionnalData ), strFileStorageServiceProviderName ); } @@ -177,8 +185,8 @@ protected String getEncryptedUrl( String strUrl, String dataToEncrypt, String st { String idEncrytped = RsaService.encryptRsa( dataToEncrypt ); - item.addParameter( FileService.PARAMETER_PROVIDER, strFileStorageServiceProviderName ); - item.addParameter( FileService.PARAMETER_DATA, idEncrytped ); + item.addParameter( PARAMETER_PROVIDER, strFileStorageServiceProviderName ); + item.addParameter( PARAMETER_DATA, idEncrytped ); return item.getUrlWithEntity( ); } @@ -207,9 +215,9 @@ public String getName( ) private String getDataToEncrypt( Map additionnalData ) { StringBuilder sb = new StringBuilder( ); - sb.append( StringUtils.defaultIfEmpty( additionnalData.get( FileService.PARAMETER_FILE_ID ), "" ) ).append( _separator ); - sb.append( StringUtils.defaultIfEmpty( additionnalData.get( FileService.PARAMETER_RESOURCE_ID ), "" ) ).append( _separator ); - sb.append( StringUtils.defaultIfEmpty( additionnalData.get( FileService.PARAMETER_RESOURCE_TYPE ), "" ) ).append( _separator ); + sb.append( StringUtils.defaultIfEmpty( additionnalData.get( PARAMETER_FILE_ID ), "" ) ).append( _separator ); + sb.append( StringUtils.defaultIfEmpty( additionnalData.get( PARAMETER_RESOURCE_ID ), "" ) ).append( _separator ); + sb.append( StringUtils.defaultIfEmpty( additionnalData.get( PARAMETER_RESOURCE_TYPE ), "" ) ).append( _separator ); sb.append( calculateEndValidity( ) ); return sb.toString( ); @@ -236,7 +244,7 @@ protected long calculateEndValidity( ) @Override public Map getRequestDataBO( HttpServletRequest request ) { - String strEncryptedData = request.getParameter( FileService.PARAMETER_DATA ); + String strEncryptedData = request.getParameter( PARAMETER_DATA ); try { @@ -256,7 +264,7 @@ public Map getRequestDataBO( HttpServletRequest request ) @Override public Map getRequestDataFO( HttpServletRequest request ) { - String strEncryptedData = request.getParameter( FileService.PARAMETER_DATA ); + String strEncryptedData = request.getParameter( PARAMETER_DATA ); try { @@ -291,7 +299,7 @@ protected Map getDecryptedData( String strData ) @Override public void checkLinkValidity( Map fileData ) throws ExpiredLinkException { - LocalDateTime validityTime = new Timestamp( Long.parseLong( fileData.get( FileService.PARAMETER_VALIDITY_TIME ) ) ).toLocalDateTime( ); + LocalDateTime validityTime = new Timestamp( Long.parseLong( fileData.get( PARAMETER_VALIDITY_TIME ) ) ).toLocalDateTime( ); if ( LocalDateTime.now( ).isAfter( validityTime ) ) { diff --git a/src/java/fr/paris/lutece/portal/service/file/implementation/DefaultFileNoRBACService.java b/src/java/fr/paris/lutece/portal/service/file/implementation/DefaultFileNoRBACService.java index 0074b9000..e112dc25e 100644 --- a/src/java/fr/paris/lutece/portal/service/file/implementation/DefaultFileNoRBACService.java +++ b/src/java/fr/paris/lutece/portal/service/file/implementation/DefaultFileNoRBACService.java @@ -37,6 +37,7 @@ import fr.paris.lutece.portal.service.admin.AccessDeniedException; import fr.paris.lutece.portal.service.file.IFileRBACService; import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Named; import java.util.Map; @@ -46,6 +47,7 @@ * */ @ApplicationScoped +@Named( "defaultFileNoRBACService" ) public class DefaultFileNoRBACService implements IFileRBACService { private static final long serialVersionUID = 1L; diff --git a/src/java/fr/paris/lutece/portal/service/file/implementation/DefaultFileRBACService.java b/src/java/fr/paris/lutece/portal/service/file/implementation/DefaultFileRBACService.java index 952490e79..217ca7c9c 100644 --- a/src/java/fr/paris/lutece/portal/service/file/implementation/DefaultFileRBACService.java +++ b/src/java/fr/paris/lutece/portal/service/file/implementation/DefaultFileRBACService.java @@ -33,16 +33,15 @@ */ package fr.paris.lutece.portal.service.file.implementation; +import java.util.Map; + import fr.paris.lutece.api.user.User; import fr.paris.lutece.portal.service.admin.AccessDeniedException; -import fr.paris.lutece.portal.service.file.FileService; import fr.paris.lutece.portal.service.file.IFileRBACService; import fr.paris.lutece.portal.service.i18n.I18nService; import fr.paris.lutece.portal.service.rbac.RBACService; import jakarta.enterprise.context.ApplicationScoped; -import jakarta.enterprise.inject.Alternative; - -import java.util.Map; +import jakarta.inject.Named; /** * @@ -50,18 +49,21 @@ * */ @ApplicationScoped -@Alternative +@Named( "defaultFileRBACService" ) public class DefaultFileRBACService implements IFileRBACService { private static final long serialVersionUID = 1L; private static final String MESSAGE_ACCESS_DENIED_KEY = "portal.file.download.access.denied"; + public static final String PERMISSION_VIEW = "VIEW"; + + public static final String PARAMETER_RESOURCE_ID = "resource_id"; + public static final String PARAMETER_RESOURCE_TYPE = "resource_type"; @Override public void checkAccessRights( Map fileData, User user ) throws AccessDeniedException { - if ( !RBACService.isAuthorized( fileData.get( FileService.PARAMETER_RESOURCE_ID ), fileData.get( FileService.PARAMETER_RESOURCE_TYPE ), - FileService.PERMISSION_VIEW, user ) ) + if ( !RBACService.isAuthorized( fileData.get( PARAMETER_RESOURCE_ID ), fileData.get( PARAMETER_RESOURCE_TYPE ), PERMISSION_VIEW, user ) ) { throw new AccessDeniedException( I18nService.getLocalizedString( MESSAGE_ACCESS_DENIED_KEY, I18nService.getDefaultLocale( ) ) ); } diff --git a/src/java/fr/paris/lutece/portal/service/file/implementation/DefaultFileServiceProducer.java b/src/java/fr/paris/lutece/portal/service/file/implementation/DefaultFileServiceProducer.java new file mode 100644 index 000000000..77828b22b --- /dev/null +++ b/src/java/fr/paris/lutece/portal/service/file/implementation/DefaultFileServiceProducer.java @@ -0,0 +1,32 @@ +package fr.paris.lutece.portal.service.file.implementation; + +import org.eclipse.microprofile.config.inject.ConfigProperty; + +import fr.paris.lutece.portal.service.file.IFileDownloadUrlService; +import fr.paris.lutece.portal.service.file.IFileRBACService; +import fr.paris.lutece.portal.service.file.IFileStoreService; +import fr.paris.lutece.portal.service.file.IFileStoreServiceProvider; +import fr.paris.lutece.portal.service.util.CdiHelper; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.inject.Produces; +import jakarta.inject.Named; + +@ApplicationScoped +public class DefaultFileServiceProducer +{ + @Produces + @ApplicationScoped + @Named( "defaultFileServiceProvider" ) + public IFileStoreServiceProvider produceBean( @ConfigProperty( name = "lutece.defaultFileServiceProvider.fileStoreService" ) String fileStoreImplName, + @ConfigProperty( name = "lutece.defaultFileServiceProvider.rbacService" ) String rbacImplName, + @ConfigProperty( name = "lutece.defaultFileServiceProvider.downloadService" ) String dlImplName ) + { + DefaultFileStoreServiceProvider provider = new DefaultFileStoreServiceProvider( "defaultFileServiceProvider", + CdiHelper.getReference( IFileStoreService.class, fileStoreImplName ), + CdiHelper.getReference( IFileDownloadUrlService.class, dlImplName ), + CdiHelper.getReference( IFileRBACService.class, rbacImplName ), + true ); + + return provider; + } +} diff --git a/src/java/fr/paris/lutece/portal/service/file/implementation/DefaultFileStoreServiceProvider.java b/src/java/fr/paris/lutece/portal/service/file/implementation/DefaultFileStoreServiceProvider.java new file mode 100644 index 000000000..a36dd2386 --- /dev/null +++ b/src/java/fr/paris/lutece/portal/service/file/implementation/DefaultFileStoreServiceProvider.java @@ -0,0 +1,203 @@ +package fr.paris.lutece.portal.service.file.implementation; + +import java.io.InputStream; +import java.util.Map; + +import fr.paris.lutece.api.user.User; +import fr.paris.lutece.portal.business.file.File; +import fr.paris.lutece.portal.service.admin.AccessDeniedException; +import fr.paris.lutece.portal.service.admin.AdminAuthenticationService; +import fr.paris.lutece.portal.service.file.ExpiredLinkException; +import fr.paris.lutece.portal.service.file.FileServiceException; +import fr.paris.lutece.portal.service.file.IFileDownloadUrlService; +import fr.paris.lutece.portal.service.file.IFileRBACService; +import fr.paris.lutece.portal.service.file.IFileStoreService; +import fr.paris.lutece.portal.service.file.IFileStoreServiceProvider; +import fr.paris.lutece.portal.service.security.SecurityService; +import fr.paris.lutece.portal.service.security.UserNotSignedException; +import fr.paris.lutece.portal.service.upload.MultipartItem; +import jakarta.servlet.http.HttpServletRequest; + +public class DefaultFileStoreServiceProvider implements IFileStoreServiceProvider +{ + + private static final long serialVersionUID = 1L; + + private static final Object PARAMETER_FILE_ID = "file_id"; + + protected String _name; + protected boolean _isDefault = false; + + IFileStoreService _fsService; + IFileDownloadUrlService _dlService; + IFileRBACService _rbacService; + + public DefaultFileStoreServiceProvider( String _name, IFileStoreService _fsService, IFileDownloadUrlService _dlService, IFileRBACService _rbacService, + boolean _isDefault ) + { + super( ); + this._name = _name; + this._dlService = _dlService; + this._rbacService = _rbacService; + this._fsService = _fsService; + this._isDefault = _isDefault; + } + + @Override + public String getName( ) + { + return _name; + } + + @Override + public boolean isDefault( ) + { + return _isDefault; + } + + @Override + public String storeFile( File file ) throws FileServiceException + { + return _fsService.storeFile( file ); + } + + @Override + public File getFile( String strKey ) throws FileServiceException + { + + return _fsService.getFile( strKey ); + } + + @Override + public String storeFileItem( MultipartItem fileItem ) throws FileServiceException + { + return _fsService.storeFileItem( fileItem ); + } + + @Override + public String storeInputStream( InputStream inputStream ) throws FileServiceException + { + return _fsService.storeInputStream( inputStream ); + } + + @Override + public String storeBytes( byte [ ] blob ) throws FileServiceException + { + return _fsService.storeBytes( blob ); + } + + @Override + public fr.paris.lutece.portal.business.file.File getFileMetaData( String strKey ) throws FileServiceException + { + return _fsService.getFileMetaData( strKey ); + } + + @Override + public InputStream getInputStream( String strKey ) throws FileServiceException + { + return _fsService.getInputStream( strKey ); + } + + @Override + public void delete( String strKey ) throws FileServiceException + { + _fsService.delete( strKey ); + } + + @Override + public File getFileFromRequestBO( HttpServletRequest request ) + throws AccessDeniedException, ExpiredLinkException, UserNotSignedException, FileServiceException + { + Map fileData = _dlService.getRequestDataBO( request ); + + // check access rights + checkAccessRights( fileData, AdminAuthenticationService.getInstance( ).getRegisteredUser( request ) ); + + // check validity + checkLinkValidity( fileData ); + + String strFileId = fileData.get( PARAMETER_FILE_ID ); + + return getFile( strFileId ); + } + + @Override + public File getFileFromRequestFO( HttpServletRequest request ) + throws AccessDeniedException, ExpiredLinkException, UserNotSignedException, FileServiceException + { + Map fileData = _dlService.getRequestDataFO( request ); + + // check access rights + checkAccessRights( fileData, SecurityService.getInstance( ).getRegisteredUser( request ) ); + + // check validity + checkLinkValidity( fileData ); + + String strFileId = fileData.get( PARAMETER_FILE_ID ); + + return getFile( strFileId ); + } + + @Override + public String getFileDownloadUrlFO( String strKey ) + { + return _dlService.getFileDownloadUrlFO( strKey, this.getName( ) ); + } + + @Override + public void checkAccessRights( Map fileData, User user ) throws AccessDeniedException, UserNotSignedException + { + if ( _rbacService != null ) + { + _rbacService.checkAccessRights( fileData, user ); + } + } + + @Override + public String getFileDownloadUrlFO( String strKey, Map additionnalData ) + { + return _dlService.getFileDownloadUrlFO( strKey, additionnalData, this.getName( ) ); + } + + @Override + public String getFileDownloadUrlBO( String strKey ) + { + return _dlService.getFileDownloadUrlBO( strKey, this.getName( ) ); + } + + @Override + public String getFileDownloadUrlBO( String strKey, Map additionnalData ) + { + return _dlService.getFileDownloadUrlBO( strKey, additionnalData, this.getName( ) ); + } + + @Override + public void checkLinkValidity( Map fileData ) throws ExpiredLinkException + { + _dlService.checkLinkValidity( fileData ); + } + + /** + * get FileService implementations + * + * @return a string + */ + public String getDetail( ) + { + + Class fsClass = this.getClass( ); + if ( fsClass.getName( ).contains( "$" ) ) + { + fsClass = fsClass.getSuperclass( ); + } + + StringBuffer sb = new StringBuffer( ); + sb.append( "FileStoreServiceProvider : " + fsClass.getName( ) ).append( "\n" ); + sb.append( " - default : " + _isDefault ).append( "\n" ); + sb.append( " - fs : " + _fsService.getClass( ).getName( ) ).append( "\n" ); + sb.append( " - dwnld : " + _dlService.getClass( ).getName( ) ).append( "\n" ); + sb.append( " - rbac : " + _rbacService.getClass( ).getName( ) ).append( "\n" ); + + return sb.toString( ); + } +} diff --git a/src/java/fr/paris/lutece/portal/service/file/implementation/LocalDatabaseFileService.java b/src/java/fr/paris/lutece/portal/service/file/implementation/LocalDatabaseFileService.java index bd7da0e56..a312827f2 100755 --- a/src/java/fr/paris/lutece/portal/service/file/implementation/LocalDatabaseFileService.java +++ b/src/java/fr/paris/lutece/portal/service/file/implementation/LocalDatabaseFileService.java @@ -36,116 +36,39 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; -import java.util.Map; - -import jakarta.enterprise.context.ApplicationScoped; -import jakarta.inject.Inject; -import jakarta.servlet.http.HttpServletRequest; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; -import fr.paris.lutece.api.user.User; import fr.paris.lutece.portal.business.file.File; import fr.paris.lutece.portal.business.file.FileHome; import fr.paris.lutece.portal.business.physicalfile.PhysicalFile; import fr.paris.lutece.portal.business.physicalfile.PhysicalFileHome; -import fr.paris.lutece.portal.service.admin.AccessDeniedException; -import fr.paris.lutece.portal.service.admin.AdminAuthenticationService; -import fr.paris.lutece.portal.service.file.ExpiredLinkException; -import fr.paris.lutece.portal.service.file.FileService; -import fr.paris.lutece.portal.service.file.IFileDownloadUrlService; -import fr.paris.lutece.portal.service.file.IFileRBACService; -import fr.paris.lutece.portal.service.file.IFileStoreServiceProvider; -import fr.paris.lutece.portal.service.security.SecurityService; -import fr.paris.lutece.portal.service.security.UserNotSignedException; +import fr.paris.lutece.portal.service.file.IFileStoreService; import fr.paris.lutece.portal.service.upload.MultipartItem; import fr.paris.lutece.portal.service.util.AppException; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import jakarta.inject.Named; /** * - * DatabaseBlobStoreService. + * Database file Service. * */ @ApplicationScoped -public class LocalDatabaseFileService implements IFileStoreServiceProvider +@Named( "localDatabaseFileService" ) +public class LocalDatabaseFileService implements IFileStoreService { private static final long serialVersionUID = 1L; - @Inject - private IFileDownloadUrlService _fileDownloadUrlService; - @Inject - private IFileRBACService _fileRBACService; - private String _strName= "defaultDatabaseFileStoreProvider"; - private boolean _bDefault = true; - - /** - * Constructor - */ - public LocalDatabaseFileService( ) - { - } - /** - * init - * - * @param _fileDownloadUrlService - * @param _fileRBACService - */ - @Deprecated - public LocalDatabaseFileService( IFileDownloadUrlService _fileDownloadUrlService, IFileRBACService _fileRBACService ) - { - this._fileDownloadUrlService = _fileDownloadUrlService; - this._fileRBACService = _fileRBACService; - } - - /** - * get the FileRBACService - * - * @return the FileRBACService - */ - public IFileRBACService getFileRBACService( ) - { - return _fileRBACService; - } - - /** - * set the FileRBACService - * - * @param fileRBACService - */ - public void setFileRBACService( IFileRBACService fileRBACService ) - { - this._fileRBACService = fileRBACService; - } - - /** - * Get the downloadService - * - * @return the downloadService - */ - public IFileDownloadUrlService getDownloadUrlService( ) - { - return _fileDownloadUrlService; - } - - /** - * Sets the downloadService - * - * @param downloadUrlService - * downloadService - */ - public void setDownloadUrlService( IFileDownloadUrlService downloadUrlService ) - { - _fileDownloadUrlService = downloadUrlService; - } - /** * {@inheritDoc} */ @Override public String getName( ) { - return _strName; + return "LocalDatabaseFileService"; } /** @@ -190,24 +113,23 @@ public File getFile( String strKey, boolean withPhysicalFile ) if ( StringUtils.isNotBlank( strKey ) ) { int nfileId = Integer.parseInt( strKey ); - + // get meta data File file = FileHome.findByPrimaryKey( nfileId ); // check if the file exists and was inserted with this provider - if ( file == null - || ( file.getOrigin( ) == null && getName( ) != null ) - || ( file.getOrigin( ) != null && !file.getOrigin( ).equals( getName( ) ) ) ) + if ( file == null || ( file.getOrigin( ) == null && getName( ) != null ) + || ( file.getOrigin( ) != null && !file.getOrigin( ).equals( getName( ) ) ) ) { - return null; + return null; } - + if ( withPhysicalFile ) { // get file content file.setPhysicalFile( PhysicalFileHome.findByPrimaryKey( file.getPhysicalFile( ).getIdPhysicalFile( ) ) ); } - + return file; } @@ -225,7 +147,7 @@ public String storeBytes( byte [ ] blob ) physicalFile.setValue( blob ); file.setPhysicalFile( physicalFile ); file.setOrigin( getName( ) ); - + int nFileId = FileHome.create( file ); return String.valueOf( nFileId ); @@ -274,7 +196,7 @@ public String storeFileItem( MultipartItem fileItem ) file.setMimeType( fileItem.getContentType( ) ); file.setOrigin( getName( ) ); - + PhysicalFile physicalFile = new PhysicalFile( ); byte [ ] byteArray; @@ -303,31 +225,12 @@ public String storeFileItem( MultipartItem fileItem ) public String storeFile( File file ) { file.setOrigin( getName( ) ); - + int nFileId = FileHome.create( file ); return String.valueOf( nFileId ); } - public void setDefault( boolean bDefault ) - { - this._bDefault = bDefault; - } - - public void setName( String strName ) - { - _strName = strName; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isDefault( ) - { - return _bDefault; - } - /** * {@inheritDoc} */ @@ -339,100 +242,4 @@ public InputStream getInputStream( String strKey ) return new ByteArrayInputStream( file.getPhysicalFile( ).getValue( ) ); } - - /** - * {@inheritDoc} - */ - @Override - public String getFileDownloadUrlFO( String strKey ) - { - return _fileDownloadUrlService.getFileDownloadUrlFO( strKey, getName( ) ); - } - - /** - * {@inheritDoc} - */ - @Override - public String getFileDownloadUrlFO( String strKey, Map additionnalData ) - { - return _fileDownloadUrlService.getFileDownloadUrlFO( strKey, additionnalData, getName( ) ); - } - - /** - * {@inheritDoc} - */ - @Override - public String getFileDownloadUrlBO( String strKey ) - { - return _fileDownloadUrlService.getFileDownloadUrlBO( strKey, getName( ) ); - } - - /** - * {@inheritDoc} - */ - @Override - public String getFileDownloadUrlBO( String strKey, Map additionnalData ) - { - return _fileDownloadUrlService.getFileDownloadUrlBO( strKey, additionnalData, getName( ) ); - } - - /** - * {@inheritDoc} - */ - @Override - public void checkAccessRights( Map fileData, User user ) throws AccessDeniedException, UserNotSignedException - { - if ( _fileRBACService != null ) - { - _fileRBACService.checkAccessRights( fileData, user ); - } - } - - /** - * {@inheritDoc} - */ - @Override - public void checkLinkValidity( Map fileData ) throws ExpiredLinkException - { - _fileDownloadUrlService.checkLinkValidity( fileData ); - } - - /** - * {@inheritDoc} - */ - @Override - public File getFileFromRequestBO( HttpServletRequest request ) throws AccessDeniedException, ExpiredLinkException, UserNotSignedException - { - Map fileData = _fileDownloadUrlService.getRequestDataBO( request ); - - // check access rights - checkAccessRights( fileData, AdminAuthenticationService.getInstance( ).getRegisteredUser( request ) ); - - // check validity - checkLinkValidity( fileData ); - - String strFileId = fileData.get( FileService.PARAMETER_FILE_ID ); - - return getFile( strFileId ); - } - - /** - * {@inheritDoc} - */ - @Override - public File getFileFromRequestFO( HttpServletRequest request ) throws AccessDeniedException, ExpiredLinkException, UserNotSignedException - { - - Map fileData = _fileDownloadUrlService.getRequestDataFO( request ); - - // check access rights - checkAccessRights( fileData, SecurityService.getInstance( ).getRegisteredUser( request ) ); - - // check validity - checkLinkValidity( fileData ); - - String strFileId = fileData.get( FileService.PARAMETER_FILE_ID ); - - return getFile( strFileId ); - } } diff --git a/src/test/java/fr/paris/lutece/portal/service/file/FileServiceTest.java b/src/test/java/fr/paris/lutece/portal/service/file/FileServiceTest.java index c03888056..c61d9cbb1 100755 --- a/src/test/java/fr/paris/lutece/portal/service/file/FileServiceTest.java +++ b/src/test/java/fr/paris/lutece/portal/service/file/FileServiceTest.java @@ -33,8 +33,6 @@ */ package fr.paris.lutece.portal.service.file; -import java.io.FileInputStream; -import java.io.InputStream; import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; @@ -45,18 +43,17 @@ import java.util.Locale; import java.util.Map; - import org.apache.commons.io.FileUtils; import org.apache.http.NameValuePair; import org.apache.http.client.utils.URLEncodedUtils; import org.junit.jupiter.api.Test; - import fr.paris.lutece.portal.business.file.File; import fr.paris.lutece.portal.business.physicalfile.PhysicalFile; import fr.paris.lutece.portal.business.user.AdminUser; import fr.paris.lutece.portal.service.admin.AccessDeniedException; import fr.paris.lutece.portal.service.admin.AdminAuthenticationService; +import fr.paris.lutece.portal.service.file.implementation.DefaultFileDownloadService; import fr.paris.lutece.portal.service.security.UserNotSignedException; import fr.paris.lutece.portal.service.upload.MultipartItem; import fr.paris.lutece.test.LuteceTestCase; @@ -65,6 +62,7 @@ import fr.paris.lutece.util.http.MockMultipartItem; import fr.paris.lutece.util.http.TemporaryMultipartItemFactory; import jakarta.inject.Inject; +import jakarta.inject.Named; import jakarta.servlet.http.HttpServletRequest; /** @@ -74,8 +72,60 @@ public class FileServiceTest extends LuteceTestCase { @Inject - private FileService _fileService; - + @Named( "localDatabaseFileService" ) + IFileStoreService LocalDatabaseFileService; + + @Inject + @Named( "defaultFileNoRBACService" ) + IFileRBACService defaultFileNoRBACService; + + @Inject + @Named( "defaultFileRBACService" ) + IFileRBACService defaultFileRBACService; + + @Inject + @Named( "defaultFileDownloadService" ) + IFileDownloadUrlService defaultFileDownloadService; + + @Inject + @Named( "defaultFileServiceProvider" ) + IFileStoreServiceProvider _fileServiceProvider; + + @Inject + FileService _fileService; + + /** + * test store Lutece File with deprecated FileService + * + * @throws java.io.UnsupportedEncodingException + */ + @Test + public void testStoreFileWithFileService( ) throws UnsupportedEncodingException + { + File file = getOneLuteceFile( ); + + try + { + String strFileId = _fileService.getFileStoreServiceProvider( ).storeFile( file ); + + File storedFile = _fileService.getFileStoreServiceProvider( ).getFile( strFileId ); + + assertEquals( file.getTitle( ), storedFile.getTitle( ) ); + + // test delete + _fileService.getFileStoreServiceProvider( ).delete( strFileId ); + storedFile = _fileService.getFileStoreServiceProvider( ).getFile( strFileId ); + + assertNull( storedFile ); + + } + catch( FileServiceException e ) + { + fail( e.getMessage( ) ); + } + + } + /** * test store Lutece File * @@ -86,24 +136,24 @@ public void testStoreFile( ) throws UnsupportedEncodingException { File file = getOneLuteceFile( ); - try + try { - String strFileId = _fileService.getFileStoreServiceProvider( ).storeFile( file ); - - File storedFile = _fileService.getFileStoreServiceProvider( ).getFile( strFileId ); - - assertEquals( file.getTitle( ), storedFile.getTitle( ) ); - - // test delete - _fileService.getFileStoreServiceProvider( ).delete( strFileId ); - storedFile = _fileService.getFileStoreServiceProvider( ).getFile( strFileId ); - - assertNull( storedFile ); - + String strFileId = _fileServiceProvider.storeFile( file ); + + File storedFile = _fileServiceProvider.getFile( strFileId ); + + assertEquals( file.getTitle( ), storedFile.getTitle( ) ); + + // test delete + _fileServiceProvider.delete( strFileId ); + storedFile = _fileServiceProvider.getFile( strFileId ); + + assertNull( storedFile ); + } catch( FileServiceException e ) { - fail( e.getMessage( ) ); + fail( e.getMessage( ) ); } } @@ -116,20 +166,20 @@ public void testStoreFile( ) throws UnsupportedEncodingException @Test public void testStoreBytes( ) throws IOException { - try - { - java.io.File file = getOneFile( ); - byte [ ] fileInBytes = FileUtils.readFileToByteArray( file ); - - String strFileId = _fileService.getFileStoreServiceProvider( ).storeBytes( fileInBytes ); - - File storedFile = _fileService.getFileStoreServiceProvider( ).getFile( strFileId ); - - assertEquals( fileInBytes.length, storedFile.getPhysicalFile( ).getValue( ).length ); - } + try + { + java.io.File file = getOneFile( ); + byte [ ] fileInBytes = FileUtils.readFileToByteArray( file ); + + String strFileId = _fileServiceProvider.storeBytes( fileInBytes ); + + File storedFile = _fileServiceProvider.getFile( strFileId ); + + assertEquals( fileInBytes.length, storedFile.getPhysicalFile( ).getValue( ).length ); + } catch( FileServiceException e ) { - fail( e.getMessage( ) ); + fail( e.getMessage( ) ); } } @@ -142,24 +192,24 @@ public void testStoreBytes( ) throws IOException public void testStoreFileItem( ) throws IOException { - try - { - java.io.File file = getOneFile( ); - MultipartItem fileItem = getOneFileItem( file ); + try + { + java.io.File file = getOneFile( ); + MultipartItem fileItem = getOneFileItem( file ); - byte [ ] fileInBytes = FileUtils.readFileToByteArray( file ); - InputStream inputStream = new FileInputStream( file ); + byte [ ] fileInBytes = FileUtils.readFileToByteArray( file ); - String strFileId = _fileService.getFileStoreServiceProvider( ).storeFileItem( fileItem ); + String strFileId = _fileServiceProvider.storeFileItem( fileItem ); - File storedFile = _fileService.getFileStoreServiceProvider( ).getFile( strFileId ); + File storedFile = _fileServiceProvider.getFile( strFileId ); - assertEquals( fileInBytes.length, storedFile.getPhysicalFile( ).getValue( ).length ); - }catch( FileServiceException e ) + assertEquals( fileInBytes.length, storedFile.getPhysicalFile( ).getValue( ).length ); + } + catch( FileServiceException e ) { - fail( e.getMessage( ) ); - } - } + fail( e.getMessage( ) ); + } + } /** * test store fileitem @@ -176,33 +226,33 @@ public void testDownloadFileBO( ) throws IOException, AccessDeniedException, Exp try { - String strFileId = _fileService.getFileStoreServiceProvider( ).storeFile( file ); - - Map data = new HashMap<>( ); - data.put( FileService.PARAMETER_RESOURCE_ID, "123" ); - data.put( FileService.PARAMETER_RESOURCE_TYPE, "TEST" ); - - String strUrl = _fileService.getFileStoreServiceProvider( ).getFileDownloadUrlBO( strFileId, data ); - assertNotNull( strUrl ); - - List params = URLEncodedUtils.parse( strUrl, Charset.forName( "UTF-8" ) ); - - MockHttpServletRequest request = new MockHttpServletRequest( ); - for ( NameValuePair param : params ) - { - request.addParameter( param.getName( ), param.getValue( ) ); - } - - // add mock BO authentication - registerAdminUserAdmin( request ); - - File storedFile = _fileService.getFileStoreServiceProvider( ).getFileFromRequestBO( request ); - - assertEquals( file.getPhysicalFile( ).getValue( ).length, storedFile.getPhysicalFile( ).getValue( ).length ); + String strFileId = _fileServiceProvider.storeFile( file ); + + Map data = new HashMap<>( ); + data.put( DefaultFileDownloadService.PARAMETER_RESOURCE_ID, "123" ); + data.put( DefaultFileDownloadService.PARAMETER_RESOURCE_TYPE, "TEST" ); + + String strUrl = _fileServiceProvider.getFileDownloadUrlBO( strFileId, data ); + assertNotNull( strUrl ); + + List params = URLEncodedUtils.parse( strUrl, Charset.forName( "UTF-8" ) ); + + MockHttpServletRequest request = new MockHttpServletRequest( ); + for ( NameValuePair param : params ) + { + request.addParameter( param.getName( ), param.getValue( ) ); + } + + // add mock BO authentication + registerAdminUserAdmin( request ); + + File storedFile = _fileServiceProvider.getFileFromRequestBO( request ); + + assertEquals( file.getPhysicalFile( ).getValue( ).length, storedFile.getPhysicalFile( ).getValue( ).length ); } catch( FileServiceException e ) { - fail( e.getMessage( ) ); + fail( e.getMessage( ) ); } } @@ -222,32 +272,32 @@ public void testDownloadFileFO( ) throws IOException, AccessDeniedException, Exp try { - String strFileId = _fileService.getFileStoreServiceProvider( ).storeFile( file ); - - Map data = new HashMap<>( ); - data.put( FileService.PARAMETER_RESOURCE_ID, "123" ); - data.put( FileService.PARAMETER_RESOURCE_TYPE, "TEST" ); - - String strUrl = _fileService.getFileStoreServiceProvider( ).getFileDownloadUrlFO( strFileId, data ); - assertNotNull( strUrl ); - - List params = URLEncodedUtils.parse( strUrl, Charset.forName( "UTF-8" ) ); - - MockHttpServletRequest request = new MockHttpServletRequest( ); - for ( NameValuePair param : params ) - { - request.addParameter( param.getName( ), param.getValue( ) ); - } - - // no authentication - - File storedFile = _fileService.getFileStoreServiceProvider( ).getFileFromRequestFO( request ); - - assertEquals( file.getPhysicalFile( ).getValue( ).length, storedFile.getPhysicalFile( ).getValue( ).length ); + String strFileId = _fileServiceProvider.storeFile( file ); + + Map data = new HashMap<>( ); + data.put( DefaultFileDownloadService.PARAMETER_RESOURCE_ID, "123" ); + data.put( DefaultFileDownloadService.PARAMETER_RESOURCE_TYPE, "TEST" ); + + String strUrl = _fileServiceProvider.getFileDownloadUrlFO( strFileId, data ); + assertNotNull( strUrl ); + + List params = URLEncodedUtils.parse( strUrl, Charset.forName( "UTF-8" ) ); + + MockHttpServletRequest request = new MockHttpServletRequest( ); + for ( NameValuePair param : params ) + { + request.addParameter( param.getName( ), param.getValue( ) ); + } + + // no authentication + + File storedFile = _fileServiceProvider.getFileFromRequestFO( request ); + + assertEquals( file.getPhysicalFile( ).getValue( ).length, storedFile.getPhysicalFile( ).getValue( ).length ); } catch( FileServiceException e ) { - fail( e.getMessage( ) ); + fail( e.getMessage( ) ); } } diff --git a/webapp/WEB-INF/conf/config.properties b/webapp/WEB-INF/conf/config.properties index 1f9bae191..f671748a4 100644 --- a/webapp/WEB-INF/conf/config.properties +++ b/webapp/WEB-INF/conf/config.properties @@ -100,6 +100,15 @@ service.freemarker.templateUpdateDelay=5 lutece.debug.tracePortletXml=false ################################################################################ +# Files # Duration in minutes of the validity of generated url for file download (if 0, the links will be always valid) lutece.file.download.validity=0 + +# Default file service provider configuration +lutece.defaultFileServiceProvider.fileStoreService=localDatabaseFileService +lutece.defaultFileServiceProvider.rbacService=defaultFileNoRBACService +lutece.defaultFileServiceProvider.downloadService=defaultFileDownloadService + + +