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.plugins.mylutece.modules.oauth.authentication;
35  
36  import fr.paris.lutece.plugins.mylutece.authentication.PortalAuthentication;
37  import fr.paris.lutece.plugins.mylutece.modules.oauth.service.OAuthPlugin;
38  import fr.paris.lutece.portal.service.security.LoginRedirectException;
39  import fr.paris.lutece.portal.service.security.LuteceUser;
40  import fr.paris.lutece.portal.service.util.AppException;
41  import fr.paris.lutece.portal.service.util.AppLogService;
42  import fr.paris.lutece.portal.service.util.AppPathService;
43  import fr.paris.lutece.portal.service.util.AppPropertiesService;
44  import fr.paris.lutece.portal.web.LocalVariables;
45  
46  import oauth.signpost.OAuth;
47  import oauth.signpost.OAuthConsumer;
48  import oauth.signpost.OAuthProvider;
49  
50  import oauth.signpost.commonshttp.CommonsHttpOAuthConsumer;
51  import oauth.signpost.commonshttp.CommonsHttpOAuthProvider;
52  
53  import oauth.signpost.exception.OAuthCommunicationException;
54  import oauth.signpost.exception.OAuthExpectationFailedException;
55  import oauth.signpost.exception.OAuthMessageSignerException;
56  import oauth.signpost.exception.OAuthNotAuthorizedException;
57  
58  import org.apache.commons.lang.StringUtils;
59  
60  import org.apache.http.HttpHost;
61  import org.apache.http.HttpResponse;
62  import org.apache.http.HttpStatus;
63  import org.apache.http.auth.AuthScope;
64  import org.apache.http.auth.Credentials;
65  import org.apache.http.auth.NTCredentials;
66  import org.apache.http.auth.UsernamePasswordCredentials;
67  import org.apache.http.client.ClientProtocolException;
68  import org.apache.http.client.CredentialsProvider;
69  import org.apache.http.client.HttpClient;
70  import org.apache.http.client.methods.HttpGet;
71  import org.apache.http.conn.params.ConnRoutePNames;
72  import org.apache.http.impl.client.BasicCredentialsProvider;
73  import org.apache.http.impl.client.DefaultHttpClient;
74  
75  import java.io.IOException;
76  
77  import javax.security.auth.login.LoginException;
78  
79  import javax.servlet.http.Cookie;
80  import javax.servlet.http.HttpServletRequest;
81  
82  
83  /**
84   *
85   * OAuthAuthentication. (version 1.0a). <br>
86   * Uses <strong>httpAccess.*</strong> properties to perform HTTP requests.
87   * @see <a href="http://oauth.net/">oauth.net</a>
88   */
89  public class OAuthAuthentication extends PortalAuthentication
90  {
91      private static final String PROPERTY_PROXY_HOST = "httpAccess.proxyHost";
92      private static final String PROPERTY_PROXY_PORT = "httpAccess.proxyPort";
93      private static final String PROPERTY_PROXY_USERNAME = "httpAccess.proxyUserName";
94      private static final String PROPERTY_PROXY_PASSWORD = "httpAccess.proxyPassword";
95      private static final String PROPERTY_DOMAIN_NAME = "httpAccess.domainName";
96      private static final String PARAMETER_OAUTH_VERIFIER = OAuth.OAUTH_VERIFIER;
97      private static final String PARAMETER_OAUTH_TOKEN = OAuth.OAUTH_TOKEN;
98      private static final String CONSTANT_CALLBACK_URL = "jsp/site/plugins/mylutece/modules/oauth/Redirect.jsp?auth_provider=";
99  
100     /**
101      * A cookie will be created for a token request, to store the token secret, since Token Secret is not passed through callback url.
102      */
103     private static final String CONSTANT_COOKIE_TOKEN_SECRET = "oauth_token_secret";
104     private String _strAuthServiceName;
105     private String _strName;
106     private String _strIconUrl;
107     private String _strRequestTokenEndpointUrl;
108     private String _strAccessTokenEndpointUrl;
109     private String _strAuthorizeWebsiteUrl;
110     private String _strConsumerKey;
111     private String _strConsumerSecret;
112     private String _strCredentialUrl;
113     private String _strCredentialFormat;
114 
115     /**
116      *
117      *{@inheritDoc}
118      */
119     public LuteceUser getAnonymousUser(  )
120     {
121         return new OAuthUser( LuteceUser.ANONYMOUS_USERNAME, this );
122     }
123 
124     /**
125      *
126      *{@inheritDoc}
127      */
128     public String getAuthServiceName(  )
129     {
130         return _strAuthServiceName;
131     }
132 
133     /**
134      *
135      *{@inheritDoc}
136      */
137     public String getAuthType( HttpServletRequest request )
138     {
139         return HttpServletRequest.BASIC_AUTH;
140     }
141 
142     /**
143      *
144      *{@inheritDoc}
145      */
146     public String getIconUrl(  )
147     {
148         return _strIconUrl;
149     }
150 
151     /**
152      *
153      *{@inheritDoc}
154      */
155     public String getName(  )
156     {
157         return _strName;
158     }
159 
160     /**
161      *
162      *{@inheritDoc}
163      */
164     public String getPluginName(  )
165     {
166         return OAuthPlugin.PLUGIN_NAME;
167     }
168 
169     /**
170      *
171      *{@inheritDoc}
172      */
173     public boolean isUserInRole( LuteceUser user, HttpServletRequest request, String strRole )
174     {
175         return false;
176     }
177 
178     /**
179      * Builds a new {@link HttpClient}
180      * @return new HttpClient
181      */
182     private HttpClient getHttpClient(  )
183     {
184         DefaultHttpClient client = new DefaultHttpClient(  );
185 
186         String strUserName = AppPropertiesService.getProperty( PROPERTY_PROXY_USERNAME );
187         String strPassword = AppPropertiesService.getProperty( PROPERTY_PROXY_PASSWORD );
188         String strDomainName = AppPropertiesService.getProperty( PROPERTY_DOMAIN_NAME );
189 
190         if ( StringUtils.isNotBlank( strUserName ) && StringUtils.isNotBlank( strPassword ) )
191         {
192             // at least Userpasswordcredz
193             Credentials creds;
194 
195             if ( StringUtils.isBlank( strDomainName ) )
196             {
197                 creds = new UsernamePasswordCredentials( strUserName, strPassword );
198             }
199             else
200             {
201                 creds = new NTCredentials( strUserName, strPassword, "", strDomainName );
202             }
203 
204             CredentialsProvider credsProvider = new BasicCredentialsProvider(  );
205             credsProvider.setCredentials( AuthScope.ANY, creds );
206             client.setCredentialsProvider( credsProvider );
207 
208             HttpHost proxy = new HttpHost( AppPropertiesService.getProperty( PROPERTY_PROXY_HOST ),
209                     AppPropertiesService.getPropertyInt( PROPERTY_PROXY_PORT, 8080 ) );
210             client.getParams(  ).setParameter( ConnRoutePNames.DEFAULT_PROXY, proxy );
211         }
212 
213         return client;
214     }
215 
216     /**
217      * Gets the request token url. Builds the provider. CallBack url is {@link #CONSTANT_CALLBACK_URL}
218      * @param request the request
219      * @param consumer the consumer
220      * @return the redirect url
221      * @throws OAuthMessageSignerException if occurs
222      * @throws OAuthNotAuthorizedException if occurs
223      * @throws OAuthExpectationFailedException if occurs
224      * @throws OAuthCommunicationException if occurs
225      */
226     private String getRedirectUrl( HttpServletRequest request, OAuthConsumer consumer )
227         throws OAuthMessageSignerException, OAuthNotAuthorizedException, OAuthExpectationFailedException,
228             OAuthCommunicationException
229     {
230         return getProvider(  )
231                    .retrieveRequestToken( consumer,
232             AppPathService.getBaseUrl( request ) + CONSTANT_CALLBACK_URL + this.getName(  ) );
233     }
234 
235     /**
236      * Builds a provider
237      * @return the provider
238      */
239     private OAuthProvider getProvider(  )
240     {
241         CommonsHttpOAuthProvider provider = new CommonsHttpOAuthProvider( getRequestTokenEndpointUrl(  ),
242                 getAccessTokenEndpointUrl(  ), getAuthorizeWebsiteUrl(  ) );
243 
244         provider.setHttpClient( getHttpClient(  ) );
245 
246         return provider;
247     }
248 
249     /**
250      * Buids a consumer
251      * @return the {@link OAuthConsumer}
252      */
253     private OAuthConsumer getConsumer(  )
254     {
255         return new CommonsHttpOAuthConsumer( getConsumerKey(  ), getConsumerSecret(  ) );
256     }
257 
258     /**
259      *
260      *{@inheritDoc}
261      */
262     @Override
263     public boolean isDelegatedAuthentication(  )
264     {
265         return true;
266     }
267 
268     /**
269      *
270      *{@inheritDoc}
271      */
272     public LuteceUser login( String strUserName, String strUserPassword, HttpServletRequest request )
273         throws LoginException, LoginRedirectException
274     {
275         OAuthConsumer consumer = getConsumer(  );
276 
277         try
278         {
279             // FIXME : version specific
280             String url = getRedirectUrl( request, consumer );
281 
282             if ( AppLogService.isDebugEnabled(  ) )
283             {
284                 AppLogService.debug( "Url to visit : " + url );
285                 AppLogService.debug( "Token secret : " + consumer.getTokenSecret(  ) );
286             }
287 
288             // create cookie to store token secret
289             createCookie( CONSTANT_COOKIE_TOKEN_SECRET, consumer.getTokenSecret(  ) );
290 
291             throw new LoginRedirectException( url );
292         }
293         catch ( OAuthMessageSignerException e )
294         {
295             AppLogService.error( e.getMessage(  ), e );
296         }
297         catch ( OAuthExpectationFailedException e )
298         {
299             AppLogService.error( e.getMessage(  ), e );
300         }
301         catch ( OAuthCommunicationException e )
302         {
303             AppLogService.error( e.getMessage(  ), e );
304         }
305         catch ( OAuthNotAuthorizedException e )
306         {
307             AppLogService.error( e.getMessage(  ), e );
308         }
309 
310         return null;
311     }
312 
313     /**
314      * Gets the user
315      * @param request the reuqest
316      * @return the user found.
317      */
318     public OAuthUser getUser( HttpServletRequest request )
319     {
320         String strToken = request.getParameter( PARAMETER_OAUTH_TOKEN );
321         String strVerifier = request.getParameter( PARAMETER_OAUTH_VERIFIER );
322 
323         if ( StringUtils.isNotBlank( strToken ) && StringUtils.isNotBlank( strVerifier ) )
324         {
325             // FIXME : version specific
326             Cookie cookie = findCookie( request, CONSTANT_COOKIE_TOKEN_SECRET );
327 
328             OAuthConsumer consumer = getConsumer(  );
329             consumer.setTokenWithSecret( strToken, cookie.getValue(  ) );
330 
331             try
332             {
333                 OAuthProvider provider = getProvider(  );
334                 provider.setOAuth10a( true );
335                 provider.retrieveAccessToken( consumer, strVerifier );
336 
337                 // FIXME :  username.
338                 OAuthUser user = new OAuthUser( this.getName(  ), this );
339                 user.setToken( strToken );
340                 user.setVerifier( strVerifier );
341                 user.setTokenSecret( cookie.getValue(  ) );
342 
343                 // get user infos
344                 if ( StringUtils.isNotBlank( getCredentialUrl(  ) ) )
345                 {
346                     HttpGet getMethod = new HttpGet( getCredentialUrl(  ) );
347 
348                     consumer.sign( getMethod );
349 
350                     HttpResponse httpResponse = getHttpClient(  ).execute( getMethod );
351 
352                     int nStatusCode = httpResponse.getStatusLine(  ).getStatusCode(  );
353 
354                     if ( nStatusCode == HttpStatus.SC_OK )
355                     {
356                         OAuthCredentialsRetrieverUtils.doRetrieveUserInfo( httpResponse, user, getCredentialFormat(  ) );
357                     }
358                     else
359                     {
360                         AppLogService.error( httpResponse.getStatusLine(  ) );
361                         throw new AppException( "Unable to find user infos : " + httpResponse.getStatusLine(  ) );
362                     }
363                 }
364 
365                 return user;
366             }
367             catch ( OAuthMessageSignerException e )
368             {
369                 AppLogService.error( e.getMessage(  ), e );
370             }
371             catch ( OAuthNotAuthorizedException e )
372             {
373                 AppLogService.error( e.getMessage(  ), e );
374             }
375             catch ( OAuthExpectationFailedException e )
376             {
377                 AppLogService.error( e.getMessage(  ), e );
378             }
379             catch ( OAuthCommunicationException e )
380             {
381                 AppLogService.error( e.getMessage(  ), e );
382             }
383             catch ( ClientProtocolException e )
384             {
385                 AppLogService.error( e.getMessage(  ), e );
386             }
387             catch ( IOException e )
388             {
389                 AppLogService.error( e.getMessage(  ), e );
390             }
391             catch ( IllegalStateException e )
392             {
393                 AppLogService.error( e.getMessage(  ), e );
394             }
395         }
396 
397         return null;
398     }
399 
400     /**
401      * Finds a cookie by its name
402      * @param request the request
403      * @param strKey the cookie name
404      * @return the cookie found, <code>null</code> otherwise.
405      */
406     private Cookie findCookie( HttpServletRequest request, String strKey )
407     {
408         if ( StringUtils.isBlank( strKey ) )
409         {
410             return null;
411         }
412 
413         for ( Cookie cookie : request.getCookies(  ) )
414         {
415             if ( strKey.equals( cookie.getName(  ) ) )
416             {
417                 return cookie;
418             }
419         }
420 
421         return null;
422     }
423 
424     /**
425      * Creates a cookie
426      * @param strKey the cookie name
427      * @param strValue the value
428      */
429     private void createCookie( String strKey, String strValue )
430     {
431         Cookie cookie = new Cookie( strKey, strValue );
432         LocalVariables.getResponse(  ).addCookie( cookie );
433     }
434 
435     /**
436      *
437      *{@inheritDoc}
438      */
439     public void logout( LuteceUser user )
440     {
441         // nothing
442     }
443 
444     // SETTERS
445     /**
446      * Sets the auth service name
447      * @param strAuthServiceName the service name
448      */
449     public void setAuthServiceName( String strAuthServiceName )
450     {
451         _strAuthServiceName = strAuthServiceName;
452     }
453 
454     /**
455      * Set the name
456      * @param strName the service key
457      */
458     public void setName( String strName )
459     {
460         _strName = strName;
461     }
462 
463     /**
464      * Sets the icon url
465      * @param strIconUrl icon url
466      */
467     public void setIconUrl( String strIconUrl )
468     {
469         _strIconUrl = strIconUrl;
470     }
471 
472     /**
473      * The request token url
474      * @return the request token url
475      */
476     public String getRequestTokenEndpointUrl(  )
477     {
478         return _strRequestTokenEndpointUrl;
479     }
480 
481     /**
482      * The request token url
483      * @param strRequestTokenEndpointUrl the request token url
484      */
485     public void setRequestTokenEndpointUrl( String strRequestTokenEndpointUrl )
486     {
487         _strRequestTokenEndpointUrl = strRequestTokenEndpointUrl;
488     }
489 
490     /**
491      * The access token url
492      * @return the access token url
493      */
494     public String getAccessTokenEndpointUrl(  )
495     {
496         return _strAccessTokenEndpointUrl;
497     }
498 
499     /**
500      * Access token url
501      * @param strAccessTokenEndpointUrl the access token url
502      */
503     public void setAccessTokenEndpointUrl( String strAccessTokenEndpointUrl )
504     {
505         _strAccessTokenEndpointUrl = strAccessTokenEndpointUrl;
506     }
507 
508     /**
509      * Gets The authorize/authenticate url
510      * @return The authorize/authenticate url
511      */
512     public String getAuthorizeWebsiteUrl(  )
513     {
514         return _strAuthorizeWebsiteUrl;
515     }
516 
517     /**
518      * The authorize/authenticate url
519      * @param strAuthorizeWebsiteUrl the authorize url
520      */
521     public void setAuthorizeWebsiteUrl( String strAuthorizeWebsiteUrl )
522     {
523         _strAuthorizeWebsiteUrl = strAuthorizeWebsiteUrl;
524     }
525 
526     /**
527      * Returns the consumer key
528      * @return the consumer key
529      */
530     public String getConsumerKey(  )
531     {
532         return _strConsumerKey;
533     }
534 
535     /**
536      * Sets the consumer key
537      * @param strConsumerKey the consumer key
538      */
539     public void setConsumerKey( String strConsumerKey )
540     {
541         _strConsumerKey = strConsumerKey;
542     }
543 
544     /**
545      * Returns the consumer secret
546      * @return consumer secret
547      */
548     public String getConsumerSecret(  )
549     {
550         return _strConsumerSecret;
551     }
552 
553     /**
554      * Consumer secret
555      * @param strConsumerSecret consumer secret
556      */
557     public void setConsumerSecret( String strConsumerSecret )
558     {
559         _strConsumerSecret = strConsumerSecret;
560     }
561 
562     /**
563      * {@inheritDoc}
564      */
565     @Override
566     public String toString(  )
567     {
568         return super.toString(  ) + "{" + this.getConsumerKey(  ) + "," + this.getConsumerSecret(  ) + "," +
569         this.getAuthorizeWebsiteUrl(  ) + "," + this.getRequestTokenEndpointUrl(  ) + "," +
570         this.getAccessTokenEndpointUrl(  ) + "}";
571     }
572 
573     /**
574      *  credential url
575      *  @return  credential url
576      */
577     public String getCredentialUrl(  )
578     {
579         return _strCredentialUrl;
580     }
581 
582     /**
583      * Set the credential url
584      * @param strCredentialUrl credential url
585      */
586     public void setCredentialUrl( String strCredentialUrl )
587     {
588         _strCredentialUrl = strCredentialUrl;
589     }
590 
591     /**
592      * Gets the credentials format
593      * @return format
594      */
595     public String getCredentialFormat(  )
596     {
597         return _strCredentialFormat;
598     }
599 
600     /**
601      * Credentials format
602      * @param strCredentialFormat strCredentialFormat
603      */
604     public void setCredentialFormat( String strCredentialFormat )
605     {
606         this._strCredentialFormat = strCredentialFormat;
607     }
608 }