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.file.implementation;
35  
36  import fr.paris.lutece.portal.service.file.ExpiredLinkException;
37  import fr.paris.lutece.portal.service.file.FileService;
38  import static fr.paris.lutece.portal.service.file.FileService.PARAMETER_VALIDITY_TIME;
39  import fr.paris.lutece.portal.service.file.IFileDownloadUrlService;
40  import fr.paris.lutece.portal.service.security.RsaService;
41  import fr.paris.lutece.portal.service.util.AppLogService;
42  import fr.paris.lutece.portal.service.util.AppPathService;
43  import fr.paris.lutece.util.url.UrlItem;
44  import java.security.GeneralSecurityException;
45  import java.sql.Timestamp;
46  import java.time.LocalDateTime;
47  import java.util.HashMap;
48  import java.util.Map;
49  import javax.servlet.http.HttpServletRequest;
50  import org.apache.commons.lang3.StringUtils;
51  
52  /**
53   * 
54   * DatabaseBlobStoreService.
55   * 
56   */
57  public class DefaultFileDownloadService implements IFileDownloadUrlService
58  {
59      private static final long serialVersionUID = 1L;
60  
61      // constants
62      protected static final String URL_FO = "jsp/site/file/download";
63      protected static final String URL_BO = "jsp/admin/file/download";
64      private static final String SERVICE_NAME = "DefaultFileDownloadService";
65      private static final String DEFAULT_SEPARATOR = "/";
66  
67      private String _separator = DEFAULT_SEPARATOR;
68  
69      // Keys
70      public static final String KEY_LINK_VALIDITY_TIME = "link_validity_time";
71  
72      public String getSeparator( )
73      {
74          return _separator;
75      }
76  
77      public void setSeparator( String separator )
78      {
79          _separator = separator;
80      }
81  
82      /**
83       * Build the additionnel data map to provide encryption data
84       * 
85       * @param strFileId
86       * @param strResourceId
87       * @param strResourceType
88       * @return the map
89       */
90      public static Map<String, String> buildAdditionnalDatas( String strFileId, String strResourceId, String strResourceType )
91      {
92          Map<String, String> map = new HashMap<>( );
93  
94          map.put( FileService.PARAMETER_FILE_ID, strFileId );
95          map.put( FileService.PARAMETER_RESOURCE_ID, strResourceId );
96          map.put( FileService.PARAMETER_RESOURCE_TYPE, strResourceType );
97  
98          return map;
99      }
100 
101     /**
102      * {@inheritDoc}
103      */
104     @Override
105     public String getFileDownloadUrlFO( String strFileKey, String strFileStorageServiceProviderName )
106     {
107 
108         return getFileDownloadUrlFO( strFileKey, null, strFileStorageServiceProviderName );
109     }
110 
111     /**
112      * {@inheritDoc}
113      */
114     @Override
115     public String getFileDownloadUrlFO( String strFileKey, Map<String, String> additionnalData, String strFileStorageServiceProviderName )
116     {
117         StringBuilder sbUrl = new StringBuilder( );
118 
119         sbUrl.append( AppPathService.getBaseUrl( null ) );
120         sbUrl.append( URL_FO );
121 
122         if ( additionnalData == null )
123         {
124             additionnalData = new HashMap<>( );
125         }
126         additionnalData.put( FileService.PARAMETER_FILE_ID, strFileKey );
127 
128         return getEncryptedUrl( sbUrl.toString( ), getDataToEncrypt( additionnalData ), strFileStorageServiceProviderName );
129     }
130 
131     /**
132      * {@inheritDoc}
133      */
134     @Override
135     public String getFileDownloadUrlBO( String strFileKey, String strFileStorageServiceProviderName )
136     {
137         return getFileDownloadUrlBO( strFileKey, null, strFileStorageServiceProviderName );
138     }
139 
140     /**
141      * {@inheritDoc}
142      */
143     @Override
144     public String getFileDownloadUrlBO( String strFileKey, Map<String, String> additionnalData, String strFileStorageServiceProviderName )
145     {
146 
147         StringBuilder sbUrl = new StringBuilder( );
148 
149         sbUrl.append( AppPathService.getBaseUrl( null ) );
150         sbUrl.append( URL_BO );
151 
152         if ( additionnalData == null )
153         {
154             additionnalData = new HashMap<>( );
155         }
156         additionnalData.put( FileService.PARAMETER_FILE_ID, strFileKey );
157 
158         return getEncryptedUrl( sbUrl.toString( ), getDataToEncrypt( additionnalData ), strFileStorageServiceProviderName );
159     }
160 
161     /**
162      * get encrypted url
163      * 
164      * @param strUrl
165      * @param additionnalData
166      * 
167      * @return the url, null otherwise
168      */
169     protected String getEncryptedUrl( String strUrl, String dataToEncrypt, String strFileStorageServiceProviderName )
170     {
171         UrlItemil/url/UrlItem.html#UrlItem">UrlItem item = new UrlItem( strUrl );
172 
173         try
174         {
175             String idEncrytped = RsaService.encryptRsa( dataToEncrypt );
176 
177             item.addParameter( FileService.PARAMETER_PROVIDER, strFileStorageServiceProviderName );
178             item.addParameter( FileService.PARAMETER_DATA, idEncrytped );
179 
180             return item.getUrlWithEntity( );
181         }
182         catch( GeneralSecurityException e )
183         {
184             AppLogService.error( e.getMessage( ), e );
185             return null;
186         }
187     }
188 
189     /**
190      * {@inheritDoc}
191      */
192     @Override
193     public String getName( )
194     {
195         return SERVICE_NAME;
196     }
197 
198     /**
199      * get data to encrypt
200      * 
201      * @param fileDownloadData
202      * @return the map of datas to encrypt in the url
203      */
204     private String getDataToEncrypt( Map<String, String> additionnalData )
205     {
206         StringBuilder sb = new StringBuilder( );
207         sb.append( StringUtils.defaultIfEmpty( additionnalData.get( FileService.PARAMETER_FILE_ID ), "" ) ).append( _separator );
208         sb.append( StringUtils.defaultIfEmpty( additionnalData.get( FileService.PARAMETER_RESOURCE_ID ), "" ) ).append( _separator );
209         sb.append( StringUtils.defaultIfEmpty( additionnalData.get( FileService.PARAMETER_RESOURCE_TYPE ), "" ) ).append( _separator );
210         sb.append( calculateEndValidity( ) );
211 
212         return sb.toString( );
213     }
214 
215     /**
216      * get end validity time
217      * 
218      * @return the end time of url validity
219      */
220     protected long calculateEndValidity( )
221     {
222         LocalDateTime endValidity = LocalDateTime.MAX;
223         if ( getValidityTime( ) > 0 )
224         {
225             endValidity = LocalDateTime.now( ).plusMinutes( LINK_VALIDITY_TIME );
226         }
227         return Timestamp.valueOf( endValidity ).getTime( );
228     }
229 
230     /**
231      * {@inheritDoc}
232      */
233     @Override
234     public Map<String, String> getRequestDataBO( HttpServletRequest request )
235     {
236         String strEncryptedData = request.getParameter( FileService.PARAMETER_DATA );
237 
238         try
239         {
240             String strDecryptedData = RsaService.decryptRsa( strEncryptedData );
241             return getDecryptedData( strDecryptedData );
242         }
243         catch( GeneralSecurityException e )
244         {
245             AppLogService.error( e.getMessage( ), e );
246             return null;
247         }
248     }
249 
250     /**
251      * {@inheritDoc}
252      */
253     @Override
254     public Map<String, String> getRequestDataFO( HttpServletRequest request )
255     {
256         String strEncryptedData = request.getParameter( FileService.PARAMETER_DATA );
257 
258         try
259         {
260             String strDecryptedData = RsaService.decryptRsa( strEncryptedData );
261             return getDecryptedData( strDecryptedData );
262         }
263         catch( GeneralSecurityException e )
264         {
265             AppLogService.error( e.getMessage( ), e );
266             return null;
267         }
268     }
269 
270     /**
271      * get map of datas from encrypted url data parameter
272      * 
273      * @param data
274      * @return the map of
275      */
276     protected Map<String, String> getDecryptedData( String strData )
277     {
278         String [ ] data = strData.split( _separator );
279         Map<String, String> fileData = buildAdditionnalDatas( data [0], data [1], data [2] );
280         fileData.put( PARAMETER_VALIDITY_TIME, data [3] );
281 
282         return fileData;
283     }
284 
285     /**
286      * {@inheritDoc}
287      */
288     @Override
289     public void checkLinkValidity( Map<String, String> fileData ) throws ExpiredLinkException
290     {
291         LocalDateTime validityTime = new Timestamp( Long.parseLong( fileData.get( FileService.PARAMETER_VALIDITY_TIME ) ) ).toLocalDateTime( );
292 
293         if ( LocalDateTime.now( ).isAfter( validityTime ) )
294         {
295             throw new ExpiredLinkException( "Link expired on : " + validityTime.toString( ) );
296         }
297     }
298 }