1   /* Copyright 2004, 2005, 2006 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  import org.acegisecurity.util.StringSplitUtils;
23  
24  import org.apache.commons.codec.binary.Base64;
25  import org.apache.commons.codec.digest.DigestUtils;
26  
27  import org.springframework.mock.web.MockHttpServletRequest;
28  import org.springframework.mock.web.MockHttpServletResponse;
29  
30  import org.springframework.util.StringUtils;
31  
32  import java.util.Map;
33  
34  
35  /**
36   * Tests {@link DigestProcessingFilterEntryPoint}.
37   *
38   * @author Ben Alex
39   * @version $Id: DigestProcessingFilterEntryPointTests.java 1496 2006-05-23 13:38:33Z benalex $
40   */
41  public class DigestProcessingFilterEntryPointTests extends TestCase {
42      //~ Constructors ===================================================================================================
43  
44      public DigestProcessingFilterEntryPointTests() {
45          super();
46      }
47  
48      public DigestProcessingFilterEntryPointTests(String arg0) {
49          super(arg0);
50      }
51  
52      //~ Methods ========================================================================================================
53  
54      private void checkNonceValid(String nonce) {
55          // Check the nonce seems to be generated correctly
56          // format of nonce is:  
57          //   base64(expirationTime + ":" + md5Hex(expirationTime + ":" + key))
58          assertTrue(Base64.isArrayByteBase64(nonce.getBytes()));
59  
60          String decodedNonce = new String(Base64.decodeBase64(nonce.getBytes()));
61          String[] nonceTokens = StringUtils.delimitedListToStringArray(decodedNonce, ":");
62          assertEquals(2, nonceTokens.length);
63  
64          String expectedNonceSignature = DigestUtils.md5Hex(nonceTokens[0] + ":" + "key");
65          assertEquals(expectedNonceSignature, nonceTokens[1]);
66      }
67  
68      public static void main(String[] args) {
69          junit.textui.TestRunner.run(DigestProcessingFilterEntryPointTests.class);
70      }
71  
72      public final void setUp() throws Exception {
73          super.setUp();
74      }
75  
76      public void testDetectsMissingKey() throws Exception {
77          DigestProcessingFilterEntryPoint ep = new DigestProcessingFilterEntryPoint();
78          ep.setRealmName("realm");
79  
80          try {
81              ep.afterPropertiesSet();
82              fail("Should have thrown IllegalArgumentException");
83          } catch (IllegalArgumentException expected) {
84              assertEquals("key must be specified", expected.getMessage());
85          }
86      }
87  
88      public void testDetectsMissingRealmName() throws Exception {
89          DigestProcessingFilterEntryPoint ep = new DigestProcessingFilterEntryPoint();
90          ep.setKey("dcdc");
91          ep.setNonceValiditySeconds(12);
92  
93          try {
94              ep.afterPropertiesSet();
95              fail("Should have thrown IllegalArgumentException");
96          } catch (IllegalArgumentException expected) {
97              assertEquals("realmName must be specified", expected.getMessage());
98          }
99      }
100 
101     public void testGettersSetters() {
102         DigestProcessingFilterEntryPoint ep = new DigestProcessingFilterEntryPoint();
103         assertEquals(300, ep.getNonceValiditySeconds()); // 5 mins default
104         ep.setRealmName("realm");
105         assertEquals("realm", ep.getRealmName());
106         ep.setKey("dcdc");
107         assertEquals("dcdc", ep.getKey());
108         ep.setNonceValiditySeconds(12);
109         assertEquals(12, ep.getNonceValiditySeconds());
110     }
111 
112     public void testNormalOperation() throws Exception {
113         DigestProcessingFilterEntryPoint ep = new DigestProcessingFilterEntryPoint();
114         ep.setRealmName("hello");
115         ep.setKey("key");
116 
117         MockHttpServletRequest request = new MockHttpServletRequest();
118         request.setRequestURI("/some_path");
119 
120         MockHttpServletResponse response = new MockHttpServletResponse();
121 
122         ep.afterPropertiesSet();
123 
124         ep.commence(request, response, new DisabledException("foobar"));
125 
126         // Check response is properly formed
127         assertEquals(401, response.getStatus());
128         assertEquals(true, response.getHeader("WWW-Authenticate").toString().startsWith("Digest "));
129 
130         // Break up response header
131         String header = response.getHeader("WWW-Authenticate").toString().substring(7);
132         String[] headerEntries = StringUtils.commaDelimitedListToStringArray(header);
133         Map headerMap = StringSplitUtils.splitEachArrayElementAndCreateMap(headerEntries, "=", "\"");
134 
135         assertEquals("hello", headerMap.get("realm"));
136         assertEquals("auth", headerMap.get("qop"));
137         assertNull(headerMap.get("stale"));
138 
139         checkNonceValid((String) headerMap.get("nonce"));
140     }
141 
142     public void testOperationIfDueToStaleNonce() throws Exception {
143         DigestProcessingFilterEntryPoint ep = new DigestProcessingFilterEntryPoint();
144         ep.setRealmName("hello");
145         ep.setKey("key");
146 
147         MockHttpServletRequest request = new MockHttpServletRequest();
148         request.setRequestURI("/some_path");
149 
150         MockHttpServletResponse response = new MockHttpServletResponse();
151 
152         ep.afterPropertiesSet();
153 
154         ep.commence(request, response, new NonceExpiredException("expired nonce"));
155 
156         // Check response is properly formed
157         assertEquals(401, response.getStatus());
158         assertTrue(response.getHeader("WWW-Authenticate").toString().startsWith("Digest "));
159 
160         // Break up response header
161         String header = response.getHeader("WWW-Authenticate").toString().substring(7);
162         String[] headerEntries = StringUtils.commaDelimitedListToStringArray(header);
163         Map headerMap = StringSplitUtils.splitEachArrayElementAndCreateMap(headerEntries, "=", "\"");
164 
165         assertEquals("hello", headerMap.get("realm"));
166         assertEquals("auth", headerMap.get("qop"));
167         assertEquals("true", headerMap.get("stale"));
168 
169         checkNonceValid((String) headerMap.get("nonce"));
170     }
171 }