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.security;
35  
36  import fr.paris.lutece.portal.business.event.LuteceUserEvent;
37  import fr.paris.lutece.portal.service.datastore.DatastoreService;
38  import fr.paris.lutece.portal.service.event.LuteceUserEventManager;
39  import fr.paris.lutece.portal.service.init.LuteceInitException;
40  import fr.paris.lutece.portal.service.util.AppLogService;
41  import fr.paris.lutece.portal.service.util.AppPropertiesService;
42  import fr.paris.lutece.util.url.UrlItem;
43  
44  import java.security.Principal;
45  
46  import java.util.Collection;
47  import java.util.Enumeration;
48  import java.util.List;
49  
50  import javax.security.auth.login.LoginException;
51  
52  import javax.servlet.http.HttpServletRequest;
53  import javax.servlet.http.HttpSession;
54  
55  /**
56   * This class provides a security service to register and check user authentication
57   */
58  public final class SecurityService
59  {
60      /**
61       * Session attribute that stores the LuteceUser object attached to the session
62       */
63      private static final String ATTRIBUTE_LUTECE_USER = "lutece_user";
64  
65      private static final String PROPERTY_AUTHENTICATION_CLASS = "mylutece.authentication.class";
66      private static final String PROPERTY_AUTHENTICATION_ENABLE = "mylutece.authentication.enable";
67      private static final String PROPERTY_PORTAL_AUTHENTICATION_REQUIRED = "mylutece.portal.authentication.required";
68  
69      private static final String URL_INTERROGATIVE = "?";
70      private static final String URL_AMPERSAND = "&";
71      private static final String URL_EQUAL = "=";
72  
73      private static final String CONSTANT_ACTION_LOGIN_USER = "user.loginUser";
74      private static final String CONSTANT_ACTION_LOGOUT_USER = "user.logoutUser";
75      private static final String CONSTANT_FO = "FO";
76  
77      private static SecurityServicerity/SecurityService.html#SecurityService">SecurityService _singleton = new SecurityService( );
78      private static LuteceAuthentication _authenticationService;
79      private static boolean _bEnable;
80  
81      /**
82       * Private constructor
83       */
84      private SecurityService( )
85      {
86      }
87  
88      /**
89       * Initialize service
90       * 
91       * @throws LuteceInitException
92       *             if an error occurs
93       */
94      public static synchronized void init( ) throws LuteceInitException
95      {
96          _bEnable = false;
97  
98          String strEnable = AppPropertiesService.getProperty( PROPERTY_AUTHENTICATION_ENABLE, "false" );
99  
100         if ( strEnable.equalsIgnoreCase( "true" ) )
101         {
102             _authenticationService = getPortalAuthentication( );
103 
104             if ( _authenticationService != null )
105             {
106                 _bEnable = true;
107             }
108         }
109         else
110         {
111             // in case authentication is disabled after having been enabled
112             _authenticationService = null;
113         }
114     }
115 
116     /**
117      * Get the unique instance of the Security Service
118      * 
119      * @return The instance
120      */
121     public static SecurityService getInstance( )
122     {
123         return _singleton;
124     }
125 
126     /**
127      * Returns the authentication's activation : enable or disable
128      * 
129      * @return true if the authentication is active, false otherwise
130      */
131     public static boolean isAuthenticationEnable( )
132     {
133         return _bEnable;
134     }
135 
136     /**
137      * Gets the LuteceUser attached to the current Http session
138      * 
139      * @param request
140      *            The Http request
141      * @return A LuteceUser object if found
142      * @throws UserNotSignedException
143      *             If there is no current user
144      */
145     public LuteceUser getRemoteUser( HttpServletRequest request ) throws UserNotSignedException
146     {
147         LuteceUser user = getRegisteredUser( request );
148 
149         if ( user == null )
150         {
151             // User is not registered by Lutece, but it may be authenticated by another
152             // system
153             if ( _authenticationService.isExternalAuthentication( ) || _authenticationService.isMultiAuthenticationSupported( ) )
154             {
155                 user = _authenticationService.getHttpAuthenticatedUser( request );
156 
157                 if ( ( user == null ) && isPortalAuthenticationRequired( ) )
158                 {
159                     throw new UserNotSignedException( );
160                 }
161 
162                 registerUser( request, user );
163             }
164             else
165             {
166                 throw new UserNotSignedException( );
167             }
168         }
169 
170         return user;
171     }
172 
173     /**
174      * Returns the user's principal
175      * 
176      * @param request
177      *            The HTTP request
178      * @return The user's principal
179      * @throws UserNotSignedException
180      *             The UserNotSignedException
181      */
182     public Principal getUserPrincipal( HttpServletRequest request ) throws UserNotSignedException
183     {
184         return getRemoteUser( request );
185     }
186 
187     /**
188      * Checks if the user is associated to a given role
189      * 
190      * @param request
191      *            The Http request
192      * @param strRole
193      *            The Role name
194      * @return Returns true if the user is associated to the given role
195      */
196     public boolean isUserInRole( HttpServletRequest request, String strRole )
197     {
198         LuteceUser user;
199 
200         if ( !isAuthenticationEnable( ) )
201         {
202             return true;
203         }
204 
205         try
206         {
207             user = getRemoteUser( request );
208         }
209         catch( UserNotSignedException e )
210         {
211             return false;
212         }
213 
214         return _authenticationService.isUserInRole( user, request, strRole );
215     }
216 
217     /**
218      * Checks if the user is associated to a at least a role
219      * 
220      * @param request
221      *            The Http request
222      * @param listRoles
223      *            The Role list
224      * @return Returns true if the user is associated to any role
225      */
226     public boolean isUserInAnyRole( HttpServletRequest request, List<String> listRoles )
227     {
228         boolean bAutorized = false;
229         for ( String strRole : listRoles )
230         {
231             if ( isUserInRole( request, strRole ) )
232             {
233                 bAutorized = true;
234                 break;
235             }
236         }
237         return bAutorized;
238     }
239 
240     /**
241      * get all roles for this user : - user's roles - user's groups roles
242      *
243      * @param user
244      *            The user
245      * @return Array of roles
246      */
247     public String [ ] getRolesByUser( LuteceUser user )
248     {
249         return _authenticationService.getRolesByUser( user );
250     }
251 
252     /**
253      * Checks user's login with the Authentication service.
254      * 
255      * @param request
256      *            The Http request
257      * @param strUserName
258      *            The user's login
259      * @param strPassword
260      *            The user's password
261      * @throws LoginException
262      *             The LoginException
263      * @throws LoginRedirectException
264      *             if redirect exception
265      */
266     public void loginUser( HttpServletRequest request, final String strUserName, final String strPassword ) throws LoginException, LoginRedirectException
267     {
268         LuteceUser user = _authenticationService.login( strUserName, strPassword, request );
269 
270         _authenticationService.updateDateLastLogin( user, request );
271 
272         if ( _authenticationService.findResetPassword( request, strUserName ) )
273         {
274             String redirect = _authenticationService.getResetPasswordPageUrl( request );
275             registerUser( request, user );
276             AccessLogService.getInstance( ).info( AccessLoggerConstants.EVENT_TYPE_CONNECT, CONSTANT_ACTION_LOGIN_USER, user, null, CONSTANT_FO );
277 
278             throw new LoginRedirectException( redirect );
279         }
280 
281         registerUser( request, user );
282 
283         AccessLogService.getInstance( ).info( AccessLoggerConstants.EVENT_TYPE_CONNECT, CONSTANT_ACTION_LOGIN_USER, user, null, CONSTANT_FO );
284 
285     }
286 
287     /**
288      * Logout the user
289      * 
290      * @param request
291      *            The HTTP request
292      */
293     public void logoutUser( HttpServletRequest request )
294     {
295         LuteceUser user;
296 
297         try
298         {
299             user = getRemoteUser( request );
300         }
301         catch( UserNotSignedException e )
302         {
303             return;
304         }
305 
306         _authenticationService.logout( user );
307         HttpSession session = request.getSession( false );
308         if(session!=null)
309         {
310         	session.invalidate();
311         }
312         LuteceUserEventManager.getInstance().notifyListeners( new LuteceUserEvent( user,LuteceUserEvent.EventType.LOGOUT ) );
313         AccessLogService.getInstance( ).info( AccessLoggerConstants.EVENT_TYPE_DISCONNECT, CONSTANT_ACTION_LOGOUT_USER, user, null, CONSTANT_FO );
314     }
315 
316     /**
317      * Retrieves the portal authentication service configured in the config.properties
318      * 
319      * @return A PortalAuthentication object
320      * @throws LuteceInitException
321      *             If an error occurred
322      */
323     private static LuteceAuthentication getPortalAuthentication( ) throws LuteceInitException
324     {
325         String strAuthenticationClass = AppPropertiesService.getProperty( PROPERTY_AUTHENTICATION_CLASS );
326         LuteceAuthentication authentication = null;
327 
328         if ( ( strAuthenticationClass != null ) && !strAuthenticationClass.equals( "" ) )
329         {
330             try
331             {
332                 authentication = (LuteceAuthentication) Class.forName( strAuthenticationClass ).newInstance( );
333                 AppLogService.info( "Authentication service loaded : {}", authentication.getAuthServiceName( ) );
334             }
335             catch( InstantiationException | IllegalAccessException | ClassNotFoundException e )
336             {
337                 throw new LuteceInitException( "Error instantiating Authentication Class", e );
338             }
339         }
340 
341         return authentication;
342     }
343 
344     /**
345      * Register the user in the Http session
346      * 
347      * @param request
348      *            The Http request
349      * @param user
350      *            The current user
351      */
352     public void registerUser( HttpServletRequest request, LuteceUser user )
353     {
354         HttpSession session = request.getSession( true );
355         session.setAttribute( ATTRIBUTE_LUTECE_USER, user );
356         
357         if ( user != null )
358         {
359         	LuteceUserEventManager.getInstance().notifyListeners( new LuteceUserEvent( user, LuteceUserEvent.EventType.LOGIN_SUCCESSFUL ) );
360         }
361     }
362 
363     /**
364      * Unregister the user in the Http session
365      * 
366      * @param request
367      *            The Http request     
368      */
369     public void unregisterUser( HttpServletRequest request )
370     {
371         HttpSession session = request.getSession( true );
372         LuteceUser/../../../../../fr/paris/lutece/portal/service/security/LuteceUser.html#LuteceUser">LuteceUser user = (LuteceUser)session.getAttribute( ATTRIBUTE_LUTECE_USER );
373         
374         if ( user != null )
375         {
376         	session.removeAttribute( ATTRIBUTE_LUTECE_USER );
377         }
378     }
379 
380     /**
381      * Gets the Lutece user registered in the Http session
382      * 
383      * @param request
384      *            The HTTP request
385      * @return The User registered or null if the user has not been registered
386      */
387     public LuteceUser getRegisteredUser( HttpServletRequest request )
388     {
389         HttpSession session = ( request != null ) ? request.getSession( false ) : null;
390 
391         if ( session != null )
392         {
393             return (LuteceUser) session.getAttribute( ATTRIBUTE_LUTECE_USER );
394         }
395 
396         return null;
397     }
398 
399     /**
400      * Returns the authentication type : External or Lutece portal based
401      * 
402      * @return true if the user is already authenticated or false if it needs to login.
403      */
404     public boolean isExternalAuthentication( )
405     {
406         return _authenticationService.isExternalAuthentication( );
407     }
408 
409     /**
410      * Returns the Login page URL of the Authentication Service
411      * 
412      * @return The URL
413      */
414     public String getLoginPageUrl( )
415     {
416         return _authenticationService.getLoginPageUrl( );
417     }
418 
419     /**
420      * Returns the DoLogin URL of the Authentication Service
421      * 
422      * @return The URL
423      */
424     public String getDoLoginUrl( )
425     {
426         return _authenticationService.getDoLoginUrl( );
427     }
428 
429     /**
430      * Returns the DoLogout URL of the Authentication Service
431      * 
432      * @return The URL
433      */
434     public String getDoLogoutUrl( )
435     {
436         return _authenticationService.getDoLogoutUrl( );
437     }
438 
439     /**
440      * Returns the new account page URL of the Authentication Service
441      * 
442      * @return The URL
443      */
444     public String getNewAccountPageUrl( )
445     {
446         return _authenticationService.getNewAccountPageUrl( );
447     }
448 
449     /**
450      * Returns the view account page URL of the Authentication Service
451      * 
452      * @return The URL
453      */
454     public String getViewAccountPageUrl( )
455     {
456         return _authenticationService.getViewAccountPageUrl( );
457     }
458 
459     /**
460      * Returns the lost password URL of the Authentication Service
461      * 
462      * @return The URL
463      */
464     public String getLostPasswordPageUrl( )
465     {
466         return _authenticationService.getLostPasswordPageUrl( );
467     }
468 
469     // Added in v1.3
470 
471     /**
472      * Returns the access denied template
473      * 
474      * @return The template
475      */
476     public String getAccessDeniedTemplate( )
477     {
478         return _authenticationService.getAccessDeniedTemplate( );
479     }
480 
481     /**
482      * Returns the access controled template
483      * 
484      * @return The template
485      */
486     public String getAccessControledTemplate( )
487     {
488         return _authenticationService.getAccessControledTemplate( );
489     }
490 
491     /**
492      * Returns whether or not the portal needs authentication
493      * 
494      * @return true if the access needs authentication, otherwise
495      * @since 1.3.1
496      */
497     public boolean isPortalAuthenticationRequired( )
498     {
499         String strAuthenticationRequired = DatastoreService.getDataValue( PROPERTY_PORTAL_AUTHENTICATION_REQUIRED, "false" );
500 
501         return strAuthenticationRequired.equals( "true" );
502     }
503 
504     /**
505      * Checks user's login with the Authentication service. Used during remote authentication validation We don't have to put user informations in session,
506      * since it is only used in external applications
507      * 
508      * @param request
509      *            the request
510      * @param strUserName
511      *            The user's login
512      * @param strPassword
513      *            The user's password
514      * @return user's informations
515      * @throws LoginException
516      *             The LoginException
517      * @throws LoginRedirectException
518      *             The redirect exception
519      */
520     public LuteceUser remoteLoginUser( final HttpServletRequest request, final String strUserName, final String strPassword )
521             throws LoginException, LoginRedirectException
522     {
523         return _authenticationService.login( strUserName, strPassword, request );
524     }
525 
526     /**
527      * Return true if the requested url is equal to LoginUrl
528      * 
529      * @param request
530      *            The Http servlet request
531      * @return True if the requested url is equal to LoginUrl, false else.
532      */
533     public boolean isLoginUrl( HttpServletRequest request )
534     {
535         if ( ( getLoginPageUrl( ) == null ) || ( request == null ) )
536         {
537             return false;
538         }
539 
540         String strRequestUrl = request.getRequestURI( );
541         UrlItem/url/UrlItem.html#UrlItem">UrlItem url = new UrlItem( strRequestUrl );
542 
543         for ( String strParamValueLoginPageUrl : getLoginPageUrl( ).substring( getLoginPageUrl( ).indexOf( URL_INTERROGATIVE ) + 1 ).split( URL_AMPERSAND ) )
544         {
545             String [ ] arrayParamValueLoginPageUrl = strParamValueLoginPageUrl.split( URL_EQUAL );
546             Enumeration<String> enumParams = request.getParameterNames( );
547 
548             while ( enumParams.hasMoreElements( ) )
549             {
550                 String strRequestParameter = enumParams.nextElement( );
551 
552                 if ( arrayParamValueLoginPageUrl [0].equals( strRequestParameter )
553                         && arrayParamValueLoginPageUrl [1].equals( request.getParameter( strRequestParameter ) ) )
554                 {
555                     url.addParameter( strRequestParameter, request.getParameter( strRequestParameter ) );
556                 }
557             }
558         }
559 
560         return url.getUrl( ).endsWith( getLoginPageUrl( ) ) && !getLoginPageUrl( ).equals( "" );
561     }
562 
563     /**
564      * Tells whether or not the authentication service can provide a list of all its users
565      * 
566      * @return true if the service can return a users list
567      */
568     boolean isUsersListAvailable( )
569     {
570         return _authenticationService.isUsersListAvailable( );
571     }
572 
573     /**
574      * Returns all users managed by the authentication service if this feature is available.
575      * 
576      * @return A collection of Lutece users or null if the service doesn't provide a users list
577      */
578     public Collection<LuteceUser> getUsers( )
579     {
580         return _authenticationService.getUsers( );
581     }
582 
583     /**
584      * Returns user managed by the authentication service if this feature is available.
585      * 
586      * @param strUserLogin
587      *            the user login
588      * @return A Lutece user or null if the service doesn't provide LuteceUser
589      */
590     public LuteceUser getUser( String strUserLogin )
591     {
592         return _authenticationService.getUser( strUserLogin );
593     }
594 
595     /**
596      * <b>true</b> when the service provides multi authentication support
597      * 
598      * @return <code>true</code> if multi authentication is supported, <code>false</code> otherwise.
599      */
600     public boolean isMultiAuthenticationSupported( )
601     {
602         return _authenticationService.isMultiAuthenticationSupported( );
603     }
604 
605     /**
606      * Gets the actual authentication implementation
607      * 
608      * @return {@link LuteceAuthentication} implementation
609      */
610     public LuteceAuthentication getAuthenticationService( )
611     {
612         return _authenticationService;
613     }
614 }