Sfoglia il codice sorgente

SEC-3076: Add Method Level Security Meta Annotations

Rob Winch 10 anni fa
parent
commit
cbed1d75ee

+ 39 - 0
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;
+	}
+}

+ 29 - 0
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 {}

+ 31 - 0
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 {
+
+}

+ 30 - 0
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) {}
+
+}

+ 67 - 0
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"));
+	}
+}

+ 29 - 0
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 { }

+ 26 - 0
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() {}
+}

+ 55 - 0
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();
+	}
+}

+ 13 - 0
config/src/test/resources/org/springframework/security/config/method/PreAuthorizeTests-context.xml

@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<b:beans xmlns:b="http://www.springframework.org/schema/beans"
+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xmlns="http://www.springframework.org/schema/security"
+	xmlns:c="http://www.springframework.org/schema/c"
+	xsi:schemaLocation="http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-4.0.xsd
+		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
+
+	<global-method-security pre-post-annotations="enabled"/>
+
+	<b:bean class="org.springframework.security.config.method.PreAuthorizeServiceImpl"/>
+
+</b:beans>

+ 13 - 0
config/src/test/resources/org/springframework/security/config/method/SecuredTests-context.xml

@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<b:beans xmlns:b="http://www.springframework.org/schema/beans"
+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xmlns="http://www.springframework.org/schema/security"
+	xmlns:c="http://www.springframework.org/schema/c"
+	xsi:schemaLocation="http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-4.0.xsd
+		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
+
+	<global-method-security secured-annotations="enabled"/>
+
+	<b:bean class="org.springframework.security.config.method.SecuredServiceImpl"/>
+
+</b:beans>

+ 26 - 1
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
 
-* <<test-method-meta-annotations>>
+* Meta Annotation Support
+** <<test-method-meta-annotations>>
+** <<method-security-meta-annotations>>
 
 === 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.