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 }