View Javadoc
1   /*
2    * Copyright (c) 2002-2025, 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.filter;
35  
36  import java.io.IOException;
37  import java.util.Collection;
38  
39  import javax.servlet.FilterChain;
40  import javax.servlet.FilterConfig;
41  import javax.servlet.ServletException;
42  import javax.servlet.ServletRequest;
43  import javax.servlet.ServletResponse;
44  import javax.servlet.http.HttpServletRequest;
45  import javax.servlet.http.HttpServletResponse;
46  
47  import org.apache.logging.log4j.LogManager;
48  import org.apache.logging.log4j.Logger;
49  
50  import fr.paris.lutece.portal.business.securityheader.SecurityHeader;
51  import fr.paris.lutece.portal.business.securityheader.SecurityHeaderPageCategory;
52  import fr.paris.lutece.portal.business.securityheader.SecurityHeaderType;
53  import fr.paris.lutece.portal.service.security.SecurityService;
54  import fr.paris.lutece.portal.service.securityheader.SecurityHeaderService;
55  import fr.paris.lutece.portal.service.spring.SpringContextService;
56  import fr.paris.lutece.portal.service.util.AppPathService;
57  import fr.paris.lutece.portal.service.util.AppPropertiesService;
58  
59  /**
60   * Page security header filter
61   * This filter is used to add security headers to the response when the requested resource is a jsp page except
62   * for BO admin authenticated pages. For those pages, security headers adding is managed by AuthenticationFilter class.
63   * When the request resource is a API REST endpoint, security headers adding is done in RestApiSecurityHeaderFilter class
64   * 
65   */
66  public class PageSecurityHeaderFilter implements javax.servlet.Filter
67  {
68  
69  	private Logger _logger = LogManager.getLogger( "lutece.securityHeader" );
70  	private static final String PROPERTY_JSP_URL_PORTAL_LOGOUT = "mylutece.url.doLogout";
71  	
72      /**
73       * Initializes the filter
74       * 
75       * @param filterConfig
76       *            The filter config
77       * @throws ServletException
78       *             If an error occured
79       */
80      public void init( FilterConfig filterConfig ) throws ServletException
81      {
82  
83      }
84      
85      /**
86       * Apply the filter
87       * 
88       * @param request
89       *            The HTTP request
90       * @param response
91       *            The HTTP response
92       * @param filterChain
93       *            The Filter Chain
94       * @throws IOException
95       *             If an error occured
96       * @throws ServletException
97       *             If an error occured
98       */
99      public void doFilter( ServletRequest request, ServletResponse response, FilterChain filterChain ) throws IOException, ServletException
100     {
101     	HttpServletRequest req = ( HttpServletRequest ) request;
102     	HttpServletResponse resp = ( HttpServletResponse )response;
103         
104     	addAllPagesHeaders( req, resp );
105     	addFoAuthenticatedPagesHeaders( req, resp );
106     	addFoLogoutPageHeaders( req, resp );
107     	
108         filterChain.doFilter( req, resp );
109     }
110     
111     /**
112      * Adds active security headers that must be added to all pages to the response
113      * 
114      * @param request
115      *            The HTTP request
116      * @param response
117      *            The HTTP response
118      */
119     private void addAllPagesHeaders( HttpServletRequest request, HttpServletResponse response )
120     {
121     	addActiveHeaders( request, response, SecurityHeaderType.PAGE, SecurityHeaderPageCategory.ALL );
122     }
123      
124     /**
125      * Adds active security headers that must be added to FO authenticated pages to the response
126      * 
127      * @param request
128      *            The HTTP request
129      * @param response
130      *            The HTTP response
131      */
132     private void addFoAuthenticatedPagesHeaders( HttpServletRequest request, HttpServletResponse response )
133     {
134     	if( SecurityService.getInstance( ).getRegisteredUser( request ) != null )
135     	{
136     		addActiveHeaders( request, response, SecurityHeaderType.PAGE, SecurityHeaderPageCategory.AUTHENTICATED_ADMIN_FRONT_OFFICE );
137     	}
138     }
139     
140     /**
141      * Adds active security headers that must be added to the logout page to the response 
142      * 
143      * @param request
144      *            The HTTP request
145      * @param response
146      *            The HTTP response
147      */
148     private void addFoLogoutPageHeaders( HttpServletRequest request, HttpServletResponse response )
149     {
150     	String logoutPage = getAbsoluteUrl( request, AppPropertiesService.getProperty( PROPERTY_JSP_URL_PORTAL_LOGOUT ) );
151     	if( logoutPage != null )
152     	{
153     		if( getRequestedUrl( request ).equals( logoutPage ) )
154     		{
155     			addActiveHeaders( request, response, SecurityHeaderType.PAGE, SecurityHeaderPageCategory.LOGOUT_FO );
156     		}
157     	}
158     }
159     
160     /**
161      * 
162      * Adds active security headers of the specified type and the specified category to the response
163      * 
164      * @param request
165      *            The HTTP request
166      * @param response
167      *            The HTTP response
168      * @param typePage
169      *            The type of page
170      * @param pageCategory
171      *            The category of page (if type is equals to page)
172      */
173     private void addActiveHeaders( HttpServletRequest request, HttpServletResponse response, SecurityHeaderType typePage, SecurityHeaderPageCategory pageCategory )
174     {
175     	Collection<SecurityHeader> securityHeadersToAddList = getSecurityHeaderService( ).findActive( typePage.getCode( ), pageCategory.getCode( ) );
176     	if( securityHeadersToAddList != null )
177     	{
178     		for( SecurityHeader securityHeader : securityHeadersToAddList )
179         	{
180         		response.setHeader( securityHeader.getName( ), securityHeader.getValue( ) );
181         		_logger.debug( "Security header added to page {} - name : {}, value : {} ", request.getServletPath( ), securityHeader.getName( ), securityHeader.getValue( ) );
182         	}
183     	}   	
184     }
185     
186     /**
187      * Destroy the filter
188      */
189     public void destroy( )
190     {
191         
192     }
193     
194     /**
195      * Returns the security header service.
196      * 
197      * @return the security header service
198      */
199     private SecurityHeaderService getSecurityHeaderService( )
200     {
201     	return SpringContextService.getBean( "securityHeaderService" );
202     }
203     
204     /**
205      * Return the absolute representation of the requested url
206      * 
207      * @param request
208      *            the http request (provides the base path if needed)
209      * @return the requested url has a string
210      *
211      */
212     private String getRequestedUrl( HttpServletRequest request )
213     {
214         return AppPathService.getBaseUrl( request ) + request.getServletPath( ).substring( 1 );
215     }
216     
217     /**
218      * Returns the absolute url corresponding to the given one, if the later was found to be relative. An url starting with "http://" is absolute. A relative
219      * url should be given relatively to the webapp root.
220      * 
221      * @param request
222      *            the http request (provides the base path if needed)
223      * @param strUrl
224      *            the url to transform
225      * @return the corresponding absolute url
226      *
227      */
228     private String getAbsoluteUrl( HttpServletRequest request, String strUrl )
229     {
230         if ( ( strUrl != null ) && !strUrl.startsWith( "http://" ) && !strUrl.startsWith( "https://" ) )
231         {
232             return AppPathService.getBaseUrl( request ) + strUrl;
233         }
234 
235         return strUrl;
236     }
237 }