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 package org.apache.commons.httpclient.auth;
31
32 import java.util.HashMap;
33 import java.util.Map;
34
35 import org.apache.commons.httpclient.Credentials;
36 import org.apache.commons.httpclient.Header;
37 import org.apache.commons.httpclient.HttpConnection;
38 import org.apache.commons.httpclient.HttpMethod;
39 import org.apache.commons.httpclient.HttpState;
40 import org.apache.commons.httpclient.UsernamePasswordCredentials;
41 import org.apache.commons.logging.Log;
42 import org.apache.commons.logging.LogFactory;
43
44 /***
45 * Utility methods for HTTP authorization and authentication. This class
46 * provides utility methods for generating responses to HTTP www and proxy
47 * authentication challenges.
48 *
49 * <blockquote>
50 * A client SHOULD assume that all paths at or deeper than the depth of the
51 * last symbolic element in the path field of the Request-URI also are within
52 * the protection space specified by the basic realm value of the current
53 * challenge. A client MAY preemptively send the corresponding Authorization
54 * header with requests for resources in that space without receipt of another
55 * challenge from the server. Similarly, when a client sends a request to a
56 * proxy, it may reuse a userid and password in the Proxy-Authorization header
57 * field without receiving another challenge from the proxy server.
58 * </blockquote>
59 * </p>
60 *
61 * @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
62 * @author Rodney Waldhoff
63 * @author <a href="mailto:jsdever@apache.org">Jeff Dever</a>
64 * @author Ortwin Gl�ck
65 * @author Sean C. Sullivan
66 * @author <a href="mailto:adrian@ephox.com">Adrian Sutton</a>
67 * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
68 * @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a>
69 *
70 * @deprecated no longer used
71 */
72 public final class HttpAuthenticator {
73
74 /*** Log object for this class. */
75 private static final Log LOG = LogFactory.getLog(HttpAuthenticator.class);
76
77 /***
78 * The www authenticate challange header.
79 */
80 public static final String WWW_AUTH = "WWW-Authenticate";
81
82 /***
83 * The www authenticate response header.
84 */
85 public static final String WWW_AUTH_RESP = "Authorization";
86
87 /***
88 * The proxy authenticate challange header.
89 */
90 public static final String PROXY_AUTH = "Proxy-Authenticate";
91
92 /***
93 * The proxy authenticate response header.
94 */
95 public static final String PROXY_AUTH_RESP = "Proxy-Authorization";
96
97 /*** Chooses the strongest authentication scheme supported from the
98 * array of authentication challenges. Currently only <code>NTLM</code>,
99 * <code>Digest</code>, <code>Basic</code> schemes are recognized.
100 * The <code>NTLM</code> scheme is considered the strongest and is
101 * preferred to all others. The <code>Digest</code> scheme is preferred to
102 * the <code>Basic</code> one which provides no encryption for credentials.
103 * The <code>Basic</code> scheme is used only if it is the only one
104 * supported.
105 *
106 * @param challenges The array of authentication challenges
107 *
108 * @return The strongest authentication scheme supported
109 *
110 * @throws MalformedChallengeException is thrown if an authentication
111 * challenge is malformed
112 * @throws UnsupportedOperationException when none of challenge types
113 * available is supported.
114 *
115 * @deprecated Use {@link AuthChallengeParser#parseChallenges(Header[])} and
116 * {@link AuthPolicy#getAuthScheme(String)}
117 */
118 public static AuthScheme selectAuthScheme(final Header[] challenges)
119 throws MalformedChallengeException {
120 LOG.trace("enter HttpAuthenticator.selectAuthScheme(Header[])");
121 if (challenges == null) {
122 throw new IllegalArgumentException("Array of challenges may not be null");
123 }
124 if (challenges.length == 0) {
125 throw new IllegalArgumentException("Array of challenges may not be empty");
126 }
127 String challenge = null;
128 Map challengemap = new HashMap(challenges.length);
129 for (int i = 0; i < challenges.length; i++) {
130 challenge = challenges[i].getValue();
131 String s = AuthChallengeParser.extractScheme(challenge);
132 challengemap.put(s, challenge);
133 }
134 challenge = (String) challengemap.get("ntlm");
135 if (challenge != null) {
136 return new NTLMScheme(challenge);
137 }
138 challenge = (String) challengemap.get("digest");
139 if (challenge != null) {
140 return new DigestScheme(challenge);
141 }
142 challenge = (String) challengemap.get("basic");
143 if (challenge != null) {
144 return new BasicScheme(challenge);
145 }
146 throw new UnsupportedOperationException(
147 "Authentication scheme(s) not supported: " + challengemap.toString());
148 }
149
150 private static boolean doAuthenticateDefault(
151 HttpMethod method,
152 HttpConnection conn,
153 HttpState state,
154 boolean proxy)
155 throws AuthenticationException {
156 if (method == null) {
157 throw new IllegalArgumentException("HTTP method may not be null");
158 }
159 if (state == null) {
160 throw new IllegalArgumentException("HTTP state may not be null");
161 }
162 String host = null;
163 if (conn != null) {
164 host = proxy ? conn.getProxyHost() : conn.getHost();
165 }
166 Credentials credentials = proxy
167 ? state.getProxyCredentials(null, host) : state.getCredentials(null, host);
168 if (credentials == null) {
169 return false;
170 }
171 if (!(credentials instanceof UsernamePasswordCredentials)) {
172 throw new InvalidCredentialsException(
173 "Credentials cannot be used for basic authentication: "
174 + credentials.toString());
175 }
176 String auth = BasicScheme.authenticate(
177 (UsernamePasswordCredentials) credentials,
178 method.getParams().getCredentialCharset());
179 if (auth != null) {
180 String s = proxy ? PROXY_AUTH_RESP : WWW_AUTH_RESP;
181 Header header = new Header(s, auth, true);
182 method.addRequestHeader(header);
183 return true;
184 } else {
185 return false;
186 }
187 }
188
189
190 /***
191 * Attempt to provide default authentication credentials
192 * to the given method in the given context using basic
193 * authentication scheme.
194 *
195 * @param method the HttpMethod which requires authentication
196 * @param conn the connection to a specific host. This parameter
197 * may be <tt>null</tt> if default credentials (not specific
198 * to any particular host) are to be used
199 * @param state the HttpState object providing Credentials
200 *
201 * @return true if the <tt>Authenticate</tt> response header
202 * was added
203 *
204 * @throws InvalidCredentialsException if authentication credentials
205 * are not valid or not applicable for basic scheme
206 * @throws AuthenticationException when a parsing or other error occurs
207 *
208 * @see HttpState#setCredentials(String,String,Credentials)
209 *
210 * @deprecated use AuthScheme
211 */
212 public static boolean authenticateDefault(
213 HttpMethod method,
214 HttpConnection conn,
215 HttpState state)
216 throws AuthenticationException {
217 LOG.trace(
218 "enter HttpAuthenticator.authenticateDefault(HttpMethod, HttpConnection, HttpState)");
219 return doAuthenticateDefault(method, conn, state, false);
220 }
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(
246 HttpMethod method,
247 HttpConnection conn,
248 HttpState state)
249 throws AuthenticationException {
250 LOG.trace("enter HttpAuthenticator.authenticateProxyDefault(HttpMethod, HttpState)");
251 return doAuthenticateDefault(method, conn, state, true);
252 }
253
254
255 private static boolean doAuthenticate(
256 AuthScheme authscheme,
257 HttpMethod method,
258 HttpConnection conn,
259 HttpState state,
260 boolean proxy)
261 throws AuthenticationException {
262 if (authscheme == null) {
263 throw new IllegalArgumentException("Authentication scheme may not be null");
264 }
265 if (method == null) {
266 throw new IllegalArgumentException("HTTP method may not be null");
267 }
268 if (state == null) {
269 throw new IllegalArgumentException("HTTP state may not be null");
270 }
271 String host = null;
272 if (conn != null) {
273 if (proxy) {
274 host = conn.getProxyHost();
275 } else {
276 host = method.getParams().getVirtualHost();
277 if (host == null) {
278 host = conn.getHost();
279 }
280 }
281 }
282 String realm = authscheme.getRealm();
283 if (LOG.isDebugEnabled()) {
284 StringBuffer buffer = new StringBuffer();
285 buffer.append("Using credentials for ");
286 if (realm == null) {
287 buffer.append("default");
288 } else {
289 buffer.append('\'');
290 buffer.append(realm);
291 buffer.append('\'');
292 }
293 buffer.append(" authentication realm at ");
294 buffer.append(host);
295 LOG.debug(buffer.toString());
296 }
297 Credentials credentials = proxy
298 ? state.getProxyCredentials(realm, host)
299 : state.getCredentials(realm, host);
300 if (credentials == null) {
301 StringBuffer buffer = new StringBuffer();
302 buffer.append("No credentials available for the ");
303 if (realm == null) {
304 buffer.append("default");
305 } else {
306 buffer.append('\'');
307 buffer.append(realm);
308 buffer.append('\'');
309 }
310 buffer.append(" authentication realm at ");
311 buffer.append(host);
312 throw new CredentialsNotAvailableException(buffer.toString());
313 }
314 String auth = authscheme.authenticate(credentials, method);
315 if (auth != null) {
316 String s = proxy ? PROXY_AUTH_RESP : WWW_AUTH_RESP;
317 Header header = new Header(s, auth, true);
318 method.addRequestHeader(header);
319 return true;
320 } else {
321 return false;
322 }
323 }
324
325 /***
326 * Attempt to provide requisite authentication credentials to the
327 * given method in the given context using the given
328 * authentication scheme.
329 *
330 * @param authscheme The authentication scheme to be used
331 * @param method The HttpMethod which requires authentication
332 * @param conn the connection to a specific host. This parameter
333 * may be <tt>null</tt> if default credentials (not specific
334 * to any particular host) are to be used
335 * @param state The HttpState object providing Credentials
336 *
337 * @return true if the <tt>Authenticate</tt> response header was added
338 *
339 * @throws CredentialsNotAvailableException if authentication credentials
340 * required to respond to the authentication challenge are not available
341 * @throws AuthenticationException when a parsing or other error occurs
342
343 * @see HttpState#setCredentials(String,String,Credentials)
344 *
345 * @deprecated use AuthScheme
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 CredentialsNotAvailableException if authentication credentials
376 * required to respond to the authentication challenge are not available
377 * @throws AuthenticationException when a parsing or other error occurs
378
379 * @see HttpState#setCredentials(String,String,Credentials)
380 *
381 * @deprecated use AuthScheme
382 */
383 public static boolean authenticateProxy(
384 AuthScheme authscheme,
385 HttpMethod method,
386 HttpConnection conn,
387 HttpState state
388 ) throws AuthenticationException {
389 LOG.trace("enter HttpAuthenticator.authenticateProxy(AuthScheme, HttpMethod, HttpState)");
390 return doAuthenticate(authscheme, method, conn, state, true);
391 }
392 }