/* * Copyright 2002-2016 the original author or authors. * * 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.expression.method; import static org.assertj.core.api.Assertions.assertThat; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.util.Collection; import java.util.List; import org.junit.Before; import org.junit.Test; import org.springframework.expression.Expression; import org.springframework.security.access.ConfigAttribute; import org.springframework.security.access.annotation.sec2150.MethodInvocationFactory; import org.springframework.security.access.intercept.method.MockMethodInvocation; import org.springframework.security.access.prepost.PostAuthorize; import org.springframework.security.access.prepost.PostFilter; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.access.prepost.PreFilter; import org.springframework.security.access.prepost.PrePostAnnotationSecurityMetadataSource; import org.springframework.test.util.ReflectionTestUtils; /** * * @author Luke Taylor * @since 3.0 */ public class PrePostAnnotationSecurityMetadataSourceTests { private PrePostAnnotationSecurityMetadataSource mds = new PrePostAnnotationSecurityMetadataSource( new ExpressionBasedAnnotationAttributeFactory( new DefaultMethodSecurityExpressionHandler())); private MockMethodInvocation voidImpl1; private MockMethodInvocation voidImpl2; private MockMethodInvocation voidImpl3; private MockMethodInvocation listImpl1; private MockMethodInvocation notherListImpl1; private MockMethodInvocation notherListImpl2; private MockMethodInvocation annotatedAtClassLevel; private MockMethodInvocation annotatedAtInterfaceLevel; private MockMethodInvocation annotatedAtMethodLevel; @Before public void setUpData() throws Exception { voidImpl1 = new MockMethodInvocation(new ReturnVoidImpl1(), ReturnVoid.class, "doSomething", List.class); voidImpl2 = new MockMethodInvocation(new ReturnVoidImpl2(), ReturnVoid.class, "doSomething", List.class); voidImpl3 = new MockMethodInvocation(new ReturnVoidImpl3(), ReturnVoid.class, "doSomething", List.class); listImpl1 = new MockMethodInvocation(new ReturnAListImpl1(), ReturnAList.class, "doSomething", List.class); notherListImpl1 = new MockMethodInvocation(new ReturnAnotherListImpl1(), ReturnAnotherList.class, "doSomething", List.class); notherListImpl2 = new MockMethodInvocation(new ReturnAnotherListImpl2(), ReturnAnotherList.class, "doSomething", List.class); annotatedAtClassLevel = new MockMethodInvocation( new CustomAnnotationAtClassLevel(), ReturnVoid.class, "doSomething", List.class); annotatedAtInterfaceLevel = new MockMethodInvocation( new CustomAnnotationAtInterfaceLevel(), ReturnVoid2.class, "doSomething", List.class); annotatedAtMethodLevel = new MockMethodInvocation( new CustomAnnotationAtMethodLevel(), ReturnVoid.class, "doSomething", List.class); } @Test public void classLevelPreAnnotationIsPickedUpWhenNoMethodLevelExists() throws Exception { ConfigAttribute[] attrs = mds.getAttributes(voidImpl1).toArray( new ConfigAttribute[0]); assertThat(attrs.length).isEqualTo(1); assertThat(attrs[0] instanceof PreInvocationExpressionAttribute).isTrue(); PreInvocationExpressionAttribute pre = (PreInvocationExpressionAttribute) attrs[0]; assertThat(pre.getAuthorizeExpression()).isNotNull(); assertThat(pre.getAuthorizeExpression().getExpressionString()).isEqualTo("someExpression"); assertThat(pre.getFilterExpression()).isNull(); } @Test public void mixedClassAndMethodPreAnnotationsAreBothIncluded() { ConfigAttribute[] attrs = mds.getAttributes(voidImpl2).toArray( new ConfigAttribute[0]); assertThat(attrs.length).isEqualTo(1); assertThat(attrs[0] instanceof PreInvocationExpressionAttribute).isTrue(); PreInvocationExpressionAttribute pre = (PreInvocationExpressionAttribute) attrs[0]; assertThat(pre.getAuthorizeExpression().getExpressionString()).isEqualTo("someExpression"); assertThat(pre.getFilterExpression()).isNotNull(); assertThat(pre.getFilterExpression().getExpressionString()).isEqualTo("somePreFilterExpression"); } @Test public void methodWithPreFilterOnlyIsAllowed() { ConfigAttribute[] attrs = mds.getAttributes(voidImpl3).toArray( new ConfigAttribute[0]); assertThat(attrs.length).isEqualTo(1); assertThat(attrs[0] instanceof PreInvocationExpressionAttribute).isTrue(); PreInvocationExpressionAttribute pre = (PreInvocationExpressionAttribute) attrs[0]; assertThat(pre.getAuthorizeExpression().getExpressionString()).isEqualTo("permitAll"); assertThat(pre.getFilterExpression()).isNotNull(); assertThat(pre.getFilterExpression().getExpressionString()).isEqualTo("somePreFilterExpression"); } @Test public void methodWithPostFilterOnlyIsAllowed() { ConfigAttribute[] attrs = mds.getAttributes(listImpl1).toArray( new ConfigAttribute[0]); assertThat(attrs.length).isEqualTo(2); assertThat(attrs[0] instanceof PreInvocationExpressionAttribute).isTrue(); assertThat(attrs[1] instanceof PostInvocationExpressionAttribute).isTrue(); PreInvocationExpressionAttribute pre = (PreInvocationExpressionAttribute) attrs[0]; PostInvocationExpressionAttribute post = (PostInvocationExpressionAttribute) attrs[1]; assertThat(pre.getAuthorizeExpression().getExpressionString()).isEqualTo("permitAll"); assertThat(post.getFilterExpression()).isNotNull(); assertThat(post.getFilterExpression().getExpressionString()).isEqualTo("somePostFilterExpression"); } @Test public void interfaceAttributesAreIncluded() { ConfigAttribute[] attrs = mds.getAttributes(notherListImpl1).toArray( new ConfigAttribute[0]); assertThat(attrs.length).isEqualTo(1); assertThat(attrs[0] instanceof PreInvocationExpressionAttribute).isTrue(); PreInvocationExpressionAttribute pre = (PreInvocationExpressionAttribute) attrs[0]; assertThat(pre.getFilterExpression()).isNotNull(); assertThat(pre.getAuthorizeExpression()).isNotNull(); assertThat(pre.getAuthorizeExpression().getExpressionString()).isEqualTo("interfaceMethodAuthzExpression"); assertThat(pre.getFilterExpression().getExpressionString()).isEqualTo("interfacePreFilterExpression"); } @Test public void classAttributesTakesPrecedeceOverInterfaceAttributes() { ConfigAttribute[] attrs = mds.getAttributes(notherListImpl2).toArray( new ConfigAttribute[0]); assertThat(attrs.length).isEqualTo(1); assertThat(attrs[0] instanceof PreInvocationExpressionAttribute).isTrue(); PreInvocationExpressionAttribute pre = (PreInvocationExpressionAttribute) attrs[0]; assertThat(pre.getFilterExpression()).isNotNull(); assertThat(pre.getAuthorizeExpression()).isNotNull(); assertThat(pre.getAuthorizeExpression().getExpressionString()).isEqualTo("interfaceMethodAuthzExpression"); assertThat(pre.getFilterExpression().getExpressionString()).isEqualTo("classMethodPreFilterExpression"); } @Test public void customAnnotationAtClassLevelIsDetected() throws Exception { ConfigAttribute[] attrs = mds.getAttributes(annotatedAtClassLevel).toArray( new ConfigAttribute[0]); assertThat(attrs.length).isEqualTo(1); } @Test public void customAnnotationAtInterfaceLevelIsDetected() throws Exception { ConfigAttribute[] attrs = mds.getAttributes(annotatedAtInterfaceLevel).toArray( new ConfigAttribute[0]); assertThat(attrs.length).isEqualTo(1); } @Test public void customAnnotationAtMethodLevelIsDetected() throws Exception { ConfigAttribute[] attrs = mds.getAttributes(annotatedAtMethodLevel).toArray( new ConfigAttribute[0]); assertThat(attrs.length).isEqualTo(1); } @Test public void proxyFactoryInterfaceAttributesFound() throws Exception { MockMethodInvocation mi = MethodInvocationFactory.createSec2150MethodInvocation(); Collection<ConfigAttribute> attributes = mds.getAttributes(mi); assertThat(attributes.size()).isEqualTo(1); Expression expression = (Expression) ReflectionTestUtils.getField(attributes .iterator().next(), "authorizeExpression"); assertThat(expression.getExpressionString()).isEqualTo("hasRole('ROLE_PERSON')"); } // ~ Inner Classes // ================================================================================================== public static interface ReturnVoid { public void doSomething(List<?> param); } public static interface ReturnAList { public List<?> doSomething(List<?> param); } @PreAuthorize("interfaceAuthzExpression") public static interface ReturnAnotherList { @PreAuthorize("interfaceMethodAuthzExpression") @PreFilter(filterTarget = "param", value = "interfacePreFilterExpression") public List<?> doSomething(List<?> param); } @PreAuthorize("someExpression") public static class ReturnVoidImpl1 implements ReturnVoid { public void doSomething(List<?> param) { } } @PreAuthorize("someExpression") public static class ReturnVoidImpl2 implements ReturnVoid { @PreFilter(filterTarget = "param", value = "somePreFilterExpression") public void doSomething(List<?> param) { } } public static class ReturnVoidImpl3 implements ReturnVoid { @PreFilter(filterTarget = "param", value = "somePreFilterExpression") public void doSomething(List<?> param) { } } public static class ReturnAListImpl1 implements ReturnAList { @PostFilter("somePostFilterExpression") public List<?> doSomething(List<?> param) { return param; } } public static class ReturnAListImpl2 implements ReturnAList { @PreAuthorize("someExpression") @PreFilter(filterTarget = "param", value = "somePreFilterExpression") @PostFilter("somePostFilterExpression") @PostAuthorize("somePostAuthorizeExpression") public List<?> doSomething(List<?> param) { return param; } } public static class ReturnAnotherListImpl1 implements ReturnAnotherList { public List<?> doSomething(List<?> param) { return param; } } public static class ReturnAnotherListImpl2 implements ReturnAnotherList { @PreFilter(filterTarget = "param", value = "classMethodPreFilterExpression") public List<?> doSomething(List<?> param) { return param; } } @Target({ ElementType.METHOD, ElementType.TYPE }) @Retention(RetentionPolicy.RUNTIME) @Inherited @PreAuthorize("customAnnotationExpression") public @interface CustomAnnotation { } @CustomAnnotation public static interface ReturnVoid2 { public void doSomething(List<?> param); } @CustomAnnotation public static class CustomAnnotationAtClassLevel implements ReturnVoid { public void doSomething(List<?> param) { } } public static class CustomAnnotationAtInterfaceLevel implements ReturnVoid2 { public void doSomething(List<?> param) { } } public static class CustomAnnotationAtMethodLevel implements ReturnVoid { @CustomAnnotation public void doSomething(List<?> param) { } } }