Jelajahi Sumber

SEC-1023: PermissionEvaluator based on the Acl module.

Luke Taylor 17 tahun lalu
induk
melakukan
1c3b576d91

+ 115 - 0
acl/src/main/java/org/springframework/security/acls/AclPermissionEvaluator.java

@@ -0,0 +1,115 @@
+package org.springframework.security.acls;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.security.Authentication;
+import org.springframework.security.acls.domain.BasePermission;
+import org.springframework.security.acls.objectidentity.ObjectIdentity;
+import org.springframework.security.acls.objectidentity.ObjectIdentityRetrievalStrategy;
+import org.springframework.security.acls.objectidentity.ObjectIdentityRetrievalStrategyImpl;
+import org.springframework.security.acls.sid.Sid;
+import org.springframework.security.acls.sid.SidRetrievalStrategy;
+import org.springframework.security.acls.sid.SidRetrievalStrategyImpl;
+import org.springframework.security.expression.PermissionEvaluator;
+
+/**
+ * Used by Spring Security's expression-based access control implementation to evaluate permissions for a particular
+ * object using the ACL module. Similar in behaviour to
+ * {@link org.springframework.security.vote.AclEntryVoter AclEntryVoter}.
+ *
+ * @author Luke Taylor
+ * @version $Id$
+ * @since 2.5
+ */
+public class AclPermissionEvaluator implements PermissionEvaluator {
+
+    private final Log logger = LogFactory.getLog(getClass());
+
+    private AclService aclService;
+    private ObjectIdentityRetrievalStrategy objectIdentityRetrievalStrategy = new ObjectIdentityRetrievalStrategyImpl();
+    private SidRetrievalStrategy sidRetrievalStrategy = new SidRetrievalStrategyImpl();
+
+    public AclPermissionEvaluator(AclService aclService) {
+        this.aclService = aclService;
+    }
+
+    /**
+     * Determines whether the user has the given permission(s) on the domain object using the ACL
+     * configuration. If the domain object is null, returns false (this can always be overridden using a null
+     * check in the expression itself).
+     */
+    public boolean hasPermission(Authentication authentication, Object domainObject, Object permission) {
+        if (domainObject == null) {
+            return false;
+        }
+
+        ObjectIdentity objectIdentity = objectIdentityRetrievalStrategy.getObjectIdentity(domainObject);
+
+        // Obtain the SIDs applicable to the principal
+        Sid[] sids = sidRetrievalStrategy.getSids(authentication);
+        Permission[] requiredPermission = resolvePermission(permission);
+
+        try {
+            // Lookup only ACLs for SIDs we're interested in
+            Acl acl = aclService.readAclById(objectIdentity, sids);
+
+            if (acl.isGranted(requiredPermission, sids, false)) {
+                if (logger.isDebugEnabled()) {
+                    logger.debug("Access is granted");
+                }
+
+                return true;
+            }
+
+            if (logger.isDebugEnabled()) {
+                logger.debug("Returning false - ACLs returned, but insufficient permissions for this principal");
+            }
+
+        } catch (NotFoundException nfe) {
+            if (logger.isDebugEnabled()) {
+                logger.debug("Returning false - no ACLs apply for this principal");
+            }
+        }
+
+        return false;
+    }
+
+    // TODO: Add permission resolver/PermissionFactory rewrite
+    Permission[] resolvePermission(Object permission) {
+        if (permission instanceof Integer) {
+            return new Permission[] {BasePermission.buildFromMask(((Integer)permission).intValue())};
+        }
+
+        if (permission instanceof Permission) {
+            return new Permission[] {(Permission)permission};
+        }
+
+        if (permission instanceof Permission[]) {
+            return (Permission[]) permission;
+        }
+
+        if (permission instanceof String) {
+            String permString = (String)permission;
+            Permission p = BasePermission.buildFromName(permString);
+
+            if (p == null) {
+                p = BasePermission.buildFromName(permString.toUpperCase());
+            }
+
+            if (p != null) {
+                return new Permission[] {p};
+            }
+
+        }
+        throw new IllegalArgumentException("unsupported permission: " + permission);
+    }
+
+    public void setObjectIdentityRetrievalStrategy(ObjectIdentityRetrievalStrategy objectIdentityRetrievalStrategy) {
+        this.objectIdentityRetrievalStrategy = objectIdentityRetrievalStrategy;
+    }
+
+    public void setSidRetrievalStrategy(SidRetrievalStrategy sidRetrievalStrategy) {
+        this.sidRetrievalStrategy = sidRetrievalStrategy;
+    }
+
+}

+ 56 - 0
acl/src/test/java/org/springframework/security/acls/AclPermissionEvaluatorTests.java

@@ -0,0 +1,56 @@
+package org.springframework.security.acls;
+
+import static org.junit.Assert.*;
+
+import org.jmock.Expectations;
+import org.jmock.Mockery;
+import org.jmock.integration.junit4.JUnit4Mockery;
+import org.junit.Before;
+import org.junit.Test;
+import org.springframework.security.Authentication;
+import org.springframework.security.acls.objectidentity.ObjectIdentity;
+import org.springframework.security.acls.objectidentity.ObjectIdentityRetrievalStrategy;
+import org.springframework.security.acls.sid.Sid;
+import org.springframework.security.acls.sid.SidRetrievalStrategy;
+
+/**
+ *
+ * @author Luke Taylor
+ * @version $Id$
+ * @since 2.5
+ */
+public class AclPermissionEvaluatorTests {
+    Mockery jmock = new JUnit4Mockery();
+    Authentication user;
+    private AclService service;
+    private ObjectIdentityRetrievalStrategy oidStrategy;
+    private SidRetrievalStrategy sidStrategy;
+
+    @Before
+    public void setup() throws Exception {
+        user = jmock.mock(Authentication.class);
+        service = jmock.mock(AclService.class);
+        oidStrategy = jmock.mock(ObjectIdentityRetrievalStrategy.class);
+        sidStrategy = jmock.mock(SidRetrievalStrategy.class);
+    }
+
+    @Test
+    public void hasPermissionReturnsTrueIfAclGrantsPermission() throws Exception {
+        AclPermissionEvaluator pe = new AclPermissionEvaluator(service);
+        final Acl acl = jmock.mock(Acl.class);
+        pe.setObjectIdentityRetrievalStrategy(oidStrategy);
+        pe.setSidRetrievalStrategy(sidStrategy);
+
+        jmock.checking(new Expectations() {{
+            ignoring(user);
+            ignoring(oidStrategy);
+            ignoring(sidStrategy);
+            oneOf(service).readAclById(with(any(ObjectIdentity.class)), with(any(Sid[].class)));
+                will(returnValue(acl));
+            oneOf(acl).isGranted(with(any(Permission[].class)), with(any(Sid[].class)), with(equal(false)));
+                will(returnValue(true));
+        }});
+
+        assertTrue(pe.hasPermission(user, new Object(), "read"));
+    }
+}