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.intercept.method.aopalliance;
017:
018: import junit.framework.TestCase;
019:
020: import org.acegisecurity.AccessDecisionManager;
021: import org.acegisecurity.AccessDeniedException;
022: import org.acegisecurity.AfterInvocationManager;
023: import org.acegisecurity.Authentication;
024: import org.acegisecurity.AuthenticationCredentialsNotFoundException;
025: import org.acegisecurity.AuthenticationException;
026: import org.acegisecurity.ConfigAttribute;
027: import org.acegisecurity.ConfigAttributeDefinition;
028: import org.acegisecurity.GrantedAuthority;
029: import org.acegisecurity.GrantedAuthorityImpl;
030: import org.acegisecurity.ITargetObject;
031: import org.acegisecurity.MockAccessDecisionManager;
032: import org.acegisecurity.MockAfterInvocationManager;
033: import org.acegisecurity.MockAuthenticationManager;
034: import org.acegisecurity.MockRunAsManager;
035: import org.acegisecurity.RunAsManager;
036:
037: import org.acegisecurity.context.SecurityContextHolder;
038:
039: import org.acegisecurity.intercept.method.AbstractMethodDefinitionSource;
040: import org.acegisecurity.intercept.method.MockMethodDefinitionSource;
041:
042: import org.acegisecurity.providers.UsernamePasswordAuthenticationToken;
043:
044: import org.acegisecurity.runas.RunAsManagerImpl;
045:
046: import org.springframework.context.ApplicationContext;
047: import org.springframework.context.support.ClassPathXmlApplicationContext;
048:
049: import java.lang.reflect.Method;
050:
051: import java.util.Iterator;
052:
053: /**
054: * Tests {@link MethodSecurityInterceptor}.
055: *
056: * @author Ben Alex
057: * @version $Id: MethodSecurityInterceptorTests.java 1496 2006-05-23 13:38:33Z benalex $
058: */
059: public class MethodSecurityInterceptorTests extends TestCase {
060: //~ Constructors ===================================================================================================
061:
062: public MethodSecurityInterceptorTests() {
063: super ();
064: }
065:
066: public MethodSecurityInterceptorTests(String arg0) {
067: super (arg0);
068: }
069:
070: //~ Methods ========================================================================================================
071:
072: public static void main(String[] args) {
073: junit.textui.TestRunner
074: .run(MethodSecurityInterceptorTests.class);
075: }
076:
077: private ITargetObject makeInterceptedTarget() {
078: ApplicationContext context = new ClassPathXmlApplicationContext(
079: "org/acegisecurity/intercept/method/aopalliance/applicationContext.xml");
080:
081: return (ITargetObject) context.getBean("target");
082: }
083:
084: private ITargetObject makeInterceptedTargetRejectsAuthentication() {
085: ApplicationContext context = new ClassPathXmlApplicationContext(
086: "org/acegisecurity/intercept/method/aopalliance/applicationContext.xml");
087:
088: MockAuthenticationManager authenticationManager = new MockAuthenticationManager(
089: false);
090: MethodSecurityInterceptor si = (MethodSecurityInterceptor) context
091: .getBean("securityInterceptor");
092: si.setAuthenticationManager(authenticationManager);
093:
094: return (ITargetObject) context.getBean("target");
095: }
096:
097: private ITargetObject makeInterceptedTargetWithoutAnAfterInvocationManager() {
098: ApplicationContext context = new ClassPathXmlApplicationContext(
099: "org/acegisecurity/intercept/method/aopalliance/applicationContext.xml");
100:
101: MethodSecurityInterceptor si = (MethodSecurityInterceptor) context
102: .getBean("securityInterceptor");
103: si.setAfterInvocationManager(null);
104:
105: return (ITargetObject) context.getBean("target");
106: }
107:
108: public final void setUp() throws Exception {
109: super .setUp();
110: SecurityContextHolder.getContext().setAuthentication(null);
111: }
112:
113: public void testCallingAPublicMethodFacadeWillNotRepeatSecurityChecksWhenPassedToTheSecuredMethodItFronts()
114: throws Exception {
115: ITargetObject target = makeInterceptedTarget();
116: String result = target.publicMakeLowerCase("HELLO");
117: assertEquals("hello Authentication empty", result);
118: }
119:
120: public void testCallingAPublicMethodWhenPresentingAnAuthenticationObjectWillNotChangeItsIsAuthenticatedProperty()
121: throws Exception {
122: UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
123: "Test", "Password");
124: assertTrue(!token.isAuthenticated());
125: SecurityContextHolder.getContext().setAuthentication(token);
126:
127: // The associated MockAuthenticationManager WILL accept the above UsernamePasswordAuthenticationToken
128: ITargetObject target = makeInterceptedTarget();
129: String result = target.publicMakeLowerCase("HELLO");
130: assertEquals(
131: "hello org.acegisecurity.providers.UsernamePasswordAuthenticationToken false",
132: result);
133: }
134:
135: public void testDeniesWhenAppropriate() throws Exception {
136: UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
137: "Test", "Password",
138: new GrantedAuthority[] { new GrantedAuthorityImpl(
139: "MOCK_NO_BENEFIT_TO_THIS_GRANTED_AUTHORITY") });
140: SecurityContextHolder.getContext().setAuthentication(token);
141:
142: ITargetObject target = makeInterceptedTarget();
143:
144: try {
145: target.makeUpperCase("HELLO");
146: fail("Should have thrown AccessDeniedException");
147: } catch (AccessDeniedException expected) {
148: assertTrue(true);
149: }
150: }
151:
152: public void testGetters() {
153: MockAccessDecisionManager accessDecision = new MockAccessDecisionManager();
154: MockRunAsManager runAs = new MockRunAsManager();
155: MockAuthenticationManager authManager = new MockAuthenticationManager();
156: MockMethodDefinitionSource methodSource = new MockMethodDefinitionSource(
157: false, true);
158: MockAfterInvocationManager afterInvocation = new MockAfterInvocationManager();
159:
160: MethodSecurityInterceptor si = new MethodSecurityInterceptor();
161: si.setAccessDecisionManager(accessDecision);
162: si.setRunAsManager(runAs);
163: si.setAuthenticationManager(authManager);
164: si.setObjectDefinitionSource(methodSource);
165: si.setAfterInvocationManager(afterInvocation);
166:
167: assertEquals(accessDecision, si.getAccessDecisionManager());
168: assertEquals(runAs, si.getRunAsManager());
169: assertEquals(authManager, si.getAuthenticationManager());
170: assertEquals(methodSource, si.getObjectDefinitionSource());
171: assertEquals(afterInvocation, si.getAfterInvocationManager());
172: }
173:
174: public void testMethodCallWithRunAsReplacement() throws Exception {
175: UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
176: "Test", "Password",
177: new GrantedAuthority[] { new GrantedAuthorityImpl(
178: "MOCK_UPPER") });
179: SecurityContextHolder.getContext().setAuthentication(token);
180:
181: ITargetObject target = makeInterceptedTarget();
182: String result = target.makeUpperCase("hello");
183: assertEquals(
184: "HELLO org.acegisecurity.MockRunAsAuthenticationToken true",
185: result);
186: }
187:
188: public void testMethodCallWithoutRunAsReplacement()
189: throws Exception {
190: UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
191: "Test", "Password",
192: new GrantedAuthority[] { new GrantedAuthorityImpl(
193: "MOCK_LOWER") });
194: assertTrue(token.isAuthenticated());
195: SecurityContextHolder.getContext().setAuthentication(token);
196:
197: ITargetObject target = makeInterceptedTargetWithoutAnAfterInvocationManager();
198: String result = target.makeLowerCase("HELLO");
199:
200: // Note we check the isAuthenticated remained true in following line
201: assertEquals(
202: "hello org.acegisecurity.providers.UsernamePasswordAuthenticationToken true",
203: result);
204: }
205:
206: public void testRejectionOfEmptySecurityContext() throws Exception {
207: ITargetObject target = makeInterceptedTarget();
208:
209: try {
210: target.makeUpperCase("hello");
211: fail("Should have thrown AuthenticationCredentialsNotFoundException");
212: } catch (AuthenticationCredentialsNotFoundException expected) {
213: assertTrue(true);
214: }
215: }
216:
217: public void testRejectsAccessDecisionManagersThatDoNotSupportMethodInvocation()
218: throws Exception {
219: MethodSecurityInterceptor si = new MethodSecurityInterceptor();
220: si
221: .setAccessDecisionManager(new MockAccessDecisionManagerWhichOnlySupportsStrings());
222: si.setAuthenticationManager(new MockAuthenticationManager());
223: si.setObjectDefinitionSource(new MockMethodDefinitionSource(
224: false, true));
225: si.setRunAsManager(new MockRunAsManager());
226:
227: try {
228: si.afterPropertiesSet();
229: fail("Should have thrown IllegalArgumentException");
230: } catch (IllegalArgumentException expected) {
231: assertEquals(
232: "AccessDecisionManager does not support secure object class: interface org.aopalliance.intercept.MethodInvocation",
233: expected.getMessage());
234: }
235: }
236:
237: public void testRejectsCallsWhenAuthenticationIsIncorrect()
238: throws Exception {
239: UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
240: "Test", "Password");
241: assertTrue(!token.isAuthenticated());
242: SecurityContextHolder.getContext().setAuthentication(token);
243:
244: // NB: The associated MockAuthenticationManager WILL reject the above UsernamePasswordAuthenticationToken
245: ITargetObject target = makeInterceptedTargetRejectsAuthentication();
246:
247: try {
248: target.makeLowerCase("HELLO");
249: fail("Should have thrown AuthenticationException");
250: } catch (AuthenticationException expected) {
251: assertTrue(true);
252: }
253: }
254:
255: public void testRejectsCallsWhenObjectDefinitionSourceDoesNotSupportObject()
256: throws Throwable {
257: MethodSecurityInterceptor interceptor = new MethodSecurityInterceptor();
258: interceptor
259: .setObjectDefinitionSource(new MockObjectDefinitionSourceWhichOnlySupportsStrings());
260: interceptor
261: .setAccessDecisionManager(new MockAccessDecisionManager());
262: interceptor
263: .setAuthenticationManager(new MockAuthenticationManager());
264: interceptor.setRunAsManager(new MockRunAsManager());
265:
266: try {
267: interceptor.afterPropertiesSet();
268: fail("Should have thrown IllegalArgumentException");
269: } catch (IllegalArgumentException expected) {
270: assertEquals(
271: "ObjectDefinitionSource does not support secure object class: interface org.aopalliance.intercept.MethodInvocation",
272: expected.getMessage());
273: }
274: }
275:
276: public void testRejectsCallsWhenObjectIsNull() throws Throwable {
277: MethodSecurityInterceptor interceptor = new MethodSecurityInterceptor();
278:
279: try {
280: interceptor.invoke(null);
281: fail("Should have thrown IllegalArgumentException");
282: } catch (IllegalArgumentException expected) {
283: assertEquals("Object was null", expected.getMessage());
284: }
285: }
286:
287: public void testRejectsRunAsManagersThatDoNotSupportMethodInvocation()
288: throws Exception {
289: MethodSecurityInterceptor si = new MethodSecurityInterceptor();
290: si.setAccessDecisionManager(new MockAccessDecisionManager());
291: si.setAuthenticationManager(new MockAuthenticationManager());
292: si.setObjectDefinitionSource(new MockMethodDefinitionSource(
293: false, true));
294: si
295: .setRunAsManager(new MockRunAsManagerWhichOnlySupportsStrings());
296: si.setAfterInvocationManager(new MockAfterInvocationManager());
297:
298: try {
299: si.afterPropertiesSet();
300: fail("Should have thrown IllegalArgumentException");
301: } catch (IllegalArgumentException expected) {
302: assertEquals(
303: "RunAsManager does not support secure object class: interface org.aopalliance.intercept.MethodInvocation",
304: expected.getMessage());
305: }
306: }
307:
308: public void testStartupCheckForAccessDecisionManager()
309: throws Exception {
310: MethodSecurityInterceptor si = new MethodSecurityInterceptor();
311: si.setRunAsManager(new MockRunAsManager());
312: si.setAuthenticationManager(new MockAuthenticationManager());
313: si.setAfterInvocationManager(new MockAfterInvocationManager());
314:
315: si.setObjectDefinitionSource(new MockMethodDefinitionSource(
316: false, true));
317:
318: try {
319: si.afterPropertiesSet();
320: fail("Should have thrown IllegalArgumentException");
321: } catch (IllegalArgumentException expected) {
322: assertEquals("An AccessDecisionManager is required",
323: expected.getMessage());
324: }
325: }
326:
327: public void testStartupCheckForAuthenticationManager()
328: throws Exception {
329: MethodSecurityInterceptor si = new MethodSecurityInterceptor();
330: si.setAccessDecisionManager(new MockAccessDecisionManager());
331: si.setRunAsManager(new MockRunAsManager());
332: si.setAfterInvocationManager(new MockAfterInvocationManager());
333:
334: si.setObjectDefinitionSource(new MockMethodDefinitionSource(
335: false, true));
336:
337: try {
338: si.afterPropertiesSet();
339: fail("Should have thrown IllegalArgumentException");
340: } catch (IllegalArgumentException expected) {
341: assertEquals("An AuthenticationManager is required",
342: expected.getMessage());
343: }
344: }
345:
346: public void testStartupCheckForMethodDefinitionSource()
347: throws Exception {
348: MethodSecurityInterceptor si = new MethodSecurityInterceptor();
349: si.setAccessDecisionManager(new MockAccessDecisionManager());
350: si.setAuthenticationManager(new MockAuthenticationManager());
351:
352: try {
353: si.afterPropertiesSet();
354: fail("Should have thrown IllegalArgumentException");
355: } catch (IllegalArgumentException expected) {
356: assertEquals("An ObjectDefinitionSource is required",
357: expected.getMessage());
358: }
359: }
360:
361: public void testStartupCheckForRunAsManager() throws Exception {
362: MethodSecurityInterceptor si = new MethodSecurityInterceptor();
363: si.setAccessDecisionManager(new MockAccessDecisionManager());
364: si.setAuthenticationManager(new MockAuthenticationManager());
365: si.setRunAsManager(null); // Overriding the default
366:
367: si.setObjectDefinitionSource(new MockMethodDefinitionSource(
368: false, true));
369:
370: try {
371: si.afterPropertiesSet();
372: fail("Should have thrown IllegalArgumentException");
373: } catch (IllegalArgumentException expected) {
374: assertEquals("A RunAsManager is required", expected
375: .getMessage());
376: }
377: }
378:
379: public void testStartupCheckForValidAfterInvocationManager()
380: throws Exception {
381: MethodSecurityInterceptor si = new MethodSecurityInterceptor();
382: si.setRunAsManager(new MockRunAsManager());
383: si.setAuthenticationManager(new MockAuthenticationManager());
384: si
385: .setAfterInvocationManager(new MockAfterInvocationManagerWhichOnlySupportsStrings());
386: si.setAccessDecisionManager(new MockAccessDecisionManager());
387: si.setObjectDefinitionSource(new MockMethodDefinitionSource(
388: false, true));
389:
390: try {
391: si.afterPropertiesSet();
392: fail("Should have thrown IllegalArgumentException");
393: } catch (IllegalArgumentException expected) {
394: assertTrue(expected
395: .getMessage()
396: .startsWith(
397: "AfterInvocationManager does not support secure object class:"));
398: }
399: }
400:
401: public void testValidationFailsIfInvalidAttributePresented()
402: throws Exception {
403: MethodSecurityInterceptor si = new MethodSecurityInterceptor();
404: si.setAccessDecisionManager(new MockAccessDecisionManager());
405: si.setAuthenticationManager(new MockAuthenticationManager());
406: si.setRunAsManager(new RunAsManagerImpl());
407:
408: assertTrue(si.isValidateConfigAttributes()); // check default
409: si.setObjectDefinitionSource(new MockMethodDefinitionSource(
410: true, true));
411:
412: try {
413: si.afterPropertiesSet();
414: fail("Should have thrown IllegalArgumentException");
415: } catch (IllegalArgumentException expected) {
416: assertEquals(
417: "Unsupported configuration attributes: [ANOTHER_INVALID, INVALID_ATTRIBUTE]",
418: expected.getMessage());
419: }
420: }
421:
422: public void testValidationNotAttemptedIfIsValidateConfigAttributesSetToFalse()
423: throws Exception {
424: MethodSecurityInterceptor si = new MethodSecurityInterceptor();
425: si.setAccessDecisionManager(new MockAccessDecisionManager());
426: si.setAuthenticationManager(new MockAuthenticationManager());
427:
428: assertTrue(si.isValidateConfigAttributes()); // check default
429: si.setValidateConfigAttributes(false);
430: assertTrue(!si.isValidateConfigAttributes()); // check changed
431:
432: si.setObjectDefinitionSource(new MockMethodDefinitionSource(
433: true, true));
434: si.afterPropertiesSet();
435: assertTrue(true);
436: }
437:
438: public void testValidationNotAttemptedIfMethodDefinitionSourceCannotReturnIterator()
439: throws Exception {
440: MethodSecurityInterceptor si = new MethodSecurityInterceptor();
441: si.setAccessDecisionManager(new MockAccessDecisionManager());
442: si.setRunAsManager(new MockRunAsManager());
443: si.setAuthenticationManager(new MockAuthenticationManager());
444:
445: assertTrue(si.isValidateConfigAttributes()); // check default
446: si.setObjectDefinitionSource(new MockMethodDefinitionSource(
447: true, false));
448: si.afterPropertiesSet();
449: assertTrue(true);
450: }
451:
452: //~ Inner Classes ==================================================================================================
453:
454: private class MockAccessDecisionManagerWhichOnlySupportsStrings
455: implements AccessDecisionManager {
456: public void decide(Authentication authentication,
457: Object object, ConfigAttributeDefinition config)
458: throws AccessDeniedException {
459: throw new UnsupportedOperationException(
460: "mock method not implemented");
461: }
462:
463: public boolean supports(Class clazz) {
464: if (String.class.isAssignableFrom(clazz)) {
465: return true;
466: } else {
467: return false;
468: }
469: }
470:
471: public boolean supports(ConfigAttribute attribute) {
472: return true;
473: }
474: }
475:
476: private class MockAfterInvocationManagerWhichOnlySupportsStrings
477: implements AfterInvocationManager {
478: public Object decide(Authentication authentication,
479: Object object, ConfigAttributeDefinition config,
480: Object returnedObject) throws AccessDeniedException {
481: throw new UnsupportedOperationException(
482: "mock method not implemented");
483: }
484:
485: public boolean supports(Class clazz) {
486: if (String.class.isAssignableFrom(clazz)) {
487: return true;
488: } else {
489: return false;
490: }
491: }
492:
493: public boolean supports(ConfigAttribute attribute) {
494: return true;
495: }
496: }
497:
498: private class MockObjectDefinitionSourceWhichOnlySupportsStrings
499: extends AbstractMethodDefinitionSource {
500: public Iterator getConfigAttributeDefinitions() {
501: return null;
502: }
503:
504: protected ConfigAttributeDefinition lookupAttributes(
505: Method method) {
506: throw new UnsupportedOperationException(
507: "mock method not implemented");
508: }
509:
510: public boolean supports(Class clazz) {
511: if (String.class.isAssignableFrom(clazz)) {
512: return true;
513: } else {
514: return false;
515: }
516: }
517: }
518:
519: private class MockRunAsManagerWhichOnlySupportsStrings implements
520: RunAsManager {
521: public Authentication buildRunAs(Authentication authentication,
522: Object object, ConfigAttributeDefinition config) {
523: throw new UnsupportedOperationException(
524: "mock method not implemented");
525: }
526:
527: public boolean supports(Class clazz) {
528: if (String.class.isAssignableFrom(clazz)) {
529: return true;
530: } else {
531: return false;
532: }
533: }
534:
535: public boolean supports(ConfigAttribute attribute) {
536: return true;
537: }
538: }
539: }
|