    1   /* Copyright 2004, 2005 Acegi Technology Pty Limited
    2    *
    3    * Licensed under the Apache License, Version 2.0 (the "License");
    4    * you may not use this file except in compliance with the License.
    5    * You may obtain a copy of the License at
    6    *
    7    *     http://www.apache.org/licenses/LICENSE-2.0
    8    *
    9    * Unless required by applicable law or agreed to in writing, software
    10   * distributed under the License is distributed on an "AS IS" BASIS,
    11   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12   * See the License for the specific language governing permissions and
    13   * limitations under the License.
    14   */
    15
    16  package org.acegisecurity.ui.digestauth;
    17
    18  import junit.framework.TestCase;
    19
    20  import org.acegisecurity.DisabledException;
    21
    22
    23
    24  import org.acegisecurity.util.StringSplitUtils;
    25
    26  import org.apache.commons.codec.binary.Base64;
    27  import org.apache.commons.codec.digest.DigestUtils;
    28
    29  import org.springframework.util.StringUtils;
    30  import org.springframework.mock.web.MockHttpServletRequest;
    31  import org.springframework.mock.web.MockHttpServletResponse;
    32
    33  import java.util.Map;
    34
    35
    36  /***
    37   * Tests {@link DigestProcessingFilterEntryPoint}.
    38   *
    39   * @author Ben Alex
    40   * @version $Id: DigestProcessingFilterEntryPointTests.java,v 1.3 2005/11/17 00:56:27 benalex Exp $
    41   */
    42  public class DigestProcessingFilterEntryPointTests extends TestCase {
    43      //~ Constructors ===========================================================
    44
    45      public DigestProcessingFilterEntryPointTests() {
    46          super();
    47      }
    48
    49      public DigestProcessingFilterEntryPointTests(String arg0) {
    50          super(arg0);
    51      }
    52
    53      //~ Methods ================================================================
    54
    55      public final void setUp() throws Exception {
    56          super.setUp();
    57      }
    58
    59      public static void main(String[] args) {
    60          junit.textui.TestRunner.run(DigestProcessingFilterEntryPointTests.class);
    61      }
    62
    63      public void testDetectsMissingKey() throws Exception {
    64          DigestProcessingFilterEntryPoint ep = new DigestProcessingFilterEntryPoint();
    65          ep.setRealmName("realm");
    66
    67          try {
    68              ep.afterPropertiesSet();
    69              fail("Should have thrown IllegalArgumentException");
    70          } catch (IllegalArgumentException expected) {
    71              assertEquals("key must be specified", expected.getMessage());
    72          }
    73      }
    74
    75      public void testDetectsMissingRealmName() throws Exception {
    76          DigestProcessingFilterEntryPoint ep = new DigestProcessingFilterEntryPoint();
    77          ep.setKey("dcdc");
    78          ep.setNonceValiditySeconds(12);
    79
    80          try {
    81              ep.afterPropertiesSet();
    82              fail("Should have thrown IllegalArgumentException");
    83          } catch (IllegalArgumentException expected) {
    84              assertEquals("realmName must be specified", expected.getMessage());
    85          }
    86      }
    87
    88      public void testGettersSetters() {
    89          DigestProcessingFilterEntryPoint ep = new DigestProcessingFilterEntryPoint();
    90          assertEquals(300, ep.getNonceValiditySeconds()); // 5 mins default
    91          ep.setRealmName("realm");
    92          assertEquals("realm", ep.getRealmName());
    93          ep.setKey("dcdc");
    94          assertEquals("dcdc", ep.getKey());
    95          ep.setNonceValiditySeconds(12);
    96          assertEquals(12, ep.getNonceValiditySeconds());
    97      }
    98
    99      public void testNormalOperation() throws Exception {
    100         DigestProcessingFilterEntryPoint ep = new DigestProcessingFilterEntryPoint();
    101         ep.setRealmName("hello");
    102         ep.setKey("key");
    103
    104         MockHttpServletRequest request = new MockHttpServletRequest();
    105         request.setRequestURI("/some_path");
    106         MockHttpServletResponse response = new MockHttpServletResponse();
    107
    108         ep.afterPropertiesSet();
    109
    110         ep.commence(request, response, new DisabledException("foobar"));
    111
    112         // Check response is properly formed
    113         assertEquals(401, response.getStatus());
    114         assertEquals(true, response.getHeader("WWW-Authenticate").toString().startsWith("Digest "));
    115
    116         // Break up response header
    117         String header = response.getHeader("WWW-Authenticate").toString().substring(7);
    118         String[] headerEntries = StringUtils.commaDelimitedListToStringArray(header);
    119         Map headerMap = StringSplitUtils.splitEachArrayElementAndCreateMap(headerEntries, "=", "\"");
    120
    121         assertEquals("hello", headerMap.get("realm"));
    122         assertEquals("auth", headerMap.get("qop"));
    123         assertNull(headerMap.get("stale"));
    124
    125         checkNonceValid((String) headerMap.get("nonce"));
    126     }
    127
    128     public void testOperationIfDueToStaleNonce() throws Exception {
    129         DigestProcessingFilterEntryPoint ep = new DigestProcessingFilterEntryPoint();
    130         ep.setRealmName("hello");
    131         ep.setKey("key");
    132
    133         MockHttpServletRequest request = new MockHttpServletRequest();
    134         request.setRequestURI("/some_path");
    135         MockHttpServletResponse response = new MockHttpServletResponse();
    136
    137         ep.afterPropertiesSet();
    138
    139         ep.commence(request, response,
    140             new NonceExpiredException("expired nonce"));
    141
    142         // Check response is properly formed
    143         assertEquals(401, response.getStatus());
    144         assertTrue(response.getHeader("WWW-Authenticate").toString().startsWith("Digest "));
    145
    146         // Break up response header
    147         String header = response.getHeader("WWW-Authenticate").toString().substring(7);
    148         String[] headerEntries = StringUtils.commaDelimitedListToStringArray(header);
    149         Map headerMap = StringSplitUtils.splitEachArrayElementAndCreateMap(headerEntries,
    150                 "=", "\"");
    151
    152         assertEquals("hello", headerMap.get("realm"));
    153         assertEquals("auth", headerMap.get("qop"));
    154         assertEquals("true", headerMap.get("stale"));
    155
    156         checkNonceValid((String) headerMap.get("nonce"));
    157     }
    158
    159     private void checkNonceValid(String nonce) {
    160         // Check the nonce seems to be generated correctly
    161         // format of nonce is:
    162         //   base64(expirationTime + ":" + md5Hex(expirationTime + ":" + key))
    163         assertTrue(Base64.isArrayByteBase64(nonce.getBytes()));
    164
    165         String decodedNonce = new String(Base64.decodeBase64(nonce.getBytes()));
    166         String[] nonceTokens = StringUtils.delimitedListToStringArray(decodedNonce,
    167                 ":");
    168         assertEquals(2, nonceTokens.length);
    169
    170         String expectedNonceSignature = DigestUtils.md5Hex(nonceTokens[0] + ":"
    171                 + "key");
    172         assertEquals(expectedNonceSignature, nonceTokens[1]);
    173     }
    174 }