View Javadoc
1   /*
2    * Copyright (c) 2002-2021, 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.mylutece.authentication;
35  
36  import fr.paris.lutece.plugins.mylutece.web.MyLuteceApp;
37  import fr.paris.lutece.portal.service.i18n.I18nService;
38  import fr.paris.lutece.portal.service.plugin.Plugin;
39  import fr.paris.lutece.portal.service.plugin.PluginService;
40  import fr.paris.lutece.portal.service.security.LoginRedirectException;
41  import fr.paris.lutece.portal.service.security.LuteceAuthentication;
42  import fr.paris.lutece.portal.service.security.LuteceUser;
43  import fr.paris.lutece.portal.service.util.AppLogService;
44  import fr.paris.lutece.portal.web.LocalVariables;
45  
46  import org.apache.commons.lang3.StringUtils;
47  
48  import java.util.ArrayList;
49  import java.util.Collection;
50  import java.util.HashMap;
51  import java.util.List;
52  import java.util.Map;
53  
54  import javax.security.auth.login.LoginException;
55  
56  import javax.servlet.http.HttpServletRequest;
57  
58  /**
59   * Manages serveral {@link MyLuteceAuthentication}. Call {@link #registerAuthentication(LuteceAuthentication)} to register your authentication and
60   * {@link #removeAuthentication(String)} to unregister it.
61   */
62  public class MultiLuteceAuthentication extends AbstractAuthentication
63  {
64      private static final String PROPERTY_MESSAGE_NO_AUTHENTICATION_SELECTED = "mylutece.message.noAuthenticationSelected";
65      private static final String PARAMETER_AUTH_PROVIDER = "auth_provider";
66      private static final Map<String, LuteceAuthentication> _mapAuthentications = new HashMap<String, LuteceAuthentication>( );
67  
68      /**
69       * Registers an authentication. Should be called at plugin init/install.
70       * 
71       * @param authentication
72       *            the authentication to register.
73       */
74      public static void registerAuthentication( LuteceAuthentication authentication )
75      {
76          AppLogService.info( "MultiLuteceAuthentication : Registering authentication " + authentication.getName( ) );
77          _mapAuthentications.put( authentication.getName( ), authentication );
78      }
79  
80      /**
81       * Removes the authentication from managed authentication
82       * 
83       * @param strAuthenticationName
84       *            the authentication key
85       */
86      public static void removeAuthentication( String strAuthenticationName )
87      {
88          if ( _mapAuthentications.containsKey( strAuthenticationName ) )
89          {
90              AppLogService.info( "MultiLuteceAuthentication : Unregistering authentication " + strAuthenticationName );
91              _mapAuthentications.remove( strAuthenticationName );
92          }
93          else
94          {
95              AppLogService.error( "Unable to remove authentication " + strAuthenticationName + ". Authentication not found. Available values are "
96                      + _mapAuthentications.keySet( ) );
97          }
98      }
99  
100     /**
101      * Returns the Login page URL of the Authentication Service. <br>
102      * Tries to get authentication specific login page url form request (passed through {@link LocalVariables} ), default otherswise.
103      * 
104      * @return The URL authentication specific login page url, default otherswise.
105      */
106     public String getLoginPageUrl( )
107     {
108         String strLoginUrl = MyLuteceApp.getLoginPageUrl( );
109         HttpServletRequest request = LocalVariables.getRequest( );
110 
111         if ( request != null )
112         {
113             String strAuthentication = request.getParameter( PARAMETER_AUTH_PROVIDER );
114 
115             if ( StringUtils.isNotBlank( strAuthentication ) )
116             {
117                 LuteceAuthentication authentication = _mapAuthentications.get( strAuthentication );
118                 strLoginUrl = authentication.getLoginPageUrl( );
119             }
120         }
121 
122         return strLoginUrl;
123     }
124 
125     /**
126      * Returns the DoLogin URL of the Authentication Service. <br>
127      * Tries to get authentication specific dologin page url form request (passed through {@link LocalVariables} ), default otherswise.
128      * 
129      * @return The URL
130      */
131     public String getDoLoginUrl( )
132     {
133         String strLoginUrl = MyLuteceApp.getDoLoginUrl( );
134         HttpServletRequest request = LocalVariables.getRequest( );
135 
136         if ( request != null )
137         {
138             String strAuthentication = request.getParameter( PARAMETER_AUTH_PROVIDER );
139 
140             if ( StringUtils.isNotBlank( strAuthentication ) )
141             {
142                 LuteceAuthentication authentication = _mapAuthentications.get( strAuthentication );
143                 strLoginUrl = authentication.getDoLoginUrl( );
144             }
145         }
146 
147         return strLoginUrl;
148     }
149 
150     /**
151      * Returns the new account page URL of the Authentication Service <br>
152      * Tries to get authentication specific new account page url form request (passed through {@link LocalVariables} ), default otherswise.
153      * 
154      * @return The URL
155      */
156     public String getNewAccountPageUrl( )
157     {
158         String strNewAccountUrl = MyLuteceApp.getNewAccountUrl( );
159         HttpServletRequest request = LocalVariables.getRequest( );
160 
161         if ( request != null )
162         {
163             String strAuthentication = request.getParameter( PARAMETER_AUTH_PROVIDER );
164 
165             if ( StringUtils.isNotBlank( strAuthentication ) )
166             {
167                 LuteceAuthentication authentication = _mapAuthentications.get( strAuthentication );
168                 strNewAccountUrl = authentication.getNewAccountPageUrl( );
169             }
170         }
171 
172         return strNewAccountUrl;
173     }
174 
175     /**
176      * Returns the View account page URL of the Authentication Service <br>
177      * Tries to get authentication specific view account page url form request (passed through {@link LocalVariables} ), default otherswise.
178      * 
179      * @return The URL
180      */
181     public String getViewAccountPageUrl( )
182     {
183         String strViewAccountUrl = MyLuteceApp.getViewAccountUrl( );
184         HttpServletRequest request = LocalVariables.getRequest( );
185 
186         if ( request != null )
187         {
188             String strAuthentication = request.getParameter( PARAMETER_AUTH_PROVIDER );
189 
190             if ( StringUtils.isNotBlank( strAuthentication ) )
191             {
192                 LuteceAuthentication authentication = _mapAuthentications.get( strAuthentication );
193                 strViewAccountUrl = authentication.getViewAccountPageUrl( );
194             }
195         }
196 
197         return strViewAccountUrl;
198     }
199 
200     /**
201      * Returns the lost password URL of the Authentication Service. <br>
202      * Tries to get authentication specific lost password page url form request (passed through {@link LocalVariables} ), default otherswise.
203      * 
204      * @return The URL
205      */
206     public String getLostPasswordPageUrl( )
207     {
208         String strLostPasswordUrl = MyLuteceApp.getLostPasswordUrl( );
209         HttpServletRequest request = LocalVariables.getRequest( );
210 
211         if ( request != null )
212         {
213             String strAuthentication = request.getParameter( PARAMETER_AUTH_PROVIDER );
214 
215             if ( StringUtils.isNotBlank( strAuthentication ) )
216             {
217                 LuteceAuthentication authentication = _mapAuthentications.get( strAuthentication );
218                 strLostPasswordUrl = authentication.getLostPasswordPageUrl( );
219             }
220         }
221 
222         return strLostPasswordUrl;
223     }
224 
225     /**
226      * Returns the lost login URL of the Authentication Service. <br>
227      * Tries to get authentication specific lost login page url from request (passed through {@link LocalVariables} ), default otherwise.
228      * 
229      * @return The URL
230      */
231     @Override
232     public String getLostLoginPageUrl( )
233     {
234         String strLostLoginUrl = MyLuteceApp.getLostPasswordUrl( );
235         HttpServletRequest request = LocalVariables.getRequest( );
236 
237         if ( request != null )
238         {
239             String strAuthentication = request.getParameter( PARAMETER_AUTH_PROVIDER );
240 
241             if ( StringUtils.isNotBlank( strAuthentication ) )
242             {
243                 LuteceAuthentication authentication = _mapAuthentications.get( strAuthentication );
244                 strLostLoginUrl = authentication.getLostLoginPageUrl( );
245             }
246         }
247 
248         return strLostLoginUrl;
249     }
250 
251     /**
252      * {@inheritDoc}
253      */
254     @Override
255     public boolean findResetPassword( HttpServletRequest request, String strLogin )
256     {
257         boolean bResetPasswordActive = false;
258 
259         // HttpServletRequest request = LocalVariables.getRequest( );
260         if ( request != null )
261         {
262             String strAuthentication = request.getParameter( PARAMETER_AUTH_PROVIDER );
263 
264             if ( StringUtils.isNotBlank( strAuthentication ) )
265             {
266                 LuteceAuthentication authentication = _mapAuthentications.get( strAuthentication );
267                 bResetPasswordActive = authentication.findResetPassword( request, strLogin );
268             }
269         }
270 
271         return bResetPasswordActive;
272     }
273 
274     /**
275      * {@inheritDoc}
276      */
277     @Override
278     public String getResetPasswordPageUrl( HttpServletRequest request )
279     {
280         String strResetPasswordUrl = MyLuteceApp.getResetPasswordUrl( request );
281 
282         if ( request != null )
283         {
284             String strAuthentication = request.getParameter( PARAMETER_AUTH_PROVIDER );
285 
286             if ( StringUtils.isNotBlank( strAuthentication ) )
287             {
288                 LuteceAuthentication authentication = _mapAuthentications.get( strAuthentication );
289                 strResetPasswordUrl = authentication.getResetPasswordPageUrl( request );
290             }
291         }
292 
293         return strResetPasswordUrl;
294     }
295 
296     /**
297      * Returns the disconnect URL of the Authentication Service. <br>
298      * Tries to get authentication specific dologout page url form request (passed through {@link LocalVariables} ), default otherswise.
299      * 
300      * @return The URL
301      */
302     public String getDoLogoutUrl( )
303     {
304         String strDoLogoutUrl = MyLuteceApp.getDoLogoutUrl( );
305         HttpServletRequest request = LocalVariables.getRequest( );
306 
307         if ( request != null )
308         {
309             String strAuthentication = request.getParameter( PARAMETER_AUTH_PROVIDER );
310 
311             if ( StringUtils.isNotBlank( strAuthentication ) )
312             {
313                 LuteceAuthentication authentication = _mapAuthentications.get( strAuthentication );
314                 strDoLogoutUrl = authentication.getDoLogoutUrl( );
315             }
316         }
317 
318         return strDoLogoutUrl;
319     }
320 
321     /**
322      * Returns the access denied template
323      * 
324      * @return The template
325      */
326     public String getAccessDeniedTemplate( )
327     {
328         return MyLuteceApp.getAccessDeniedTemplate( );
329     }
330 
331     /**
332      * Returns the access controled template
333      * 
334      * @return The template
335      */
336     public String getAccessControledTemplate( )
337     {
338         return MyLuteceApp.getAccessControledTemplate( );
339     }
340 
341     /**
342      *
343      * {@inheritDoc}
344      */
345     public LuteceUser getAnonymousUser( )
346     {
347         /**
348          * Anonymous user
349          *
350          */
351         final class AnonymousUser extends LuteceUser
352         {
353             AnonymousUser( )
354             {
355                 super( LuteceUser.ANONYMOUS_USERNAME, MultiLuteceAuthentication.this );
356             }
357         }
358 
359         return new AnonymousUser( );
360     }
361 
362     /**
363      *
364      * {@inheritDoc}
365      */
366     public String getAuthServiceName( )
367     {
368         return "Lutece Multi Authentication Service";
369     }
370 
371     /**
372      *
373      * {@inheritDoc}
374      */
375     public String getAuthType( HttpServletRequest request )
376     {
377         return HttpServletRequest.BASIC_AUTH;
378     }
379 
380     /**
381      *
382      * Finds the http authenticated user. <br>
383      * 
384      * @param request
385      *            the reuqest
386      * @return the first successfully recovered user, <code>null</code> otherwise.
387      */
388     public LuteceUser getHttpAuthenticatedUser( HttpServletRequest request )
389     {
390         LuteceUser luteceUser = null;
391 
392         for ( LuteceAuthentication luteceAuthentication : getListLuteceAuthentication( ) )
393         {
394             if ( luteceAuthentication.isExternalAuthentication( ) )
395             {
396                 luteceUser = luteceAuthentication.getHttpAuthenticatedUser( request );
397 
398                 if ( luteceUser != null )
399                 {
400                     break;
401                 }
402             }
403         }
404 
405         return luteceUser;
406     }
407 
408     /**
409      *
410      * {@inheritDoc}
411      */
412     public String [ ] getRolesByUser( LuteceUser user )
413     {
414         LuteceAuthentication userAuthentication = user.getLuteceAuthenticationService( );
415 
416         if ( userAuthentication != null )
417         {
418             return userAuthentication.getRolesByUser( user );
419         }
420 
421         return null;
422     }
423 
424     /**
425      *
426      * Tries to get user from any authentication. <br>
427      * Due to huge calculation, this method should not be called often.
428      * 
429      * @param strUserLogin
430      *            user login
431      * @return the LuteceUser found, <code>null</code> otherwise.
432      */
433     public LuteceUser getUser( String strUserLogin )
434     {
435         // try to get user from any authentication
436         for ( LuteceAuthentication authentication : getListLuteceAuthentication( ) )
437         {
438             LuteceUser user = authentication.getUser( strUserLogin );
439 
440             if ( user != null )
441             {
442                 return user;
443             }
444         }
445 
446         return null;
447     }
448 
449     /**
450      *
451      * Gets all known users from all authentications.
452      * 
453      * @return all kown users list.
454      */
455     public Collection<LuteceUser> getUsers( )
456     {
457         List<LuteceUser> listUsers = new ArrayList<LuteceUser>( );
458 
459         for ( LuteceAuthentication luteceAuthentication : getListLuteceAuthentication( ) )
460         {
461             if ( luteceAuthentication.getUsers( ) != null )
462             {
463 
464                 listUsers.addAll( luteceAuthentication.getUsers( ) );
465 
466             }
467         }
468 
469         return listUsers;
470     }
471 
472     /**
473      *
474      * {@inheritDoc}
475      */
476     public boolean isExternalAuthentication( )
477     {
478         return true;
479     }
480 
481     /**
482      *
483      * {@inheritDoc}
484      */
485     public boolean isUserInRole( LuteceUser user, HttpServletRequest request, String strRole )
486     {
487         if ( user == null )
488         {
489             return false;
490         }
491 
492         LuteceAuthentication authentication = user.getLuteceAuthenticationService( );
493 
494         if ( authentication != null )
495         {
496             return authentication.isUserInRole( user, request, strRole );
497         }
498 
499         return false;
500     }
501 
502     /**
503      *
504      * Returns false. User list should not be directly recovered, due to use CPU usage.
505      * 
506      * @return false.
507      */
508     public boolean isUsersListAvailable( )
509     {
510         return false;
511     }
512 
513     /**
514      *
515      * {@inheritDoc}
516      */
517     public LuteceUser login( String strUserName, String strUserPassword, HttpServletRequest request ) throws LoginException, LoginRedirectException
518     {
519         LuteceUser luteceUser = null;
520         String strAuthProvider = request.getParameter( PARAMETER_AUTH_PROVIDER );
521 
522         if ( strAuthProvider != null )
523         {
524             LuteceAuthentication myLuteceAuthentication = _mapAuthentications.get( strAuthProvider );
525 
526             if ( myLuteceAuthentication != null )
527             {
528                 if ( AppLogService.isDebugEnabled( ) )
529                 {
530                     AppLogService.debug( "Using " + myLuteceAuthentication.getAuthServiceName( ) + " for user " + strUserName );
531                 }
532 
533                 luteceUser = myLuteceAuthentication.login( strUserName, strUserPassword, request );
534             }
535             else
536             {
537                 AppLogService.error( "Authentication null for key " + strAuthProvider );
538                 throw new LoginException( I18nService.getLocalizedString( PROPERTY_MESSAGE_NO_AUTHENTICATION_SELECTED, request.getLocale( ) ) );
539             }
540         }
541         else
542         {
543             throw new LoginException( I18nService.getLocalizedString( PROPERTY_MESSAGE_NO_AUTHENTICATION_SELECTED, request.getLocale( ) ) );
544         }
545 
546         return luteceUser;
547     }
548 
549     /**
550      *
551      * {@inheritDoc}
552      */
553     public void logout( LuteceUser user )
554     {
555         if ( user != null )
556         {
557             LuteceAuthentication luteceAuthentication = user.getLuteceAuthenticationService( );
558 
559             if ( luteceAuthentication != null )
560             {
561                 luteceAuthentication.logout( user );
562             }
563             else
564             {
565                 AppLogService.error( "No auth provider found for " + user.getName( ) + ". Brute force logout." );
566 
567                 for ( LuteceAuthentication authentication : _mapAuthentications.values( ) )
568                 {
569                     authentication.logout( user );
570                 }
571             }
572         }
573         else
574         {
575             AppLogService.error( "Tried to logout null user" );
576         }
577     }
578 
579     /**
580      *
581      * {@inheritDoc}
582      */
583     public boolean isMultiAuthenticationSupported( )
584     {
585         return true;
586     }
587 
588     /**
589      * Gets the authentication by its key
590      * 
591      * @param strKey
592      *            the key
593      * @return the {@link LuteceAuthentication} found, <code>null</code> otherwise.
594      */
595     public LuteceAuthentication getLuteceAuthentication( String strKey )
596     {
597         return _mapAuthentications.get( strKey );
598     }
599 
600     /**
601      *
602      * {@inheritDoc}
603      */
604     public boolean isDelegatedAuthentication( )
605     {
606         return false;
607     }
608 
609     /**
610      * Returns all known security authentication services
611      * 
612      * @return all known security authentication services
613      */
614     public List<LuteceAuthentication> getListLuteceAuthentication( )
615     {
616         List<LuteceAuthentication> listAuthentications = new ArrayList<LuteceAuthentication>( );
617 
618         for ( LuteceAuthentication authentication : _mapAuthentications.values( ) )
619         {
620             Plugin plugin = PluginService.getPlugin( authentication.getPluginName( ) );
621 
622             if ( ( plugin != null ) && plugin.isInstalled( ) )
623             {
624                 listAuthentications.add( authentication );
625             }
626             else
627                 if ( AppLogService.isDebugEnabled( ) )
628                 {
629                     AppLogService.debug( "Authentication : Plugin not found or not installed for plugin name " + authentication.getPluginName( ) );
630                 }
631         }
632 
633         return listAuthentications;
634     }
635 
636     /**
637      * No icon directlty shown for this authentication.
638      * 
639      * @return icon url
640      */
641     public String getIconUrl( )
642     {
643         return null;
644     }
645 
646     /**
647      *
648      * Always <code>null</code>, not supposed to be identifiable
649      * 
650      * @return null
651      */
652     public String getName( )
653     {
654         return null;
655     }
656 
657     /**
658      *
659      * Always <code>null</code>, this implementation is not plugin related.
660      * 
661      * @return null
662      */
663     public String getPluginName( )
664     {
665         return null;
666     }
667 
668     /**
669      * {@inheritDoc}
670      */
671     @Override
672     public void updateDateLastLogin( LuteceUser user, HttpServletRequest request )
673     {
674         if ( request != null )
675         {
676             String strAuthentication = request.getParameter( PARAMETER_AUTH_PROVIDER );
677 
678             if ( StringUtils.isNotBlank( strAuthentication ) )
679             {
680                 LuteceAuthentication authentication = _mapAuthentications.get( strAuthentication );
681                 authentication.updateDateLastLogin( user, request );
682             }
683         }
684     }
685 }