1 /*
2 * $Header: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/auth/HttpAuthenticator.java,v 1.6 2003/04/22 17:25:40 olegk Exp $
3 * $Revision: 1.6 $
4 * $Date: 2003/04/22 17:25:40 $
5 *
6 * ====================================================================
7 *
8 * The Apache Software License, Version 1.1
9 *
10 * Copyright (c) 1999-2003 The Apache Software Foundation. All rights
11 * reserved.
12 *
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
15 * are met:
16 *
17 * 1. Redistributions of source code must retain the above copyright
18 * notice, this list of conditions and the following disclaimer.
19 *
20 * 2. Redistributions in binary form must reproduce the above copyright
21 * notice, this list of conditions and the following disclaimer in
22 * the documentation and/or other materials provided with the
23 * distribution.
24 *
25 * 3. The end-user documentation included with the redistribution, if
26 * any, must include the following acknowlegement:
27 * "This product includes software developed by the
28 * Apache Software Foundation (http://www.apache.org/)."
29 * Alternately, this acknowlegement may appear in the software itself,
30 * if and wherever such third-party acknowlegements normally appear.
31 *
32 * 4. The names "The Jakarta Project", "Commons", and "Apache Software
33 * Foundation" must not be used to endorse or promote products derived
34 * from this software without prior written permission. For written
35 * permission, please contact apache@apache.org.
36 *
37 * 5. Products derived from this software may not be called "Apache"
38 * nor may "Apache" appear in their names without prior written
39 * permission of the Apache Group.
40 *
41 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
42 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
43 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
44 * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
45 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
46 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
47 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
48 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
49 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
50 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
51 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
52 * SUCH DAMAGE.
53 * ====================================================================
54 *
55 * This software consists of voluntary contributions made by many
56 * individuals on behalf of the Apache Software Foundation. For more
57 * information on the Apache Software Foundation, please see
58 * <http://www.apache.org/>.
59 *
60 * [Additional notices, if required by prior licensing conditions]
61 *
62 */
63
64 package org.apache.commons.httpclient.auth;
65
66 import java.util.Map;
67 import java.util.HashMap;
68 import org.apache.commons.httpclient.Header;
69 import org.apache.commons.httpclient.HttpConnection;
70 import org.apache.commons.httpclient.HttpMethod;
71 import org.apache.commons.httpclient.Credentials;
72 import org.apache.commons.httpclient.HttpState;
73 import org.apache.commons.httpclient.UsernamePasswordCredentials;
74 import org.apache.commons.logging.Log;
75 import org.apache.commons.logging.LogFactory;
76
77 /***
78 * Utility methods for HTTP authorization and authentication. This class
79 * provides utility methods for generating responses to HTTP www and proxy
80 * authentication challenges.
81 *
82 * <blockquote>
83 * A client SHOULD assume that all paths at or deeper than the depth of the
84 * last symbolic element in the path field of the Request-URI also are within
85 * the protection space specified by the basic realm value of the current
86 * challenge. A client MAY preemptively send the corresponding Authorization
87 * header with requests for resources in that space without receipt of another
88 * challenge from the server. Similarly, when a client sends a request to a
89 * proxy, it may reuse a userid and password in the Proxy-Authorization header
90 * field without receiving another challenge from the proxy server.
91 * </blockquote>
92 * </p>
93 *
94 * @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
95 * @author Rodney Waldhoff
96 * @author <a href="mailto:jsdever@apache.org">Jeff Dever</a>
97 * @author Ortwin Gl�ck
98 * @author Sean C. Sullivan
99 * @author <a href="mailto:adrian@ephox.com">Adrian Sutton</a>
100 * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
101 * @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a>
102 */
103 public final class HttpAuthenticator {
104
105 /*** Log object for this class. */
106 private static final Log LOG = LogFactory.getLog(HttpAuthenticator.class);
107
108 /***
109 * The www authenticate challange header.
110 */
111 public static final String WWW_AUTH = "WWW-Authenticate";
112
113
114 /***
115 * The www authenticate response header.
116 */
117 public static final String WWW_AUTH_RESP = "Authorization";
118
119
120 /***
121 * The proxy authenticate challange header.
122 */
123 public static final String PROXY_AUTH = "Proxy-Authenticate";
124
125
126 /***
127 * The proxy authenticate response header.
128 */
129 public static final String PROXY_AUTH_RESP = "Proxy-Authorization";
130
131 /*** Chooses the strongest authentication scheme supported from the
132 * array of authentication challenges. Currently only <code>NTLM</code>,
133 * <code>Digest</code>, <code>Basic</code> schemes are recognized.
134 * The <code>NTLM</code> scheme is considered the strongest and is
135 * preferred to all others. The <code>Digest</code> scheme is preferred to
136 * the <code>Basic</code> one which provides no encryption for credentials.
137 * The <code>Basic</code> scheme is used only if it is the only one
138 * supported.
139 *
140 * @param challenges The array of authentication challenges
141 *
142 * @return The strongest authentication scheme supported
143 *
144 * @throws MalformedChallengeException is thrown if an authentication
145 * challenge is malformed
146 * @throws UnsupportedOperationException when none of challenge types
147 * available is supported.
148 */
149 public static AuthScheme selectAuthScheme(final Header[] challenges)
150 throws MalformedChallengeException {
151 LOG.trace("enter HttpAuthenticator.selectAuthScheme(Header[])");
152 if (challenges == null) {
153 throw new IllegalArgumentException("Array of challenges may not be null");
154 }
155 if (challenges.length == 0) {
156 throw new IllegalArgumentException("Array of challenges may not be empty");
157 }
158 String challenge = null;
159 Map challengemap = new HashMap(challenges.length);
160 for (int i = 0; i < challenges.length; i++) {
161 challenge = challenges[i].getValue();
162 String s = AuthChallengeParser.extractScheme(challenge);
163 challengemap.put(s, challenge);
164 }
165 challenge = (String) challengemap.get("ntlm");
166 if (challenge != null) {
167 return new NTLMScheme(challenge);
168 }
169 challenge = (String) challengemap.get("digest");
170 if (challenge != null) {
171 return new DigestScheme(challenge);
172 }
173 challenge = (String) challengemap.get("basic");
174 if (challenge != null) {
175 return new BasicScheme(challenge);
176 }
177 throw new UnsupportedOperationException(
178 "Authentication scheme(s) not supported: " + challengemap.toString());
179 }
180
181
182 private static boolean doAuthenticateDefault(
183 HttpMethod method,
184 HttpConnection conn,
185 HttpState state,
186 boolean proxy)
187 throws AuthenticationException {
188 if (method == null) {
189 throw new IllegalArgumentException("HTTP method may not be null");
190 }
191 if (state == null) {
192 throw new IllegalArgumentException("HTTP state may not be null");
193 }
194 String host = null;
195 if (conn != null) {
196 host = proxy ? conn.getProxyHost() : conn.getHost();
197 }
198 Credentials credentials = proxy
199 ? state.getProxyCredentials(null, host) : state.getCredentials(null, host);
200 if (credentials == null) {
201 return false;
202 }
203 if (!(credentials instanceof UsernamePasswordCredentials)) {
204 throw new AuthenticationException(
205 "Credentials cannot be used for basic authentication: "
206 + credentials.toString());
207 }
208 String auth = BasicScheme.authenticate((UsernamePasswordCredentials) credentials);
209 if (auth != null) {
210 String s = proxy ? PROXY_AUTH_RESP : WWW_AUTH_RESP;
211 method.setRequestHeader(s, auth);
212 return true;
213 } else {
214 return false;
215 }
216 }
217
218
219 /***
220 * Attempt to provide default authentication credentials
221 * to the given method in the given context using basic
222 * authentication scheme.
223 *
224 * @param method the HttpMethod which requires authentication
225 * @param conn the connection to a specific host. This parameter
226 * may be <tt>null</tt> if default credentials (not specific
227 * to any particular host) are to be used
228 * @param state the HttpState object providing Credentials
229 *
230 * @return true if the <tt>Authenticate</tt> response header
231 * was added
232 *
233 * @throws AuthenticationException when a parsing or other error occurs
234
235 * @see HttpState#setCredentials(String,String,Credentials)
236 */
237 public static boolean authenticateDefault(
238 HttpMethod method,
239 HttpConnection conn,
240 HttpState state)
241 throws AuthenticationException {
242 LOG.trace(
243 "enter HttpAuthenticator.authenticateDefault(HttpMethod, HttpConnection, HttpState)");
244 return doAuthenticateDefault(method, conn, state, false);
245 }
246
247
248 /***
249 * Attempt to provide default proxy authentication credentials
250 * to the given method in the given context using basic
251 * authentication scheme.
252 *
253 * @param method the HttpMethod which requires authentication
254 * @param conn the connection to a specific host. This parameter
255 * may be <tt>null</tt> if default credentials (not specific
256 * to any particular host) are to be used
257 * @param state the HttpState object providing Credentials
258 *
259 * @return true if the <tt>Proxy-Authenticate</tt> response header
260 * was added
261 *
262 * @throws AuthenticationException when a parsing or other error occurs
263
264 * @see HttpState#setCredentials(String,Credentials)
265 */
266 public static boolean authenticateProxyDefault(
267 HttpMethod method,
268 HttpConnection conn,
269 HttpState state)
270 throws AuthenticationException {
271 LOG.trace("enter HttpAuthenticator.authenticateProxyDefault(HttpMethod, HttpState)");
272 return doAuthenticateDefault(method, conn, state, true);
273 }
274
275
276 private static boolean doAuthenticate(
277 AuthScheme authscheme,
278 HttpMethod method,
279 HttpConnection conn,
280 HttpState state,
281 boolean proxy)
282 throws AuthenticationException {
283 if (authscheme == null) {
284 throw new IllegalArgumentException("Authentication scheme may not be null");
285 }
286 if (method == null) {
287 throw new IllegalArgumentException("HTTP method may not be null");
288 }
289 if (state == null) {
290 throw new IllegalArgumentException("HTTP state may not be null");
291 }
292 String host = null;
293 if (conn != null) {
294 host = proxy ? conn.getProxyHost() : conn.getHost();
295 }
296 String realm = authscheme.getRealm();
297 // TODO: To be removed in the future. Required for backward compatibility
298 if (realm == null) {
299 Header hostheader = method.getRequestHeader("host");
300 if (hostheader != null) {
301 realm = hostheader.getValue();
302 }
303 }
304 if (LOG.isDebugEnabled()) {
305 if (realm == null) {
306 LOG.debug("Using default authentication realm");
307 } else {
308 LOG.debug("Using '" + realm + "' authentication realm");
309 }
310 }
311 Credentials credentials = proxy
312 ? state.getProxyCredentials(realm, host)
313 : state.getCredentials(realm, host);
314 if (credentials == null) {
315 throw new AuthenticationException(
316 "No credentials available for the " + authscheme.getSchemeName()
317 + "authentication realm '" + realm + "'");
318 }
319 String auth = authscheme.authenticate(credentials, method.getName(), method.getPath());
320 if (auth != null) {
321 String s = proxy ? PROXY_AUTH_RESP : WWW_AUTH_RESP;
322 method.setRequestHeader(s, auth);
323 return true;
324 } else {
325 return false;
326 }
327 }
328
329 /***
330 * Attempt to provide requisite authentication credentials to the
331 * given method in the given context using the given
332 * authentication scheme.
333 *
334 * @param authscheme The authentication scheme to be used
335 * @param method The HttpMethod which requires authentication
336 * @param conn the connection to a specific host. This parameter
337 * may be <tt>null</tt> if default credentials (not specific
338 * to any particular host) are to be used
339 * @param state The HttpState object providing Credentials
340 *
341 * @return true if the <tt>Authenticate</tt> response header was added
342 *
343 * @throws AuthenticationException when a parsing or other error occurs
344
345 * @see HttpState#setCredentials(String,Credentials)
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 AuthenticationException when a parsing or other error occurs
376
377 * @see HttpState#setCredentials(String,Credentials)
378 */
379 public static boolean authenticateProxy(
380 AuthScheme authscheme,
381 HttpMethod method,
382 HttpConnection conn,
383 HttpState state
384 ) throws AuthenticationException {
385 LOG.trace("enter HttpAuthenticator.authenticateProxy(AuthScheme, HttpMethod, HttpState)");
386 return doAuthenticate(authscheme, method, conn, state, true);
387 }
388 }
This page was automatically generated by Maven