View Javadoc

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