1. /*
  2. * $Header: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/auth/HttpAuthenticator.java,v 1.18 2004/04/18 23:51:36 jsdever Exp $
  3. * $Revision: 1.18 $
  4. * $Date: 2004/04/18 23:51:36 $
  5. *
  6. * ====================================================================
  7. *
  8. * Copyright 1999-2004 The Apache Software Foundation
  9. *
  10. * Licensed under the Apache License, Version 2.0 (the "License");
  11. * you may not use this file except in compliance with the License.
  12. * You may obtain a copy of the License at
  13. *
  14. * http://www.apache.org/licenses/LICENSE-2.0
  15. *
  16. * Unless required by applicable law or agreed to in writing, software
  17. * distributed under the License is distributed on an "AS IS" BASIS,
  18. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  19. * See the License for the specific language governing permissions and
  20. * limitations under the License.
  21. * ====================================================================
  22. *
  23. * This software consists of voluntary contributions made by many
  24. * individuals on behalf of the Apache Software Foundation. For more
  25. * information on the Apache Software Foundation, please see
  26. * <http://www.apache.org/>.
  27. *
  28. */
  29. package org.apache.commons.httpclient.auth;
  30. import java.util.HashMap;
  31. import java.util.Map;
  32. import org.apache.commons.httpclient.Credentials;
  33. import org.apache.commons.httpclient.Header;
  34. import org.apache.commons.httpclient.HttpConnection;
  35. import org.apache.commons.httpclient.HttpMethod;
  36. import org.apache.commons.httpclient.HttpState;
  37. import org.apache.commons.httpclient.UsernamePasswordCredentials;
  38. import org.apache.commons.logging.Log;
  39. import org.apache.commons.logging.LogFactory;
  40. /**
  41. * Utility methods for HTTP authorization and authentication. This class
  42. * provides utility methods for generating responses to HTTP www and proxy
  43. * authentication challenges.
  44. *
  45. * <blockquote>
  46. * A client SHOULD assume that all paths at or deeper than the depth of the
  47. * last symbolic element in the path field of the Request-URI also are within
  48. * the protection space specified by the basic realm value of the current
  49. * challenge. A client MAY preemptively send the corresponding Authorization
  50. * header with requests for resources in that space without receipt of another
  51. * challenge from the server. Similarly, when a client sends a request to a
  52. * proxy, it may reuse a userid and password in the Proxy-Authorization header
  53. * field without receiving another challenge from the proxy server.
  54. * </blockquote>
  55. * </p>
  56. *
  57. * @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
  58. * @author Rodney Waldhoff
  59. * @author <a href="mailto:jsdever@apache.org">Jeff Dever</a>
  60. * @author Ortwin Glück
  61. * @author Sean C. Sullivan
  62. * @author <a href="mailto:adrian@ephox.com">Adrian Sutton</a>
  63. * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
  64. * @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a>
  65. *
  66. * @deprecated no longer used
  67. */
  68. public final class HttpAuthenticator {
  69. /** Log object for this class. */
  70. private static final Log LOG = LogFactory.getLog(HttpAuthenticator.class);
  71. /**
  72. * The www authenticate challange header.
  73. */
  74. public static final String WWW_AUTH = "WWW-Authenticate";
  75. /**
  76. * The www authenticate response header.
  77. */
  78. public static final String WWW_AUTH_RESP = "Authorization";
  79. /**
  80. * The proxy authenticate challange header.
  81. */
  82. public static final String PROXY_AUTH = "Proxy-Authenticate";
  83. /**
  84. * The proxy authenticate response header.
  85. */
  86. public static final String PROXY_AUTH_RESP = "Proxy-Authorization";
  87. /** Chooses the strongest authentication scheme supported from the
  88. * array of authentication challenges. Currently only <code>NTLM</code>,
  89. * <code>Digest</code>, <code>Basic</code> schemes are recognized.
  90. * The <code>NTLM</code> scheme is considered the strongest and is
  91. * preferred to all others. The <code>Digest</code> scheme is preferred to
  92. * the <code>Basic</code> one which provides no encryption for credentials.
  93. * The <code>Basic</code> scheme is used only if it is the only one
  94. * supported.
  95. *
  96. * @param challenges The array of authentication challenges
  97. *
  98. * @return The strongest authentication scheme supported
  99. *
  100. * @throws MalformedChallengeException is thrown if an authentication
  101. * challenge is malformed
  102. * @throws UnsupportedOperationException when none of challenge types
  103. * available is supported.
  104. *
  105. * @deprecated Use {@link AuthChallengeParser#parseChallenges(Header[])} and
  106. * {@link AuthPolicy#getAuthScheme(String)}
  107. */
  108. public static AuthScheme selectAuthScheme(final Header[] challenges)
  109. throws MalformedChallengeException {
  110. LOG.trace("enter HttpAuthenticator.selectAuthScheme(Header[])");
  111. if (challenges == null) {
  112. throw new IllegalArgumentException("Array of challenges may not be null");
  113. }
  114. if (challenges.length == 0) {
  115. throw new IllegalArgumentException("Array of challenges may not be empty");
  116. }
  117. String challenge = null;
  118. Map challengemap = new HashMap(challenges.length);
  119. for (int i = 0; i < challenges.length; i++) {
  120. challenge = challenges[i].getValue();
  121. String s = AuthChallengeParser.extractScheme(challenge);
  122. challengemap.put(s, challenge);
  123. }
  124. challenge = (String) challengemap.get("ntlm");
  125. if (challenge != null) {
  126. return new NTLMScheme(challenge);
  127. }
  128. challenge = (String) challengemap.get("digest");
  129. if (challenge != null) {
  130. return new DigestScheme(challenge);
  131. }
  132. challenge = (String) challengemap.get("basic");
  133. if (challenge != null) {
  134. return new BasicScheme(challenge);
  135. }
  136. throw new UnsupportedOperationException(
  137. "Authentication scheme(s) not supported: " + challengemap.toString());
  138. }
  139. private static boolean doAuthenticateDefault(
  140. HttpMethod method,
  141. HttpConnection conn,
  142. HttpState state,
  143. boolean proxy)
  144. throws AuthenticationException {
  145. if (method == null) {
  146. throw new IllegalArgumentException("HTTP method may not be null");
  147. }
  148. if (state == null) {
  149. throw new IllegalArgumentException("HTTP state may not be null");
  150. }
  151. String host = null;
  152. if (conn != null) {
  153. host = proxy ? conn.getProxyHost() : conn.getHost();
  154. }
  155. Credentials credentials = proxy
  156. ? state.getProxyCredentials(null, host) : state.getCredentials(null, host);
  157. if (credentials == null) {
  158. return false;
  159. }
  160. if (!(credentials instanceof UsernamePasswordCredentials)) {
  161. throw new InvalidCredentialsException(
  162. "Credentials cannot be used for basic authentication: "
  163. + credentials.toString());
  164. }
  165. String auth = BasicScheme.authenticate(
  166. (UsernamePasswordCredentials) credentials,
  167. method.getParams().getCredentialCharset());
  168. if (auth != null) {
  169. String s = proxy ? PROXY_AUTH_RESP : WWW_AUTH_RESP;
  170. Header header = new Header(s, auth, true);
  171. method.addRequestHeader(header);
  172. return true;
  173. } else {
  174. return false;
  175. }
  176. }
  177. /**
  178. * Attempt to provide default authentication credentials
  179. * to the given method in the given context using basic
  180. * authentication scheme.
  181. *
  182. * @param method the HttpMethod which requires authentication
  183. * @param conn the connection to a specific host. This parameter
  184. * may be <tt>null</tt> if default credentials (not specific
  185. * to any particular host) are to be used
  186. * @param state the HttpState object providing Credentials
  187. *
  188. * @return true if the <tt>Authenticate</tt> response header
  189. * was added
  190. *
  191. * @throws InvalidCredentialsException if authentication credentials
  192. * are not valid or not applicable for basic scheme
  193. * @throws AuthenticationException when a parsing or other error occurs
  194. *
  195. * @see HttpState#setCredentials(String,String,Credentials)
  196. *
  197. * @deprecated use AuthScheme
  198. */
  199. public static boolean authenticateDefault(
  200. HttpMethod method,
  201. HttpConnection conn,
  202. HttpState state)
  203. throws AuthenticationException {
  204. LOG.trace(
  205. "enter HttpAuthenticator.authenticateDefault(HttpMethod, HttpConnection, HttpState)");
  206. return doAuthenticateDefault(method, conn, state, false);
  207. }
  208. /**
  209. * Attempt to provide default proxy authentication credentials
  210. * to the given method in the given context using basic
  211. * authentication scheme.
  212. *
  213. * @param method the HttpMethod which requires authentication
  214. * @param conn the connection to a specific host. This parameter
  215. * may be <tt>null</tt> if default credentials (not specific
  216. * to any particular host) are to be used
  217. * @param state the HttpState object providing Credentials
  218. *
  219. * @return true if the <tt>Proxy-Authenticate</tt> response header
  220. * was added
  221. *
  222. * @throws InvalidCredentialsException if authentication credentials
  223. * are not valid or not applicable for basic scheme
  224. * @throws AuthenticationException when a parsing or other error occurs
  225. * @see HttpState#setCredentials(String,String,Credentials)
  226. *
  227. * @deprecated use AuthScheme
  228. */
  229. public static boolean authenticateProxyDefault(
  230. HttpMethod method,
  231. HttpConnection conn,
  232. HttpState state)
  233. throws AuthenticationException {
  234. LOG.trace("enter HttpAuthenticator.authenticateProxyDefault(HttpMethod, HttpState)");
  235. return doAuthenticateDefault(method, conn, state, true);
  236. }
  237. private static boolean doAuthenticate(
  238. AuthScheme authscheme,
  239. HttpMethod method,
  240. HttpConnection conn,
  241. HttpState state,
  242. boolean proxy)
  243. throws AuthenticationException {
  244. if (authscheme == null) {
  245. throw new IllegalArgumentException("Authentication scheme may not be null");
  246. }
  247. if (method == null) {
  248. throw new IllegalArgumentException("HTTP method may not be null");
  249. }
  250. if (state == null) {
  251. throw new IllegalArgumentException("HTTP state may not be null");
  252. }
  253. String host = null;
  254. if (conn != null) {
  255. if (proxy) {
  256. host = conn.getProxyHost();
  257. } else {
  258. host = conn.getVirtualHost();
  259. if (host == null) {
  260. host = conn.getHost();
  261. }
  262. }
  263. }
  264. String realm = authscheme.getRealm();
  265. if (LOG.isDebugEnabled()) {
  266. StringBuffer buffer = new StringBuffer();
  267. buffer.append("Using credentials for ");
  268. if (realm == null) {
  269. buffer.append("default");
  270. } else {
  271. buffer.append('\'');
  272. buffer.append(realm);
  273. buffer.append('\'');
  274. }
  275. buffer.append(" authentication realm at ");
  276. buffer.append(host);
  277. LOG.debug(buffer.toString());
  278. }
  279. Credentials credentials = proxy
  280. ? state.getProxyCredentials(realm, host)
  281. : state.getCredentials(realm, host);
  282. if (credentials == null) {
  283. StringBuffer buffer = new StringBuffer();
  284. buffer.append("No credentials available for the ");
  285. if (realm == null) {
  286. buffer.append("default");
  287. } else {
  288. buffer.append('\'');
  289. buffer.append(realm);
  290. buffer.append('\'');
  291. }
  292. buffer.append(" authentication realm at ");
  293. buffer.append(host);
  294. throw new CredentialsNotAvailableException(buffer.toString());
  295. }
  296. String auth = authscheme.authenticate(credentials, method);
  297. if (auth != null) {
  298. String s = proxy ? PROXY_AUTH_RESP : WWW_AUTH_RESP;
  299. Header header = new Header(s, auth, true);
  300. method.addRequestHeader(header);
  301. return true;
  302. } else {
  303. return false;
  304. }
  305. }
  306. /**
  307. * Attempt to provide requisite authentication credentials to the
  308. * given method in the given context using the given
  309. * authentication scheme.
  310. *
  311. * @param authscheme The authentication scheme to be used
  312. * @param method The HttpMethod which requires authentication
  313. * @param conn the connection to a specific host. This parameter
  314. * may be <tt>null</tt> if default credentials (not specific
  315. * to any particular host) are to be used
  316. * @param state The HttpState object providing Credentials
  317. *
  318. * @return true if the <tt>Authenticate</tt> response header was added
  319. *
  320. * @throws CredentialsNotAvailableException if authentication credentials
  321. * required to respond to the authentication challenge are not available
  322. * @throws AuthenticationException when a parsing or other error occurs
  323. * @see HttpState#setCredentials(String,String,Credentials)
  324. *
  325. * @deprecated use AuthScheme
  326. */
  327. public static boolean authenticate(
  328. AuthScheme authscheme,
  329. HttpMethod method,
  330. HttpConnection conn,
  331. HttpState state)
  332. throws AuthenticationException {
  333. LOG.trace(
  334. "enter HttpAuthenticator.authenticate(AuthScheme, HttpMethod, HttpConnection, "
  335. + "HttpState)");
  336. return doAuthenticate(authscheme, method, conn, state, false);
  337. }
  338. /**
  339. * Attempt to provide requisite proxy authentication credentials
  340. * to the given method in the given context using
  341. * the given authentication scheme.
  342. *
  343. * @param authscheme The authentication scheme to be used
  344. * @param method the HttpMethod which requires authentication
  345. * @param conn the connection to a specific host. This parameter
  346. * may be <tt>null</tt> if default credentials (not specific
  347. * to any particular host) are to be used
  348. * @param state the HttpState object providing Credentials
  349. *
  350. * @return true if the <tt>Proxy-Authenticate</tt> response header
  351. * was added
  352. *
  353. * @throws CredentialsNotAvailableException if authentication credentials
  354. * required to respond to the authentication challenge are not available
  355. * @throws AuthenticationException when a parsing or other error occurs
  356. * @see HttpState#setCredentials(String,String,Credentials)
  357. *
  358. * @deprecated use AuthScheme
  359. */
  360. public static boolean authenticateProxy(
  361. AuthScheme authscheme,
  362. HttpMethod method,
  363. HttpConnection conn,
  364. HttpState state
  365. ) throws AuthenticationException {
  366. LOG.trace("enter HttpAuthenticator.authenticateProxy(AuthScheme, HttpMethod, HttpState)");
  367. return doAuthenticate(authscheme, method, conn, state, true);
  368. }
  369. }