diff --git a/config/src/test/java/org/springframework/security/config/method/Contact.java b/config/src/test/java/org/springframework/security/config/method/Contact.java new file mode 100644 index 0000000000..12f3e433bd --- /dev/null +++ b/config/src/test/java/org/springframework/security/config/method/Contact.java @@ -0,0 +1,39 @@ +/* + * Copyright 2002-2015 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.config.method; + +/** + * @author Rob Winch + * + */ +public class Contact { + private String name; + + /** + * @param name + */ + public Contact(String name) { + super(); + this.name = name; + } + + /** + * @return the name + */ + public String getName() { + return name; + } +} \ No newline at end of file diff --git a/config/src/test/java/org/springframework/security/config/method/ContactPermission.java b/config/src/test/java/org/springframework/security/config/method/ContactPermission.java new file mode 100644 index 0000000000..2a260aba8a --- /dev/null +++ b/config/src/test/java/org/springframework/security/config/method/ContactPermission.java @@ -0,0 +1,29 @@ +/* + * Copyright 2002-2015 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.config.method; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +import org.springframework.security.access.prepost.PreAuthorize; + +/** + * @author Rob Winch + * + */ +@Retention(RetentionPolicy.RUNTIME) +@PreAuthorize("#contact.name == authentication.name") +public @interface ContactPermission {} diff --git a/config/src/test/java/org/springframework/security/config/method/PreAuthorizeAdminRole.java b/config/src/test/java/org/springframework/security/config/method/PreAuthorizeAdminRole.java new file mode 100644 index 0000000000..c7c013685b --- /dev/null +++ b/config/src/test/java/org/springframework/security/config/method/PreAuthorizeAdminRole.java @@ -0,0 +1,31 @@ +/* + * Copyright 2002-2015 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.config.method; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +import org.springframework.security.access.prepost.PreAuthorize; + +/** + * @author Rob Winch + * + */ +@Retention(RetentionPolicy.RUNTIME) +@PreAuthorize("hasRole('ADMIN')") +public @interface PreAuthorizeAdminRole { + +} diff --git a/config/src/test/java/org/springframework/security/config/method/PreAuthorizeServiceImpl.java b/config/src/test/java/org/springframework/security/config/method/PreAuthorizeServiceImpl.java new file mode 100644 index 0000000000..91905c6654 --- /dev/null +++ b/config/src/test/java/org/springframework/security/config/method/PreAuthorizeServiceImpl.java @@ -0,0 +1,30 @@ +/* + * Copyright 2002-2015 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.config.method; + +/** + * @author Rob Winch + * + */ +public class PreAuthorizeServiceImpl { + + @PreAuthorizeAdminRole + public void preAuthorizeAdminRole() {} + + @ContactPermission + public void contactPermission(Contact contact) {} + +} \ No newline at end of file diff --git a/config/src/test/java/org/springframework/security/config/method/PreAuthorizeTests.java b/config/src/test/java/org/springframework/security/config/method/PreAuthorizeTests.java new file mode 100644 index 0000000000..5f863916ec --- /dev/null +++ b/config/src/test/java/org/springframework/security/config/method/PreAuthorizeTests.java @@ -0,0 +1,67 @@ +/* + * Copyright 2002-2015 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.config.method; + +import org.junit.After; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.security.authentication.TestingAuthenticationToken; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +/** + * + * @author Rob Winch + * + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration +public class PreAuthorizeTests { + @Autowired + PreAuthorizeServiceImpl service; + + @After + public void cleanup() { + SecurityContextHolder.clearContext(); + } + + @Test(expected = AccessDeniedException.class) + public void preAuthorizeAdminRoleDenied() { + SecurityContextHolder.getContext().setAuthentication(new TestingAuthenticationToken("user", "pass", "ROLE_USER")); + service.preAuthorizeAdminRole(); + } + + @Test + public void preAuthorizeAdminRoleGranted() { + SecurityContextHolder.getContext().setAuthentication(new TestingAuthenticationToken("user", "pass", "ROLE_ADMIN")); + service.preAuthorizeAdminRole(); + } + + @Test + public void preAuthorizeContactPermissionGranted() { + SecurityContextHolder.getContext().setAuthentication(new TestingAuthenticationToken("user", "pass", "ROLE_ADMIN")); + service.contactPermission(new Contact("user")); + } + + @Test(expected = AccessDeniedException.class) + public void preAuthorizeContactPermissionDenied() { + SecurityContextHolder.getContext().setAuthentication(new TestingAuthenticationToken("user", "pass", "ROLE_ADMIN")); + service.contactPermission(new Contact("admin")); + } +} \ No newline at end of file diff --git a/config/src/test/java/org/springframework/security/config/method/SecuredAdminRole.java b/config/src/test/java/org/springframework/security/config/method/SecuredAdminRole.java new file mode 100644 index 0000000000..60df1d01e2 --- /dev/null +++ b/config/src/test/java/org/springframework/security/config/method/SecuredAdminRole.java @@ -0,0 +1,29 @@ +/* + * Copyright 2002-2015 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.config.method; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +import org.springframework.security.access.annotation.Secured; + +/** + * @author Rob Winch + * + */ +@Retention(RetentionPolicy.RUNTIME) +@Secured("ROLE_ADMIN") +public @interface SecuredAdminRole { } \ No newline at end of file diff --git a/config/src/test/java/org/springframework/security/config/method/SecuredServiceImpl.java b/config/src/test/java/org/springframework/security/config/method/SecuredServiceImpl.java new file mode 100644 index 0000000000..73bd991649 --- /dev/null +++ b/config/src/test/java/org/springframework/security/config/method/SecuredServiceImpl.java @@ -0,0 +1,26 @@ +/* + * Copyright 2002-2015 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.config.method; + +/** +* +* @author Rob Winch +* +*/ +public class SecuredServiceImpl { + @SecuredAdminRole + public void securedAdminRole() {} +} diff --git a/config/src/test/java/org/springframework/security/config/method/SecuredTests.java b/config/src/test/java/org/springframework/security/config/method/SecuredTests.java new file mode 100644 index 0000000000..9983039541 --- /dev/null +++ b/config/src/test/java/org/springframework/security/config/method/SecuredTests.java @@ -0,0 +1,55 @@ +/* + * Copyright 2002-2015 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.config.method; + +import org.junit.After; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.security.authentication.TestingAuthenticationToken; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +/** + * + * @author Rob Winch + * + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration +public class SecuredTests { + @Autowired + SecuredServiceImpl service; + + @After + public void cleanup() { + SecurityContextHolder.clearContext(); + } + + @Test(expected = AccessDeniedException.class) + public void securedAdminRoleDenied() { + SecurityContextHolder.getContext().setAuthentication(new TestingAuthenticationToken("user", "pass", "ROLE_USER")); + service.securedAdminRole(); + } + + @Test + public void securedAdminRoleGranted() { + SecurityContextHolder.getContext().setAuthentication(new TestingAuthenticationToken("user", "pass", "ROLE_ADMIN")); + service.securedAdminRole(); + } +} \ No newline at end of file diff --git a/config/src/test/resources/org/springframework/security/config/method/PreAuthorizeTests-context.xml b/config/src/test/resources/org/springframework/security/config/method/PreAuthorizeTests-context.xml new file mode 100644 index 0000000000..a28e01a6e5 --- /dev/null +++ b/config/src/test/resources/org/springframework/security/config/method/PreAuthorizeTests-context.xml @@ -0,0 +1,13 @@ + + + + + + + + \ No newline at end of file diff --git a/config/src/test/resources/org/springframework/security/config/method/SecuredTests-context.xml b/config/src/test/resources/org/springframework/security/config/method/SecuredTests-context.xml new file mode 100644 index 0000000000..d4b5c1391a --- /dev/null +++ b/config/src/test/resources/org/springframework/security/config/method/SecuredTests-context.xml @@ -0,0 +1,13 @@ + + + + + + + + diff --git a/docs/manual/src/docs/asciidoc/index.adoc b/docs/manual/src/docs/asciidoc/index.adoc index f5f9f6c686..21ea16f36c 100644 --- a/docs/manual/src/docs/asciidoc/index.adoc +++ b/docs/manual/src/docs/asciidoc/index.adoc @@ -369,7 +369,9 @@ This will give you access to the entire project history (including all releases [[new]] == What's new in Spring Security 4.1 -* <> +* Meta Annotation Support +** <> +** <> === What's new in Spring Security 4.0 @@ -4727,6 +4729,29 @@ To use `hasPermission()` expressions, you have to explicitly configure a `Permis Where `myPermissionEvaluator` is the bean which implements `PermissionEvaluator`. Usually this will be the implementation from the ACL module which is called`AclPermissionEvaluator`. See the "Contacts" sample application configuration for more details. +===== Method Security Meta Annotations + +You can make use of meta annotations for method security to make your code more readable. +This is especially convenient if you find that you are repeating the same complex expression throughout your code base. +For example, consider the following: + +[source,java] +---- +@PreAuthorize("#contact.name == authentication.name") +---- + +Instead of repeating this everywhere, we can create a meta annotation that can be used instead. + +[source,java] +---- +@Retention(RetentionPolicy.RUNTIME) +@PreAuthorize("#contact.name == authentication.name") +public @interface ContactPermission {} +---- + +Meta annotations can be used for any of the Spring Security method security annotations. +In order to remain compliant with the specification JSR-250 annotations do not support meta annotations. + [[advanced-topics]] = Additional Topics In this part we cover features which require a knowledge of previous chapters as well as some of the more advanced and less-commonly used features of the framework.