View Javadoc

1   /*
2    * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//httpclient/src/java/org/apache/commons/httpclient/auth/HttpAuthenticator.java,v 1.19 2004/10/06 17:32:04 olegk Exp $
3    * $Revision: 155418 $
4    * $Date: 2005-02-26 08:01:52 -0500 (Sat, 26 Feb 2005) $
5    *
6    * ====================================================================
7    *
8    *  Copyright 1999-2004 The Apache Software Foundation
9    *
10   *  Licensed under the Apache License, Version 2.0 (the "License");
11   *  you may not use this file except in compliance with the License.
12   *  You may obtain a copy of the License at
13   *
14   *      http://www.apache.org/licenses/LICENSE-2.0
15   *
16   *  Unless required by applicable law or agreed to in writing, software
17   *  distributed under the License is distributed on an "AS IS" BASIS,
18   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19   *  See the License for the specific language governing permissions and
20   *  limitations under the License.
21   * ====================================================================
22   *
23   * This software consists of voluntary contributions made by many
24   * individuals on behalf of the Apache Software Foundation.  For more
25   * information on the Apache Software Foundation, please see
26   * <http://www.apache.org/>.
27   *
28   */
29  
30  package org.apache.commons.httpclient.auth;
31  
32  import java.util.HashMap;
33  import java.util.Map;
34  
35  import org.apache.commons.httpclient.Credentials;
36  import org.apache.commons.httpclient.Header;
37  import org.apache.commons.httpclient.HttpConnection;
38  import org.apache.commons.httpclient.HttpMethod;
39  import org.apache.commons.httpclient.HttpState;
40  import org.apache.commons.httpclient.UsernamePasswordCredentials;
41  import org.apache.commons.logging.Log;
42  import org.apache.commons.logging.LogFactory;
43  
44  /***
45   * Utility methods for HTTP authorization and authentication.  This class
46   * provides utility methods for generating responses to HTTP www and proxy
47   * authentication challenges.
48   * 
49   * <blockquote>
50   * A client SHOULD assume that all paths at or deeper than the depth of the
51   * last symbolic element in the path field of the Request-URI also are within
52   * the protection space specified by the basic realm value of the current
53   * challenge. A client MAY preemptively send the corresponding Authorization
54   * header with requests for resources in that space without receipt of another
55   * challenge from the server. Similarly, when a client sends a request to a
56   * proxy, it may reuse a userid and password in the Proxy-Authorization header
57   * field without receiving another challenge from the proxy server.
58   * </blockquote>
59   * </p>
60   * 
61   * @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
62   * @author Rodney Waldhoff
63   * @author <a href="mailto:jsdever@apache.org">Jeff Dever</a>
64   * @author Ortwin Gl�ck
65   * @author Sean C. Sullivan
66   * @author <a href="mailto:adrian@ephox.com">Adrian Sutton</a>
67   * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
68   * @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a>
69   * 
70   * @deprecated no longer used
71   */
72  public final class HttpAuthenticator {
73  
74      /*** Log object for this class. */
75      private static final Log LOG = LogFactory.getLog(HttpAuthenticator.class);
76  
77      /***
78       * The www authenticate challange header.
79       */
80      public static final String WWW_AUTH = "WWW-Authenticate";
81  
82      /***
83       * The www authenticate response header.
84       */
85      public static final String WWW_AUTH_RESP = "Authorization";
86  
87      /***
88       * The proxy authenticate challange header.
89       */
90      public static final String PROXY_AUTH = "Proxy-Authenticate";
91  
92      /***
93       * The proxy authenticate response header.
94       */
95      public static final String PROXY_AUTH_RESP = "Proxy-Authorization";
96  
97      /*** Chooses the strongest authentication scheme supported from the
98       * array of authentication challenges. Currently only <code>NTLM</code>,
99       * <code>Digest</code>, <code>Basic</code> schemes are recognized. 
100      * The <code>NTLM</code> scheme is considered the strongest and is 
101      * preferred to all others. The <code>Digest</code> scheme is preferred to 
102      * the <code>Basic</code> one which provides no encryption for credentials.
103      * The <code>Basic</code> scheme is used only if it is the only one 
104      * supported.
105      * 
106      * @param challenges The array of authentication challenges
107      * 
108      * @return The strongest authentication scheme supported
109      * 
110      * @throws MalformedChallengeException is thrown if an authentication 
111      *  challenge is malformed
112      * @throws UnsupportedOperationException when none of challenge types
113      *  available is supported.
114      * 
115      * @deprecated Use {@link AuthChallengeParser#parseChallenges(Header[])} and 
116      *      {@link AuthPolicy#getAuthScheme(String)}
117      */
118     public static AuthScheme selectAuthScheme(final Header[] challenges)
119       throws MalformedChallengeException {
120         LOG.trace("enter HttpAuthenticator.selectAuthScheme(Header[])");
121         if (challenges == null) {
122             throw new IllegalArgumentException("Array of challenges may not be null");
123         }
124         if (challenges.length == 0) {
125             throw new IllegalArgumentException("Array of challenges may not be empty");
126         }
127         String challenge = null;
128         Map challengemap = new HashMap(challenges.length); 
129         for (int i = 0; i < challenges.length; i++) {
130             challenge = challenges[i].getValue();
131             String s = AuthChallengeParser.extractScheme(challenge);
132             challengemap.put(s, challenge);
133         }
134         challenge = (String) challengemap.get("ntlm");
135         if (challenge != null) {
136             return new NTLMScheme(challenge);
137         }
138         challenge = (String) challengemap.get("digest");
139         if (challenge != null) {
140             return new DigestScheme(challenge);
141         }
142         challenge = (String) challengemap.get("basic");
143         if (challenge != null) {
144             return new BasicScheme(challenge);
145         }
146         throw new UnsupportedOperationException(
147           "Authentication scheme(s) not supported: " + challengemap.toString()); 
148     }
149     
150     private static boolean doAuthenticateDefault(
151         HttpMethod method, 
152         HttpConnection conn,
153         HttpState state, 
154         boolean proxy)
155       throws AuthenticationException {
156         if (method == null) {
157             throw new IllegalArgumentException("HTTP method may not be null");
158         }
159         if (state == null) {
160             throw new IllegalArgumentException("HTTP state may not be null");
161         }
162         String host = null;
163         if (conn != null) {
164             host = proxy ? conn.getProxyHost() : conn.getHost();
165         }
166         Credentials credentials = proxy 
167             ? state.getProxyCredentials(null, host) : state.getCredentials(null, host);
168         if (credentials == null) {
169             return false;
170         }
171         if (!(credentials instanceof UsernamePasswordCredentials)) {
172             throw new InvalidCredentialsException(
173              "Credentials cannot be used for basic authentication: " 
174               + credentials.toString());
175         }
176         String auth = BasicScheme.authenticate(
177             (UsernamePasswordCredentials) credentials,
178             method.getParams().getCredentialCharset());
179         if (auth != null) {
180             String s = proxy ? PROXY_AUTH_RESP : WWW_AUTH_RESP;
181             Header header = new Header(s, auth, true);
182             method.addRequestHeader(header);
183             return true;
184         } else {
185             return false;
186         }
187     }
188     
189     
190     /***
191      * Attempt to provide default authentication credentials 
192      * to the given method in the given context using basic 
193      * authentication scheme.
194      * 
195      * @param method the HttpMethod which requires authentication
196      * @param conn the connection to a specific host. This parameter 
197      *   may be <tt>null</tt> if default credentials (not specific 
198      *   to any particular host) are to be used
199      * @param state the HttpState object providing Credentials
200      * 
201      * @return true if the <tt>Authenticate</tt> response header 
202      *   was added
203      * 
204      * @throws InvalidCredentialsException if authentication credentials
205      *         are not valid or not applicable for basic scheme
206      * @throws AuthenticationException when a parsing or other error occurs
207      *
208      * @see HttpState#setCredentials(String,String,Credentials)
209      * 
210      * @deprecated use AuthScheme
211      */
212     public static boolean authenticateDefault(
213         HttpMethod method, 
214         HttpConnection conn,
215         HttpState state)
216       throws AuthenticationException {
217         LOG.trace(
218             "enter HttpAuthenticator.authenticateDefault(HttpMethod, HttpConnection, HttpState)");
219         return doAuthenticateDefault(method, conn, state, false);
220     }
221 
222 
223     /***
224      * Attempt to provide default proxy authentication credentials 
225      * to the given method in the given context using basic 
226      * authentication scheme.
227      * 
228      * @param method the HttpMethod which requires authentication
229      * @param conn the connection to a specific host. This parameter 
230      *   may be <tt>null</tt> if default credentials (not specific 
231      *   to any particular host) are to be used
232      * @param state the HttpState object providing Credentials
233      * 
234      * @return true if the <tt>Proxy-Authenticate</tt> response header 
235      *   was added
236      * 
237      * @throws InvalidCredentialsException if authentication credentials
238      *         are not valid or not applicable for basic scheme
239      * @throws AuthenticationException when a parsing or other error occurs
240 
241      * @see HttpState#setCredentials(String,String,Credentials)
242      * 
243      * @deprecated use AuthScheme
244      */
245     public static boolean authenticateProxyDefault(
246         HttpMethod method, 
247         HttpConnection conn,
248         HttpState state)
249       throws AuthenticationException {
250         LOG.trace("enter HttpAuthenticator.authenticateProxyDefault(HttpMethod, HttpState)");
251         return doAuthenticateDefault(method, conn, state, true);
252     }
253 
254 
255     private static boolean doAuthenticate(
256         AuthScheme authscheme, 
257         HttpMethod method, 
258         HttpConnection conn,
259         HttpState state, 
260         boolean proxy)
261        throws AuthenticationException {
262         if (authscheme == null) {
263             throw new IllegalArgumentException("Authentication scheme may not be null");
264         }
265         if (method == null) {
266             throw new IllegalArgumentException("HTTP method may not be null");
267         }
268         if (state == null) {
269             throw new IllegalArgumentException("HTTP state may not be null");
270         }
271         String host = null;
272         if (conn != null) {
273             if (proxy) {
274                 host = conn.getProxyHost();
275             } else {
276                 host = method.getParams().getVirtualHost();
277                 if (host == null) {
278                     host = conn.getHost();
279                 }
280             }
281         }
282         String realm = authscheme.getRealm();
283         if (LOG.isDebugEnabled()) {
284             StringBuffer buffer = new StringBuffer();
285             buffer.append("Using credentials for ");
286             if (realm == null) {
287                 buffer.append("default");
288             } else {
289                 buffer.append('\'');
290                 buffer.append(realm);
291                 buffer.append('\'');
292             }
293             buffer.append(" authentication realm at "); 
294             buffer.append(host); 
295             LOG.debug(buffer.toString());
296         }
297         Credentials credentials = proxy 
298             ? state.getProxyCredentials(realm, host) 
299             : state.getCredentials(realm, host);
300         if (credentials == null) {
301             StringBuffer buffer = new StringBuffer();
302             buffer.append("No credentials available for the "); 
303             if (realm == null) {
304                 buffer.append("default");
305             } else {
306                 buffer.append('\'');
307                 buffer.append(realm);
308                 buffer.append('\'');
309             }
310             buffer.append(" authentication realm at "); 
311             buffer.append(host); 
312             throw new CredentialsNotAvailableException(buffer.toString());
313         }
314         String auth = authscheme.authenticate(credentials, method);
315         if (auth != null) {
316             String s = proxy ? PROXY_AUTH_RESP : WWW_AUTH_RESP;
317             Header header = new Header(s, auth, true);
318             method.addRequestHeader(header);
319             return true;
320         } else {
321             return false;
322         }
323     }
324 
325     /***
326      * Attempt to provide requisite authentication credentials to the 
327      * given method in the given context using the given 
328      * authentication scheme.
329      * 
330      * @param authscheme The authentication scheme to be used
331      * @param method The HttpMethod which requires authentication
332      * @param conn the connection to a specific host. This parameter 
333      *   may be <tt>null</tt> if default credentials (not specific 
334      *   to any particular host) are to be used
335      * @param state The HttpState object providing Credentials
336      * 
337      * @return true if the <tt>Authenticate</tt> response header was added
338      * 
339      * @throws CredentialsNotAvailableException if authentication credentials
340      *         required to respond to the authentication challenge are not available
341      * @throws AuthenticationException when a parsing or other error occurs
342 
343      * @see HttpState#setCredentials(String,String,Credentials)
344      * 
345      * @deprecated use AuthScheme
346      */
347     public static boolean authenticate(
348         AuthScheme authscheme, 
349         HttpMethod method, 
350         HttpConnection conn,
351         HttpState state) 
352         throws AuthenticationException {
353        LOG.trace(
354             "enter HttpAuthenticator.authenticate(AuthScheme, HttpMethod, HttpConnection, "
355             + "HttpState)");
356         return doAuthenticate(authscheme, method, conn, state, false);
357     }
358 
359 
360     /***
361      * Attempt to provide requisite proxy authentication credentials 
362      * to the given method in the given context using 
363      * the given authentication scheme.
364      * 
365      * @param authscheme The authentication scheme to be used
366      * @param method the HttpMethod which requires authentication
367      * @param conn the connection to a specific host. This parameter 
368      *   may be <tt>null</tt> if default credentials (not specific 
369      *   to any particular host) are to be used
370      * @param state the HttpState object providing Credentials
371      * 
372      * @return true if the <tt>Proxy-Authenticate</tt> response header 
373      *  was added
374      * 
375      * @throws CredentialsNotAvailableException if authentication credentials
376      *         required to respond to the authentication challenge are not available
377      * @throws AuthenticationException when a parsing or other error occurs
378 
379      * @see HttpState#setCredentials(String,String,Credentials)
380      * 
381      * @deprecated use AuthScheme
382      */
383     public static boolean authenticateProxy(
384         AuthScheme authscheme, 
385         HttpMethod method, 
386         HttpConnection conn,
387         HttpState state
388     ) throws AuthenticationException {
389        LOG.trace("enter HttpAuthenticator.authenticateProxy(AuthScheme, HttpMethod, HttpState)");
390        return doAuthenticate(authscheme, method, conn, state, true);
391     }
392 }