diff --git a/acl/src/main/java/org/springframework/security/acls/AclEntryVoter.java b/acl/src/main/java/org/springframework/security/acls/AclEntryVoter.java index 4e694b0247..13c3154fd8 100644 --- a/acl/src/main/java/org/springframework/security/acls/AclEntryVoter.java +++ b/acl/src/main/java/org/springframework/security/acls/AclEntryVoter.java @@ -96,7 +96,10 @@ import org.springframework.util.StringUtils; * All comparisons and prefixes are case sensitive. * * @author Ben Alex + * @deprecated please use {@link AclPermissionEvaluator} instead. Spring Method Security annotations + * may also prove useful, for example {@code @PreAuthorize("hasPermission(#id, ObjectsReturnType.class, read)")} */ +@Deprecated public class AclEntryVoter extends AbstractAclVoter { private static final Log logger = LogFactory.getLog(AclEntryVoter.class); diff --git a/acl/src/main/java/org/springframework/security/acls/afterinvocation/AbstractAclProvider.java b/acl/src/main/java/org/springframework/security/acls/afterinvocation/AbstractAclProvider.java index 14fb8730d7..50e46dfcdf 100644 --- a/acl/src/main/java/org/springframework/security/acls/afterinvocation/AbstractAclProvider.java +++ b/acl/src/main/java/org/springframework/security/acls/afterinvocation/AbstractAclProvider.java @@ -20,6 +20,7 @@ import java.util.List; import org.springframework.security.access.AfterInvocationProvider; import org.springframework.security.access.ConfigAttribute; +import org.springframework.security.acls.AclPermissionEvaluator; import org.springframework.security.acls.domain.ObjectIdentityRetrievalStrategyImpl; import org.springframework.security.acls.domain.SidRetrievalStrategyImpl; import org.springframework.security.acls.model.Acl; @@ -39,7 +40,10 @@ import org.springframework.util.ObjectUtils; * services. * * @author Ben Alex + * @deprecated please use {@link AclPermissionEvaluator} instead. Spring Method Security annotations + * may also prove useful, for example {@code @PostAuthorize("hasPermission(filterObject, read)")} */ +@Deprecated public abstract class AbstractAclProvider implements AfterInvocationProvider { protected final AclService aclService; diff --git a/acl/src/main/java/org/springframework/security/acls/afterinvocation/AclEntryAfterInvocationCollectionFilteringProvider.java b/acl/src/main/java/org/springframework/security/acls/afterinvocation/AclEntryAfterInvocationCollectionFilteringProvider.java index fb788322dc..10ca4148f5 100644 --- a/acl/src/main/java/org/springframework/security/acls/afterinvocation/AclEntryAfterInvocationCollectionFilteringProvider.java +++ b/acl/src/main/java/org/springframework/security/acls/afterinvocation/AclEntryAfterInvocationCollectionFilteringProvider.java @@ -26,6 +26,7 @@ import org.springframework.core.log.LogMessage; import org.springframework.security.access.AccessDeniedException; import org.springframework.security.access.AuthorizationServiceException; import org.springframework.security.access.ConfigAttribute; +import org.springframework.security.acls.AclPermissionEvaluator; import org.springframework.security.acls.model.AclService; import org.springframework.security.acls.model.Permission; import org.springframework.security.core.Authentication; @@ -62,7 +63,10 @@ import org.springframework.security.core.Authentication; * * @author Ben Alex * @author Paulo Neves + * @deprecated please use {@link AclPermissionEvaluator} instead. Spring Method Security annotations + * may also prove useful, for example {@code @PostFilter("hasPermission(filterObject, read)")} */ +@Deprecated public class AclEntryAfterInvocationCollectionFilteringProvider extends AbstractAclProvider { protected static final Log logger = LogFactory.getLog(AclEntryAfterInvocationCollectionFilteringProvider.class); diff --git a/acl/src/main/java/org/springframework/security/acls/afterinvocation/AclEntryAfterInvocationProvider.java b/acl/src/main/java/org/springframework/security/acls/afterinvocation/AclEntryAfterInvocationProvider.java index 7659cee298..95e71a4b98 100644 --- a/acl/src/main/java/org/springframework/security/acls/afterinvocation/AclEntryAfterInvocationProvider.java +++ b/acl/src/main/java/org/springframework/security/acls/afterinvocation/AclEntryAfterInvocationProvider.java @@ -27,6 +27,7 @@ import org.springframework.context.MessageSourceAware; import org.springframework.context.support.MessageSourceAccessor; import org.springframework.security.access.AccessDeniedException; import org.springframework.security.access.ConfigAttribute; +import org.springframework.security.acls.AclPermissionEvaluator; import org.springframework.security.acls.model.AclService; import org.springframework.security.acls.model.Permission; import org.springframework.security.core.Authentication; @@ -59,7 +60,10 @@ import org.springframework.security.core.SpringSecurityMessageSource; * granted and null will be returned. *

* All comparisons and prefixes are case sensitive. + * @deprecated please use {@link AclPermissionEvaluator} instead. Spring Method Security annotations + * may also prove useful, for example {@code @PostAuthorize("hasPermission(filterObject, read)")} */ +@Deprecated public class AclEntryAfterInvocationProvider extends AbstractAclProvider implements MessageSourceAware { protected static final Log logger = LogFactory.getLog(AclEntryAfterInvocationProvider.class); diff --git a/acl/src/main/java/org/springframework/security/acls/afterinvocation/ArrayFilterer.java b/acl/src/main/java/org/springframework/security/acls/afterinvocation/ArrayFilterer.java index c9c9d01fd5..5b5e84fa41 100644 --- a/acl/src/main/java/org/springframework/security/acls/afterinvocation/ArrayFilterer.java +++ b/acl/src/main/java/org/springframework/security/acls/afterinvocation/ArrayFilterer.java @@ -32,7 +32,9 @@ import org.springframework.core.log.LogMessage; * * @author Ben Alex * @author Paulo Neves + * @deprecated please see {@code PostFilter} */ +@Deprecated class ArrayFilterer implements Filterer { protected static final Log logger = LogFactory.getLog(ArrayFilterer.class); diff --git a/acl/src/main/java/org/springframework/security/acls/afterinvocation/CollectionFilterer.java b/acl/src/main/java/org/springframework/security/acls/afterinvocation/CollectionFilterer.java index 8322a9c1aa..99c7583647 100644 --- a/acl/src/main/java/org/springframework/security/acls/afterinvocation/CollectionFilterer.java +++ b/acl/src/main/java/org/springframework/security/acls/afterinvocation/CollectionFilterer.java @@ -31,7 +31,9 @@ import org.springframework.core.log.LogMessage; * * @author Ben Alex * @author Paulo Neves + * @deprecated please see {@code PostFilter} */ +@Deprecated class CollectionFilterer implements Filterer { protected static final Log logger = LogFactory.getLog(CollectionFilterer.class); diff --git a/acl/src/main/java/org/springframework/security/acls/afterinvocation/Filterer.java b/acl/src/main/java/org/springframework/security/acls/afterinvocation/Filterer.java index f41bfa0bc7..953f6109e7 100644 --- a/acl/src/main/java/org/springframework/security/acls/afterinvocation/Filterer.java +++ b/acl/src/main/java/org/springframework/security/acls/afterinvocation/Filterer.java @@ -23,7 +23,9 @@ import java.util.Iterator; * * @author Ben Alex * @author Paulo Neves + * @deprecated please use {@code PreFilter} and {@code @PostFilter} instead */ +@Deprecated interface Filterer extends Iterable { /** diff --git a/docs/modules/ROOT/pages/servlet/authorization/acls.adoc b/docs/modules/ROOT/pages/servlet/authorization/acls.adoc index b8c9189d7c..314d524be2 100644 --- a/docs/modules/ROOT/pages/servlet/authorization/acls.adoc +++ b/docs/modules/ROOT/pages/servlet/authorization/acls.adoc @@ -202,11 +202,139 @@ Instead, you need to write code similar to that shown in the preceding example f You should consider using AOP on your services layer to automatically integrate the ACL information with your services layer operations. We have found this approach to be effective. -Once you have used the techniques described here to store some ACL information in the database, the next step is to actually use the ACL information as part of authorization decision logic. -You have a number of choices here. -You could write your own `AccessDecisionVoter` or `AfterInvocationProvider` that (respectively) fires before or after a method invocation. -Such classes would use `AclService` to retrieve the relevant ACL and then call `Acl.isGranted(Permission[] permission, Sid[] sids, boolean administrativeMode)` to decide whether permission is granted or denied. -Alternately, you could use our `AclEntryVoter`, `AclEntryAfterInvocationProvider` or `AclEntryAfterInvocationCollectionFilteringProvider` classes. -All of these classes provide a declarative-based approach to evaluating ACL information at runtime, freeing you from needing to write any code. +== Using the PermissionEvaluator -See the https://github.com/spring-projects/spring-security-samples[sample applications] to learn how to use these classes. +Once you have used the techniques described here to store some ACL information in the database, the next step is to actually use the ACL information as part of authorization decision logic. + +You have a number of choices here with the primary one being using `AclPermissionEvaluator` in your `@PreAuthorize`, `@PostAuthorize`, `@PreFilter`, and `@PostFilter` annotation expressions. + +This is a sample listing of the components needed to wire an `AclPersmissionEvaluator` into your authorization logic: + +[tabs] +====== +Java:: ++ +[source,java,role="primary"] +---- +@EnableMethodSecurity +@Configuration +class SecurityConfig { + @Bean + static MethodSecurityExpressionHandler expressionHandler(AclPermissionEvaluator aclPermissionEvaluator) { + final DefaultMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler(); + expressionHandler.setPermissionEvaluator(aclPermissionEvaluator); + return expressionHandler; + } + + @Bean + static AclPermissionEvaluator aclPermissionEvaluator(AclService aclService) { + return new AclPermissionEvaluator(aclService); + } + + @Bean + static JdbcMutableAclService aclService(DataSource dataSource, LookupStrategy lookupStrategy, AclCache aclCache) { + return new JdbcMutableAclService(dataSource, lookupStrategy, aclCache); + } + + @Bean + static LookupStrategy lookupStrategy(DataSource dataSource, AclCache cache, + AclAuthorizationStrategy aclAuthorizationStrategy, PermissionGrantingStrategy permissionGrantingStrategy) { + return new BasicLookupStrategy(dataSource, cache, aclAuthorizationStrategy, permissionGrantingStrategy); + } + + @Bean + static AclCache aclCache(PermissionGrantingStrategy permissionGrantingStrategy, + AclAuthorizationStrategy aclAuthorizationStrategy) { + Cache cache = new ConcurrentMapCache("aclCache"); + return new SpringCacheBasedAclCache(cache, permissionGrantingStrategy, aclAuthorizationStrategy); + } + + @Bean + static AclAuthorizationStrategy aclAuthorizationStrategy() { + return new AclAuthorizationStrategyImpl(new SimpleGrantedAuthority("ADMIN")); + } + + @Bean + static PermissionGrantingStrategy permissionGrantingStrategy() { + return new DefaultPermissionGrantingStrategy(new ConsoleAuditLogger()); + } +} +---- + +Kotlin:: ++ +[source,kotlin,role="secondary"] +---- +@EnableMethodSecurity +@Configuration +internal object SecurityConfig { + @Bean + fun expressionHandler(aclPermissionEvaluator: AclPermissionEvaluator?): MethodSecurityExpressionHandler { + val expressionHandler = DefaultMethodSecurityExpressionHandler() + expressionHandler.setPermissionEvaluator(aclPermissionEvaluator) + return expressionHandler + } + + @Bean + fun aclPermissionEvaluator(aclService: AclService?): AclPermissionEvaluator { + return AclPermissionEvaluator(aclService) + } + + @Bean + fun aclService(dataSource: DataSource?, lookupStrategy: LookupStrategy?, aclCache: AclCache?): JdbcMutableAclService { + return JdbcMutableAclService(dataSource, lookupStrategy, aclCache) + } + + @Bean + fun lookupStrategy(dataSource: DataSource?, cache: AclCache?, + aclAuthorizationStrategy: AclAuthorizationStrategy?, permissionGrantingStrategy: PermissionGrantingStrategy?): LookupStrategy { + return BasicLookupStrategy(dataSource, cache, aclAuthorizationStrategy, permissionGrantingStrategy) + } + + @Bean + fun aclCache(permissionGrantingStrategy: PermissionGrantingStrategy?, + aclAuthorizationStrategy: AclAuthorizationStrategy?): AclCache { + val cache: Cache = ConcurrentMapCache("aclCache") + return SpringCacheBasedAclCache(cache, permissionGrantingStrategy, aclAuthorizationStrategy) + } + + @Bean + fun aclAuthorizationStrategy(): AclAuthorizationStrategy { + return AclAuthorizationStrategyImpl(SimpleGrantedAuthority("ADMIN")) + } + + @Bean + fun permissionGrantingStrategy(): PermissionGrantingStrategy { + return DefaultPermissionGrantingStrategy(ConsoleAuditLogger()) + } +} +---- +====== + + +Then using xref:servlet/authorization/method-security.adoc#authorizing-with-annotations[method-based security] you can use `hasPermission` in your annotation expressions like so: + +[tabs] +====== +Java:: ++ +[source,java,role="primary"] +---- +@GetMapping +@PostFilter("hasPermission(filterObject, read)") +Iterable getAll() { + return this.messagesRepository.findAll(); +} +---- + +Kotlin:: ++ +[source,kotlin,role="secondary"] +---- +@GetMapping +@PostFilter("hasPermission(filterObject, read)") +fun getAll(): Iterable { + return this.messagesRepository.findAll() +} +---- +======