Search code examples
springspring-securityspring-security-acl

Grant permission to existing object identities with Spring Security ACL


I'm using the Spring Security ACL implementation and am wondering what's the best way of granting a new permission to a role/user (security identity - SID) for existing object identities.

For example, let's say I have the following (I'm mostly omitting the primary keys and some other columns and simply reference by string values for readability):

  • ACL_SID: ROLE_TEST
  • ACL_CLASS: test_class
  • ACL_OBJECT_IDENTITY:
    • id: 1
    • object_id_class: test_class
    • object_id_identity: someObjectInstanceId
  • ACL_ENTRY:
    • acl_object_identity: 1
    • sid: ROLE_TEST
    • mask: CREATE (this would be an integer in the db)

Now, I want to grant the permission WRITE to the role ROLE_TEST for all future and existing objects of class test_class. For objects created in the future I will simply check the role for its permissions and grant them. But what about the existing objects?

Does Spring provide anything to easily do this or do I have to write my own custom code to do the following (which wouldn't be so bad but if Spring already provides this, I would rather not do it myself):

  1. Retrieve all ACL entries with the SID of the role/user I want to grant the new permission to and that reference an object identity which has the appropriate object_id_class.
  2. For each result, create a new ACL entry that is identical to the result except for the mask, which would reflect the new permission.

Solution

  • Since I couldn't find any build-in Spring support for this, I ended up with the following custom code. Not the most efficient but it does what I need it to do.

    If anyone knows of a better solution or has suggestions for improving the below code, please let me know.

    Custom JDBC ACL Service to retrieve all existing object identities by type (acl_class):

    import org.springframework.jdbc.core.JdbcTemplate;
    import org.springframework.security.acls.domain.GrantedAuthoritySid;
    import org.springframework.security.acls.domain.ObjectIdentityImpl;
    import org.springframework.security.acls.domain.PrincipalSid;
    import org.springframework.security.acls.model.ObjectIdentity;
    import org.springframework.security.acls.model.Sid;
    
    import javax.sql.DataSource;
    import java.io.Serializable;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Map;
    
    public class CustomJdbcAclService {
    
        private final JdbcTemplate jdbcTemplate;
    
        private final static String SELECT_EXISTING_BY_CLASS_AND_SID =
                "SELECT DISTINCT " +
                "acl_object_identity.object_id_identity " +
                "FROM acl_object_identity " +
                "LEFT JOIN acl_entry on acl_entry.acl_object_identity = acl_object_identity.id " +
                "LEFT JOIN acl_class on acl_class.id = acl_object_identity.object_id_class " +
                "LEFT JOIN acl_sid on acl_entry.sid = acl_sid.id " +
                "WHERE acl_class.class = ? && acl_sid.sid = ? && acl_entry.granting = 1";
    
        public CustomJdbcAclService(DataSource dataSource) {
            this.jdbcTemplate = new JdbcTemplate(dataSource);
        }
    
        public List<ObjectIdentity> getExistingObjectIdentities(String type, Sid sid) {
            String sidName = getSidName(sid);
    
            List<Map<String, Object>> results = jdbcTemplate.queryForList(SELECT_EXISTING_BY_CLASS_AND_SID, type, sidName);
            List<ObjectIdentity> oids = new ArrayList<>();
    
            for (Map<String, Object> result : results) {
                oids.add(new ObjectIdentityImpl(type, (Serializable) result.get("object_id_identity")));
            }
    
            return oids;
        }
    
        private String getSidName(Sid sid) {
            String sidName;
    
            if (sid instanceof PrincipalSid) {
                sidName = ((PrincipalSid) sid).getPrincipal();
            } else if (sid instanceof GrantedAuthoritySid) {
                sidName = ((GrantedAuthoritySid) sid).getGrantedAuthority();
            } else {
                throw new IllegalArgumentException("Unsupported implementation of Sid");
            }
            return sidName;
        }
    
    }
    

    I call the above from another ACL service:

    public class MyAclService {
    
    public void grantPermissionsToExisting(String type, Sid securityIdentity, Permission... permissions) {
            List<ObjectIdentity> existingOids = customJdbcAclService.getExistingObjectIdentities(type, securityIdentity);
            for (ObjectIdentity oid : existingOids) {
                // grantPermissions() gets the ACL of the object identity and inserts the new ACEs
                grantPermissions(oid, securityIdentity, permissions);
            }
        }
    
    }