Java Source Code: org.apache.commons.httpclient.auth.TestDigestAuth


   1: /*
   2:  * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//httpclient/src/test/org/apache/commons/httpclient/auth/TestDigestAuth.java,v 1.2 2004/11/07 12:31:42 olegk Exp $
   3:  * $Revision: 480424 $
   4:  * $Date: 2006-11-29 06:56:49 +0100 (Wed, 29 Nov 2006) $
   5:  * ====================================================================
   6:  *
   7:  *  Licensed to the Apache Software Foundation (ASF) under one or more
   8:  *  contributor license agreements.  See the NOTICE file distributed with
   9:  *  this work for additional information regarding copyright ownership.
  10:  *  The ASF licenses this file to You under the Apache License, Version 2.0
  11:  *  (the "License"); you may not use this file except in compliance with
  12:  *  the License.  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: 
  30: package org.apache.commons.httpclient.auth;
  31: 
  32: import java.io.IOException;
  33: import java.util.Map;
  34: 
  35: import org.apache.commons.httpclient.FakeHttpMethod;
  36: import org.apache.commons.httpclient.Header;
  37: import org.apache.commons.httpclient.HttpClient;
  38: import org.apache.commons.httpclient.HttpStatus;
  39: import org.apache.commons.httpclient.HttpVersion;
  40: import org.apache.commons.httpclient.UsernamePasswordCredentials;
  41: import org.apache.commons.httpclient.protocol.Protocol;
  42: import org.apache.commons.httpclient.server.HttpService;
  43: import org.apache.commons.httpclient.server.RequestLine;
  44: import org.apache.commons.httpclient.server.SimpleHttpServer;
  45: import org.apache.commons.httpclient.server.SimpleRequest;
  46: import org.apache.commons.httpclient.server.SimpleResponse;
  47: 
  48: import junit.framework.Test;
  49: import junit.framework.TestCase;
  50: import junit.framework.TestSuite;
  51: 
  52: /**
  53:  * Test Methods for DigestScheme Authentication.
  54:  *
  55:  * @author Rodney Waldhoff
  56:  * @author <a href="mailto:jsdever@apache.org">Jeff Dever</a>
  57:  * @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a>
  58:  */
  59:	  public class TestDigestAuth extends TestCase {
  60:
  61:    // ------------------------------------------------------------ Constructor
  62:	      public TestDigestAuth(String testName) {
  63:        super(testName);
  64:    }
  65:
  66:    // ------------------------------------------------------------------- Main
  67:	      public static void main(String args[]) {
  68:        String[] testCaseName = { TestDigestAuth.class.getName() };
  69:        junit.textui.TestRunner.main(testCaseName);
  70:    }
  71:
  72:    // ------------------------------------------------------- TestCase Methods
  73:
  74:	      public static Test suite() {
  75:        return new TestSuite(TestDigestAuth.class);
  76:    }
  77:
  78:	      public void testDigestAuthenticationWithNoRealm() throws Exception {
  79:        String challenge = "Digest";
  80:	          try {
  81:            AuthScheme authscheme = new DigestScheme();
  82:            authscheme.processChallenge(challenge);
  83:            fail("Should have thrown MalformedChallengeException");
  84:        } catch(MalformedChallengeException e) {
  85:            // expected
  86:        }
  87:    }
  88:
  89:	      public void testDigestAuthenticationWithNoRealm2() throws Exception {
  90:        String challenge = "Digest ";
  91:	          try {
  92:            AuthScheme authscheme = new DigestScheme();
  93:            authscheme.processChallenge(challenge);
  94:            fail("Should have thrown MalformedChallengeException");
  95:        } catch(MalformedChallengeException e) {
  96:            // expected
  97:        }
  98:    }
  99:
 100:	      public void testDigestAuthenticationWithDefaultCreds() throws Exception {
 101:        String challenge = "Digest realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\"";
 102:        FakeHttpMethod method = new FakeHttpMethod("/");
 103:        UsernamePasswordCredentials cred = new UsernamePasswordCredentials("username","password");
 104:        AuthScheme authscheme = new DigestScheme();
 105:        authscheme.processChallenge(challenge);
 106:        String response = authscheme.authenticate(cred, method);
 107:        Map table = AuthChallengeParser.extractParams(response);
 108:        assertEquals("username", table.get("username"));
 109:        assertEquals("realm1", table.get("realm"));
 110:        assertEquals("/", table.get("uri"));
 111:        assertEquals("f2a3f18799759d4f1a1c068b92b573cb", table.get("nonce"));
 112:        assertEquals("e95a7ddf37c2eab009568b1ed134f89a", table.get("response"));
 113:    }
 114:
 115:	      public void testDigestAuthentication() throws Exception {
 116:        String challenge = "Digest realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\"";
 117:        UsernamePasswordCredentials cred = new UsernamePasswordCredentials("username","password");
 118:        FakeHttpMethod method = new FakeHttpMethod("/");
 119:        AuthScheme authscheme = new DigestScheme();
 120:        authscheme.processChallenge(challenge);
 121:        String response = authscheme.authenticate(cred, method);
 122:        Map table = AuthChallengeParser.extractParams(response);
 123:        assertEquals("username", table.get("username"));
 124:        assertEquals("realm1", table.get("realm"));
 125:        assertEquals("/", table.get("uri"));
 126:        assertEquals("f2a3f18799759d4f1a1c068b92b573cb", table.get("nonce"));
 127:        assertEquals("e95a7ddf37c2eab009568b1ed134f89a", table.get("response"));
 128:    }
 129:
 130:	      public void testDigestAuthenticationWithQueryStringInDigestURI() throws Exception {
 131:        String challenge = "Digest realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\"";
 132:        UsernamePasswordCredentials cred = new UsernamePasswordCredentials("username","password");
 133:        FakeHttpMethod method = new FakeHttpMethod("/");
 134:        method.setQueryString("param=value");
 135:        AuthScheme authscheme = new DigestScheme();
 136:        authscheme.processChallenge(challenge);
 137:        String response = authscheme.authenticate(cred, method);
 138:        Map table = AuthChallengeParser.extractParams(response);
 139:        assertEquals("username", table.get("username"));
 140:        assertEquals("realm1", table.get("realm"));
 141:        assertEquals("/?param=value", table.get("uri"));
 142:        assertEquals("f2a3f18799759d4f1a1c068b92b573cb", table.get("nonce"));
 143:        assertEquals("a847f58f5fef0bc087bcb9c3eb30e042", table.get("response"));
 144:    }
 145:
 146:	      public void testDigestAuthenticationWithMultipleRealms() throws Exception {
 147:        String challenge1 = "Digest realm=\"realm1\", nonce=\"abcde\"";
 148:        String challenge2 = "Digest realm=\"realm2\", nonce=\"123546\"";
 149:        UsernamePasswordCredentials cred = new UsernamePasswordCredentials("username","password");
 150:        UsernamePasswordCredentials cred2 = new UsernamePasswordCredentials("uname2","password2");
 151:
 152:        FakeHttpMethod method = new FakeHttpMethod("/");
 153:        AuthScheme authscheme1 = new DigestScheme();
 154:        authscheme1.processChallenge(challenge1);
 155:        String response1 = authscheme1.authenticate(cred, method);
 156:        Map table = AuthChallengeParser.extractParams(response1);
 157:        assertEquals("username", table.get("username"));
 158:        assertEquals("realm1", table.get("realm"));
 159:        assertEquals("/", table.get("uri"));
 160:        assertEquals("abcde", table.get("nonce"));
 161:        assertEquals("786f500303eac1478f3c2865e676ed68", table.get("response"));
 162:
 163:        AuthScheme authscheme2 = new DigestScheme();
 164:        authscheme2.processChallenge(challenge2);
 165:        String response2 = authscheme2.authenticate(cred2, method);
 166:        table = AuthChallengeParser.extractParams(response2);
 167:        assertEquals("uname2", table.get("username"));
 168:        assertEquals("realm2", table.get("realm"));
 169:        assertEquals("/", table.get("uri"));
 170:        assertEquals("123546", table.get("nonce"));
 171:        assertEquals("0283edd9ef06a38b378b3b74661391e9", table.get("response"));
 172:    }
 173:
 174:    /** 
 175:     * Test digest authentication using the MD5-sess algorithm.
 176:     */
 177:	      public void testDigestAuthenticationMD5Sess() throws Exception {
 178:        // Example using Digest auth with MD5-sess
 179:
 180:        String realm="realm";
 181:        String username="username";
 182:        String password="password";
 183:        String nonce="e273f1776275974f1a120d8b92c5b3cb";
 184:
 185:        String challenge="Digest realm=\"" + realm + "\", "
 186:            + "nonce=\"" + nonce + "\", "
 187:            + "opaque=\"SomeString\", "
 188:            + "stale=false, "
 189:            + "algorithm=MD5-sess, "
 190:            + "qop=\"auth,auth-int\""; // we pass both but expect auth to be used
 191:
 192:        UsernamePasswordCredentials cred =
 193:            new UsernamePasswordCredentials(username, password);
 194:        FakeHttpMethod method = new FakeHttpMethod("/");
 195:
 196:        AuthScheme authscheme = new DigestScheme();
 197:        authscheme.processChallenge(challenge);
 198:        String response = authscheme.authenticate(cred, method);
 199:        assertTrue(response.indexOf("nc=00000001") > 0); // test for quotes
 200:        assertTrue(response.indexOf("qop=auth") > 0); // test for quotes
 201:        Map table = AuthChallengeParser.extractParams(response);
 202:        assertEquals(username, table.get("username"));
 203:        assertEquals(realm, table.get("realm"));
 204:        assertEquals("MD5-sess", table.get("algorithm"));
 205:        assertEquals("/", table.get("uri"));
 206:        assertEquals(nonce, table.get("nonce"));
 207:        assertEquals(1, Integer.parseInt((String) table.get("nc"),16));
 208:        assertTrue(null != table.get("cnonce"));
 209:        assertEquals("SomeString", table.get("opaque"));
 210:        assertEquals("auth", table.get("qop"));
 211:        //@TODO: add better check
 212:        assertTrue(null != table.get("response")); 
 213:    }
 214:
 215:    /** 
 216:     * Test digest authentication using the MD5-sess algorithm.
 217:     */
 218:	      public void testDigestAuthenticationMD5SessNoQop() throws Exception {
 219:        // Example using Digest auth with MD5-sess
 220:
 221:        String realm="realm";
 222:        String username="username";
 223:        String password="password";
 224:        String nonce="e273f1776275974f1a120d8b92c5b3cb";
 225:
 226:        String challenge="Digest realm=\"" + realm + "\", "
 227:            + "nonce=\"" + nonce + "\", "
 228:            + "opaque=\"SomeString\", "
 229:            + "stale=false, "
 230:            + "algorithm=MD5-sess";
 231:
 232:        UsernamePasswordCredentials cred =
 233:            new UsernamePasswordCredentials(username, password);
 234:        FakeHttpMethod method = new FakeHttpMethod("/");
 235:
 236:        AuthScheme authscheme = new DigestScheme();
 237:        authscheme.processChallenge(challenge);
 238:        String response = authscheme.authenticate(cred, method);
 239:
 240:        Map table = AuthChallengeParser.extractParams(response);
 241:        assertEquals(username, table.get("username"));
 242:        assertEquals(realm, table.get("realm"));
 243:        assertEquals("MD5-sess", table.get("algorithm"));
 244:        assertEquals("/", table.get("uri"));
 245:        assertEquals(nonce, table.get("nonce"));
 246:        assertTrue(null == table.get("nc"));
 247:        assertEquals("SomeString", table.get("opaque"));
 248:        assertTrue(null == table.get("qop"));
 249:        //@TODO: add better check
 250:        assertTrue(null != table.get("response")); 
 251:    }
 252:
 253:    /** 
 254:     * Test digest authentication with invalud qop value
 255:     */
 256:	      public void testDigestAuthenticationMD5SessInvalidQop() throws Exception {
 257:        // Example using Digest auth with MD5-sess
 258:
 259:        String realm="realm";
 260:        String username="username";
 261:        String password="password";
 262:        String nonce="e273f1776275974f1a120d8b92c5b3cb";
 263:
 264:        String challenge="Digest realm=\"" + realm + "\", "
 265:            + "nonce=\"" + nonce + "\", "
 266:            + "opaque=\"SomeString\", "
 267:            + "stale=false, "
 268:            + "algorithm=MD5-sess, "
 269:            + "qop=\"jakarta\""; // jakarta is an invalid qop value
 270:
 271:        UsernamePasswordCredentials cred =
 272:            new UsernamePasswordCredentials(username, password);
 273:	          try {
 274:            AuthScheme authscheme = new DigestScheme();
 275:            authscheme.processChallenge(challenge);
 276:            fail("MalformedChallengeException exception expected due to invalid qop value");
 277:        } catch(MalformedChallengeException e) {
 278:        }
 279:    }
 280:
 281:	      private class StaleNonceService implements HttpService {
 282:
 283:	          public StaleNonceService() {
 284:            super();
 285:        }
 286:
 287:        public boolean process(final SimpleRequest request, final SimpleResponse response)
 288:            throws IOException
 289:	          {
 290:            RequestLine requestLine = request.getRequestLine();
 291:            HttpVersion ver = requestLine.getHttpVersion();
 292:            Header auth = request.getFirstHeader("Authorization");
 293:	              if (auth == null) { 
 294:                response.setStatusLine(ver, HttpStatus.SC_UNAUTHORIZED);
 295:                response.addHeader(new Header("WWW-Authenticate", 
 296:                        "Digest realm=\"realm1\", nonce=\"ABC123\""));
 297:                response.setBodyString("Authorization required");
 298:                return true;
 299:            } else {
 300:                Map table = AuthChallengeParser.extractParams(auth.getValue());
 301:                String nonce = (String)table.get("nonce");
 302:	                  if (nonce.equals("ABC123")) {
 303:                    response.setStatusLine(ver, HttpStatus.SC_UNAUTHORIZED);
 304:                    response.addHeader(new Header("WWW-Authenticate", 
 305:                            "Digest realm=\"realm1\", nonce=\"321CBA\", stale=\"true\""));
 306:                    response.setBodyString("Authorization required");
 307:                    return true;
 308:                } else {
 309:                    response.setStatusLine(ver, HttpStatus.SC_OK);
 310:                    response.setBodyString("Authorization successful");
 311:                    return true;
 312:                }
 313:            }
 314:        }
 315:    }
 316:
 317:    
 318:	      public void testDigestAuthenticationWithStaleNonce() throws Exception {
 319:        // configure the server
 320:        SimpleHttpServer server = new SimpleHttpServer(); // use arbitrary port
 321:        server.setTestname(getName());
 322:        server.setHttpService(new StaleNonceService());
 323:
 324:        // configure the client
 325:        HttpClient client = new HttpClient();
 326:        client.getHostConfiguration().setHost(
 327:                server.getLocalAddress(), server.getLocalPort(),
 328:                Protocol.getProtocol("http"));
 329:        
 330:        client.getState().setCredentials(AuthScope.ANY, 
 331:                new UsernamePasswordCredentials("username","password"));
 332:        
 333:        FakeHttpMethod httpget = new FakeHttpMethod("/");
 334:	          try {
 335:            client.executeMethod(httpget);
 336:        } finally {
 337:            httpget.releaseConnection();
 338:        }
 339:        assertNotNull(httpget.getStatusLine());
 340:        assertEquals(HttpStatus.SC_OK, httpget.getStatusLine().getStatusCode());
 341:        Map table = AuthChallengeParser.extractParams(
 342:                httpget.getRequestHeader("Authorization").getValue());
 343:        assertEquals("username", table.get("username"));
 344:        assertEquals("realm1", table.get("realm"));
 345:        assertEquals("/", table.get("uri"));
 346:        assertEquals("321CBA", table.get("nonce"));
 347:        assertEquals("7f5948eefa115296e9279225041527b3", table.get("response"));
 348:        server.destroy();
 349:    }
 350:
 351:}