Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: com/xpn/xwiki/test/MyFormAuthentication.java


1   /**
2    * ===================================================================
3    *
4    * Copyright (c) 2003,2004 Ludovic Dubost, All rights reserved.
5    *
6    * This program is free software; you can redistribute it and/or
7    * modify it under the terms of the GNU Lesser General Public License
8    * as published by the Free Software Foundation; either version 2
9    * of the License, or (at your option) any later version.
10   *
11   * This program is distributed in the hope that it will be useful,
12   * but WITHOUT ANY WARRANTY; without even the implied warranty of
13   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14   * GNU Lesser General Public License for more details, published at 
15   * http://www.gnu.org/copyleft/lesser.html or in lesser.txt in the
16   * root folder of this distribution.
17  
18   * Created by
19   * User: Ludovic Dubost
20   * Date: 29 avr. 2004
21   * Time: 16:06:29
22   */
23  package com.xpn.xwiki.test;
24  
25  import org.apache.cactus.Cookie;
26  import org.apache.cactus.WebRequest;
27  import org.apache.cactus.client.authentication.AbstractAuthentication;
28  import org.apache.cactus.client.authentication.Authentication;
29  import org.apache.cactus.internal.WebRequestImpl;
30  import org.apache.cactus.internal.client.connector.http.HttpClientConnectionHelper;
31  import org.apache.cactus.internal.configuration.Configuration;
32  import org.apache.cactus.internal.configuration.WebConfiguration;
33  import org.apache.cactus.util.ChainedRuntimeException;
34  import org.apache.commons.httpclient.HttpMethod;
35  import org.apache.commons.httpclient.HttpState;
36  import org.apache.commons.logging.Log;
37  import org.apache.commons.logging.LogFactory;
38  
39  import java.net.HttpURLConnection;
40  import java.net.MalformedURLException;
41  import java.net.URL;
42  import java.util.ArrayList;
43  import java.util.List;
44  
45  /**
46   * Form-based authentication implementation. An instance of this class
47   * can be reused across several tests as it caches the session cookie.
48   * Thus the first time it is used to authenticate the user, it calls
49   * the security URL (which is by default the context URL prepended by
50   * "j_security_check"), caches the returned session cookie and adds the
51   * cookie for the next request. The second time it is called, it simply
52   * addes the session cookie for the next request.
53   *
54   * @since 1.5
55   *
56   * @version $Id: MyFormAuthentication.java 322 2004-06-29 17:58:57Z ldubost $
57   */
58  public class MyFormAuthentication extends AbstractAuthentication
59  {
60      /**
61       * The logger.
62       */
63      private static final Log LOGGER =
64          LogFactory.getLog(MyFormAuthentication.class);
65  
66      /**
67       * The expected HTTP response status code when the authentication
68       * is succeeded.
69       */
70      private int expectedAuthResponse = HttpURLConnection.HTTP_MOVED_TEMP;
71  
72      /**
73       * The URL to use when attempting to log in, if for whatever reason
74       * the default URL is incorrect.
75       */
76      private URL securityCheckURL;
77  
78      /**
79       * The cookie name of the session.
80       */
81      private String sessionCookieName = "JSESSIONID";
82  
83      /**
84       * We store the session cookie.
85       */
86      private Cookie jsessionCookie;
87  
88      /**
89       * We store all cookies
90       */
91      private List cookies;
92  
93      /**
94       * {@link org.apache.cactus.WebRequest} object that will be used to connect to the
95       * security URL.
96       */
97      private WebRequest securityRequest = new WebRequestImpl();
98  
99      /**
100      * @param theName user name of the Credential
101      * @param thePassword user password of the Credential
102      */
103     public MyFormAuthentication(String theName, String thePassword)
104     {
105         super(theName, thePassword);
106     }
107 
108     /**
109      * @see Authentication#configure
110      */
111     public void configure(HttpState theState, HttpMethod theMethod,
112         WebRequest theRequest, Configuration theConfiguration)
113     {
114         // Only authenticate the first time this instance is used.
115         if (this.jsessionCookie == null)
116         {
117            authenticate(theRequest, theConfiguration);
118         }
119 
120         // Sets the session id cookie for the next request.
121         if (this.jsessionCookie != null)
122         {
123             for (int i=0;i<cookies.size();i++)
124              theRequest.addCookie((Cookie)cookies.get(i));
125         }
126     }
127 
128     /**
129      * @return the {@link WebRequest} that will be used to connect to the
130      * security URL. It can be used to add additional HTTP parameters such
131      * as proprietary ones required by some containers.
132      */
133     public WebRequest getSecurityRequest()
134     {
135         return this.securityRequest;
136     }
137 
138     /**
139      * This sets the URL to use when attempting to log in. This method is used
140      * if for whatever reason the default URL is incorrect.
141      *
142      * @param theUrl A URL to use to attempt to login.
143      */
144     public void setSecurityCheckURL(URL theUrl)
145     {
146         this.securityCheckURL = theUrl;
147     }
148 
149     /**
150      * This returns the URL to use when attempting to log in. By default, it's
151      * the context URL defined in the Cactus configuration with
152      * "/j_security_check" appended.
153      *
154      * @param theConfiguration the Cactus configuration
155      * @return the URL that is being used to attempt to login.
156      */
157     public URL getSecurityCheckURL(Configuration theConfiguration)
158     {
159         if (this.securityCheckURL == null)
160         {
161             // Configure default
162             String stringUrl =
163                 ((WebConfiguration) theConfiguration).getContextURL()
164                 + "/j_security_check";
165 
166             try
167             {
168                 this.securityCheckURL = new URL(stringUrl);
169             }
170             catch (MalformedURLException e)
171             {
172                 throw new ChainedRuntimeException(
173                     "Unable to create default Security Check URL ["
174                     + stringUrl + "]");
175             }
176         }
177 
178         LOGGER.debug("Using security check URL [" + this.securityCheckURL
179             + "]");
180 
181         return securityCheckURL;
182     }
183 
184 
185     /**
186      * Get the cookie name of the session.
187      * @return the cookie name of the session
188      */
189     private String getSessionCookieName()
190     {
191         return this.sessionCookieName;
192     }
193 
194     /**
195      * Set the cookie name of the session to theName.
196      * If theName is null, the change request will be ignored.
197      * The default is &quot;<code>JSESSIONID</code>&quot;.
198      * @param theName the cookie name of the session
199      */
200     public void setSessionCookieName(String theName)
201     {
202         if (theName != null)
203         {
204             this.sessionCookieName = theName;
205         }
206     }
207 
208 
209     /**
210      * Get the expected HTTP response status code for an authentication request
211      * which should be successful.
212      * @return the expected HTTP response status code
213      */
214     protected int getExpectedAuthResponse()
215     {
216         return this.expectedAuthResponse;
217     }
218 
219     /**
220      * Set the expected HTTP response status code for an authentication request
221      * which should be successful.
222      * The default is HttpURLConnection.HTTP_MOVED_TEMP.
223      * @param theExpectedCode the expected HTTP response status code value
224      */
225     public void setExpectedAuthResponse(int theExpectedCode)
226     {
227         this.expectedAuthResponse = theExpectedCode;
228     }
229 
230 
231     /**
232      * Get a cookie required to be set by set-cookie header field.
233      * @param theConnection a {@link HttpURLConnection}
234      * @param theTarget the target cookie name
235      * @return the {@link Cookie}
236      */
237     private Cookie getCookie(HttpURLConnection theConnection, String theTarget)
238     {
239         // Check (possible multiple) cookies for a target.
240         int i = 1;
241         String key = theConnection.getHeaderFieldKey(i);
242         while (key != null)
243         {
244             if (key.equalsIgnoreCase("set-cookie"))
245             {
246                 // Cookie is in the form:
247                 // "NAME=VALUE; expires=DATE; path=PATH;
248                 //  domain=DOMAIN_NAME; secure"
249                 // The only thing we care about is finding a cookie with
250                 // the name "JSESSIONID" and caching the value.
251                 String cookiestr = theConnection.getHeaderField(i);
252                 String nameValue = cookiestr.substring(0,
253                     cookiestr.indexOf(";"));
254                 int equalsChar = nameValue.indexOf("=");
255                 String name = nameValue.substring(0, equalsChar);
256                 String value = nameValue.substring(equalsChar + 1);
257                 if (name.equalsIgnoreCase(theTarget))
258                 {
259                     String host = theConnection.getURL().getHost();
260                       // Let's force it to localhost as it seems to fail on Linux
261                     host = "localhost";
262 
263                     /*
264                       String[] hosts = {
265                            "127.0.0.1",
266                            "localhost",
267                            "localhost.localdomain"
268                       };
269                       for (int h = 0; h < hosts.length; h++) {
270 
271                       }
272                       */
273                       return new Cookie(host,
274                          name, value);
275                 }
276             }
277             key = theConnection.getHeaderFieldKey(++i);
278         }
279         return null;
280     }
281 
282     private List getAllCookies(HttpURLConnection theConnection)
283     {
284         // Check (possible multiple) cookies for a target.
285         int i = 1;
286         List cookies = new ArrayList();
287         String key = theConnection.getHeaderFieldKey(i);
288         while (key != null)
289         {
290             if (key.equalsIgnoreCase("set-cookie"))
291             {
292                 // Cookie is in the form:
293                 // "NAME=VALUE; expires=DATE; path=PATH;
294                 //  domain=DOMAIN_NAME; secure"
295                 // The only thing we care about is finding a cookie with
296                 // the name "JSESSIONID" and caching the value.
297                 String cookiestr = theConnection.getHeaderField(i);
298                 String nameValue = cookiestr.substring(0,
299                     cookiestr.indexOf(";"));
300                 int equalsChar = nameValue.indexOf("=");
301                 String name = nameValue.substring(0, equalsChar);
302                 String value = nameValue.substring(equalsChar + 1);
303                 // String host = theConnection.getURL().getHost();
304                 // Let's force it to localhost as it seems to fail on Linux
305                 String [] hosts = { "localhost", "127.0.0.1", "localhost.localdomain" };
306                 for (int h = 0; h < hosts.length; h ++) {
307                     String host = hosts[h];
308                     Cookie cookie = new Cookie(host, name, value);
309                     cookie.setPath("/");
310                     cookies.add(cookie);
311 
312                 }
313             }
314             key = theConnection.getHeaderFieldKey(++i);
315         }
316         return cookies;
317     }
318 
319 
320     /**
321      * Check if the pre-auth step can be considered as succeeded or not.
322      * As default, the step considered as succeeded
323      * if the response status code of <code>theConnection</code>
324      * is less than 400.
325      *
326      * @param theConnection a <code>HttpURLConnection</code> value
327      * @exception Exception if the pre-auth step should be considered as failed
328      */
329     protected void checkPreAuthResponse(HttpURLConnection theConnection)
330         throws Exception
331     {
332         if (theConnection.getResponseCode() >= 400)
333         {
334             throw new Exception("Received a status code ["
335                 + theConnection.getResponseCode()
336                 + "] and was expecting less than 400");
337         }
338     }
339 
340 
341     /**
342      * Get login session cookie.
343      * This is the first step to start login session:
344      * <dl>
345      *   <dt> C-&gt;S: </dt>
346      *   <dd> try to connect to a restricted resource </dd>
347      *   <dt> S-&gt;C: </dt>
348      *   <dd> redirect or forward to the login page with set-cookie header </dd>
349      * </ol>
350      * @param theRequest a request to connect to a restricted resource
351      * @param theConfiguration a <code>Configuration</code> value
352      * @return the <code>Cookie</code>
353      */
354     private Cookie getSecureSessionIdCookie(WebRequest theRequest,
355         Configuration theConfiguration)
356     {
357         HttpURLConnection connection;
358         String resource = null;
359 
360         try
361         {
362             // Create a helper that will connect to a restricted resource.
363             WebConfiguration webConfig = (WebConfiguration) theConfiguration;
364             resource = webConfig.getRedirectorURL(theRequest);
365 
366             HttpClientConnectionHelper helper =
367                 new HttpClientConnectionHelper(resource);
368 
369             WebRequest request =
370                 new WebRequestImpl((WebConfiguration) theConfiguration);
371 
372             theRequest.setAuthentication(null);
373 
374             // Make the connection using a default web request.
375             connection = helper.connect(theRequest, theConfiguration);
376 
377             theRequest.setAuthentication(this);
378 
379             checkPreAuthResponse(connection);
380         }
381         catch (Throwable e)
382         {
383             throw new ChainedRuntimeException(
384                 "Failed to connect to the secured redirector: " + resource, e);
385         }
386 
387         return getCookie(connection, getSessionCookieName());
388     }
389 
390 
391     /**
392      * Check if the auth step can be considered as succeeded or not.
393      * As default, the step considered as succeeded
394      * if the response status code of <code>theConnection</code>
395      * equals <code>getExpectedAuthResponse()</code>.
396      *
397      * @param theConnection a <code>HttpURLConnection</code> value
398      * @exception Exception if the auth step should be considered as failed
399      */
400     protected void checkAuthResponse(HttpURLConnection theConnection)
401         throws Exception
402     {
403         if (theConnection.getResponseCode() != getExpectedAuthResponse())
404         {
405 
406             throw new Exception("Received a status code ["
407                 + theConnection.getResponseCode()
408                 + "] and was expecting a ["
409                 + getExpectedAuthResponse() + "]\nURL: "
410                 + theConnection.getURL().toString() + "\nPost: "
411                 + theConnection.getRequestProperties().toString());
412         }
413     }
414 
415 
416     /**
417      * Authenticate the principal by calling the security URL.
418      *
419      * @param theRequest the web request used to connect to the Redirector
420      * @param theConfiguration the Cactus configuration
421      */
422     public void authenticate(WebRequest theRequest,
423         Configuration theConfiguration)
424     {
425         this.jsessionCookie = getSecureSessionIdCookie(theRequest,
426             theConfiguration);
427 
428         try
429         {
430             // Create a helper that will connect to the security check URL.
431             HttpClientConnectionHelper helper =
432                 new HttpClientConnectionHelper(
433                     getSecurityCheckURL(theConfiguration).toString());
434 
435             // Configure a web request with the JSESSIONID cookie,
436             // the username and the password.
437             WebRequest request = getSecurityRequest();
438             ((WebRequestImpl) request).setConfiguration(theConfiguration);
439             request.addCookie(this.jsessionCookie);
440             request.addParameter("j_username", getName(),
441                 WebRequest.POST_METHOD);
442             request.addParameter("j_password", getPassword(),
443                 WebRequest.POST_METHOD);
444             request.addParameter("j_rememberme", "true",
445                 WebRequest.POST_METHOD);
446 
447             // Make the connection using the configured web request.
448             HttpURLConnection connection = helper.connect(request,
449                 theConfiguration);
450 
451             checkAuthResponse(connection);
452             cookies = getAllCookies(connection);
453         }
454         catch (Throwable e)
455         {
456             this.jsessionCookie = null;
457             throw new ChainedRuntimeException(
458                 "Failed to authenticate the principal", e);
459         }
460     }
461 }