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.modules.openam.service;
35  
36  import java.util.ArrayList;
37  import java.util.Enumeration;
38  import java.util.HashMap;
39  import java.util.List;
40  import java.util.Map;
41  import java.util.Map.Entry;
42  
43  import javax.servlet.http.Cookie;
44  import javax.servlet.http.HttpServletRequest;
45  import javax.servlet.http.HttpServletResponse;
46  
47  import org.apache.commons.lang3.StringUtils;
48  
49  import fr.paris.lutece.plugins.mylutece.authentication.MultiLuteceAuthentication;
50  import fr.paris.lutece.plugins.mylutece.modules.openam.authentication.OpenamAuthentication;
51  import fr.paris.lutece.plugins.mylutece.modules.openam.authentication.OpenamUser;
52  import fr.paris.lutece.portal.service.spring.SpringContextService;
53  import fr.paris.lutece.portal.service.util.AppPropertiesService;
54  
55  /**
56   *
57   * OpenamService
58   */
59  public final class OpenamService
60  {
61      public static final String ERROR_ALREADY_SUBSCRIBE = "ALREADY_SUBSCRIBE";
62      public static final String ERROR_DURING_SUBSCRIBE = "ERROR_DURING_SUBSCRIBE";
63      private static final String AUTHENTICATION_BEAN_NAME = "mylutece-openam.authentication";
64      private static boolean _bAgentEnable;
65      private static OpenamService _singleton;
66      private static final String PROPERTY_AGENT_ENABLE = "mylutece-openam.agentEnable";
67      private static final String PROPERTY_COOKIE_OPENAM_NAME = "mylutece-openam.cookieName";
68      private static final String PROPERTY_COOKIE_OPENAM_DOMAIN = "mylutece-openam.cookieDomain";
69      private static final String PROPERTY_COOKIE_OPENAM_PATH = "mylutece-openam.cookiePath";
70      private static final String PROPERTY_COOKIE_OPENAM_MAX_AGE = "mylutece-openam.cookieMaxAge";
71      private static final String PROPERTY_COOKIE_OPENAM_MAX_SECURE = "mylutece-openam.cookieSecure";
72      public static final String PROPERTY_USER_KEY_NAME = "mylutece-openam.attributeKeyUsername";
73      public static final String PROPERTY_USER_MAPPING_ATTRIBUTES = "mylutece-openam.userMappingAttributes";
74      public static final String CONSTANT_LUTECE_USER_PROPERTIES_PATH = "mylutece-openam.attribute";
75      private static String COOKIE_OPENAM_NAME;
76      private static String COOKIE_OPENAM_DOMAIN;
77      private static String COOKIE_OPENAM_PATH;
78      private static int COOKIE_OPENAM_MAX_AGE;
79      private static boolean COOKIE_OPENAM_SECURE;
80      private static final String SEPARATOR = ",";
81      private static Map<String, List<String>> ATTRIBUTE_USER_MAPPING;
82      private static String ATTRIBUTE_USER_KEY_NAME;
83  
84      /**
85       * Empty constructor
86       */
87      private OpenamService( )
88      {
89          // nothing
90      }
91  
92      /**
93       * Gets the instance
94       *
95       * @return the instance
96       */
97      public static OpenamService getInstance( )
98      {
99          if ( _singleton == null )
100         {
101             _singleton = new OpenamService( );
102             COOKIE_OPENAM_NAME = AppPropertiesService.getProperty( PROPERTY_COOKIE_OPENAM_NAME );
103             COOKIE_OPENAM_DOMAIN = AppPropertiesService.getProperty( PROPERTY_COOKIE_OPENAM_DOMAIN );
104             COOKIE_OPENAM_PATH = AppPropertiesService.getProperty( PROPERTY_COOKIE_OPENAM_PATH );
105             COOKIE_OPENAM_MAX_AGE = AppPropertiesService.getPropertyInt( PROPERTY_COOKIE_OPENAM_MAX_AGE, 60 * 30 );
106             COOKIE_OPENAM_SECURE = AppPropertiesService.getPropertyBoolean( PROPERTY_COOKIE_OPENAM_MAX_SECURE, true );
107 
108             ATTRIBUTE_USER_KEY_NAME = AppPropertiesService.getProperty( PROPERTY_USER_KEY_NAME );
109 
110             String strUserMappingAttributes = AppPropertiesService.getProperty( PROPERTY_USER_MAPPING_ATTRIBUTES );
111             ATTRIBUTE_USER_MAPPING = new HashMap<String, List<String>>( );
112 
113             if ( StringUtils.isNotBlank( strUserMappingAttributes ) )
114             {
115                 String [ ] tabUserProperties = strUserMappingAttributes.split( SEPARATOR );
116                 String userProperties;
117 
118                 for ( int i = 0; i < tabUserProperties.length; i++ )
119                 {
120                     userProperties = AppPropertiesService.getProperty( CONSTANT_LUTECE_USER_PROPERTIES_PATH + "." + tabUserProperties [i] );
121 
122                     if ( StringUtils.isNotBlank( userProperties ) )
123                     {
124                         if ( !ATTRIBUTE_USER_MAPPING.containsKey( userProperties ) )
125                         {
126                             ATTRIBUTE_USER_MAPPING.put( userProperties, new ArrayList<String>( ) );
127                         }
128                         ATTRIBUTE_USER_MAPPING.get( userProperties ).add( tabUserProperties [i] );
129 
130                     }
131                 }
132             }
133         }
134 
135         return _singleton;
136     }
137 
138     /**
139      * Inits plugin. Registers authentication
140      */
141     public void init( )
142     {
143         _bAgentEnable = AppPropertiesService.getPropertyBoolean( PROPERTY_AGENT_ENABLE, false );
144 
145         OpenamAuthentication./fr/paris/lutece/plugins/mylutece/modules/openam/authentication/OpenamAuthentication.html#OpenamAuthentication">OpenamAuthentication authentication = (OpenamAuthentication) SpringContextService.getPluginBean( OpenamPlugin.PLUGIN_NAME, AUTHENTICATION_BEAN_NAME );
146 
147         if ( authentication != null )
148         {
149             MultiLuteceAuthentication.registerAuthentication( authentication );
150         }
151         else
152         {
153             OpenamAPI._logger.error( "OpenamAuthentication not found, please check your openam_context.xml configuration" );
154         }
155     }
156 
157     /**
158      * Process login
159      *
160      * @param request
161      *            The HTTP request
162      * @param strUserName
163      *            The user's name
164      * @param strUserPassword
165      *            The user's password
166      * @param openamAuthentication
167      *            The authentication
168      * @return The LuteceUser
169      */
170     public OpenamUser doLogin( HttpServletRequest request, String strUserName, String strUserPassword, OpenamAuthentication openamAuthentication )
171             throws OpenamAuthenticationAgentException
172     {
173         String strTokenId;
174         OpenamUser user = null;
175 
176         Map<String, String> headerUserInformations = null;
177 
178         if ( isAgentEnabled( ) )
179         {
180             headerUserInformations = getUserInformationInHeaderRequest( request );
181 
182             if ( ( headerUserInformations != null ) && !headerUserInformations.isEmpty( ) && headerUserInformations.containsKey( ATTRIBUTE_USER_KEY_NAME ) )
183             {
184                 user = new OpenamUser( headerUserInformations.get( ATTRIBUTE_USER_KEY_NAME ), openamAuthentication, getConnectionCookie( request ) );
185                 addUserAttributes( headerUserInformations, user );
186             }
187             else
188             {
189                 throw new OpenamAuthenticationAgentException( );
190             }
191         }
192 
193         else
194         {
195             try
196             {
197                 strTokenId = OpenamAPIService.doLogin( strUserName, strUserPassword );
198 
199                 if ( strTokenId != null )
200                 {
201                     Map<String, String> userInformations = OpenamAPIService.getUserInformations( strTokenId, strUserName, COOKIE_OPENAM_NAME,
202                             ATTRIBUTE_USER_MAPPING, ATTRIBUTE_USER_KEY_NAME );
203 
204                     // test contains guid
205                     if ( ( userInformations != null ) && userInformations.containsKey( ATTRIBUTE_USER_KEY_NAME ) )
206                     {
207                         user = new OpenamUser( userInformations.get( ATTRIBUTE_USER_KEY_NAME ), openamAuthentication, strTokenId );
208                         addUserAttributes( userInformations, user );
209                     }
210                 }
211             }
212             catch( OpenamAPIException ex )
213             {
214                 OpenamAPI._logger.error( "Error During Login Openam" + ex.getMessage( ) );
215             }
216         }
217 
218         return user;
219     }
220 
221     /**
222      * Logout to openam
223      *
224      * @param user
225      *            the User
226      */
227     public void doLogout( OpenamUser user )
228     {
229         try
230         {
231             OpenamAPIService.doDisconnect( COOKIE_OPENAM_NAME, user.getSubjectId( ) );
232         }
233         catch( OpenamAPIException ex )
234         {
235             OpenamAPI._logger.error( "Error During Logout Openam" + ex.getMessage( ) );
236         }
237     }
238 
239     /**
240      * Gets the authenticated user
241      *
242      * @param request
243      *            The HTTP request
244      * @param openamAuthentication
245      *            The Authentication
246      * @return The LuteceUser
247      */
248     public OpenamUser getHttpAuthenticatedUser( HttpServletRequest request, OpenamAuthentication openamAuthentication )
249     {
250         OpenamUser user = null;
251         Map<String, String> headerUserInformations = null;
252 
253         if ( isAgentEnabled( ) )
254         {
255             headerUserInformations = getUserInformationInHeaderRequest( request );
256         }
257 
258         if ( ( headerUserInformations != null ) && !headerUserInformations.isEmpty( ) && headerUserInformations.containsKey( ATTRIBUTE_USER_KEY_NAME ) )
259         {
260             user = new OpenamUser( headerUserInformations.get( ATTRIBUTE_USER_KEY_NAME ), openamAuthentication, getConnectionCookie( request ) );
261             addUserAttributes( headerUserInformations, user );
262         }
263         else
264         {
265             String strTokenId = getConnectionCookie( request );
266 
267             if ( !StringUtils.isEmpty( strTokenId ) )
268             {
269                 try
270                 {
271                     String strUserId = OpenamAPIService.isValidate( strTokenId );
272 
273                     if ( strUserId != null )
274                     {
275                         Map<String, String> userInformations = OpenamAPIService.getUserInformations( strTokenId, strUserId, COOKIE_OPENAM_NAME,
276                                 ATTRIBUTE_USER_MAPPING, ATTRIBUTE_USER_KEY_NAME );
277 
278                         // test contains guid
279                         if ( ( userInformations != null ) && userInformations.containsKey( ATTRIBUTE_USER_KEY_NAME ) )
280                         {
281                             user = new OpenamUser( userInformations.get( ATTRIBUTE_USER_KEY_NAME ), openamAuthentication, strTokenId );
282                             addUserAttributes( userInformations, user );
283                         }
284                     }
285                 }
286                 catch( OpenamAPIException ex )
287                 {
288                     OpenamAPI._logger.error( "Error getting Openam user Informations" + ex.getMessage( ) );
289                 }
290             }
291         }
292 
293         return user;
294     }
295 
296     /**
297      * Extract the value of the connection cookie
298      *
299      * @param request
300      *            The HTTP request
301      * @return The cookie's value
302      */
303     public String getConnectionCookie( HttpServletRequest request )
304     {
305         Cookie [ ] cookies = request.getCookies( );
306         String strOpenamCookie = null;
307 
308         if ( cookies != null )
309         {
310             for ( Cookie cookie : cookies )
311             {
312                 if ( cookie.getName( ).equals( COOKIE_OPENAM_NAME ) )
313                 {
314                     strOpenamCookie = cookie.getValue( );
315                     OpenamAPI._logger.debug( "getHttpAuthenticatedUser : cookie '" + COOKIE_OPENAM_NAME + "' found - value=" + strOpenamCookie );
316                 }
317             }
318         }
319 
320         return strOpenamCookie;
321     }
322 
323     /**
324      * true if the token is validated
325      * 
326      * @param strTokenId
327      *            the token id
328      * @return true if the token is validated
329      */
330     public boolean isTokenValidated( String strTokenId )
331     {
332 
333         if ( !StringUtils.isEmpty( strTokenId ) )
334         {
335             try
336             {
337                 String strUserId = OpenamAPIService.isValidate( strTokenId );
338                 return !StringUtils.isEmpty( strUserId );
339 
340             }
341             catch( OpenamAPIException ex )
342             {
343                 OpenamAPI._logger.error( "Error getting Openam user Informations" + ex.getMessage( ) );
344             }
345         }
346 
347         return false;
348 
349     }
350 
351     /**
352      * set a paris connect cokkie in the HttpServletResponse
353      *
354      * @param strPCUID
355      *            the user PCUID
356      * @param response
357      *            The HTTP response
358      */
359     public void setConnectionCookie( String strPCUID, HttpServletResponse response )
360     {
361         // set a connexion cookie to let the user access other PC Services
362         // without sign in
363         Cookie openamCookie = new Cookie( COOKIE_OPENAM_NAME, strPCUID );
364         openamCookie.setDomain( COOKIE_OPENAM_DOMAIN );
365         openamCookie.setSecure( COOKIE_OPENAM_SECURE );
366         openamCookie.setMaxAge( COOKIE_OPENAM_MAX_AGE );
367         openamCookie.setPath( COOKIE_OPENAM_PATH );
368 
369         response.addCookie( openamCookie );
370     }
371 
372     /**
373      * set a paris connect cokkie in the HttpServletResponse
374      *
375      * @param strPCUID
376      *            the user PCUID
377      * @param response
378      *            The HTTP response
379      */
380     public void removeConnectionCookie( HttpServletResponse response )
381     {
382         // remove openam cookie using the setMaxAgeParameters
383         Cookie openamCookie = new Cookie( COOKIE_OPENAM_NAME, null );
384         openamCookie.setDomain( COOKIE_OPENAM_DOMAIN );
385         openamCookie.setSecure( COOKIE_OPENAM_SECURE );
386         openamCookie.setMaxAge( 0 );
387         openamCookie.setPath( COOKIE_OPENAM_PATH );
388         response.addCookie( openamCookie );
389     }
390 
391     /**
392      * Fill user's data
393      *
394      * @param user
395      *            The User
396      * @param strUserData
397      *            Data in JSON format
398      */
399     private void addUserAttributes( Map<String, String> userInformations, OpenamUser user )
400     {
401         for ( Entry<String, String> entry : userInformations.entrySet( ) )
402         {
403             if ( ATTRIBUTE_USER_MAPPING.containsKey( entry.getKey( ) ) )
404             {
405                 for ( String strUserInfo : ATTRIBUTE_USER_MAPPING.get( entry.getKey( ) ) )
406                 {
407                     user.setUserInfo( strUserInfo, entry.getValue( ) );
408                 }
409             }
410         }
411 
412         Map<String, String> mapIdentitiesInformations;
413         // Add Identities Informations
414         mapIdentitiesInformations = getIdentityInformations( user.getName( ), ATTRIBUTE_USER_MAPPING );
415         // Add All Identities Informations in the lutece user attributes map
416         user.getUserInfos( ).putAll( mapIdentitiesInformations );
417         if ( mapIdentitiesInformations != null )
418         {
419             for ( Entry<String, String> entry : mapIdentitiesInformations.entrySet( ) )
420             {
421                 if ( ATTRIBUTE_USER_MAPPING.containsKey( entry.getKey( ) ) )
422                 {
423                     for ( String strUserInfo : ATTRIBUTE_USER_MAPPING.get( entry.getKey( ) ) )
424                     {
425                         user.setUserInfo( strUserInfo, entry.getValue( ) );
426                     }
427                 }
428             }
429 
430             userInformations.putAll( mapIdentitiesInformations );
431 
432         }
433     }
434 
435     /**
436      *
437      * @return true if the user Agent is enabled
438      */
439     private boolean isAgentEnabled( )
440     {
441         return _bAgentEnable;
442     }
443 
444     private Map<String, String> getUserInformationInHeaderRequest( HttpServletRequest request )
445     {
446         Map<String, String> userInformations = new HashMap<String, String>( );
447         Enumeration headerNames = request.getHeaderNames( );
448 
449         String strKey;
450 
451         while ( headerNames.hasMoreElements( ) )
452         {
453             strKey = (String) headerNames.nextElement( );
454 
455             if ( ATTRIBUTE_USER_MAPPING.containsKey( strKey ) || ATTRIBUTE_USER_KEY_NAME.equals( strKey ) )
456             {
457                 userInformations.put( strKey, request.getHeader( strKey ) );
458             }
459         }
460 
461         if ( OpenamAPI._bDebug )
462         {
463             headerNames = request.getHeaderNames( );
464             OpenamAPI._logger.debug( "Openam Headers Informations" );
465 
466             while ( headerNames.hasMoreElements( ) )
467             {
468                 strKey = (String) headerNames.nextElement( );
469                 OpenamAPI._logger.debug( strKey + "=" + request.getHeader( strKey ) );
470             }
471         }
472 
473         return userInformations;
474     }
475 
476     public Map<String, String> getIdentityInformations( String strName, Map<String, List<String>> attributeUserMapping )
477     {
478         for ( IIdentityProviderService identityProviderService : SpringContextService.getBeansOfType( IIdentityProviderService.class ) )
479         {
480             return identityProviderService.getIdentityInformations( strName );
481         }
482 
483         return new HashMap<String, String>( );
484     }
485 }