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