DefaultFileDownloadService.java

  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.file.implementation;

  35. import fr.paris.lutece.portal.service.file.ExpiredLinkException;
  36. import fr.paris.lutece.portal.service.file.FileService;
  37. import static fr.paris.lutece.portal.service.file.FileService.PARAMETER_VALIDITY_TIME;
  38. import fr.paris.lutece.portal.service.file.IFileDownloadUrlService;
  39. import fr.paris.lutece.portal.service.security.RsaService;
  40. import fr.paris.lutece.portal.service.util.AppLogService;
  41. import fr.paris.lutece.portal.service.util.AppPathService;
  42. import fr.paris.lutece.util.url.UrlItem;
  43. import java.security.GeneralSecurityException;
  44. import java.sql.Timestamp;
  45. import java.time.LocalDateTime;
  46. import java.util.HashMap;
  47. import java.util.Map;
  48. import javax.servlet.http.HttpServletRequest;
  49. import org.apache.commons.lang3.StringUtils;

  50. /**
  51.  *
  52.  * DatabaseBlobStoreService.
  53.  *
  54.  */
  55. public class DefaultFileDownloadService implements IFileDownloadUrlService
  56. {
  57.     private static final long serialVersionUID = 1L;

  58.     // constants
  59.     protected static final String URL_FO = "jsp/site/file/download";
  60.     protected static final String URL_BO = "jsp/admin/file/download";
  61.     private static final String SERVICE_NAME = "DefaultFileDownloadService";
  62.     private static final String DEFAULT_SEPARATOR = "/";

  63.     private String _separator = DEFAULT_SEPARATOR;

  64.     // Keys
  65.     public static final String KEY_LINK_VALIDITY_TIME = "link_validity_time";

  66.     public String getSeparator( )
  67.     {
  68.         return _separator;
  69.     }

  70.     public void setSeparator( String separator )
  71.     {
  72.         _separator = separator;
  73.     }

  74.     /**
  75.      * Build the additionnel data map to provide encryption data
  76.      *
  77.      * @param strFileId
  78.      * @param strResourceId
  79.      * @param strResourceType
  80.      * @return the map
  81.      */
  82.     public static Map<String, String> buildAdditionnalDatas( String strFileId, String strResourceId, String strResourceType )
  83.     {
  84.         Map<String, String> map = new HashMap<>( );

  85.         map.put( FileService.PARAMETER_FILE_ID, strFileId );
  86.         map.put( FileService.PARAMETER_RESOURCE_ID, strResourceId );
  87.         map.put( FileService.PARAMETER_RESOURCE_TYPE, strResourceType );

  88.         return map;
  89.     }

  90.     /**
  91.      * {@inheritDoc}
  92.      */
  93.     @Override
  94.     public String getFileDownloadUrlFO( String strFileKey, String strFileStorageServiceProviderName )
  95.     {

  96.         return getFileDownloadUrlFO( strFileKey, null, strFileStorageServiceProviderName );
  97.     }

  98.     /**
  99.      * {@inheritDoc}
  100.      */
  101.     @Override
  102.     public String getFileDownloadUrlFO( String strFileKey, Map<String, String> additionnalData, String strFileStorageServiceProviderName )
  103.     {
  104.         StringBuilder sbUrl = new StringBuilder( );

  105.         sbUrl.append( AppPathService.getBaseUrl( null ) );
  106.         sbUrl.append( URL_FO );

  107.         if ( additionnalData == null )
  108.         {
  109.             additionnalData = new HashMap<>( );
  110.         }
  111.         additionnalData.put( FileService.PARAMETER_FILE_ID, strFileKey );

  112.         return getEncryptedUrl( sbUrl.toString( ), getDataToEncrypt( additionnalData ), strFileStorageServiceProviderName );
  113.     }

  114.     /**
  115.      * {@inheritDoc}
  116.      */
  117.     @Override
  118.     public String getFileDownloadUrlBO( String strFileKey, String strFileStorageServiceProviderName )
  119.     {
  120.         return getFileDownloadUrlBO( strFileKey, null, strFileStorageServiceProviderName );
  121.     }

  122.     /**
  123.      * {@inheritDoc}
  124.      */
  125.     @Override
  126.     public String getFileDownloadUrlBO( String strFileKey, Map<String, String> additionnalData, String strFileStorageServiceProviderName )
  127.     {

  128.         StringBuilder sbUrl = new StringBuilder( );

  129.         sbUrl.append( AppPathService.getBaseUrl( null ) );
  130.         sbUrl.append( URL_BO );

  131.         if ( additionnalData == null )
  132.         {
  133.             additionnalData = new HashMap<>( );
  134.         }
  135.         additionnalData.put( FileService.PARAMETER_FILE_ID, strFileKey );

  136.         return getEncryptedUrl( sbUrl.toString( ), getDataToEncrypt( additionnalData ), strFileStorageServiceProviderName );
  137.     }

  138.     /**
  139.      * get encrypted url
  140.      *
  141.      * @param strUrl
  142.      * @param additionnalData
  143.      *
  144.      * @return the url, null otherwise
  145.      */
  146.     protected String getEncryptedUrl( String strUrl, String dataToEncrypt, String strFileStorageServiceProviderName )
  147.     {
  148.         UrlItem item = new UrlItem( strUrl );

  149.         try
  150.         {
  151.             String idEncrytped = RsaService.encryptRsa( dataToEncrypt );

  152.             item.addParameter( FileService.PARAMETER_PROVIDER, strFileStorageServiceProviderName );
  153.             item.addParameter( FileService.PARAMETER_DATA, idEncrytped );

  154.             return item.getUrlWithEntity( );
  155.         }
  156.         catch( GeneralSecurityException e )
  157.         {
  158.             AppLogService.error( e.getMessage( ), e );
  159.             return null;
  160.         }
  161.     }

  162.     /**
  163.      * {@inheritDoc}
  164.      */
  165.     @Override
  166.     public String getName( )
  167.     {
  168.         return SERVICE_NAME;
  169.     }

  170.     /**
  171.      * get data to encrypt
  172.      *
  173.      * @param fileDownloadData
  174.      * @return the map of datas to encrypt in the url
  175.      */
  176.     private String getDataToEncrypt( Map<String, String> additionnalData )
  177.     {
  178.         StringBuilder sb = new StringBuilder( );
  179.         sb.append( StringUtils.defaultIfEmpty( additionnalData.get( FileService.PARAMETER_FILE_ID ), "" ) ).append( _separator );
  180.         sb.append( StringUtils.defaultIfEmpty( additionnalData.get( FileService.PARAMETER_RESOURCE_ID ), "" ) ).append( _separator );
  181.         sb.append( StringUtils.defaultIfEmpty( additionnalData.get( FileService.PARAMETER_RESOURCE_TYPE ), "" ) ).append( _separator );
  182.         sb.append( calculateEndValidity( ) );

  183.         return sb.toString( );
  184.     }

  185.     /**
  186.      * get end validity time
  187.      *
  188.      * @return the end time of url validity
  189.      */
  190.     protected long calculateEndValidity( )
  191.     {
  192.         LocalDateTime endValidity = LocalDateTime.MAX;
  193.         if ( getValidityTime( ) > 0 )
  194.         {
  195.             endValidity = LocalDateTime.now( ).plusMinutes( LINK_VALIDITY_TIME );
  196.         }
  197.         return Timestamp.valueOf( endValidity ).getTime( );
  198.     }

  199.     /**
  200.      * {@inheritDoc}
  201.      */
  202.     @Override
  203.     public Map<String, String> getRequestDataBO( HttpServletRequest request )
  204.     {
  205.         String strEncryptedData = request.getParameter( FileService.PARAMETER_DATA );

  206.         try
  207.         {
  208.             String strDecryptedData = RsaService.decryptRsa( strEncryptedData );
  209.             return getDecryptedData( strDecryptedData );
  210.         }
  211.         catch( GeneralSecurityException e )
  212.         {
  213.             AppLogService.error( e.getMessage( ), e );
  214.             return null;
  215.         }
  216.     }

  217.     /**
  218.      * {@inheritDoc}
  219.      */
  220.     @Override
  221.     public Map<String, String> getRequestDataFO( HttpServletRequest request )
  222.     {
  223.         String strEncryptedData = request.getParameter( FileService.PARAMETER_DATA );

  224.         try
  225.         {
  226.             String strDecryptedData = RsaService.decryptRsa( strEncryptedData );
  227.             return getDecryptedData( strDecryptedData );
  228.         }
  229.         catch( GeneralSecurityException e )
  230.         {
  231.             AppLogService.error( e.getMessage( ), e );
  232.             return null;
  233.         }
  234.     }

  235.     /**
  236.      * get map of datas from encrypted url data parameter
  237.      *
  238.      * @param data
  239.      * @return the map of
  240.      */
  241.     protected Map<String, String> getDecryptedData( String strData )
  242.     {
  243.         String [ ] data = strData.split( _separator );
  244.         Map<String, String> fileData = buildAdditionnalDatas( data [0], data [1], data [2] );
  245.         fileData.put( PARAMETER_VALIDITY_TIME, data [3] );

  246.         return fileData;
  247.     }

  248.     /**
  249.      * {@inheritDoc}
  250.      */
  251.     @Override
  252.     public void checkLinkValidity( Map<String, String> fileData ) throws ExpiredLinkException
  253.     {
  254.         LocalDateTime validityTime = new Timestamp( Long.parseLong( fileData.get( FileService.PARAMETER_VALIDITY_TIME ) ) ).toLocalDateTime( );

  255.         if ( LocalDateTime.now( ).isAfter( validityTime ) )
  256.         {
  257.             throw new ExpiredLinkException( "Link expired on : " + validityTime.toString( ) );
  258.         }
  259.     }
  260. }