View Javadoc
1   /*
2    * Copyright (c) 2002-2014, Mairie de 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.web.user;
35  
36  import fr.paris.lutece.portal.service.admin.AccessDeniedException;
37  import fr.paris.lutece.portal.service.admin.AdminAuthenticationService;
38  import fr.paris.lutece.portal.service.admin.AdminUserService;
39  import fr.paris.lutece.portal.service.admin.PasswordResetException;
40  import fr.paris.lutece.portal.service.message.AdminMessage;
41  import fr.paris.lutece.portal.service.message.AdminMessageService;
42  import fr.paris.lutece.portal.service.security.UserNotSignedException;
43  import fr.paris.lutece.portal.service.util.AppLogService;
44  import fr.paris.lutece.portal.service.util.AppPathService;
45  import fr.paris.lutece.portal.service.util.AppPropertiesService;
46  import fr.paris.lutece.portal.web.constants.Messages;
47  import fr.paris.lutece.portal.web.constants.Parameters;
48  import fr.paris.lutece.util.url.UrlItem;
49  
50  import java.io.IOException;
51  
52  import java.util.Enumeration;
53  import java.util.StringTokenizer;
54  
55  import javax.servlet.Filter;
56  import javax.servlet.FilterChain;
57  import javax.servlet.FilterConfig;
58  import javax.servlet.ServletException;
59  import javax.servlet.ServletRequest;
60  import javax.servlet.ServletResponse;
61  import javax.servlet.http.HttpServletRequest;
62  import javax.servlet.http.HttpServletResponse;
63  
64  
65  /**
66   * Filter to prevent unauthenticated access to admin
67   */
68  public class AuthenticationFilter implements Filter
69  {
70      private static final String PROPERTY_URL_PREFIX = "path.jsp.admin.public.";
71      private static final String PROPERTY_URL_SUFFIX_LIST = "list";
72      private static final String CONSTANT_LIST_SEPARATOR = ",";
73      private static final String LOGGER_NAME = "lutece.authentication";
74      private static final String PROPERTY_RESET_EXCEPTION_MESSAGE = "User must reset his password.";
75      private static final String PROPERTY_JSP_URL_ADMIN_LOGOUT = "lutece.admin.logout.url";
76  
77      /**
78       * {@inheritDoc}
79       */
80      @Override
81      public void init( FilterConfig config ) throws ServletException
82      {
83          // Do nothing
84      }
85  
86      /**
87       * {@inheritDoc}
88       */
89      @Override
90      public void destroy(  )
91      {
92          // Do nothing
93      }
94  
95      /**
96       * {@inheritDoc}
97       */
98      @Override
99      public void doFilter( ServletRequest request, ServletResponse response, FilterChain chain )
100         throws IOException, ServletException
101     {
102         HttpServletRequest req = (HttpServletRequest) request;
103         HttpServletResponse resp = (HttpServletResponse) response;
104 
105         AppLogService.debug( LOGGER_NAME, "Accessing url : " + getResquestedUrl( req ) );
106 
107         if ( isPrivateUrl( req ) )
108         {
109             try
110             {
111                 filterAccess( req );
112             }
113             catch ( UserNotSignedException e )
114             {
115                 AdminAuthenticationService.getInstance(  ).setLoginNextUrl( req );
116 
117                 String strRedirectUrl = null;
118 
119                 if ( AdminAuthenticationService.getInstance(  ).isExternalAuthentication(  ) )
120                 {
121                     AppLogService.debug( LOGGER_NAME,
122                         "New session behind external authentication : " + getResquestedUrl( req ) );
123 
124                     strRedirectUrl = AdminMessageService.getMessageUrl( req, Messages.MESSAGE_USER_NEW_SESSION,
125                             AppPathService.getAdminMenuUrl(  ), AdminMessage.TYPE_INFO );
126                 }
127                 else
128                 {
129                     AppLogService.debug( LOGGER_NAME, "Access NOT granted to url : " + getResquestedUrl( req ) );
130 
131                     strRedirectUrl = AdminMessageService.getMessageUrl( req, Messages.MESSAGE_USER_NOT_AUTHENTICATED,
132                             getRedirectUrl( req ), AdminMessage.TYPE_WARNING );
133                 }
134 
135                 resp.sendRedirect( getAbsoluteUrl( req, strRedirectUrl ) );
136 
137                 return;
138             }
139             catch ( AccessDeniedException e )
140             {
141                 AppLogService.debug( LOGGER_NAME, "Access NOT granted to url : " + getResquestedUrl( req ) );
142 
143                 String strRedirectUrl = AdminMessageService.getMessageUrl( req, Messages.MESSAGE_AUTH_FAILURE,
144                         getRedirectUrl( req ), AdminMessage.TYPE_ERROR );
145                 resp.sendRedirect( getAbsoluteUrl( req, strRedirectUrl ) );
146 
147                 return;
148             }
149             catch ( PasswordResetException e )
150             {
151                 if ( !getResquestedUrl( req ).equals( getChangePasswordUrl( req ) ) &&
152                         !getResquestedUrl( req ).equals( getLoginUrl( req ) ) )
153                 {
154                     String strRedirectUrl = AdminMessageService.getMessageUrl( req,
155                             Messages.MESSAGE_USER_MUST_CHANGE_PASSWORD, getChangePasswordUrl( req ),
156                             AdminMessage.TYPE_ERROR );
157                     resp.sendRedirect( getAbsoluteUrl( req, strRedirectUrl ) );
158 
159                     return;
160                 }
161             }
162         }
163 
164         chain.doFilter( request, response );
165     }
166 
167     /**
168      * Build the url to redirect to if not logged.
169      * This is actually the login page of the authentication module, completed with the request parameters.
170      * @param request the http request
171      * @return the string representation of the redirection url - absolute - with request parameters.
172      */
173     private String getRedirectUrl( HttpServletRequest request )
174     {
175         UrlItem url = new UrlItem( getLoginUrl( request ) );
176 
177         Enumeration<String> enumParams = request.getParameterNames(  );
178 
179         String strParamName;
180 
181         while ( enumParams.hasMoreElements(  ) )
182         {
183             strParamName = enumParams.nextElement(  );
184 
185             if ( !strParamName.equals( Parameters.ACCESS_CODE ) && !strParamName.equals( Parameters.PASSWORD ) )
186             {
187                 url.addParameter( strParamName, request.getParameter( strParamName ) );
188             }
189         }
190 
191         return url.getUrl(  );
192     }
193 
194     /**
195      * Get the absolute login url
196      *
197      * @param request the http request
198      * @return the login url, in its absolute form
199      *
200      * */
201     private String getLoginUrl( HttpServletRequest request )
202     {
203         String strLoginUrl = AdminAuthenticationService.getInstance(  ).getLoginPageUrl(  );
204 
205         return getAbsoluteUrl( request, strLoginUrl );
206     }
207 
208     /**
209      * Gets the logout url.
210      *
211      * @param request the request
212      * @return the logout url
213      */
214     private String getLogoutUrl( HttpServletRequest request )
215     {
216         return getAbsoluteUrl( request, AppPropertiesService.getProperty( PROPERTY_JSP_URL_ADMIN_LOGOUT ) );
217     }
218 
219     /**
220      * Get the absolute login url
221      *
222      * @param request the http request
223      * @return the login url, in its absolute form
224      *
225      * */
226     private String getChangePasswordUrl( HttpServletRequest request )
227     {
228         String strChangePasswordUrl = AdminAuthenticationService.getInstance(  ).getChangePasswordPageUrl(  );
229 
230         return getAbsoluteUrl( request, strChangePasswordUrl );
231     }
232 
233     /**
234      * Check wether a given url is to be considered as private (ie that
235      * needs a successful authentication to be accessed) or public (ie that
236      * can be access without being authenticated)
237      * @param request the http request
238      * @return true if the url needs to be authenticated, false otherwise
239      *
240      * */
241     private boolean isPrivateUrl( HttpServletRequest request )
242     {
243         boolean bIsRestricted = true;
244         String strUrl = getResquestedUrl( request );
245 
246         if ( strUrl.equals( getLoginUrl( request ) ) || strUrl.equals( getLogoutUrl( request ) ) )
247         {
248             bIsRestricted = false;
249         }
250         else if ( isInPublicUrlList( request, strUrl ) )
251         {
252             bIsRestricted = false;
253         }
254 
255         return bIsRestricted;
256     }
257 
258     /**
259      * check that the access is granted
260      *  @param request The HTTP request
261      *  @throws AccessDeniedException If the user is not allowed
262      *  @throws UserNotSignedException If the user is not signed
263      *
264      **/
265     private static void filterAccess( HttpServletRequest request )
266         throws UserNotSignedException, AccessDeniedException
267     {
268         if ( AdminAuthenticationService.getInstance(  ).isExternalAuthentication(  ) )
269         {
270             // The authentication is external
271             // Should register the user if it's not already done
272             AdminAuthenticationService.getInstance(  ).getRemoteUser( request );
273         }
274         else
275         {
276             if ( AdminAuthenticationService.getInstance(  ).getRegisteredUser( request ) == null )
277             {
278                 // Authentication is required to access to the admin
279                 throw new UserNotSignedException(  );
280             }
281 
282             if ( AdminUserService.getAdminUser( request ).isPasswordReset(  ) )
283             {
284                 throw new PasswordResetException( PROPERTY_RESET_EXCEPTION_MESSAGE );
285             }
286         }
287     }
288 
289     /**
290      * Checks if the requested is in the list of urls that are under
291      * jsp/admin but shouldn't be protected
292      * @param request the http request (provides the base path if needed)
293      * @param strRequestedUrl the url to test : it should start with "http://" is absolute, or should be relative to the webapp root otherwise
294      * @return true if the url is in the list, false otherwise
295      *
296      * */
297     private boolean isInPublicUrlList( HttpServletRequest request, String strRequestedUrl )
298     {
299         // recovers list from the
300         String strList = AppPropertiesService.getProperty( PROPERTY_URL_PREFIX + PROPERTY_URL_SUFFIX_LIST );
301 
302         // extracts each item (separated by a comma) from the includes list
303         StringTokenizer strTokens = new StringTokenizer( strList, CONSTANT_LIST_SEPARATOR );
304 
305         while ( strTokens.hasMoreTokens(  ) )
306         {
307             String strName = strTokens.nextToken(  );
308             String strUrl = AppPropertiesService.getProperty( PROPERTY_URL_PREFIX + strName );
309             strUrl = getAbsoluteUrl( request, strUrl );
310 
311             if ( strRequestedUrl.equals( strUrl ) )
312             {
313                 return true;
314             }
315         }
316 
317         return false;
318     }
319 
320     /**
321      * Returns the absolute url corresponding to the given one, if the later
322      * was found to be relative. An url starting with "http://" is absolute.
323      * A relative url should be given relatively to the webapp root.
324      * @param request the http request (provides the base path if needed)
325      * @param strUrl the url to transform
326      * @return the corresponding absolute url
327      *
328      * */
329     private String getAbsoluteUrl( HttpServletRequest request, String strUrl )
330     {
331         if ( ( strUrl != null ) && !strUrl.startsWith( "http://" ) && !strUrl.startsWith( "https://" ) )
332         {
333             return AppPathService.getBaseUrl( request ) + strUrl;
334         }
335 
336         return strUrl;
337     }
338 
339     /**
340      * Return the absolute representation of the requested url
341      * @param request the http request (provides the base path if needed)
342      * @return the requested url has a string
343      *
344      * */
345     private String getResquestedUrl( HttpServletRequest request )
346     {
347         return AppPathService.getBaseUrl( request ) + request.getServletPath(  ).substring( 1 );
348     }
349 }