001: /* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
002: *
003: * Licensed under the Apache License, Version 2.0 (the "License");
004: * you may not use this file except in compliance with the License.
005: * You may obtain a copy of the License at
006: *
007: * http://www.apache.org/licenses/LICENSE-2.0
008: *
009: * Unless required by applicable law or agreed to in writing, software
010: * distributed under the License is distributed on an "AS IS" BASIS,
011: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012: * See the License for the specific language governing permissions and
013: * limitations under the License.
014: */
015:
016: package org.acegisecurity.providers.cas;
017:
018: import junit.framework.TestCase;
019:
020: import org.acegisecurity.Authentication;
021: import org.acegisecurity.AuthenticationException;
022: import org.acegisecurity.BadCredentialsException;
023: import org.acegisecurity.GrantedAuthority;
024: import org.acegisecurity.GrantedAuthorityImpl;
025:
026: import org.acegisecurity.providers.TestingAuthenticationToken;
027: import org.acegisecurity.providers.UsernamePasswordAuthenticationToken;
028: import org.acegisecurity.providers.cas.ticketvalidator.AbstractTicketValidator;
029:
030: import org.acegisecurity.ui.cas.CasProcessingFilter;
031:
032: import org.acegisecurity.userdetails.User;
033: import org.acegisecurity.userdetails.UserDetails;
034:
035: import java.util.HashMap;
036: import java.util.List;
037: import java.util.Map;
038: import java.util.Vector;
039:
040: /**
041: * Tests {@link CasAuthenticationProvider}.
042: *
043: * @author Ben Alex
044: * @version $Id: CasAuthenticationProviderTests.java 1496 2006-05-23 13:38:33Z benalex $
045: */
046: public class CasAuthenticationProviderTests extends TestCase {
047: //~ Constructors ===================================================================================================
048:
049: public CasAuthenticationProviderTests() {
050: super ();
051: }
052:
053: public CasAuthenticationProviderTests(String arg0) {
054: super (arg0);
055: }
056:
057: //~ Methods ========================================================================================================
058:
059: public static void main(String[] args) {
060: junit.textui.TestRunner
061: .run(CasAuthenticationProviderTests.class);
062: }
063:
064: private UserDetails makeUserDetails() {
065: return new User("user", "password", true, true, true, true,
066: new GrantedAuthority[] {
067: new GrantedAuthorityImpl("ROLE_ONE"),
068: new GrantedAuthorityImpl("ROLE_TWO") });
069: }
070:
071: private UserDetails makeUserDetailsFromAuthoritiesPopulator() {
072: return new User("user", "password", true, true, true, true,
073: new GrantedAuthority[] {
074: new GrantedAuthorityImpl("ROLE_A"),
075: new GrantedAuthorityImpl("ROLE_B") });
076: }
077:
078: public final void setUp() throws Exception {
079: super .setUp();
080: }
081:
082: public void testAuthenticateStateful() throws Exception {
083: CasAuthenticationProvider cap = new CasAuthenticationProvider();
084: cap.setCasAuthoritiesPopulator(new MockAuthoritiesPopulator());
085: cap.setCasProxyDecider(new MockProxyDecider(true));
086: cap.setKey("qwerty");
087:
088: StatelessTicketCache cache = new MockStatelessTicketCache();
089: cap.setStatelessTicketCache(cache);
090: cap.setTicketValidator(new MockTicketValidator(true));
091: cap.afterPropertiesSet();
092:
093: UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
094: CasProcessingFilter.CAS_STATEFUL_IDENTIFIER, "ST-123");
095:
096: Authentication result = cap.authenticate(token);
097:
098: // Confirm ST-123 was NOT added to the cache
099: assertTrue(cache.getByTicketId("ST-456") == null);
100:
101: if (!(result instanceof CasAuthenticationToken)) {
102: fail("Should have returned a CasAuthenticationToken");
103: }
104:
105: CasAuthenticationToken casResult = (CasAuthenticationToken) result;
106: assertEquals(makeUserDetailsFromAuthoritiesPopulator(),
107: casResult.getPrincipal());
108: assertEquals(
109: "PGTIOU-0-R0zlgrl4pdAQwBvJWO3vnNpevwqStbSGcq3vKB2SqSFFRnjPHt",
110: casResult.getProxyGrantingTicketIou());
111: assertEquals(
112: "https://localhost/portal/j_acegi_cas_security_check",
113: casResult.getProxyList().get(0));
114: assertEquals("ST-123", casResult.getCredentials());
115: assertEquals(new GrantedAuthorityImpl("ROLE_A"), casResult
116: .getAuthorities()[0]);
117: assertEquals(new GrantedAuthorityImpl("ROLE_B"), casResult
118: .getAuthorities()[1]);
119: assertEquals(cap.getKey().hashCode(), casResult.getKeyHash());
120:
121: // Now confirm the CasAuthenticationToken is automatically re-accepted.
122: // To ensure TicketValidator not called again, set it to deliver an exception...
123: cap.setTicketValidator(new MockTicketValidator(false));
124:
125: Authentication laterResult = cap.authenticate(result);
126: assertEquals(result, laterResult);
127: }
128:
129: public void testAuthenticateStateless() throws Exception {
130: CasAuthenticationProvider cap = new CasAuthenticationProvider();
131: cap.setCasAuthoritiesPopulator(new MockAuthoritiesPopulator());
132: cap.setCasProxyDecider(new MockProxyDecider(true));
133: cap.setKey("qwerty");
134:
135: StatelessTicketCache cache = new MockStatelessTicketCache();
136: cap.setStatelessTicketCache(cache);
137: cap.setTicketValidator(new MockTicketValidator(true));
138: cap.afterPropertiesSet();
139:
140: UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
141: CasProcessingFilter.CAS_STATELESS_IDENTIFIER, "ST-456");
142:
143: Authentication result = cap.authenticate(token);
144:
145: // Confirm ST-456 was added to the cache
146: assertTrue(cache.getByTicketId("ST-456") != null);
147:
148: if (!(result instanceof CasAuthenticationToken)) {
149: fail("Should have returned a CasAuthenticationToken");
150: }
151:
152: assertEquals(makeUserDetailsFromAuthoritiesPopulator(), result
153: .getPrincipal());
154: assertEquals("ST-456", result.getCredentials());
155:
156: // Now try to authenticate again. To ensure TicketValidator not
157: // called again, set it to deliver an exception...
158: cap.setTicketValidator(new MockTicketValidator(false));
159:
160: // Previously created UsernamePasswordAuthenticationToken is OK
161: Authentication newResult = cap.authenticate(token);
162: assertEquals(makeUserDetailsFromAuthoritiesPopulator(),
163: newResult.getPrincipal());
164: assertEquals("ST-456", newResult.getCredentials());
165: }
166:
167: public void testDetectsAMissingTicketId() throws Exception {
168: CasAuthenticationProvider cap = new CasAuthenticationProvider();
169: cap.setCasAuthoritiesPopulator(new MockAuthoritiesPopulator());
170: cap.setCasProxyDecider(new MockProxyDecider(true));
171: cap.setKey("qwerty");
172:
173: StatelessTicketCache cache = new MockStatelessTicketCache();
174: cap.setStatelessTicketCache(cache);
175: cap.setTicketValidator(new MockTicketValidator(true));
176: cap.afterPropertiesSet();
177:
178: UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
179: CasProcessingFilter.CAS_STATEFUL_IDENTIFIER, "");
180:
181: try {
182: Authentication result = cap.authenticate(token);
183: fail("Should have thrown BadCredentialsException");
184: } catch (BadCredentialsException expected) {
185: assertEquals(
186: "Failed to provide a CAS service ticket to validate",
187: expected.getMessage());
188: }
189: }
190:
191: public void testDetectsAnInvalidKey() throws Exception {
192: CasAuthenticationProvider cap = new CasAuthenticationProvider();
193: cap.setCasAuthoritiesPopulator(new MockAuthoritiesPopulator());
194: cap.setCasProxyDecider(new MockProxyDecider(true));
195: cap.setKey("qwerty");
196:
197: StatelessTicketCache cache = new MockStatelessTicketCache();
198: cap.setStatelessTicketCache(cache);
199: cap.setTicketValidator(new MockTicketValidator(true));
200: cap.afterPropertiesSet();
201:
202: CasAuthenticationToken token = new CasAuthenticationToken(
203: "WRONG_KEY",
204: makeUserDetails(),
205: "credentials",
206: new GrantedAuthority[] { new GrantedAuthorityImpl("XX") },
207: makeUserDetails(), new Vector(), "IOU-xxx");
208:
209: try {
210: Authentication result = cap.authenticate(token);
211: fail("Should have thrown BadCredentialsException");
212: } catch (BadCredentialsException expected) {
213: assertEquals(
214: "The presented CasAuthenticationToken does not contain the expected key",
215: expected.getMessage());
216: }
217: }
218:
219: public void testDetectsMissingAuthoritiesPopulator()
220: throws Exception {
221: CasAuthenticationProvider cap = new CasAuthenticationProvider();
222: cap.setCasProxyDecider(new MockProxyDecider());
223: cap.setKey("qwerty");
224: cap.setStatelessTicketCache(new MockStatelessTicketCache());
225: cap.setTicketValidator(new MockTicketValidator(true));
226:
227: try {
228: cap.afterPropertiesSet();
229: fail("Should have thrown IllegalArgumentException");
230: } catch (IllegalArgumentException expected) {
231: assertEquals("A casAuthoritiesPopulator must be set",
232: expected.getMessage());
233: }
234: }
235:
236: public void testDetectsMissingKey() throws Exception {
237: CasAuthenticationProvider cap = new CasAuthenticationProvider();
238: cap.setCasAuthoritiesPopulator(new MockAuthoritiesPopulator());
239: cap.setCasProxyDecider(new MockProxyDecider());
240: cap.setStatelessTicketCache(new MockStatelessTicketCache());
241: cap.setTicketValidator(new MockTicketValidator(true));
242:
243: try {
244: cap.afterPropertiesSet();
245: fail("Should have thrown IllegalArgumentException");
246: } catch (IllegalArgumentException expected) {
247: assertEquals(
248: "A Key is required so CasAuthenticationProvider can identify tokens it previously authenticated",
249: expected.getMessage());
250: }
251: }
252:
253: public void testDetectsMissingProxyDecider() throws Exception {
254: CasAuthenticationProvider cap = new CasAuthenticationProvider();
255: cap.setCasAuthoritiesPopulator(new MockAuthoritiesPopulator());
256: cap.setKey("qwerty");
257: cap.setStatelessTicketCache(new MockStatelessTicketCache());
258: cap.setTicketValidator(new MockTicketValidator(true));
259:
260: try {
261: cap.afterPropertiesSet();
262: fail("Should have thrown IllegalArgumentException");
263: } catch (IllegalArgumentException expected) {
264: assertEquals("A casProxyDecider must be set", expected
265: .getMessage());
266: }
267: }
268:
269: public void testDetectsMissingStatelessTicketCache()
270: throws Exception {
271: CasAuthenticationProvider cap = new CasAuthenticationProvider();
272: cap.setCasAuthoritiesPopulator(new MockAuthoritiesPopulator());
273: cap.setCasProxyDecider(new MockProxyDecider());
274: cap.setKey("qwerty");
275: cap.setTicketValidator(new MockTicketValidator(true));
276:
277: try {
278: cap.afterPropertiesSet();
279: fail("Should have thrown IllegalArgumentException");
280: } catch (IllegalArgumentException expected) {
281: assertEquals("A statelessTicketCache must be set", expected
282: .getMessage());
283: }
284: }
285:
286: public void testDetectsMissingTicketValidator() throws Exception {
287: CasAuthenticationProvider cap = new CasAuthenticationProvider();
288: cap.setCasAuthoritiesPopulator(new MockAuthoritiesPopulator());
289: cap.setCasProxyDecider(new MockProxyDecider(true));
290: cap.setKey("qwerty");
291: cap.setStatelessTicketCache(new MockStatelessTicketCache());
292:
293: try {
294: cap.afterPropertiesSet();
295: fail("Should have thrown IllegalArgumentException");
296: } catch (IllegalArgumentException expected) {
297: assertEquals("A ticketValidator must be set", expected
298: .getMessage());
299: }
300: }
301:
302: public void testGettersSetters() throws Exception {
303: CasAuthenticationProvider cap = new CasAuthenticationProvider();
304: cap.setCasAuthoritiesPopulator(new MockAuthoritiesPopulator());
305: cap.setCasProxyDecider(new MockProxyDecider());
306: cap.setKey("qwerty");
307: cap.setStatelessTicketCache(new MockStatelessTicketCache());
308: cap.setTicketValidator(new MockTicketValidator(true));
309: cap.afterPropertiesSet();
310:
311: assertTrue(cap.getCasAuthoritiesPopulator() != null);
312: assertTrue(cap.getCasProxyDecider() != null);
313: assertEquals("qwerty", cap.getKey());
314: assertTrue(cap.getStatelessTicketCache() != null);
315: assertTrue(cap.getTicketValidator() != null);
316: }
317:
318: public void testIgnoresClassesItDoesNotSupport() throws Exception {
319: CasAuthenticationProvider cap = new CasAuthenticationProvider();
320: cap.setCasAuthoritiesPopulator(new MockAuthoritiesPopulator());
321: cap.setCasProxyDecider(new MockProxyDecider());
322: cap.setKey("qwerty");
323: cap.setStatelessTicketCache(new MockStatelessTicketCache());
324: cap.setTicketValidator(new MockTicketValidator(true));
325: cap.afterPropertiesSet();
326:
327: TestingAuthenticationToken token = new TestingAuthenticationToken(
328: "user", "password",
329: new GrantedAuthority[] { new GrantedAuthorityImpl(
330: "ROLE_A") });
331: assertFalse(cap.supports(TestingAuthenticationToken.class));
332:
333: // Try it anyway
334: assertEquals(null, cap.authenticate(token));
335: }
336:
337: public void testIgnoresUsernamePasswordAuthenticationTokensWithoutCasIdentifiersAsPrincipal()
338: throws Exception {
339: CasAuthenticationProvider cap = new CasAuthenticationProvider();
340: cap.setCasAuthoritiesPopulator(new MockAuthoritiesPopulator());
341: cap.setCasProxyDecider(new MockProxyDecider());
342: cap.setKey("qwerty");
343: cap.setStatelessTicketCache(new MockStatelessTicketCache());
344: cap.setTicketValidator(new MockTicketValidator(true));
345: cap.afterPropertiesSet();
346:
347: UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
348: "some_normal_user", "password",
349: new GrantedAuthority[] { new GrantedAuthorityImpl(
350: "ROLE_A") });
351: assertEquals(null, cap.authenticate(token));
352: }
353:
354: public void testSupports() {
355: CasAuthenticationProvider cap = new CasAuthenticationProvider();
356: assertTrue(cap
357: .supports(UsernamePasswordAuthenticationToken.class));
358: assertTrue(cap.supports(CasAuthenticationToken.class));
359: }
360:
361: //~ Inner Classes ==================================================================================================
362:
363: private class MockAuthoritiesPopulator implements
364: CasAuthoritiesPopulator {
365: public UserDetails getUserDetails(String casUserId)
366: throws AuthenticationException {
367: return makeUserDetailsFromAuthoritiesPopulator();
368: }
369: }
370:
371: private class MockProxyDecider implements CasProxyDecider {
372: private boolean acceptProxy;
373:
374: public MockProxyDecider(boolean acceptProxy) {
375: this .acceptProxy = acceptProxy;
376: }
377:
378: private MockProxyDecider() {
379: super ();
380: }
381:
382: public void confirmProxyListTrusted(List proxyList)
383: throws ProxyUntrustedException {
384: if (acceptProxy) {
385: return;
386: } else {
387: throw new ProxyUntrustedException(
388: "As requested from mock");
389: }
390: }
391: }
392:
393: private class MockStatelessTicketCache implements
394: StatelessTicketCache {
395: private Map cache = new HashMap();
396:
397: public CasAuthenticationToken getByTicketId(String serviceTicket) {
398: return (CasAuthenticationToken) cache.get(serviceTicket);
399: }
400:
401: public void putTicketInCache(CasAuthenticationToken token) {
402: cache.put(token.getCredentials().toString(), token);
403: }
404:
405: public void removeTicketFromCache(CasAuthenticationToken token) {
406: throw new UnsupportedOperationException(
407: "mock method not implemented");
408: }
409:
410: public void removeTicketFromCache(String serviceTicket) {
411: throw new UnsupportedOperationException(
412: "mock method not implemented");
413: }
414: }
415:
416: private class MockTicketValidator extends AbstractTicketValidator {
417: private boolean returnTicket;
418:
419: public MockTicketValidator(boolean returnTicket) {
420: this .returnTicket = returnTicket;
421: }
422:
423: private MockTicketValidator() {
424: super ();
425: }
426:
427: public TicketResponse confirmTicketValid(String serviceTicket)
428: throws AuthenticationException {
429: if (returnTicket) {
430: List list = new Vector();
431: list
432: .add("https://localhost/portal/j_acegi_cas_security_check");
433:
434: return new TicketResponse("marissa", list,
435: "PGTIOU-0-R0zlgrl4pdAQwBvJWO3vnNpevwqStbSGcq3vKB2SqSFFRnjPHt");
436: }
437:
438: throw new BadCredentialsException("As requested from mock");
439: }
440: }
441: }
|