/* * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.security.access.intercept.aopalliance; import static org.assertj.core.api.Assertions.*; import static org.mockito.Mockito.*; import org.aopalliance.intercept.MethodInvocation; import org.junit.*; import org.springframework.aop.framework.ProxyFactory; import org.springframework.context.ApplicationEventPublisher; import org.springframework.security.ITargetObject; import org.springframework.security.TargetObject; import org.springframework.security.access.AccessDecisionManager; import org.springframework.security.access.AccessDeniedException; import org.springframework.security.access.ConfigAttribute; import org.springframework.security.access.SecurityConfig; import org.springframework.security.access.event.AuthorizationFailureEvent; import org.springframework.security.access.event.AuthorizedEvent; import org.springframework.security.access.intercept.AfterInvocationManager; import org.springframework.security.access.intercept.RunAsManager; import org.springframework.security.access.intercept.RunAsUserToken; import org.springframework.security.access.method.MethodSecurityMetadataSource; import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.TestingAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; import java.util.*; /** * Tests {@link MethodSecurityInterceptor}. * * @author Ben Alex * @author Rob Winch */ @SuppressWarnings("unchecked") public class MethodSecurityInterceptorTests { private TestingAuthenticationToken token; private MethodSecurityInterceptor interceptor; private ITargetObject realTarget; private ITargetObject advisedTarget; private AccessDecisionManager adm; private MethodSecurityMetadataSource mds; private AuthenticationManager authman; private ApplicationEventPublisher eventPublisher; // ~ Methods // ======================================================================================================== @Before public final void setUp() throws Exception { SecurityContextHolder.clearContext(); token = new TestingAuthenticationToken("Test", "Password"); interceptor = new MethodSecurityInterceptor(); adm = mock(AccessDecisionManager.class); authman = mock(AuthenticationManager.class); mds = mock(MethodSecurityMetadataSource.class); eventPublisher = mock(ApplicationEventPublisher.class); interceptor.setAccessDecisionManager(adm); interceptor.setAuthenticationManager(authman); interceptor.setSecurityMetadataSource(mds); interceptor.setApplicationEventPublisher(eventPublisher); createTarget(false); } @After public void tearDown() throws Exception { SecurityContextHolder.clearContext(); } private void createTarget(boolean useMock) { realTarget = useMock ? mock(ITargetObject.class) : new TargetObject(); ProxyFactory pf = new ProxyFactory(realTarget); pf.addAdvice(interceptor); advisedTarget = (ITargetObject) pf.getProxy(); } @Test public void gettersReturnExpectedData() { RunAsManager runAs = mock(RunAsManager.class); AfterInvocationManager aim = mock(AfterInvocationManager.class); interceptor.setRunAsManager(runAs); interceptor.setAfterInvocationManager(aim); assertThat(interceptor.getAccessDecisionManager()).isEqualTo(adm); assertThat(interceptor.getRunAsManager()).isEqualTo(runAs); assertThat(interceptor.getAuthenticationManager()).isEqualTo(authman); assertThat(interceptor.getSecurityMetadataSource()).isEqualTo(mds); assertThat(interceptor.getAfterInvocationManager()).isEqualTo(aim); } @Test(expected = IllegalArgumentException.class) public void missingAccessDecisionManagerIsDetected() throws Exception { interceptor.setAccessDecisionManager(null); interceptor.afterPropertiesSet(); } @Test(expected = IllegalArgumentException.class) public void missingAuthenticationManagerIsDetected() throws Exception { interceptor.setAuthenticationManager(null); interceptor.afterPropertiesSet(); } @Test(expected = IllegalArgumentException.class) public void missingMethodSecurityMetadataSourceIsRejected() throws Exception { interceptor.setSecurityMetadataSource(null); interceptor.afterPropertiesSet(); } @Test(expected = IllegalArgumentException.class) public void missingRunAsManagerIsRejected() throws Exception { interceptor.setRunAsManager(null); interceptor.afterPropertiesSet(); } @Test(expected = IllegalArgumentException.class) public void initializationRejectsSecurityMetadataSourceThatDoesNotSupportMethodInvocation() throws Throwable { when(mds.supports(MethodInvocation.class)).thenReturn(false); interceptor.afterPropertiesSet(); } @Test(expected = IllegalArgumentException.class) public void initializationRejectsAccessDecisionManagerThatDoesNotSupportMethodInvocation() throws Exception { when(mds.supports(MethodInvocation.class)).thenReturn(true); when(adm.supports(MethodInvocation.class)).thenReturn(false); interceptor.afterPropertiesSet(); } @Test(expected = IllegalArgumentException.class) public void intitalizationRejectsRunAsManagerThatDoesNotSupportMethodInvocation() throws Exception { final RunAsManager ram = mock(RunAsManager.class); when(ram.supports(MethodInvocation.class)).thenReturn(false); interceptor.setRunAsManager(ram); interceptor.afterPropertiesSet(); } @Test(expected = IllegalArgumentException.class) public void intitalizationRejectsAfterInvocationManagerThatDoesNotSupportMethodInvocation() throws Exception { final AfterInvocationManager aim = mock(AfterInvocationManager.class); when(aim.supports(MethodInvocation.class)).thenReturn(false); interceptor.setAfterInvocationManager(aim); interceptor.afterPropertiesSet(); } @Test(expected = IllegalArgumentException.class) public void initializationFailsIfAccessDecisionManagerRejectsConfigAttributes() throws Exception { when(adm.supports(any(ConfigAttribute.class))).thenReturn(false); interceptor.afterPropertiesSet(); } @Test public void validationNotAttemptedIfIsValidateConfigAttributesSetToFalse() throws Exception { when(adm.supports(MethodInvocation.class)).thenReturn(true); when(mds.supports(MethodInvocation.class)).thenReturn(true); interceptor.setValidateConfigAttributes(false); interceptor.afterPropertiesSet(); verify(mds, never()).getAllConfigAttributes(); verify(adm, never()).supports(any(ConfigAttribute.class)); } @Test public void validationNotAttemptedIfMethodSecurityMetadataSourceReturnsNullForAttributes() throws Exception { when(adm.supports(MethodInvocation.class)).thenReturn(true); when(mds.supports(MethodInvocation.class)).thenReturn(true); when(mds.getAllConfigAttributes()).thenReturn(null); interceptor.setValidateConfigAttributes(true); interceptor.afterPropertiesSet(); verify(adm, never()).supports(any(ConfigAttribute.class)); } @Test public void callingAPublicMethodFacadeWillNotRepeatSecurityChecksWhenPassedToTheSecuredMethodItFronts() { mdsReturnsNull(); String result = advisedTarget.publicMakeLowerCase("HELLO"); assertThat(result).isEqualTo("hello Authentication empty"); } @Test public void callingAPublicMethodWhenPresentingAnAuthenticationObjectDoesntChangeItsAuthenticatedProperty() { mdsReturnsNull(); SecurityContextHolder.getContext().setAuthentication(token); assertThat(advisedTarget.publicMakeLowerCase("HELLO")).isEqualTo("hello org.springframework.security.authentication.TestingAuthenticationToken false"); assertThat(!token.isAuthenticated()).isTrue(); } @Test(expected = AuthenticationException.class) public void callIsntMadeWhenAuthenticationManagerRejectsAuthentication() throws Exception { final TestingAuthenticationToken token = new TestingAuthenticationToken("Test", "Password"); SecurityContextHolder.getContext().setAuthentication(token); mdsReturnsUserRole(); when(authman.authenticate(token)).thenThrow( new BadCredentialsException("rejected")); advisedTarget.makeLowerCase("HELLO"); } @Test public void callSucceedsIfAccessDecisionManagerGrantsAccess() throws Exception { token.setAuthenticated(true); interceptor.setPublishAuthorizationSuccess(true); SecurityContextHolder.getContext().setAuthentication(token); mdsReturnsUserRole(); String result = advisedTarget.makeLowerCase("HELLO"); // Note we check the isAuthenticated remained true in following line assertThat(result).isEqualTo("hello org.springframework.security.authentication.TestingAuthenticationToken true"); verify(eventPublisher).publishEvent(any(AuthorizedEvent.class)); } @Test public void callIsntMadeWhenAccessDecisionManagerRejectsAccess() throws Exception { SecurityContextHolder.getContext().setAuthentication(token); // Use mocked target to make sure invocation doesn't happen (not in expectations // so test would fail) createTarget(true); mdsReturnsUserRole(); when(authman.authenticate(token)).thenReturn(token); doThrow(new AccessDeniedException("rejected")).when(adm).decide( any(Authentication.class), any(MethodInvocation.class), any(List.class)); try { advisedTarget.makeUpperCase("HELLO"); fail("Expected Exception"); } catch (AccessDeniedException expected) { } verify(eventPublisher).publishEvent(any(AuthorizationFailureEvent.class)); } @Test(expected = IllegalArgumentException.class) public void rejectsNullSecuredObjects() throws Throwable { interceptor.invoke(null); } @Test public void runAsReplacementIsCorrectlySet() throws Exception { SecurityContext ctx = SecurityContextHolder.getContext(); ctx.setAuthentication(token); token.setAuthenticated(true); final RunAsManager runAs = mock(RunAsManager.class); final RunAsUserToken runAsToken = new RunAsUserToken("key", "someone", "creds", token.getAuthorities(), TestingAuthenticationToken.class); interceptor.setRunAsManager(runAs); mdsReturnsUserRole(); when(runAs.buildRunAs(eq(token), any(MethodInvocation.class), any(List.class))) .thenReturn(runAsToken); String result = advisedTarget.makeUpperCase("hello"); assertThat(result).isEqualTo("HELLO org.springframework.security.access.intercept.RunAsUserToken true"); // Check we've changed back assertThat(SecurityContextHolder.getContext()).isSameAs(ctx); assertThat(SecurityContextHolder.getContext().getAuthentication()).isSameAs(token); } // SEC-1967 @Test public void runAsReplacementCleansAfterException() throws Exception { createTarget(true); when(realTarget.makeUpperCase(anyString())).thenThrow(new RuntimeException()); SecurityContext ctx = SecurityContextHolder.getContext(); ctx.setAuthentication(token); token.setAuthenticated(true); final RunAsManager runAs = mock(RunAsManager.class); final RunAsUserToken runAsToken = new RunAsUserToken("key", "someone", "creds", token.getAuthorities(), TestingAuthenticationToken.class); interceptor.setRunAsManager(runAs); mdsReturnsUserRole(); when(runAs.buildRunAs(eq(token), any(MethodInvocation.class), any(List.class))) .thenReturn(runAsToken); try { advisedTarget.makeUpperCase("hello"); fail("Expected Exception"); } catch (RuntimeException success) { } // Check we've changed back assertThat(SecurityContextHolder.getContext()).isSameAs(ctx); assertThat(SecurityContextHolder.getContext().getAuthentication()).isSameAs(token); } @Test(expected = AuthenticationCredentialsNotFoundException.class) public void emptySecurityContextIsRejected() throws Exception { mdsReturnsUserRole(); advisedTarget.makeUpperCase("hello"); } @Test public void afterInvocationManagerIsNotInvokedIfExceptionIsRaised() throws Throwable { MethodInvocation mi = mock(MethodInvocation.class); token.setAuthenticated(true); SecurityContextHolder.getContext().setAuthentication(token); mdsReturnsUserRole(); AfterInvocationManager aim = mock(AfterInvocationManager.class); interceptor.setAfterInvocationManager(aim); when(mi.proceed()).thenThrow(new Throwable()); try { interceptor.invoke(mi); fail("Expected exception"); } catch (Throwable expected) { } verifyZeroInteractions(aim); } void mdsReturnsNull() { when(mds.getAttributes(any(MethodInvocation.class))).thenReturn(null); } void mdsReturnsUserRole() { when(mds.getAttributes(any(MethodInvocation.class))).thenReturn( SecurityConfig.createList("ROLE_USER")); } }