1   /*
2    * $Header: /home/cvs/jakarta-commons/httpclient/src/test/org/apache/commons/httpclient/TestAuthenticator.java,v 1.43 2004/06/13 12:13:08 olegk Exp $
3    * $Revision: 1.43 $
4    * $Date: 2004/06/13 12:13:08 $
5    * ====================================================================
6    *
7    *  Copyright 1999-2004 The Apache Software Foundation
8    *
9    *  Licensed under the Apache License, Version 2.0 (the "License");
10   *  you may not use this file except in compliance with the License.
11   *  You may obtain a copy of the License at
12   *
13   *      http://www.apache.org/licenses/LICENSE-2.0
14   *
15   *  Unless required by applicable law or agreed to in writing, software
16   *  distributed under the License is distributed on an "AS IS" BASIS,
17   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18   *  See the License for the specific language governing permissions and
19   *  limitations under the License.
20   * ====================================================================
21   *
22   * This software consists of voluntary contributions made by many
23   * individuals on behalf of the Apache Software Foundation.  For more
24   * information on the Apache Software Foundation, please see
25   * <http://www.apache.org/>.
26   *
27   * [Additional notices, if required by prior licensing conditions]
28   *
29   */
30  
31  package org.apache.commons.httpclient;
32  
33  import java.util.Map;
34  
35  import junit.framework.Test;
36  import junit.framework.TestSuite;
37  
38  import org.apache.commons.httpclient.auth.AuthChallengeParser;
39  import org.apache.commons.httpclient.auth.AuthScheme;
40  import org.apache.commons.httpclient.auth.AuthScope;
41  import org.apache.commons.httpclient.auth.AuthenticationException;
42  import org.apache.commons.httpclient.auth.CredentialsNotAvailableException;
43  import org.apache.commons.httpclient.auth.DigestScheme;
44  import org.apache.commons.httpclient.auth.MalformedChallengeException;
45  import org.apache.commons.httpclient.auth.NTLMScheme;
46  
47  /***
48   * Unit tests for {@link Authenticator}.
49   *
50   * @author Rodney Waldhoff
51   * @author <a href="mailto:jsdever@apache.org">Jeff Dever</a>
52   * @version $Id: TestAuthenticator.java,v 1.43 2004/06/13 12:13:08 olegk Exp $
53   */
54  public class TestAuthenticator extends TestNoHostBase {
55  
56      // ------------------------------------------------------------ Constructor
57      public TestAuthenticator(String testName) {
58          super(testName);
59      }
60  
61      // ------------------------------------------------------------------- Main
62      public static void main(String args[]) {
63          String[] testCaseName = { TestAuthenticator.class.getName() };
64          junit.textui.TestRunner.main(testCaseName);
65      }
66  
67      // ------------------------------------------------------- TestCase Methods
68  
69      public static Test suite() {
70          return new TestSuite(TestAuthenticator.class);
71      }
72  
73  
74      // ---------------------------------- Helper functions
75  
76      protected static boolean doAuthenticate(
77          AuthScheme authscheme, 
78          HttpMethod method, 
79          HttpConnection conn,
80          HttpState state, 
81          boolean proxy)
82         throws AuthenticationException {
83  
84          if (authscheme == null) {
85              throw new IllegalArgumentException("Authentication scheme may not be null");
86          }
87          if (method == null) {
88              throw new IllegalArgumentException("HTTP method may not be null");
89          }
90          if (state == null) {
91              throw new IllegalArgumentException("HTTP state may not be null");
92          }
93          String host = AuthScope.ANY_HOST;
94          int port = AuthScope.ANY_PORT;
95          if (conn != null) {
96              if (proxy) {
97                  host = conn.getProxyHost();
98                  port = conn.getProxyPort();
99              } else {
100                 host = conn.getVirtualHost();
101                 if (host == null) {
102                     host = conn.getHost();
103                 }
104                 port = conn.getPort();
105             }
106         }
107         String realm = authscheme.getRealm();
108         AuthScope authscope = new AuthScope(host, port, realm); 
109         Credentials credentials = proxy 
110             ? state.getProxyCredentials(authscope) 
111             : state.getCredentials(authscope);
112         if (credentials == null) {
113             throw new CredentialsNotAvailableException("No credentials available");
114         }
115         String auth = authscheme.authenticate(credentials, method);
116         if (auth != null) {
117             String s = proxy ? "Proxy-Authorization" : "Authorization";
118             Header header = new Header(s, auth, true);
119             method.addRequestHeader(header);
120             return true;
121         } else {
122             return false;
123         }
124     }
125 
126     private static boolean authenticate(
127         AuthScheme authscheme, 
128         HttpMethod method, 
129         HttpConnection conn,
130         HttpState state) 
131         throws AuthenticationException {
132         return doAuthenticate(authscheme, method, conn, state, false);
133     }
134 
135     public static boolean authenticateProxy(
136         AuthScheme authscheme, 
137         HttpMethod method, 
138         HttpConnection conn,
139         HttpState state
140     ) throws AuthenticationException {
141        return doAuthenticate(authscheme, method, conn, state, true);
142     }
143 
144 
145     public void testCredentialConstructors() {
146         try {
147             new UsernamePasswordCredentials(null, null);
148             fail("IllegalArgumentException should have been thrown");
149         } catch (IllegalArgumentException e) {
150             // expected
151         }
152         try {
153             new NTCredentials("user", "password", null, null);
154             fail("IllegalArgumentException should have been thrown");
155         } catch (IllegalArgumentException e) {
156             // expected
157         }
158         try {
159             new NTCredentials("user", "password", "host", null);
160             fail("IllegalArgumentException should have been thrown");
161         } catch (IllegalArgumentException e) {
162             // expected
163         }
164         NTCredentials creds = new NTCredentials("user", null, "host", "domain");
165         assertNotNull(creds.getUserName());
166         assertNull(creds.getPassword());
167         assertNotNull(creds.getDomain());
168         assertNotNull(creds.getHost());
169     }
170 
171     // ---------------------------------- Test Methods for BasicScheme Authentication
172 
173     // Moved to a separate test case based on a new testing framework
174 
175     // --------------------------------- Test Methods for DigestScheme Authentication
176 
177     public void testDigestAuthenticationWithNoCreds() throws Exception {
178         String challenge = "Digest realm=\"realm1\", nonce=\"ABC123\"";
179         HttpState state = new HttpState();
180         HttpMethod method = new SimpleHttpMethod(new Header("WWW-Authenticate", challenge));
181         try {
182             AuthScheme authscheme = new DigestScheme();
183             authscheme.processChallenge(challenge);
184             authenticate(authscheme, method, null, state);
185             fail("Should have thrown CredentialsNotAvailableException");
186         } catch(CredentialsNotAvailableException e) {
187             // expected
188         }
189     }
190 
191     public void testDigestAuthenticationWithNoRealm() throws Exception {
192         String challenge = "Digest";
193         try {
194             AuthScheme authscheme = new DigestScheme();
195             authscheme.processChallenge(challenge);
196             fail("Should have thrown MalformedChallengeException");
197         } catch(MalformedChallengeException e) {
198             // expected
199         }
200     }
201 
202     public void testDigestAuthenticationWithNoRealm2() throws Exception {
203         String challenge = "Digest ";
204         try {
205             AuthScheme authscheme = new DigestScheme();
206             authscheme.processChallenge(challenge);
207             fail("Should have thrown MalformedChallengeException");
208         } catch(MalformedChallengeException e) {
209             // expected
210         }
211     }
212 
213     public void testDigestAuthenticationWithNullHttpState() throws Exception {
214         String challenge = "Digest realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\"";
215         HttpMethod method = new SimpleHttpMethod(new Header("WWW-Authenticate", challenge));
216         try {
217             AuthScheme authscheme = new DigestScheme();
218             authscheme.processChallenge(challenge);
219             authenticate(authscheme, method, null, null);
220             fail("Should have thrown IllegalArgumentException");
221         } catch(IllegalArgumentException e) {
222             // expected
223         }
224     }
225 
226     public void testDigestAuthenticationCaseInsensitivity() throws Exception {
227         String challenge = "dIgEsT ReAlM=\"realm1\", nOnCE=\"f2a3F18799759D4f1a1C068b92b573cB\"";
228         HttpState state = new HttpState();
229         UsernamePasswordCredentials cred = new UsernamePasswordCredentials("username","password");
230         state.setCredentials(AuthScope.ANY, cred);
231         HttpMethod method = new SimpleHttpMethod(new Header("WwW-AuThEnTiCaTe", challenge));
232         AuthScheme authscheme = new DigestScheme();
233         authscheme.processChallenge(challenge);
234         assertTrue(authenticate(authscheme, method, null, state));
235         assertTrue(null != method.getRequestHeader("Authorization"));
236     }
237 
238 
239     public void testDigestAuthenticationWithDefaultCreds() throws Exception {
240         String challenge = "Digest realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\"";
241         HttpState state = new HttpState();
242         UsernamePasswordCredentials cred = new UsernamePasswordCredentials("username","password");
243         state.setCredentials(AuthScope.ANY, cred);
244         HttpMethod method = new SimpleHttpMethod(new Header("WWW-Authenticate", challenge));
245         AuthScheme authscheme = new DigestScheme();
246         authscheme.processChallenge(challenge);
247         assertTrue(authenticate(authscheme, method, null, state));
248         assertTrue(null != method.getRequestHeader("Authorization"));
249         Map table = AuthChallengeParser.extractParams(method.getRequestHeader("Authorization").getValue());
250         assertEquals("username", table.get("username"));
251         assertEquals("realm1", table.get("realm"));
252         assertEquals("/", table.get("uri"));
253         assertEquals("f2a3f18799759d4f1a1c068b92b573cb", table.get("nonce"));
254         assertEquals("e95a7ddf37c2eab009568b1ed134f89a", table.get("response"));
255     }
256 
257     public void testDigestAuthentication() throws Exception {
258         String challenge = "Digest realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\"";
259         HttpState state = new HttpState();
260         UsernamePasswordCredentials cred = new UsernamePasswordCredentials("username","password");
261         state.setCredentials(AuthScope.ANY, cred);
262         HttpMethod method = new SimpleHttpMethod(new Header("WWW-Authenticate", challenge));
263         AuthScheme authscheme = new DigestScheme();
264         authscheme.processChallenge(challenge);
265         assertTrue(authenticate(authscheme, method, null, state));
266         assertTrue(null != method.getRequestHeader("Authorization"));
267         Map table = AuthChallengeParser.extractParams(method.getRequestHeader("Authorization").getValue());
268         assertEquals("username", table.get("username"));
269         assertEquals("realm1", table.get("realm"));
270         assertEquals("/", table.get("uri"));
271         assertEquals("f2a3f18799759d4f1a1c068b92b573cb", table.get("nonce"));
272         assertEquals("e95a7ddf37c2eab009568b1ed134f89a", table.get("response"));
273     }
274 
275     public void testDigestAuthenticationWithStaleNonce() throws Exception {
276         
277         String headers =
278             "HTTP/1.1 401 OK\r\n" +
279             "Connection: close\r\n" +
280             "Content-Length: 0\r\n" +
281             "WWW-Authenticate: Digest realm=\"realm1\", nonce=\"ABC123\"\r\n";
282         String headers2 =
283             "HTTP/1.1 401 OK\r\n" +
284             "Connection: close\r\n" +
285             "Content-Length: 0\r\n" +
286             "WWW-Authenticate: Digest realm=\"realm1\", nonce=\"321CBA\", stale=\"true\"\r\n";
287         String headers3 = 
288             "HTTP/1.1 200 OK\r\n" +
289             "Connection: close\r\n" +
290             "Server: HttpClient Test/2.0\r\n\r\n" +
291             "stuff\r\n";
292         
293         SimpleHttpConnection conn = new SimpleHttpConnection();
294         
295         conn.addResponse(headers);
296         conn.addResponse(headers2);
297         conn.addResponse(headers3);
298         UsernamePasswordCredentials cred = new UsernamePasswordCredentials("username","password");
299         client.getState().setCredentials(AuthScope.ANY, cred);
300 
301         connectionManager.setConnection(conn);
302 
303         SimpleHttpMethod method = new SimpleHttpMethod();
304         method.setDoAuthentication(true);
305         assertEquals("Authentication failed", 200, client.executeMethod(method));
306         Map table = AuthChallengeParser.extractParams(method.getRequestHeader("Authorization").getValue());
307         assertEquals("username", table.get("username"));
308         assertEquals("realm1", table.get("realm"));
309         assertEquals("/", table.get("uri"));
310         assertEquals("321CBA", table.get("nonce"));
311         assertEquals("7f5948eefa115296e9279225041527b3", table.get("response"));
312     }
313 
314     public void testDigestAuthenticationWithMultipleRealms() throws Exception {
315         String challenge1 = "Digest realm=\"realm1\", nonce=\"abcde\"";
316         String challenge2 = "Digest realm=\"realm2\", nonce=\"123546\"";
317         HttpState state = new HttpState();
318         UsernamePasswordCredentials cred = new UsernamePasswordCredentials("username","password");
319         state.setCredentials( new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT, "realm1"), cred);
320         UsernamePasswordCredentials cred2 = new UsernamePasswordCredentials("uname2","password2");
321         state.setCredentials(new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT, "realm2"), cred2);
322         AuthScheme authscheme1 = new DigestScheme();
323         authscheme1.processChallenge(challenge1);
324         AuthScheme authscheme2 = new DigestScheme();
325         authscheme2.processChallenge(challenge2);
326         {
327             HttpMethod method = new SimpleHttpMethod(new Header("WWW-Authenticate",challenge1));
328             assertTrue(authenticate(authscheme1, method, null, state));
329             assertTrue(null != method.getRequestHeader("Authorization"));
330             Map table = AuthChallengeParser.extractParams(method.getRequestHeader("Authorization").getValue());
331             assertEquals("username", table.get("username"));
332             assertEquals("realm1", table.get("realm"));
333             assertEquals("/", table.get("uri"));
334             assertEquals("abcde", table.get("nonce"));
335             assertEquals("786f500303eac1478f3c2865e676ed68", table.get("response"));
336         }
337         {
338             HttpMethod method = new SimpleHttpMethod(new Header("WWW-Authenticate",challenge2));
339             assertTrue(authenticate(authscheme2, method, null, state));
340             assertTrue(null != method.getRequestHeader("Authorization"));
341             Map table = AuthChallengeParser.extractParams(method.getRequestHeader("Authorization").getValue());
342             assertEquals("uname2", table.get("username"));
343             assertEquals("realm2", table.get("realm"));
344             assertEquals("/", table.get("uri"));
345             assertEquals("123546", table.get("nonce"));
346             assertEquals("0283edd9ef06a38b378b3b74661391e9", table.get("response"));
347         }
348     }
349 
350     /*** 
351      * Test digest authentication using the MD5-sess algorithm.
352      */
353     public void testDigestAuthenticationMD5Sess() throws Exception {
354         // Example using Digest auth with MD5-sess
355 
356         String realm="realm";
357         String username="username";
358         String password="password";
359         String nonce="e273f1776275974f1a120d8b92c5b3cb";
360 
361         String challenge="Digest realm=\"" + realm + "\", "
362             + "nonce=\"" + nonce + "\", "
363             + "opaque=\"SomeString\", "
364             + "stale=false, "
365             + "algorithm=MD5-sess, "
366             + "qop=\"auth,auth-int\""; // we pass both but expect auth to be used
367 
368         HttpState state = new HttpState();
369         UsernamePasswordCredentials cred =
370             new UsernamePasswordCredentials(username, password);
371         state.setCredentials(new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT, realm), cred);
372         AuthScheme authscheme = new DigestScheme();
373         authscheme.processChallenge(challenge);
374         HttpMethod method =
375             new SimpleHttpMethod(new Header("WWW-Authenticate", challenge));
376         assertTrue(authenticate(
377             authscheme, method, null, state));
378         assertTrue(null != method.getRequestHeader("Authorization"));
379         Map table = AuthChallengeParser.extractParams(method.getRequestHeader("Authorization").getValue());
380         assertEquals(username, table.get("username"));
381         assertEquals(realm, table.get("realm"));
382         assertEquals("MD5-sess", table.get("algorithm"));
383         assertEquals("/", table.get("uri"));
384         assertEquals(nonce, table.get("nonce"));
385         assertEquals(1, Integer.parseInt((String) table.get("nc"),16));
386         assertTrue(null != table.get("cnonce"));
387         assertEquals("SomeString", table.get("opaque"));
388         assertEquals("auth", table.get("qop"));
389         //@TODO: add better check
390         assertTrue(null != table.get("response")); 
391     }
392     
393     /*** 
394      * Test digest authentication using the MD5-sess algorithm.
395      */
396     public void testDigestAuthenticationMD5SessNoQop() throws Exception {
397         // Example using Digest auth with MD5-sess
398 
399         String realm="realm";
400         String username="username";
401         String password="password";
402         String nonce="e273f1776275974f1a120d8b92c5b3cb";
403 
404         String challenge="Digest realm=\"" + realm + "\", "
405             + "nonce=\"" + nonce + "\", "
406             + "opaque=\"SomeString\", "
407             + "stale=false, "
408             + "algorithm=MD5-sess";
409 
410         HttpState state = new HttpState();
411         UsernamePasswordCredentials cred =
412             new UsernamePasswordCredentials(username, password);
413         state.setCredentials(new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT, realm), cred);
414         AuthScheme authscheme = new DigestScheme();
415         authscheme.processChallenge(challenge);
416         HttpMethod method =
417             new SimpleHttpMethod(new Header("WWW-Authenticate", challenge));
418         assertTrue(authenticate(
419             authscheme, method, null, state));
420         assertTrue(null != method.getRequestHeader("Authorization"));
421         Map table = AuthChallengeParser.extractParams(method.getRequestHeader("Authorization").getValue());
422         assertEquals(username, table.get("username"));
423         assertEquals(realm, table.get("realm"));
424         assertEquals("MD5-sess", table.get("algorithm"));
425         assertEquals("/", table.get("uri"));
426         assertEquals(nonce, table.get("nonce"));
427         assertTrue(null == table.get("nc"));
428         assertEquals("SomeString", table.get("opaque"));
429         assertTrue(null == table.get("qop"));
430         //@TODO: add better check
431         assertTrue(null != table.get("response")); 
432     }
433     
434     /*** 
435      * Test digest authentication with invalud qop value
436      */
437     public void testDigestAuthenticationMD5SessInvalidQop() throws Exception {
438         // Example using Digest auth with MD5-sess
439 
440         String realm="realm";
441         String username="username";
442         String password="password";
443         String nonce="e273f1776275974f1a120d8b92c5b3cb";
444 
445         String challenge="Digest realm=\"" + realm + "\", "
446             + "nonce=\"" + nonce + "\", "
447             + "opaque=\"SomeString\", "
448             + "stale=false, "
449             + "algorithm=MD5-sess, "
450             + "qop=\"jakarta\""; // jakarta is an invalid qop value
451 
452         HttpState state = new HttpState();
453         UsernamePasswordCredentials cred =
454             new UsernamePasswordCredentials(username, password);
455         state.setCredentials(new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT, "realm1"), cred);
456         try {
457             AuthScheme authscheme = new DigestScheme();
458             authscheme.processChallenge(challenge);
459             fail("MalformedChallengeException exception expected due to invalid qop value");
460         } catch(MalformedChallengeException e) {
461             /* expected */
462         }
463     }    
464 
465     
466 
467     // --------------------------------- Test Methods for NTLM Authentication
468 
469     public void testNTLMAuthenticationWithNoCreds() {
470         String challenge = "NTLM";
471         HttpState state = new HttpState();
472         HttpMethod method = new SimpleHttpMethod(new Header("WWW-Authenticate", challenge));
473         method.addRequestHeader("Host", "host");
474         try {
475             AuthScheme authscheme = new NTLMScheme(challenge);
476             authenticate(authscheme, method, null, state);
477             fail("Should have thrown HttpException");
478         } catch(HttpException e) {
479             // expected
480         }
481     }
482 
483     public void testNTLMAuthenticationWithNullHttpState() throws Exception {
484         String challenge = "NTLM";
485         HttpMethod method = new SimpleHttpMethod(new Header("WWW-Authenticate", challenge));
486         method.addRequestHeader("Host", "host");
487         try {
488             AuthScheme authscheme = new NTLMScheme(challenge);
489             authenticate(authscheme, method, null, null);
490             fail("Should have thrown IllegalArgumentException");
491         } catch(IllegalArgumentException e) {
492             // expected
493         }
494     }
495 
496     public void testNTLMAuthenticationCaseInsensitivity() throws Exception {
497         String challenge = "nTlM";
498         HttpState state = new HttpState();
499         NTCredentials cred = new NTCredentials("username","password", "host",
500                 "domain");
501         state.setCredentials(AuthScope.ANY, cred);
502         HttpMethod method = new SimpleHttpMethod(new Header("WwW-AuThEnTiCaTe", challenge));
503         AuthScheme authscheme = new NTLMScheme(challenge);
504         assertTrue(authenticate(authscheme, method, null, state));
505         assertTrue(null != method.getRequestHeader("Authorization"));
506     }
507 
508     public void testNTLMAuthenticationResponse1() throws Exception {
509         String challenge = "NTLM";
510         String expected = "NTLM TlRMTVNTUAABAAAABlIAAAYABgAkAAAABAAEACAAAABIT" +
511             "1NURE9NQUlO";
512         HttpState state = new HttpState();
513         NTCredentials cred = new NTCredentials("username","password", "host",
514                 "domain");
515         state.setCredentials(AuthScope.ANY, cred);
516         HttpMethod method = new SimpleHttpMethod(new Header("WWW-Authenticate", challenge));
517         AuthScheme authscheme = new NTLMScheme(challenge);
518         assertTrue(authenticate(authscheme, method, null, state));
519         assertTrue(null != method.getRequestHeader("Authorization"));
520         assertEquals(expected,
521                 method.getRequestHeader("Authorization").getValue());
522     }
523     
524     public void testNTLMAuthenticationResponse2() throws Exception {
525         String challenge = 
526             "NTLM TlRMTVNTUAACAAAACgAKADAAAAAGgoEAPc4kP4LtCV8AAAAAAAAAAJ4AngA" +
527             "6AAAASU5UUkFFUEhPWAIAFABJAE4AVABSAEEARQBQAEgATwBYAAEAEgBCAE8AQQB" +
528             "SAEQAUgBPAE8ATQAEACgAaQBuAHQAcgBhAGUAcABoAG8AeAAuAGUAcABoAG8AeAA" +
529             "uAGMAbwBtAAMAPABCAG8AYQByAGQAcgBvAG8AbQAuAGkAbgB0AHIAYQBlAHAAaAB" +
530             "vAHgALgBlAHAAaABvAHgALgBjAG8AbQAAAAAA";
531 
532         String expected = "NTLM TlRMTVNTUAADAAAAGAAYAFIAAAAAAAAAagAAAAYABgB" +
533             "AAAAACAAIAEYAAAAEAAQATgAAAAAAAABqAAAABlIAAERPTUFJTlVTRVJOQU1FSE" +
534             "9TVAaC+vLxUEHnUtpItj9Dp4kzwQfd61Lztg==";
535         HttpState state = new HttpState();
536         NTCredentials cred = new NTCredentials("username","password", "host",
537                 "domain");
538         state.setCredentials(AuthScope.ANY, cred);
539         HttpMethod method = new SimpleHttpMethod(new Header("WWW-Authenticate", challenge));
540         AuthScheme authscheme = new NTLMScheme(challenge);
541         assertTrue(authenticate(authscheme, method, null, state));
542         assertTrue(null != method.getRequestHeader("Authorization"));
543         assertEquals(expected,
544                 method.getRequestHeader("Authorization").getValue());
545     }
546     
547     public void testNTLMAuthenticationWithDefaultCreds() throws Exception {
548         String challenge = 
549             "NTLM TlRMTVNTUAACAAAACgAKADAAAAAGgoEAPc4kP4LtCV8AAAAAAAAAAJ4AngA" +
550             "6AAAASU5UUkFFUEhPWAIAFABJAE4AVABSAEEARQBQAEgATwBYAAEAEgBCAE8AQQB" +
551             "SAEQAUgBPAE8ATQAEACgAaQBuAHQAcgBhAGUAcABoAG8AeAAuAGUAcABoAG8AeAA" +
552             "uAGMAbwBtAAMAPABCAG8AYQByAGQAcgBvAG8AbQAuAGkAbgB0AHIAYQBlAHAAaAB" +
553             "vAHgALgBlAHAAaABvAHgALgBjAG8AbQAAAAAA";
554         String expected = "NTLM TlRMTVNTUAADAAAAGAAYAFIAAAAAAAAAagAAAAYABgB" +
555             "AAAAACAAIAEYAAAAEAAQATgAAAAAAAABqAAAABlIAAERPTUFJTlVTRVJOQU1FSE" +
556             "9TVAaC+vLxUEHnUtpItj9Dp4kzwQfd61Lztg==";
557         HttpState state = new HttpState();
558         NTCredentials cred = new NTCredentials("username","password", "host",
559                 "domain");
560         state.setCredentials(AuthScope.ANY, cred);
561         HttpMethod method = new SimpleHttpMethod(new Header("WWW-Authenticate", challenge));
562         method.addRequestHeader("Host", "host");
563         AuthScheme authscheme = new NTLMScheme(challenge);
564         assertTrue(authenticate(authscheme, method, null, state));
565         assertTrue(null != method.getRequestHeader("Authorization"));
566         assertEquals(expected,
567                 method.getRequestHeader("Authorization").getValue());
568     }
569     
570     public void testNTLMAuthenticationRetry() throws Exception {
571         NTCredentials cred = new NTCredentials("username", "password", "host", "domain");
572         client.getState().setCredentials(AuthScope.ANY, cred);
573         HttpMethod method = new SimpleHttpMethod();
574         conn.addResponse(
575             "HTTP/1.1 401 Unauthorized\r\n" +
576             "WWW-Authenticate: NTLM\r\n" +
577             "Connection: close\r\n" +
578             "Server: HttpClient Test/2.0\r\n");
579         conn.addResponse(
580             "HTTP/1.1 401 Unauthorized\r\n" +
581             "WWW-Authenticate: NTLM TlRMTVNTUAACAAAAAAAAACgAAAABggAAU3J2Tm9uY2UAAAAAAAAAAA==\r\n" +
582             "Connection: close\r\n" +
583             "Server: HttpClient Test/2.0\r\n");
584         conn.addResponse(
585             "HTTP/1.1 200 OK\r\n" +
586             "Connection: close\r\n" +
587             "Server: HttpClient Test/2.0\r\n\r\n" +
588             "stuff\r\n");
589         client.executeMethod(method);
590         assertNull(method.getResponseHeader("WWW-Authenticate"));
591         assertEquals(200, method.getStatusCode());
592     }
593 
594     /*** 
595      * Test that the Unauthorized response is returned when doAuthentication is false.
596      */
597     public void testDoAuthenticateFalse() throws Exception {
598         client.getState().setCredentials(
599             new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT, "Protected"),
600             new UsernamePasswordCredentials("name", "pass"));
601         HttpMethod method = new SimpleHttpMethod();
602         method.setDoAuthentication(false);
603         conn.addResponse(
604             "HTTP/1.1 401 Unauthorized\r\n" + 
605             "WWW-Authenticate: Basic realm=\"Protected\"\r\n" +
606             "Connection: close\r\n" +
607             "Server: HttpClient Test/2.0\r\n");
608         conn.addResponse( 
609             "HTTP/1.1 200 OK\r\n" +
610             "Connection: close\r\n" +
611             "Server: HttpClient Test/2.0\r\n"); 
612         client.executeMethod(method);
613         assertNotNull(method.getResponseHeader("WWW-Authenticate"));
614         assertNull(method.getRequestHeader("Authorization"));
615         assertEquals(401, method.getStatusCode());
616 
617     }
618 
619 
620     /*** 
621      */
622     public void testInvalidCredentials() throws Exception {
623         client.getState().setCredentials(
624             new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT, "Protected"), 
625             new UsernamePasswordCredentials("name", "pass"));
626         HttpMethod method = new SimpleHttpMethod();
627         method.setDoAuthentication(false);
628         conn.addResponse(
629             "HTTP/1.1 401 Unauthorized\r\n" + 
630             "WWW-Authenticate: Basic realm=\"Protected\"\r\n" +
631             "Connection: close\r\n" +
632             "Server: HttpClient Test/2.0\r\n"
633                 );
634         client.executeMethod(method);
635         assertEquals(401, method.getStatusCode());
636     }
637 
638 
639     // --------------------------------- Test Methods for Multiple Authentication
640 
641     public void testMultipleChallengeBasic() throws Exception {
642         client.getState().setCredentials(AuthScope.ANY, 
643             new UsernamePasswordCredentials("name", "pass"));
644         HttpMethod method = new SimpleHttpMethod();
645         conn.addResponse(
646             "HTTP/1.1 401 Unauthorized\r\n" + 
647             "WWW-Authenticate: Unsupported\r\n" +
648             "WWW-Authenticate: Basic realm=\"Protected\"\r\n" +
649             "Connection: close\r\n" +
650             "Server: HttpClient Test/2.0\r\n"
651                 );
652         conn.addResponse( 
653             "HTTP/1.1 200 OK\r\n" +
654             "Connection: close\r\n" +
655             "Server: HttpClient Test/2.0\r\n"
656                 );
657         client.executeMethod(method);
658         Header authHeader = method.getRequestHeader("Authorization");
659         assertNotNull(authHeader);
660 
661         String authValue = authHeader.getValue();
662         assertTrue(authValue.startsWith("Basic"));
663     }
664 
665     public void testMultipleChallengeBasicLongRealm() throws Exception {
666         client.getState().setCredentials(AuthScope.ANY, 
667             new UsernamePasswordCredentials("name", "pass"));
668         HttpMethod method = new SimpleHttpMethod();
669         conn.addResponse(
670             "HTTP/1.1 401 Unauthorized\r\n" + 
671             "WWW-Authenticate: Unsupported\r\n" +
672             "WWW-Authenticate: Basic realm=\"This site is protected.  We put this message into the realm string, against all reasonable rationale, so that users would see it in the authentication dialog generated by your browser.\"\r\n" +
673             "Connection: close\r\n" +
674             "Server: HttpClient Test/2.0\r\n"
675                 );
676         conn.addResponse( 
677             "HTTP/1.1 200 OK\r\n" +
678             "Connection: close\r\n" +
679             "Server: HttpClient Test/2.0\r\n"
680                 ); 
681         client.executeMethod(method);
682         Header authHeader = method.getRequestHeader("Authorization");
683         assertNotNull(authHeader);
684 
685         String authValue = authHeader.getValue();
686         assertTrue(authValue.startsWith("Basic"));
687     }
688 
689 
690 
691 
692     public void testMultipleChallengeDigest() throws Exception {
693         client.getState().setCredentials(AuthScope.ANY, 
694             new UsernamePasswordCredentials("name", "pass"));
695         HttpMethod method = new SimpleHttpMethod();
696         conn.addResponse(
697             "HTTP/1.1 401 Unauthorized\r\n" + 
698             "WWW-Authenticate: Unsupported\r\n" +
699             "WWW-Authenticate: Digest realm=\"Protected\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\"\r\n" +
700             "WWW-Authenticate: Basic realm=\"Protected\"\r\n" +
701             "Connection: close\r\n" +
702             "Server: HttpClient Test/2.0\r\n"
703                 );
704         conn.addResponse( 
705             "HTTP/1.1 200 OK\r\n" +
706             "Connection: close\r\n" +
707             "Server: HttpClient Test/2.0\r\n"
708                 ); 
709         client.executeMethod(method);
710         Header authHeader = method.getRequestHeader("Authorization");
711         assertNotNull(authHeader);
712 
713         String authValue = authHeader.getValue();
714         assertTrue(authValue.startsWith("Digest"));
715     }
716 
717 
718     public void testMultipleProxyChallengeBasic() throws Exception {
719         client.getState().setProxyCredentials(
720             new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT, "Protected"), 
721             new UsernamePasswordCredentials("name", "pass"));
722         HttpMethod method = new SimpleHttpMethod();
723         conn.addResponse(
724             "HTTP/1.1 407 Proxy Authentication Required\r\n" + 
725             "Proxy-Authenticate: Basic realm=\"Protected\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\"\r\n" +
726             "Proxy-Authenticate: Unsupported\r\n" +
727             "Connection: close\r\n" +
728             "Server: HttpClient Test/2.0\r\n"
729                 );
730         conn.addResponse( 
731             "HTTP/1.1 200 OK\r\n" +
732             "Connection: close\r\n" +
733             "Server: HttpClient Test/2.0\r\n"
734                 ); 
735         client.executeMethod(method);
736         Header authHeader = method.getRequestHeader("Proxy-Authorization");
737         assertNotNull(authHeader);
738 
739         String authValue = authHeader.getValue();
740         assertTrue(authValue.startsWith("Basic"));
741     }
742 
743 
744     public void testMultipleProxyChallengeDigest() throws Exception {
745         client.getState().setProxyCredentials(
746             new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT, "Protected"), 
747             new UsernamePasswordCredentials("name", "pass"));
748         HttpMethod method = new SimpleHttpMethod();
749         conn.addResponse(
750             "HTTP/1.1 407 Proxy Authentication Required\r\n" + 
751             "Proxy-Authenticate: Basic realm=\"Protected\"\r\n" +
752             "Proxy-Authenticate: Digest realm=\"Protected\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\"\r\n" +
753             "Proxy-Authenticate: Unsupported\r\n" +
754             "Connection: close\r\n" +
755             "Server: HttpClient Test/2.0\r\n"
756                 );
757         conn.addResponse( 
758             "HTTP/1.1 200 OK\r\n" +
759             "Connection: close\r\n" +
760             "Server: HttpClient Test/2.0\r\n"
761                 ); 
762         client.executeMethod(method);
763         Header authHeader = method.getRequestHeader("Proxy-Authorization");
764         assertNotNull(authHeader);
765 
766         String authValue = authHeader.getValue();
767         assertTrue(authValue.startsWith("Digest"));
768     }
769 
770 }