View Javadoc
1   /*
2    * Copyright (c) 2002-2023, 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.plugins.adminauthenticationoauth2.web;
35  
36  import fr.paris.lutece.plugins.adminauthenticationoauth2.business.authentication.AdminOauth2Authentication;
37  import fr.paris.lutece.plugins.adminauthenticationoauth2.business.authentication.AdminOauth2User;
38  import fr.paris.lutece.plugins.adminauthenticationoauth2.service.session.Oauth2AdminUserSessionService;
39  import fr.paris.lutece.plugins.adminauthenticationoauth2.service.Oauth2Utils;
40  import fr.paris.lutece.plugins.oauth2.business.Token;
41  import fr.paris.lutece.plugins.oauth2.service.TokenService;
42  import fr.paris.lutece.portal.business.user.AdminUser;
43  import fr.paris.lutece.portal.business.user.AdminUserHome;
44  import fr.paris.lutece.portal.service.admin.AccessDeniedException;
45  import fr.paris.lutece.portal.service.admin.AdminAuthenticationService;
46  import fr.paris.lutece.portal.service.admin.AdminUserService;
47  import fr.paris.lutece.portal.service.security.UserNotSignedException;
48  import fr.paris.lutece.portal.service.spring.SpringContextService;
49  import fr.paris.lutece.portal.service.util.AppPathService;
50  import fr.paris.lutece.portal.service.util.AppPropertiesService;
51  import fr.paris.lutece.util.url.UrlItem;
52  import org.apache.commons.lang3.StringUtils;
53  
54  import javax.servlet.*;
55  import javax.servlet.http.HttpServletRequest;
56  import javax.servlet.http.HttpServletResponse;
57  import javax.servlet.http.HttpSession;
58  import java.io.IOException;
59  import java.util.Arrays;
60  import java.util.HashMap;
61  import java.util.List;
62  import java.util.Map;
63  
64  /**
65   * ParisConnectLuteceFilters
66   *
67   */
68  public class AdminOauth2Filter implements Filter
69  {
70  
71      private boolean _bUsePromptNone;
72      private boolean _bValidateRefreshToken;
73      private List<String> _listUsePromptWhiteUrls;
74      private Map<String, List<String>> _mapUsePromptWhiteHeaders;
75  
76      /**
77       *
78       * {@inheritDoc}
79       */
80      @Override
81      public void destroy( )
82      {
83          // nothing
84      }
85  
86      /**
87       *
88       * {@inheritDoc}
89       */
90      @SuppressWarnings( "deprecation" )
91      @Override
92      public void doFilter( ServletRequest servletRequest, ServletResponse response, FilterChain chain ) throws IOException, ServletException
93      {
94          HttpServletRequest request = (HttpServletRequest) servletRequest;
95          HttpServletResponse resp = (HttpServletResponse) response;
96          if ( request != null && "GET".equals( request.getMethod( ) ) )
97          {
98              AdminUser user = AdminUserService.getAdminUser( request );
99  
100             if ( user == null && isUsePomptNoneForRequest( request ) )
101             {
102                 HttpSession session = request.getSession( true );
103 
104                 if ( ( session.getAttribute( Oauth2Utils.SESSION_ERROR_LOGIN ) == null && request.getParameter( Oauth2Utils.PARAM_ERROR_LOGIN ) == null )
105                         || session.getAttribute( Oauth2Utils.SESSION_ERROR_LOGIN ) != null
106                                 && session.getAttribute( Oauth2Utils.SESSION_ERROR_LOGIN ).equals( Oauth2Utils.REINIT_ERROR_LOGIN ) )
107                 {
108                     session.setAttribute( Oauth2Utils.SESSION_ERROR_LOGIN, "" );
109                     String strRedirectLoginUrl = AdminAuthenticationService.getInstance( ).getLoginPageUrl( );
110 
111                     resp.sendRedirect( strRedirectLoginUrl );
112 
113                     return;
114                 }
115 
116                 session.setAttribute( Oauth2Utils.SESSION_ERROR_LOGIN, Oauth2Utils.REINIT_ERROR_LOGIN );
117             }
118             else
119                 if ( _bValidateRefreshToken && user instanceof AdminOauth2User )
120                 {
121                     AdminOauth2User./../fr/paris/lutece/plugins/adminauthenticationoauth2/business/authentication/AdminOauth2User.html#AdminOauth2User">AdminOauth2User oauth2User = (AdminOauth2User) user;
122                     if ( oauth2User.getToken( ) != null && oauth2User.getToken( ).getRefreshToken( ) != null )
123                     {
124                         Token token = TokenService.getService( ).getTokenByRefreshToken( oauth2User.getToken( ).getRefreshToken( ) );
125                         if ( token == null )
126                         {
127 
128                             AdminAuthenticationService.getInstance( ).logoutUser( request );
129                         }
130                         else
131                         {
132                             oauth2User.setToken( token );
133                         }
134                     }
135                     else
136                     {
137                         AdminAuthenticationService.getInstance( ).logoutUser( request );
138                     }
139                 }
140             if ( !Oauth2AdminUserSessionService.getInstance( ).isAdminUserUpToDate( request.getSession( true ).getId( ) ) )
141             {
142 
143                 AdminOauth2Authentication oauth2Authentication = SpringContextService.getBean( "adminauthenticationoauth2.authentication" );
144                 user = oauth2Authentication.getHttpAuthenticatedUser( request );
145 
146                 if ( user != null && user.getAccessCode( ) != null )
147                 {
148                     AdminUser bindUser = AdminUserHome.findUserByLogin( user.getAccessCode( ) );
149                     if ( bindUser == null )
150                     {
151                         if ( user.getLastName( ) == null )
152                         {
153                             user.setLastName( "" );
154                         }
155                         if ( user.getFirstName( ) == null )
156                         {
157                             user.setFirstName( "" );
158                         }
159                         AdminUserHome.create( user );
160                     }
161                 }
162                 try
163                 {
164                     AdminAuthenticationService.getInstance( ).registerUser( request, user );
165                 }
166                 catch( AccessDeniedException | UserNotSignedException e )
167                 {
168                     throw new RuntimeException( e );
169                 }
170             }
171 
172         }
173 
174         chain.doFilter( servletRequest, response );
175     }
176 
177     /**
178      *
179      * {@inheritDoc}
180      */
181     @Override
182     public void init( FilterConfig config )
183     {
184         _bUsePromptNone = AppPropertiesService.getPropertyBoolean( Oauth2Utils.PROPERTY_USE_PROMPT_NONE, false );
185         _bValidateRefreshToken = AppPropertiesService.getPropertyBoolean( Oauth2Utils.PROPERTY_VALIDATE_REFRESH_TOKEN, false );
186 
187         String strTabWhiteListingUrls = AppPropertiesService.getProperty( Oauth2Utils.PROPERTY_USE_PROMPT_NONE_WHITE_LISTING_URLS );
188         String strTabWhiteListingHeaders = AppPropertiesService.getProperty( Oauth2Utils.PROPERTY_USE_PROMPT_NONE_WHITE_LISTING_HEADERS );
189         if ( StringUtils.isNotBlank( strTabWhiteListingUrls ) )
190         {
191             _listUsePromptWhiteUrls = Arrays.asList( strTabWhiteListingUrls.split( Oauth2Utils.SEPARATOR ) );
192         }
193         if ( StringUtils.isNotBlank( strTabWhiteListingHeaders ) )
194         {
195             _mapUsePromptWhiteHeaders = new HashMap<>( );
196             Arrays.stream( strTabWhiteListingHeaders.split( Oauth2Utils.SEPARATOR ) )
197                     .forEach( x -> _mapUsePromptWhiteHeaders.put( x, Arrays.asList( AppPropertiesService
198                             .getProperty( Oauth2Utils.PROPERTY_USE_PROMPT_NONE_WHITE_LISTING_HEADERS + "." + x, "" ).split( Oauth2Utils.SEPARATOR ) ) ) );
199         }
200 
201     }
202 
203     private boolean isUsePomptNoneForRequest( HttpServletRequest request )
204     {
205         boolean bReturn = true;
206         if ( _bUsePromptNone )
207         {
208             // test headers white list
209             if ( _mapUsePromptWhiteHeaders != null && _mapUsePromptWhiteHeaders.size( ) > 0 )
210             {
211                 bReturn = _mapUsePromptWhiteHeaders.keySet( ).stream( ).noneMatch( x -> request.getHeader( x ) != null
212                         && _mapUsePromptWhiteHeaders.get( x ).stream( ).anyMatch( v -> v.equalsIgnoreCase( request.getHeader( x ) ) ) );
213             }
214             // test url white List
215             if ( bReturn && _listUsePromptWhiteUrls != null && !_listUsePromptWhiteUrls.isEmpty( ) )
216             {
217                 bReturn = _listUsePromptWhiteUrls.stream( ).noneMatch( x -> matchUrl( request, x ) );
218             }
219 
220         }
221         else
222         {
223             bReturn = false;
224         }
225 
226         return bReturn;
227 
228     }
229 
230     /**
231      * method to test if the URL matches the pattern
232      * 
233      * @param request
234      *            the request
235      * @param strUrlPatern
236      *            the pattern
237      * @return true if the URL matches the pattern
238      */
239     private boolean matchUrl( HttpServletRequest request, String strUrlPatern )
240     {
241         boolean bMatch = false;
242 
243         if ( strUrlPatern != null )
244         {
245             UrlItem url = new UrlItem( getResquestedUrl( request ) );
246 
247             if ( strUrlPatern.contains( Oauth2Utils.URL_INTERROGATIVE ) )
248             {
249                 for ( String strParamPatternValue : strUrlPatern.substring( strUrlPatern.indexOf( Oauth2Utils.URL_INTERROGATIVE ) + 1 )
250                         .split( Oauth2Utils.URL_AMPERSAND ) )
251                 {
252                     String [ ] arrayPatternParamValue = strParamPatternValue.split( Oauth2Utils.URL_EQUAL );
253 
254                     if ( request.getParameter( arrayPatternParamValue [0] ) != null )
255                     {
256                         url.addParameter( arrayPatternParamValue [0], request.getParameter( arrayPatternParamValue [0] ) );
257                     }
258                 }
259             }
260 
261             if ( strUrlPatern.contains( Oauth2Utils.URL_STAR ) )
262             {
263                 String strUrlPaternLeftEnd = strUrlPatern.substring( 0, strUrlPatern.indexOf( Oauth2Utils.URL_STAR ) );
264                 String strAbsoluteUrlPattern = getAbsoluteUrl( request, strUrlPaternLeftEnd );
265                 bMatch = url.getUrl( ).startsWith( strAbsoluteUrlPattern );
266             }
267             else
268             {
269                 String strAbsoluteUrlPattern = getAbsoluteUrl( request, strUrlPatern );
270                 bMatch = url.getUrl( ).equals( strAbsoluteUrlPattern );
271             }
272         }
273 
274         return bMatch;
275     }
276 
277     /**
278      * 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
279      * url should be given relatively to the webapp root.
280      *
281      * @param request
282      *            the http request (provides the base path if needed)
283      * @param strUrl
284      *            the url to transform
285      * @return the corresonding absolute url
286      *
287      */
288     private String getAbsoluteUrl( HttpServletRequest request, String strUrl )
289     {
290         if ( ( strUrl != null ) && !strUrl.startsWith( "http://" ) && !strUrl.startsWith( "https://" ) )
291         {
292             return AppPathService.getBaseUrl( request ) + strUrl;
293         }
294         else
295         {
296             return strUrl;
297         }
298     }
299 
300     /**
301      * Return the absolute representation of the requested url
302      *
303      * @param request
304      *            the http request (provides the base path if needed)
305      * @return the requested url has a string
306      *
307      */
308     private String getResquestedUrl( HttpServletRequest request )
309     {
310         return request.getRequestURL( ).toString( );
311     }
312 
313 }