1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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 }