1 /*
2 * $Header: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/auth/HttpAuthenticator.java,v 1.7.2.2 2003/08/09 18:20:34 olegk Exp $
3 * $Revision: 1.7.2.2 $
4 * $Date: 2003/08/09 18:20:34 $
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 if (proxy) {
295 host = conn.getProxyHost();
296 } else {
297 host = conn.getVirtualHost();
298 if (host == null) {
299 host = conn.getHost();
300 }
301 }
302 }
303 String realm = authscheme.getRealm();
304 if (LOG.isDebugEnabled()) {
305 StringBuffer buffer = new StringBuffer();
306 buffer.append("Authenticating with the ");
307 if (realm == null) {
308 buffer.append("default");
309 } else {
310 buffer.append('\'');
311 buffer.append(realm);
312 buffer.append('\'');
313 }
314 buffer.append(" authentication realm at ");
315 buffer.append(host);
316 LOG.debug(buffer.toString());
317 }
318 // TODO: To be removed in the future. Required for backward compatibility
319 if (realm == null) {
320 realm = host;
321 }
322 Credentials credentials = proxy
323 ? state.getProxyCredentials(realm, host)
324 : state.getCredentials(realm, host);
325 if (credentials == null) {
326 throw new AuthenticationException(
327 "No credentials available for the '" + authscheme.getRealm()
328 + "' authentication realm at " + host);
329 }
330 String auth = authscheme.authenticate(credentials, method.getName(), method.getPath());
331 if (auth != null) {
332 String s = proxy ? PROXY_AUTH_RESP : WWW_AUTH_RESP;
333 method.setRequestHeader(s, auth);
334 return true;
335 } else {
336 return false;
337 }
338 }
339
340 /***
341 * Attempt to provide requisite authentication credentials to the
342 * given method in the given context using the given
343 * authentication scheme.
344 *
345 * @param authscheme The authentication scheme to be used
346 * @param method The HttpMethod which requires authentication
347 * @param conn the connection to a specific host. This parameter
348 * may be <tt>null</tt> if default credentials (not specific
349 * to any particular host) are to be used
350 * @param state The HttpState object providing Credentials
351 *
352 * @return true if the <tt>Authenticate</tt> response header was added
353 *
354 * @throws AuthenticationException when a parsing or other error occurs
355
356 * @see HttpState#setCredentials(String,Credentials)
357 */
358 public static boolean authenticate(
359 AuthScheme authscheme,
360 HttpMethod method,
361 HttpConnection conn,
362 HttpState state)
363 throws AuthenticationException {
364 LOG.trace(
365 "enter HttpAuthenticator.authenticate(AuthScheme, HttpMethod, HttpConnection, "
366 + "HttpState)");
367 return doAuthenticate(authscheme, method, conn, state, false);
368 }
369
370
371 /***
372 * Attempt to provide requisite proxy authentication credentials
373 * to the given method in the given context using
374 * the given authentication scheme.
375 *
376 * @param authscheme The authentication scheme to be used
377 * @param method the HttpMethod which requires authentication
378 * @param conn the connection to a specific host. This parameter
379 * may be <tt>null</tt> if default credentials (not specific
380 * to any particular host) are to be used
381 * @param state the HttpState object providing Credentials
382 *
383 * @return true if the <tt>Proxy-Authenticate</tt> response header
384 * was added
385 *
386 * @throws AuthenticationException when a parsing or other error occurs
387
388 * @see HttpState#setCredentials(String,Credentials)
389 */
390 public static boolean authenticateProxy(
391 AuthScheme authscheme,
392 HttpMethod method,
393 HttpConnection conn,
394 HttpState state
395 ) throws AuthenticationException {
396 LOG.trace("enter HttpAuthenticator.authenticateProxy(AuthScheme, HttpMethod, HttpState)");
397 return doAuthenticate(authscheme, method, conn, state, true);
398 }
399 }
This page was automatically generated by Maven