Move Acl Access API
Issue gh-17847
This commit is contained in:
@@ -9,8 +9,6 @@ dependencies {
|
||||
api 'org.springframework:spring-jdbc'
|
||||
api 'org.springframework:spring-tx'
|
||||
|
||||
optional project(':spring-security-access')
|
||||
|
||||
testImplementation "org.assertj:assertj-core"
|
||||
testImplementation "org.junit.jupiter:junit-jupiter-api"
|
||||
testImplementation "org.junit.jupiter:junit-jupiter-params"
|
||||
|
||||
@@ -1,245 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* https://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.acls;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.security.access.AuthorizationServiceException;
|
||||
import org.springframework.security.access.ConfigAttribute;
|
||||
import org.springframework.security.access.vote.AbstractAclVoter;
|
||||
import org.springframework.security.acls.domain.ObjectIdentityRetrievalStrategyImpl;
|
||||
import org.springframework.security.acls.domain.SidRetrievalStrategyImpl;
|
||||
import org.springframework.security.acls.model.Acl;
|
||||
import org.springframework.security.acls.model.AclService;
|
||||
import org.springframework.security.acls.model.NotFoundException;
|
||||
import org.springframework.security.acls.model.ObjectIdentity;
|
||||
import org.springframework.security.acls.model.ObjectIdentityRetrievalStrategy;
|
||||
import org.springframework.security.acls.model.Permission;
|
||||
import org.springframework.security.acls.model.Sid;
|
||||
import org.springframework.security.acls.model.SidRetrievalStrategy;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Given a domain object instance passed as a method argument, ensures the principal has
|
||||
* appropriate permission as indicated by the {@link AclService}.
|
||||
* <p>
|
||||
* The <tt>AclService</tt> is used to retrieve the access control list (ACL) permissions
|
||||
* associated with a domain object instance for the current <tt>Authentication</tt>
|
||||
* object.
|
||||
* <p>
|
||||
* The voter will vote if any {@link ConfigAttribute#getAttribute()} matches the
|
||||
* {@link #processConfigAttribute}. The provider will then locate the first method
|
||||
* argument of type {@link #processDomainObjectClass}. Assuming that method argument is
|
||||
* non-null, the provider will then lookup the ACLs from the <code>AclManager</code> and
|
||||
* ensure the principal is {@link Acl#isGranted(List, List, boolean)} when presenting the
|
||||
* {@link #requirePermission} array to that method.
|
||||
* <p>
|
||||
* If the method argument is <tt>null</tt>, the voter will abstain from voting. If the
|
||||
* method argument could not be found, an {@link AuthorizationServiceException} will be
|
||||
* thrown.
|
||||
* <p>
|
||||
* In practical terms users will typically setup a number of <tt>AclEntryVoter</tt>s. Each
|
||||
* will have a different {@link #setProcessDomainObjectClass processDomainObjectClass},
|
||||
* {@link #processConfigAttribute} and {@link #requirePermission} combination. For
|
||||
* example, a small application might employ the following instances of
|
||||
* <tt>AclEntryVoter</tt>:
|
||||
* <ul>
|
||||
* <li>Process domain object class <code>BankAccount</code>, configuration attribute
|
||||
* <code>VOTE_ACL_BANK_ACCONT_READ</code>, require permission
|
||||
* <code>BasePermission.READ</code></li>
|
||||
* <li>Process domain object class <code>BankAccount</code>, configuration attribute
|
||||
* <code>VOTE_ACL_BANK_ACCOUNT_WRITE</code>, require permission list
|
||||
* <code>BasePermission.WRITE</code> and <code>BasePermission.CREATE</code> (allowing the
|
||||
* principal to have <b>either</b> of these two permissions)</li>
|
||||
* <li>Process domain object class <code>Customer</code>, configuration attribute
|
||||
* <code>VOTE_ACL_CUSTOMER_READ</code>, require permission
|
||||
* <code>BasePermission.READ</code></li>
|
||||
* <li>Process domain object class <code>Customer</code>, configuration attribute
|
||||
* <code>VOTE_ACL_CUSTOMER_WRITE</code>, require permission list
|
||||
* <code>BasePermission.WRITE</code> and <code>BasePermission.CREATE</code></li>
|
||||
* </ul>
|
||||
* Alternatively, you could have used a common superclass or interface for the
|
||||
* {@link #processDomainObjectClass} if both <code>BankAccount</code> and
|
||||
* <code>Customer</code> had common parents.
|
||||
*
|
||||
* <p>
|
||||
* If the principal does not have sufficient permissions, the voter will vote to deny
|
||||
* access.
|
||||
*
|
||||
* <p>
|
||||
* 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);
|
||||
|
||||
private final AclService aclService;
|
||||
|
||||
private final String processConfigAttribute;
|
||||
|
||||
private final List<Permission> requirePermission;
|
||||
|
||||
private ObjectIdentityRetrievalStrategy objectIdentityRetrievalStrategy = new ObjectIdentityRetrievalStrategyImpl();
|
||||
|
||||
private SidRetrievalStrategy sidRetrievalStrategy = new SidRetrievalStrategyImpl();
|
||||
|
||||
private String internalMethod;
|
||||
|
||||
public AclEntryVoter(AclService aclService, String processConfigAttribute, Permission[] requirePermission) {
|
||||
Assert.notNull(processConfigAttribute, "A processConfigAttribute is mandatory");
|
||||
Assert.notNull(aclService, "An AclService is mandatory");
|
||||
Assert.isTrue(!ObjectUtils.isEmpty(requirePermission), "One or more requirePermission entries is mandatory");
|
||||
this.aclService = aclService;
|
||||
this.processConfigAttribute = processConfigAttribute;
|
||||
this.requirePermission = Arrays.asList(requirePermission);
|
||||
}
|
||||
|
||||
/**
|
||||
* Optionally specifies a method of the domain object that will be used to obtain a
|
||||
* contained domain object. That contained domain object will be used for the ACL
|
||||
* evaluation. This is useful if a domain object contains a parent that an ACL
|
||||
* evaluation should be targeted for, instead of the child domain object (which
|
||||
* perhaps is being created and as such does not yet have any ACL permissions)
|
||||
* @return <code>null</code> to use the domain object, or the name of a method (that
|
||||
* requires no arguments) that should be invoked to obtain an <code>Object</code>
|
||||
* which will be the domain object used for ACL evaluation
|
||||
*/
|
||||
protected String getInternalMethod() {
|
||||
return this.internalMethod;
|
||||
}
|
||||
|
||||
public void setInternalMethod(String internalMethod) {
|
||||
this.internalMethod = internalMethod;
|
||||
}
|
||||
|
||||
protected String getProcessConfigAttribute() {
|
||||
return this.processConfigAttribute;
|
||||
}
|
||||
|
||||
public void setObjectIdentityRetrievalStrategy(ObjectIdentityRetrievalStrategy objectIdentityRetrievalStrategy) {
|
||||
Assert.notNull(objectIdentityRetrievalStrategy, "ObjectIdentityRetrievalStrategy required");
|
||||
this.objectIdentityRetrievalStrategy = objectIdentityRetrievalStrategy;
|
||||
}
|
||||
|
||||
public void setSidRetrievalStrategy(SidRetrievalStrategy sidRetrievalStrategy) {
|
||||
Assert.notNull(sidRetrievalStrategy, "SidRetrievalStrategy required");
|
||||
this.sidRetrievalStrategy = sidRetrievalStrategy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(ConfigAttribute attribute) {
|
||||
return (attribute.getAttribute() != null) && attribute.getAttribute().equals(getProcessConfigAttribute());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int vote(Authentication authentication, MethodInvocation object, Collection<ConfigAttribute> attributes) {
|
||||
for (ConfigAttribute attr : attributes) {
|
||||
if (!supports(attr)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Need to make an access decision on this invocation
|
||||
// Attempt to locate the domain object instance to process
|
||||
Object domainObject = getDomainObjectInstance(object);
|
||||
|
||||
// If domain object is null, vote to abstain
|
||||
if (domainObject == null) {
|
||||
logger.debug("Voting to abstain - domainObject is null");
|
||||
return ACCESS_ABSTAIN;
|
||||
}
|
||||
|
||||
// Evaluate if we are required to use an inner domain object
|
||||
if (StringUtils.hasText(this.internalMethod)) {
|
||||
domainObject = invokeInternalMethod(domainObject);
|
||||
}
|
||||
|
||||
// Obtain the OID applicable to the domain object
|
||||
ObjectIdentity objectIdentity = this.objectIdentityRetrievalStrategy.getObjectIdentity(domainObject);
|
||||
|
||||
// Obtain the SIDs applicable to the principal
|
||||
List<Sid> sids = this.sidRetrievalStrategy.getSids(authentication);
|
||||
|
||||
Acl acl;
|
||||
|
||||
try {
|
||||
// Lookup only ACLs for SIDs we're interested in
|
||||
acl = this.aclService.readAclById(objectIdentity, sids);
|
||||
}
|
||||
catch (NotFoundException ex) {
|
||||
logger.debug("Voting to deny access - no ACLs apply for this principal");
|
||||
return ACCESS_DENIED;
|
||||
}
|
||||
|
||||
try {
|
||||
if (acl.isGranted(this.requirePermission, sids, false)) {
|
||||
logger.debug("Voting to grant access");
|
||||
return ACCESS_GRANTED;
|
||||
}
|
||||
logger.debug("Voting to deny access - ACLs returned, but insufficient permissions for this principal");
|
||||
return ACCESS_DENIED;
|
||||
}
|
||||
catch (NotFoundException ex) {
|
||||
logger.debug("Voting to deny access - no ACLs apply for this principal");
|
||||
return ACCESS_DENIED;
|
||||
}
|
||||
}
|
||||
|
||||
// No configuration attribute matched, so abstain
|
||||
return ACCESS_ABSTAIN;
|
||||
}
|
||||
|
||||
private Object invokeInternalMethod(Object domainObject) {
|
||||
try {
|
||||
Class<?> domainObjectType = domainObject.getClass();
|
||||
Method method = domainObjectType.getMethod(this.internalMethod, new Class[0]);
|
||||
return method.invoke(domainObject);
|
||||
}
|
||||
catch (NoSuchMethodException ex) {
|
||||
throw new AuthorizationServiceException("Object of class '" + domainObject.getClass()
|
||||
+ "' does not provide the requested internalMethod: " + this.internalMethod);
|
||||
}
|
||||
catch (IllegalAccessException ex) {
|
||||
logger.debug("IllegalAccessException", ex);
|
||||
throw new AuthorizationServiceException(
|
||||
"Problem invoking internalMethod: " + this.internalMethod + " for object: " + domainObject);
|
||||
}
|
||||
catch (InvocationTargetException ex) {
|
||||
logger.debug("InvocationTargetException", ex);
|
||||
throw new AuthorizationServiceException(
|
||||
"Problem invoking internalMethod: " + this.internalMethod + " for object: " + domainObject);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
-129
@@ -1,129 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* https://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.acls.afterinvocation;
|
||||
|
||||
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;
|
||||
import org.springframework.security.acls.model.AclService;
|
||||
import org.springframework.security.acls.model.NotFoundException;
|
||||
import org.springframework.security.acls.model.ObjectIdentity;
|
||||
import org.springframework.security.acls.model.ObjectIdentityRetrievalStrategy;
|
||||
import org.springframework.security.acls.model.Permission;
|
||||
import org.springframework.security.acls.model.Sid;
|
||||
import org.springframework.security.acls.model.SidRetrievalStrategy;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* Abstract {@link AfterInvocationProvider} which provides commonly-used ACL-related
|
||||
* 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;
|
||||
|
||||
protected String processConfigAttribute;
|
||||
|
||||
protected Class<?> processDomainObjectClass = Object.class;
|
||||
|
||||
protected ObjectIdentityRetrievalStrategy objectIdentityRetrievalStrategy = new ObjectIdentityRetrievalStrategyImpl();
|
||||
|
||||
protected SidRetrievalStrategy sidRetrievalStrategy = new SidRetrievalStrategyImpl();
|
||||
|
||||
protected final List<Permission> requirePermission;
|
||||
|
||||
public AbstractAclProvider(AclService aclService, String processConfigAttribute,
|
||||
List<Permission> requirePermission) {
|
||||
Assert.hasText(processConfigAttribute, "A processConfigAttribute is mandatory");
|
||||
Assert.notNull(aclService, "An AclService is mandatory");
|
||||
Assert.isTrue(!ObjectUtils.isEmpty(requirePermission), "One or more requirePermission entries is mandatory");
|
||||
this.aclService = aclService;
|
||||
this.processConfigAttribute = processConfigAttribute;
|
||||
this.requirePermission = requirePermission;
|
||||
}
|
||||
|
||||
protected Class<?> getProcessDomainObjectClass() {
|
||||
return this.processDomainObjectClass;
|
||||
}
|
||||
|
||||
protected boolean hasPermission(Authentication authentication, Object domainObject) {
|
||||
// Obtain the OID applicable to the domain object
|
||||
ObjectIdentity objectIdentity = this.objectIdentityRetrievalStrategy.getObjectIdentity(domainObject);
|
||||
|
||||
// Obtain the SIDs applicable to the principal
|
||||
List<Sid> sids = this.sidRetrievalStrategy.getSids(authentication);
|
||||
|
||||
try {
|
||||
// Lookup only ACLs for SIDs we're interested in
|
||||
Acl acl = this.aclService.readAclById(objectIdentity, sids);
|
||||
return acl.isGranted(this.requirePermission, sids, false);
|
||||
}
|
||||
catch (NotFoundException ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void setObjectIdentityRetrievalStrategy(ObjectIdentityRetrievalStrategy objectIdentityRetrievalStrategy) {
|
||||
Assert.notNull(objectIdentityRetrievalStrategy, "ObjectIdentityRetrievalStrategy required");
|
||||
this.objectIdentityRetrievalStrategy = objectIdentityRetrievalStrategy;
|
||||
}
|
||||
|
||||
protected void setProcessConfigAttribute(String processConfigAttribute) {
|
||||
Assert.hasText(processConfigAttribute, "A processConfigAttribute is mandatory");
|
||||
this.processConfigAttribute = processConfigAttribute;
|
||||
}
|
||||
|
||||
public void setProcessDomainObjectClass(Class<?> processDomainObjectClass) {
|
||||
Assert.notNull(processDomainObjectClass, "processDomainObjectClass cannot be set to null");
|
||||
this.processDomainObjectClass = processDomainObjectClass;
|
||||
}
|
||||
|
||||
public void setSidRetrievalStrategy(SidRetrievalStrategy sidRetrievalStrategy) {
|
||||
Assert.notNull(sidRetrievalStrategy, "SidRetrievalStrategy required");
|
||||
this.sidRetrievalStrategy = sidRetrievalStrategy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(ConfigAttribute attribute) {
|
||||
return this.processConfigAttribute.equals(attribute.getAttribute());
|
||||
}
|
||||
|
||||
/**
|
||||
* This implementation supports any type of class, because it does not query the
|
||||
* presented secure object.
|
||||
* @param clazz the secure object
|
||||
* @return always <code>true</code>
|
||||
*/
|
||||
@Override
|
||||
public boolean supports(Class<?> clazz) {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
-125
@@ -1,125 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* https://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.acls.afterinvocation;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Given a <code>Collection</code> of domain object instances returned from a secure
|
||||
* object invocation, remove any <code>Collection</code> elements the principal does not
|
||||
* have appropriate permission to access as defined by the {@link AclService}.
|
||||
* <p>
|
||||
* The <code>AclService</code> is used to retrieve the access control list (ACL)
|
||||
* permissions associated with each <code>Collection</code> domain object instance element
|
||||
* for the current <code>Authentication</code> object.
|
||||
* <p>
|
||||
* This after invocation provider will fire if any {@link ConfigAttribute#getAttribute()}
|
||||
* matches the {@link #processConfigAttribute}. The provider will then lookup the ACLs
|
||||
* from the <code>AclService</code> and ensure the principal is
|
||||
* {@link org.springframework.security.acls.model.Acl#isGranted(List, List, boolean)
|
||||
* Acl.isGranted()} when presenting the {@link #requirePermission} array to that method.
|
||||
* <p>
|
||||
* If the principal does not have permission, that element will not be included in the
|
||||
* returned <code>Collection</code>.
|
||||
* <p>
|
||||
* Often users will setup a <code>BasicAclEntryAfterInvocationProvider</code> with a
|
||||
* {@link #processConfigAttribute} of <code>AFTER_ACL_COLLECTION_READ</code> and a
|
||||
* {@link #requirePermission} of <code>BasePermission.READ</code>. These are also the
|
||||
* defaults.
|
||||
* <p>
|
||||
* If the provided <code>returnObject</code> is <code>null</code>, a <code>null</code>
|
||||
* <code>Collection</code> will be returned. If the provided <code>returnObject</code> is
|
||||
* not a <code>Collection</code>, an {@link AuthorizationServiceException} will be thrown.
|
||||
* <p>
|
||||
* All comparisons and prefixes are case sensitive.
|
||||
*
|
||||
* @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);
|
||||
|
||||
public AclEntryAfterInvocationCollectionFilteringProvider(AclService aclService,
|
||||
List<Permission> requirePermission) {
|
||||
super(aclService, "AFTER_ACL_COLLECTION_READ", requirePermission);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Object decide(Authentication authentication, Object object, Collection<ConfigAttribute> config,
|
||||
Object returnedObject) throws AccessDeniedException {
|
||||
if (returnedObject == null) {
|
||||
logger.debug("Return object is null, skipping");
|
||||
return null;
|
||||
}
|
||||
|
||||
for (ConfigAttribute attr : config) {
|
||||
if (!this.supports(attr)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Need to process the Collection for this invocation
|
||||
Filterer filterer = getFilterer(returnedObject);
|
||||
|
||||
// Locate unauthorised Collection elements
|
||||
for (Object domainObject : filterer) {
|
||||
// Ignore nulls or entries which aren't instances of the configured domain
|
||||
// object class
|
||||
if (domainObject == null || !getProcessDomainObjectClass().isAssignableFrom(domainObject.getClass())) {
|
||||
continue;
|
||||
}
|
||||
if (!hasPermission(authentication, domainObject)) {
|
||||
filterer.remove(domainObject);
|
||||
logger.debug(LogMessage.of(() -> "Principal is NOT authorised for element: " + domainObject));
|
||||
}
|
||||
}
|
||||
return filterer.getFilteredObject();
|
||||
}
|
||||
return returnedObject;
|
||||
}
|
||||
|
||||
private Filterer getFilterer(Object returnedObject) {
|
||||
if (returnedObject instanceof Collection) {
|
||||
return new CollectionFilterer((Collection) returnedObject);
|
||||
}
|
||||
if (returnedObject.getClass().isArray()) {
|
||||
return new ArrayFilterer((Object[]) returnedObject);
|
||||
}
|
||||
throw new AuthorizationServiceException("A Collection or an array (or null) was required as the "
|
||||
+ "returnedObject, but the returnedObject was: " + returnedObject);
|
||||
}
|
||||
|
||||
}
|
||||
-124
@@ -1,124 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* https://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.acls.afterinvocation;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.context.MessageSource;
|
||||
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;
|
||||
import org.springframework.security.core.SpringSecurityMessageSource;
|
||||
|
||||
/**
|
||||
* Given a domain object instance returned from a secure object invocation, ensures the
|
||||
* principal has appropriate permission as defined by the {@link AclService}.
|
||||
* <p>
|
||||
* The <code>AclService</code> is used to retrieve the access control list (ACL)
|
||||
* permissions associated with a domain object instance for the current
|
||||
* <code>Authentication</code> object.
|
||||
* <p>
|
||||
* This after invocation provider will fire if any {@link ConfigAttribute#getAttribute()}
|
||||
* matches the {@link #processConfigAttribute}. The provider will then lookup the ACLs
|
||||
* from the <tt>AclService</tt> and ensure the principal is
|
||||
* {@link org.springframework.security.acls.model.Acl#isGranted(List, List, boolean)
|
||||
* Acl.isGranted(List, List, boolean)} when presenting the {@link #requirePermission}
|
||||
* array to that method.
|
||||
* <p>
|
||||
* Often users will set up an <code>AclEntryAfterInvocationProvider</code> with a
|
||||
* {@link #processConfigAttribute} of <code>AFTER_ACL_READ</code> and a
|
||||
* {@link #requirePermission} of <code>BasePermission.READ</code>. These are also the
|
||||
* defaults.
|
||||
* <p>
|
||||
* If the principal does not have sufficient permissions, an
|
||||
* <code>AccessDeniedException</code> will be thrown.
|
||||
* <p>
|
||||
* If the provided <tt>returnedObject</tt> is <code>null</code>, permission will always be
|
||||
* granted and <code>null</code> will be returned.
|
||||
* <p>
|
||||
* 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);
|
||||
|
||||
protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
|
||||
|
||||
public AclEntryAfterInvocationProvider(AclService aclService, List<Permission> requirePermission) {
|
||||
this(aclService, "AFTER_ACL_READ", requirePermission);
|
||||
}
|
||||
|
||||
public AclEntryAfterInvocationProvider(AclService aclService, String processConfigAttribute,
|
||||
List<Permission> requirePermission) {
|
||||
super(aclService, processConfigAttribute, requirePermission);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object decide(Authentication authentication, Object object, Collection<ConfigAttribute> config,
|
||||
Object returnedObject) throws AccessDeniedException {
|
||||
|
||||
if (returnedObject == null) {
|
||||
// AclManager interface contract prohibits nulls
|
||||
// As they have permission to null/nothing, grant access
|
||||
logger.debug("Return object is null, skipping");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!getProcessDomainObjectClass().isAssignableFrom(returnedObject.getClass())) {
|
||||
logger.debug("Return object is not applicable for this provider, skipping");
|
||||
return returnedObject;
|
||||
}
|
||||
|
||||
for (ConfigAttribute attr : config) {
|
||||
if (!this.supports(attr)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Need to make an access decision on this invocation
|
||||
if (hasPermission(authentication, returnedObject)) {
|
||||
return returnedObject;
|
||||
}
|
||||
|
||||
logger.debug("Denying access");
|
||||
throw new AccessDeniedException(this.messages.getMessage("AclEntryAfterInvocationProvider.noPermission",
|
||||
new Object[] { authentication.getName(), returnedObject },
|
||||
"Authentication {0} has NO permissions to the domain object {1}"));
|
||||
}
|
||||
|
||||
return returnedObject;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMessageSource(MessageSource messageSource) {
|
||||
this.messages = new MessageSourceAccessor(messageSource);
|
||||
}
|
||||
|
||||
}
|
||||
-105
@@ -1,105 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* https://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.acls.afterinvocation;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.core.log.LogMessage;
|
||||
|
||||
/**
|
||||
* A filter used to filter arrays.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @author Paulo Neves
|
||||
* @deprecated please see {@code PostFilter}
|
||||
*/
|
||||
@Deprecated
|
||||
class ArrayFilterer<T> implements Filterer<T> {
|
||||
|
||||
protected static final Log logger = LogFactory.getLog(ArrayFilterer.class);
|
||||
|
||||
private final Set<T> removeList;
|
||||
|
||||
private final T[] list;
|
||||
|
||||
ArrayFilterer(T[] list) {
|
||||
this.list = list;
|
||||
// Collect the removed objects to a HashSet so that
|
||||
// it is fast to lookup them when a filtered array
|
||||
// is constructed.
|
||||
this.removeList = new HashSet<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public T[] getFilteredObject() {
|
||||
// Recreate an array of same type and filter the removed objects.
|
||||
int originalSize = this.list.length;
|
||||
int sizeOfResultingList = originalSize - this.removeList.size();
|
||||
T[] filtered = (T[]) Array.newInstance(this.list.getClass().getComponentType(), sizeOfResultingList);
|
||||
for (int i = 0, j = 0; i < this.list.length; i++) {
|
||||
T object = this.list[i];
|
||||
if (!this.removeList.contains(object)) {
|
||||
filtered[j] = object;
|
||||
j++;
|
||||
}
|
||||
}
|
||||
logger.debug(LogMessage.of(() -> "Original array contained " + originalSize + " elements; now contains "
|
||||
+ sizeOfResultingList + " elements"));
|
||||
return filtered;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<T> iterator() {
|
||||
return new ArrayFiltererIterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(T object) {
|
||||
this.removeList.add(object);
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterator for {@link ArrayFilterer} elements.
|
||||
*/
|
||||
private class ArrayFiltererIterator implements Iterator<T> {
|
||||
|
||||
private int index = 0;
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return this.index < ArrayFilterer.this.list.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T next() {
|
||||
if (hasNext()) {
|
||||
return ArrayFilterer.this.list[this.index++];
|
||||
}
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
-80
@@ -1,80 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* https://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.acls.afterinvocation;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.core.log.LogMessage;
|
||||
|
||||
/**
|
||||
* A filter used to filter Collections.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @author Paulo Neves
|
||||
* @deprecated please see {@code PostFilter}
|
||||
*/
|
||||
@Deprecated
|
||||
class CollectionFilterer<T> implements Filterer<T> {
|
||||
|
||||
protected static final Log logger = LogFactory.getLog(CollectionFilterer.class);
|
||||
|
||||
private final Collection<T> collection;
|
||||
|
||||
private final Set<T> removeList;
|
||||
|
||||
CollectionFilterer(Collection<T> collection) {
|
||||
this.collection = collection;
|
||||
// We create a Set of objects to be removed from the Collection,
|
||||
// as ConcurrentModificationException prevents removal during
|
||||
// iteration, and making a new Collection to be returned is
|
||||
// problematic as the original Collection implementation passed
|
||||
// to the method may not necessarily be re-constructable (as
|
||||
// the Collection(collection) constructor is not guaranteed and
|
||||
// manually adding may lose sort order or other capabilities)
|
||||
this.removeList = new HashSet<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getFilteredObject() {
|
||||
// Now the Iterator has ended, remove Objects from Collection
|
||||
Iterator<T> removeIter = this.removeList.iterator();
|
||||
int originalSize = this.collection.size();
|
||||
while (removeIter.hasNext()) {
|
||||
this.collection.remove(removeIter.next());
|
||||
}
|
||||
logger.debug(LogMessage.of(() -> "Original collection contained " + originalSize + " elements; now contains "
|
||||
+ this.collection.size() + " elements"));
|
||||
return this.collection;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<T> iterator() {
|
||||
return this.collection.iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(T object) {
|
||||
this.removeList.add(object);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* https://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.acls.afterinvocation;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* Filterer strategy interface.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @author Paulo Neves
|
||||
* @deprecated please use {@code PreFilter} and {@code @PostFilter} instead
|
||||
*/
|
||||
@Deprecated
|
||||
interface Filterer<T> extends Iterable<T> {
|
||||
|
||||
/**
|
||||
* Gets the filtered collection or array.
|
||||
* @return the filtered collection or array
|
||||
*/
|
||||
Object getFilteredObject();
|
||||
|
||||
/**
|
||||
* Returns an iterator over the filtered collection or array.
|
||||
* @return an Iterator
|
||||
*/
|
||||
@Override
|
||||
Iterator<T> iterator();
|
||||
|
||||
/**
|
||||
* Removes the given object from the resulting list.
|
||||
* @param object the object to be removed
|
||||
*/
|
||||
void remove(T object);
|
||||
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004-present 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* After-invocation providers for collection and array filtering. Consider using a
|
||||
* {@code PostFilter} annotation in preference.
|
||||
*/
|
||||
package org.springframework.security.acls.afterinvocation;
|
||||
-93
@@ -1,93 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004-present 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
|
||||
*
|
||||
* https://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.acls.afterinvocation;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.security.access.ConfigAttribute;
|
||||
import org.springframework.security.access.SecurityConfig;
|
||||
import org.springframework.security.acls.model.Acl;
|
||||
import org.springframework.security.acls.model.AclService;
|
||||
import org.springframework.security.acls.model.ObjectIdentity;
|
||||
import org.springframework.security.acls.model.ObjectIdentityRetrievalStrategy;
|
||||
import org.springframework.security.acls.model.Permission;
|
||||
import org.springframework.security.acls.model.SidRetrievalStrategy;
|
||||
import org.springframework.security.core.Authentication;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
/**
|
||||
* @author Luke Taylor
|
||||
*/
|
||||
@SuppressWarnings({ "unchecked" })
|
||||
public class AclEntryAfterInvocationCollectionFilteringProviderTests {
|
||||
|
||||
@Test
|
||||
public void objectsAreRemovedIfPermissionDenied() {
|
||||
AclService service = mock(AclService.class);
|
||||
Acl acl = mock(Acl.class);
|
||||
given(acl.isGranted(any(), any(), anyBoolean())).willReturn(false);
|
||||
given(service.readAclById(any(), any())).willReturn(acl);
|
||||
AclEntryAfterInvocationCollectionFilteringProvider provider = new AclEntryAfterInvocationCollectionFilteringProvider(
|
||||
service, Arrays.asList(mock(Permission.class)));
|
||||
provider.setObjectIdentityRetrievalStrategy(mock(ObjectIdentityRetrievalStrategy.class));
|
||||
provider.setProcessDomainObjectClass(Object.class);
|
||||
provider.setSidRetrievalStrategy(mock(SidRetrievalStrategy.class));
|
||||
Object returned = provider.decide(mock(Authentication.class), new Object(),
|
||||
SecurityConfig.createList("AFTER_ACL_COLLECTION_READ"),
|
||||
new ArrayList(Arrays.asList(new Object(), new Object())));
|
||||
assertThat(returned).isInstanceOf(List.class);
|
||||
assertThat(((List) returned)).isEmpty();
|
||||
returned = provider.decide(mock(Authentication.class), new Object(),
|
||||
SecurityConfig.createList("UNSUPPORTED", "AFTER_ACL_COLLECTION_READ"),
|
||||
new Object[] { new Object(), new Object() });
|
||||
assertThat(returned instanceof Object[]).isTrue();
|
||||
assertThat(((Object[]) returned).length == 0).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void accessIsGrantedIfNoAttributesDefined() {
|
||||
AclEntryAfterInvocationCollectionFilteringProvider provider = new AclEntryAfterInvocationCollectionFilteringProvider(
|
||||
mock(AclService.class), Arrays.asList(mock(Permission.class)));
|
||||
Object returned = new Object();
|
||||
assertThat(returned).isSameAs(provider.decide(mock(Authentication.class), new Object(),
|
||||
Collections.<ConfigAttribute>emptyList(), returned));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nullReturnObjectIsIgnored() {
|
||||
AclService service = mock(AclService.class);
|
||||
AclEntryAfterInvocationCollectionFilteringProvider provider = new AclEntryAfterInvocationCollectionFilteringProvider(
|
||||
service, Arrays.asList(mock(Permission.class)));
|
||||
assertThat(provider.decide(mock(Authentication.class), new Object(),
|
||||
SecurityConfig.createList("AFTER_ACL_COLLECTION_READ"), null))
|
||||
.isNull();
|
||||
verify(service, never()).readAclById(any(ObjectIdentity.class), any(List.class));
|
||||
}
|
||||
|
||||
}
|
||||
-134
@@ -1,134 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004-present 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
|
||||
*
|
||||
* https://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.acls.afterinvocation;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.access.ConfigAttribute;
|
||||
import org.springframework.security.access.SecurityConfig;
|
||||
import org.springframework.security.acls.model.Acl;
|
||||
import org.springframework.security.acls.model.AclService;
|
||||
import org.springframework.security.acls.model.NotFoundException;
|
||||
import org.springframework.security.acls.model.ObjectIdentity;
|
||||
import org.springframework.security.acls.model.ObjectIdentityRetrievalStrategy;
|
||||
import org.springframework.security.acls.model.Permission;
|
||||
import org.springframework.security.acls.model.SidRetrievalStrategy;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.SpringSecurityMessageSource;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
/**
|
||||
* @author Luke Taylor
|
||||
*/
|
||||
@SuppressWarnings({ "unchecked" })
|
||||
public class AclEntryAfterInvocationProviderTests {
|
||||
|
||||
@Test
|
||||
public void rejectsMissingPermissions() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> new AclEntryAfterInvocationProvider(mock(AclService.class), null));
|
||||
assertThatIllegalArgumentException().isThrownBy(
|
||||
() -> new AclEntryAfterInvocationProvider(mock(AclService.class), Collections.<Permission>emptyList()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void accessIsAllowedIfPermissionIsGranted() {
|
||||
AclService service = mock(AclService.class);
|
||||
Acl acl = mock(Acl.class);
|
||||
given(acl.isGranted(any(List.class), any(List.class), anyBoolean())).willReturn(true);
|
||||
given(service.readAclById(any(), any())).willReturn(acl);
|
||||
AclEntryAfterInvocationProvider provider = new AclEntryAfterInvocationProvider(service,
|
||||
Arrays.asList(mock(Permission.class)));
|
||||
provider.setMessageSource(new SpringSecurityMessageSource());
|
||||
provider.setObjectIdentityRetrievalStrategy(mock(ObjectIdentityRetrievalStrategy.class));
|
||||
provider.setProcessDomainObjectClass(Object.class);
|
||||
provider.setSidRetrievalStrategy(mock(SidRetrievalStrategy.class));
|
||||
Object returned = new Object();
|
||||
assertThat(returned).isSameAs(provider.decide(mock(Authentication.class), new Object(),
|
||||
SecurityConfig.createList("AFTER_ACL_READ"), returned));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void accessIsGrantedIfNoAttributesDefined() {
|
||||
AclEntryAfterInvocationProvider provider = new AclEntryAfterInvocationProvider(mock(AclService.class),
|
||||
Arrays.asList(mock(Permission.class)));
|
||||
Object returned = new Object();
|
||||
assertThat(returned).isSameAs(provider.decide(mock(Authentication.class), new Object(),
|
||||
Collections.<ConfigAttribute>emptyList(), returned));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void accessIsGrantedIfObjectTypeNotSupported() {
|
||||
AclEntryAfterInvocationProvider provider = new AclEntryAfterInvocationProvider(mock(AclService.class),
|
||||
Arrays.asList(mock(Permission.class)));
|
||||
provider.setProcessDomainObjectClass(String.class);
|
||||
// Not a String
|
||||
Object returned = new Object();
|
||||
assertThat(returned).isSameAs(provider.decide(mock(Authentication.class), new Object(),
|
||||
SecurityConfig.createList("AFTER_ACL_READ"), returned));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void accessIsDeniedIfPermissionIsNotGranted() {
|
||||
AclService service = mock(AclService.class);
|
||||
Acl acl = mock(Acl.class);
|
||||
given(acl.isGranted(any(List.class), any(List.class), anyBoolean())).willReturn(false);
|
||||
// Try a second time with no permissions found
|
||||
given(acl.isGranted(any(), any(List.class), anyBoolean())).willThrow(new NotFoundException(""));
|
||||
given(service.readAclById(any(), any())).willReturn(acl);
|
||||
AclEntryAfterInvocationProvider provider = new AclEntryAfterInvocationProvider(service,
|
||||
Arrays.asList(mock(Permission.class)));
|
||||
provider.setProcessConfigAttribute("MY_ATTRIBUTE");
|
||||
provider.setMessageSource(new SpringSecurityMessageSource());
|
||||
provider.setObjectIdentityRetrievalStrategy(mock(ObjectIdentityRetrievalStrategy.class));
|
||||
provider.setProcessDomainObjectClass(Object.class);
|
||||
provider.setSidRetrievalStrategy(mock(SidRetrievalStrategy.class));
|
||||
assertThatExceptionOfType(AccessDeniedException.class)
|
||||
.isThrownBy(() -> provider.decide(mock(Authentication.class), new Object(),
|
||||
SecurityConfig.createList("UNSUPPORTED", "MY_ATTRIBUTE"), new Object()));
|
||||
// Second scenario with no acls found
|
||||
assertThatExceptionOfType(AccessDeniedException.class)
|
||||
.isThrownBy(() -> provider.decide(mock(Authentication.class), new Object(),
|
||||
SecurityConfig.createList("UNSUPPORTED", "MY_ATTRIBUTE"), new Object()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nullReturnObjectIsIgnored() {
|
||||
AclService service = mock(AclService.class);
|
||||
AclEntryAfterInvocationProvider provider = new AclEntryAfterInvocationProvider(service,
|
||||
Arrays.asList(mock(Permission.class)));
|
||||
assertThat(provider.decide(mock(Authentication.class), new Object(),
|
||||
SecurityConfig.createList("AFTER_ACL_COLLECTION_READ"), null))
|
||||
.isNull();
|
||||
verify(service, never()).readAclById(any(ObjectIdentity.class), any(List.class));
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user