001: /*
002: * $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 $
003: * $Revision: 480424 $
004: * $Date: 2006-11-29 06:56:49 +0100 (Wed, 29 Nov 2006) $
005: *
006: * ====================================================================
007: *
008: * Licensed to the Apache Software Foundation (ASF) under one or more
009: * contributor license agreements. See the NOTICE file distributed with
010: * this work for additional information regarding copyright ownership.
011: * The ASF licenses this file to You under the Apache License, Version 2.0
012: * (the "License"); you may not use this file except in compliance with
013: * the License. You may obtain a copy of the License at
014: *
015: * http://www.apache.org/licenses/LICENSE-2.0
016: *
017: * Unless required by applicable law or agreed to in writing, software
018: * distributed under the License is distributed on an "AS IS" BASIS,
019: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
020: * See the License for the specific language governing permissions and
021: * limitations under the License.
022: * ====================================================================
023: *
024: * This software consists of voluntary contributions made by many
025: * individuals on behalf of the Apache Software Foundation. For more
026: * information on the Apache Software Foundation, please see
027: * <http://www.apache.org/>.
028: *
029: */
030:
031: package org.apache.commons.httpclient.auth;
032:
033: import java.util.HashMap;
034: import java.util.Map;
035:
036: import org.apache.commons.httpclient.Credentials;
037: import org.apache.commons.httpclient.Header;
038: import org.apache.commons.httpclient.HttpConnection;
039: import org.apache.commons.httpclient.HttpMethod;
040: import org.apache.commons.httpclient.HttpState;
041: import org.apache.commons.httpclient.UsernamePasswordCredentials;
042: import org.apache.commons.logging.Log;
043: import org.apache.commons.logging.LogFactory;
044:
045: /**
046: * Utility methods for HTTP authorization and authentication. This class
047: * provides utility methods for generating responses to HTTP www and proxy
048: * authentication challenges.
049: *
050: * <blockquote>
051: * A client SHOULD assume that all paths at or deeper than the depth of the
052: * last symbolic element in the path field of the Request-URI also are within
053: * the protection space specified by the basic realm value of the current
054: * challenge. A client MAY preemptively send the corresponding Authorization
055: * header with requests for resources in that space without receipt of another
056: * challenge from the server. Similarly, when a client sends a request to a
057: * proxy, it may reuse a userid and password in the Proxy-Authorization header
058: * field without receiving another challenge from the proxy server.
059: * </blockquote>
060: * </p>
061: *
062: * @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
063: * @author Rodney Waldhoff
064: * @author <a href="mailto:jsdever@apache.org">Jeff Dever</a>
065: * @author Ortwin Gl�ck
066: * @author Sean C. Sullivan
067: * @author <a href="mailto:adrian@ephox.com">Adrian Sutton</a>
068: * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
069: * @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a>
070: *
071: * @deprecated no longer used
072: */
073: public final class HttpAuthenticator {
074:
075: /** Log object for this class. */
076: private static final Log LOG = LogFactory
077: .getLog(HttpAuthenticator.class);
078:
079: /**
080: * The www authenticate challange header.
081: */
082: public static final String WWW_AUTH = "WWW-Authenticate";
083:
084: /**
085: * The www authenticate response header.
086: */
087: public static final String WWW_AUTH_RESP = "Authorization";
088:
089: /**
090: * The proxy authenticate challange header.
091: */
092: public static final String PROXY_AUTH = "Proxy-Authenticate";
093:
094: /**
095: * The proxy authenticate response header.
096: */
097: public static final String PROXY_AUTH_RESP = "Proxy-Authorization";
098:
099: /** Chooses the strongest authentication scheme supported from the
100: * array of authentication challenges. Currently only <code>NTLM</code>,
101: * <code>Digest</code>, <code>Basic</code> schemes are recognized.
102: * The <code>NTLM</code> scheme is considered the strongest and is
103: * preferred to all others. The <code>Digest</code> scheme is preferred to
104: * the <code>Basic</code> one which provides no encryption for credentials.
105: * The <code>Basic</code> scheme is used only if it is the only one
106: * supported.
107: *
108: * @param challenges The array of authentication challenges
109: *
110: * @return The strongest authentication scheme supported
111: *
112: * @throws MalformedChallengeException is thrown if an authentication
113: * challenge is malformed
114: * @throws UnsupportedOperationException when none of challenge types
115: * available is supported.
116: *
117: * @deprecated Use {@link AuthChallengeParser#parseChallenges(Header[])} and
118: * {@link AuthPolicy#getAuthScheme(String)}
119: */
120: public static AuthScheme selectAuthScheme(final Header[] challenges)
121: throws MalformedChallengeException {
122: LOG.trace("enter HttpAuthenticator.selectAuthScheme(Header[])");
123: if (challenges == null) {
124: throw new IllegalArgumentException(
125: "Array of challenges may not be null");
126: }
127: if (challenges.length == 0) {
128: throw new IllegalArgumentException(
129: "Array of challenges may not be empty");
130: }
131: String challenge = null;
132: Map challengemap = new HashMap(challenges.length);
133: for (int i = 0; i < challenges.length; i++) {
134: challenge = challenges[i].getValue();
135: String s = AuthChallengeParser.extractScheme(challenge);
136: challengemap.put(s, challenge);
137: }
138: challenge = (String) challengemap.get("ntlm");
139: if (challenge != null) {
140: return new NTLMScheme(challenge);
141: }
142: challenge = (String) challengemap.get("digest");
143: if (challenge != null) {
144: return new DigestScheme(challenge);
145: }
146: challenge = (String) challengemap.get("basic");
147: if (challenge != null) {
148: return new BasicScheme(challenge);
149: }
150: throw new UnsupportedOperationException(
151: "Authentication scheme(s) not supported: "
152: + challengemap.toString());
153: }
154:
155: private static boolean doAuthenticateDefault(HttpMethod method,
156: HttpConnection conn, HttpState state, boolean proxy)
157: throws AuthenticationException {
158: if (method == null) {
159: throw new IllegalArgumentException(
160: "HTTP method may not be null");
161: }
162: if (state == null) {
163: throw new IllegalArgumentException(
164: "HTTP state may not be null");
165: }
166: String host = null;
167: if (conn != null) {
168: host = proxy ? conn.getProxyHost() : conn.getHost();
169: }
170: Credentials credentials = proxy ? state.getProxyCredentials(
171: null, host) : state.getCredentials(null, host);
172: if (credentials == null) {
173: return false;
174: }
175: if (!(credentials instanceof UsernamePasswordCredentials)) {
176: throw new InvalidCredentialsException(
177: "Credentials cannot be used for basic authentication: "
178: + credentials.toString());
179: }
180: String auth = BasicScheme.authenticate(
181: (UsernamePasswordCredentials) credentials, method
182: .getParams().getCredentialCharset());
183: if (auth != null) {
184: String s = proxy ? PROXY_AUTH_RESP : WWW_AUTH_RESP;
185: Header header = new Header(s, auth, true);
186: method.addRequestHeader(header);
187: return true;
188: } else {
189: return false;
190: }
191: }
192:
193: /**
194: * Attempt to provide default authentication credentials
195: * to the given method in the given context using basic
196: * authentication scheme.
197: *
198: * @param method the HttpMethod which requires authentication
199: * @param conn the connection to a specific host. This parameter
200: * may be <tt>null</tt> if default credentials (not specific
201: * to any particular host) are to be used
202: * @param state the HttpState object providing Credentials
203: *
204: * @return true if the <tt>Authenticate</tt> response header
205: * was added
206: *
207: * @throws InvalidCredentialsException if authentication credentials
208: * are not valid or not applicable for basic scheme
209: * @throws AuthenticationException when a parsing or other error occurs
210: *
211: * @see HttpState#setCredentials(String,String,Credentials)
212: *
213: * @deprecated use AuthScheme
214: */
215: public static boolean authenticateDefault(HttpMethod method,
216: HttpConnection conn, HttpState state)
217: throws AuthenticationException {
218: LOG
219: .trace("enter HttpAuthenticator.authenticateDefault(HttpMethod, HttpConnection, HttpState)");
220: return doAuthenticateDefault(method, conn, state, false);
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(HttpMethod method,
246: HttpConnection conn, HttpState state)
247: throws AuthenticationException {
248: LOG
249: .trace("enter HttpAuthenticator.authenticateProxyDefault(HttpMethod, HttpState)");
250: return doAuthenticateDefault(method, conn, state, true);
251: }
252:
253: private static boolean doAuthenticate(AuthScheme authscheme,
254: HttpMethod method, HttpConnection conn, HttpState state,
255: boolean proxy) throws AuthenticationException {
256: if (authscheme == null) {
257: throw new IllegalArgumentException(
258: "Authentication scheme may not be null");
259: }
260: if (method == null) {
261: throw new IllegalArgumentException(
262: "HTTP method may not be null");
263: }
264: if (state == null) {
265: throw new IllegalArgumentException(
266: "HTTP state may not be null");
267: }
268: String host = null;
269: if (conn != null) {
270: if (proxy) {
271: host = conn.getProxyHost();
272: } else {
273: host = method.getParams().getVirtualHost();
274: if (host == null) {
275: host = conn.getHost();
276: }
277: }
278: }
279: String realm = authscheme.getRealm();
280: if (LOG.isDebugEnabled()) {
281: StringBuffer buffer = new StringBuffer();
282: buffer.append("Using credentials for ");
283: if (realm == null) {
284: buffer.append("default");
285: } else {
286: buffer.append('\'');
287: buffer.append(realm);
288: buffer.append('\'');
289: }
290: buffer.append(" authentication realm at ");
291: buffer.append(host);
292: LOG.debug(buffer.toString());
293: }
294: Credentials credentials = proxy ? state.getProxyCredentials(
295: realm, host) : state.getCredentials(realm, host);
296: if (credentials == null) {
297: StringBuffer buffer = new StringBuffer();
298: buffer.append("No credentials available for the ");
299: if (realm == null) {
300: buffer.append("default");
301: } else {
302: buffer.append('\'');
303: buffer.append(realm);
304: buffer.append('\'');
305: }
306: buffer.append(" authentication realm at ");
307: buffer.append(host);
308: throw new CredentialsNotAvailableException(buffer
309: .toString());
310: }
311: String auth = authscheme.authenticate(credentials, method);
312: if (auth != null) {
313: String s = proxy ? PROXY_AUTH_RESP : WWW_AUTH_RESP;
314: Header header = new Header(s, auth, true);
315: method.addRequestHeader(header);
316: return true;
317: } else {
318: return false;
319: }
320: }
321:
322: /**
323: * Attempt to provide requisite authentication credentials to the
324: * given method in the given context using the given
325: * authentication scheme.
326: *
327: * @param authscheme The authentication scheme to be used
328: * @param method The HttpMethod which requires authentication
329: * @param conn the connection to a specific host. This parameter
330: * may be <tt>null</tt> if default credentials (not specific
331: * to any particular host) are to be used
332: * @param state The HttpState object providing Credentials
333: *
334: * @return true if the <tt>Authenticate</tt> response header was added
335: *
336: * @throws CredentialsNotAvailableException if authentication credentials
337: * required to respond to the authentication challenge are not available
338: * @throws AuthenticationException when a parsing or other error occurs
339:
340: * @see HttpState#setCredentials(String,String,Credentials)
341: *
342: * @deprecated use AuthScheme
343: */
344: public static boolean authenticate(AuthScheme authscheme,
345: HttpMethod method, HttpConnection conn, HttpState state)
346: throws AuthenticationException {
347: LOG
348: .trace("enter HttpAuthenticator.authenticate(AuthScheme, HttpMethod, HttpConnection, "
349: + "HttpState)");
350: return doAuthenticate(authscheme, method, conn, state, false);
351: }
352:
353: /**
354: * Attempt to provide requisite proxy authentication credentials
355: * to the given method in the given context using
356: * the given authentication scheme.
357: *
358: * @param authscheme The authentication scheme to be used
359: * @param method the HttpMethod which requires authentication
360: * @param conn the connection to a specific host. This parameter
361: * may be <tt>null</tt> if default credentials (not specific
362: * to any particular host) are to be used
363: * @param state the HttpState object providing Credentials
364: *
365: * @return true if the <tt>Proxy-Authenticate</tt> response header
366: * was added
367: *
368: * @throws CredentialsNotAvailableException if authentication credentials
369: * required to respond to the authentication challenge are not available
370: * @throws AuthenticationException when a parsing or other error occurs
371:
372: * @see HttpState#setCredentials(String,String,Credentials)
373: *
374: * @deprecated use AuthScheme
375: */
376: public static boolean authenticateProxy(AuthScheme authscheme,
377: HttpMethod method, HttpConnection conn, HttpState state)
378: throws AuthenticationException {
379: LOG
380: .trace("enter HttpAuthenticator.authenticateProxy(AuthScheme, HttpMethod, HttpState)");
381: return doAuthenticate(authscheme, method, conn, state, true);
382: }
383: }
|