diff --git a/acl/src/main/java/org/springframework/security/acls/domain/AclAuthorizationStrategyImpl.java b/acl/src/main/java/org/springframework/security/acls/domain/AclAuthorizationStrategyImpl.java index 35ceed5c8ce..9c73dabe194 100644 --- a/acl/src/main/java/org/springframework/security/acls/domain/AclAuthorizationStrategyImpl.java +++ b/acl/src/main/java/org/springframework/security/acls/domain/AclAuthorizationStrategyImpl.java @@ -83,7 +83,7 @@ public void securityCheck(Acl acl, int changeType) { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); // Check if authorized by virtue of ACL ownership - Sid currentUser = new PrincipalSid(authentication); + Sid currentUser = createCurrentUser(authentication); if (currentUser.equals(acl.getOwner()) && ((changeType == CHANGE_GENERAL) || (changeType == CHANGE_OWNERSHIP))) { @@ -119,6 +119,17 @@ public void securityCheck(Acl acl, int changeType) { "Principal does not have required ACL permissions to perform requested operation"); } + /** + * Creates a principal-like sid from the authentication information. + * + * @param authentication the authentication information that can provide principal and thus the sid's id will be + * dependant on the value inside + * @return a sid with the ID taken from the authentication information + */ + protected Sid createCurrentUser(Authentication authentication) { + return new PrincipalSid(authentication); + } + public void setSidRetrievalStrategy(SidRetrievalStrategy sidRetrievalStrategy) { Assert.notNull(sidRetrievalStrategy, "SidRetrievalStrategy required"); this.sidRetrievalStrategy = sidRetrievalStrategy; diff --git a/acl/src/main/java/org/springframework/security/acls/jdbc/BasicLookupStrategy.java b/acl/src/main/java/org/springframework/security/acls/jdbc/BasicLookupStrategy.java index ab469d10d46..b4abee260bf 100644 --- a/acl/src/main/java/org/springframework/security/acls/jdbc/BasicLookupStrategy.java +++ b/acl/src/main/java/org/springframework/security/acls/jdbc/BasicLookupStrategy.java @@ -74,7 +74,7 @@ * * @author Ben Alex */ -public final class BasicLookupStrategy implements LookupStrategy { +public class BasicLookupStrategy implements LookupStrategy { public final static String DEFAULT_SELECT_CLAUSE = "select acl_object_identity.object_id_identity, " + "acl_entry.ace_order, " @@ -256,7 +256,7 @@ public void setValues(PreparedStatement ps) throws SQLException { * should not throw {@link NotFoundException}, as a chain of {@link LookupStrategy}s may be used * to automatically create entries if required) */ - public Map readAclsById(List objects, List sids) { + public final Map readAclsById(List objects, List sids) { Assert.isTrue(batchSize >= 1, "BatchSize must be >= 1"); Assert.notEmpty(objects, "Objects to lookup required"); @@ -428,17 +428,33 @@ private AclImpl convert(Map inputMap, Long currentIdentity) { return result; } + /** + * Creates a particular implementation of {@link Sid} depending on the arguments. + * + * @param sid the name of the sid representing its unique identifier. In typical ACL database schema it's + * located in table {@code acl_sid} table, {@code sid} column. + * @param isPrincipal whether it's a user or granted authority like role + * @return the instance of Sid with the {@code sidName} as an identifier + */ + protected Sid createSid(boolean isPrincipal, String sid) { + if (isPrincipal) { + return new PrincipalSid(sid); + } else { + return new GrantedAuthoritySid(sid); + } + } + /** * Sets the {@code PermissionFactory} instance which will be used to convert loaded permission * data values to {@code Permission}s. A {@code DefaultPermissionFactory} will be used by default. * * @param permissionFactory */ - public void setPermissionFactory(PermissionFactory permissionFactory) { + public final void setPermissionFactory(PermissionFactory permissionFactory) { this.permissionFactory = permissionFactory; } - public void setBatchSize(int batchSize) { + public final void setBatchSize(int batchSize) { this.batchSize = batchSize; } @@ -448,28 +464,28 @@ public void setBatchSize(int batchSize) { * * @param selectClause the select clause, which defaults to {@link #DEFAULT_SELECT_CLAUSE}. */ - public void setSelectClause(String selectClause) { + public final void setSelectClause(String selectClause) { this.selectClause = selectClause; } /** * The SQL for the where clause used in the lookupPrimaryKey method. */ - public void setLookupPrimaryKeysWhereClause(String lookupPrimaryKeysWhereClause) { + public final void setLookupPrimaryKeysWhereClause(String lookupPrimaryKeysWhereClause) { this.lookupPrimaryKeysWhereClause = lookupPrimaryKeysWhereClause; } /** * The SQL for the where clause used in the lookupObjectIdentities method. */ - public void setLookupObjectIdentitiesWhereClause(String lookupObjectIdentitiesWhereClause) { + public final void setLookupObjectIdentitiesWhereClause(String lookupObjectIdentitiesWhereClause) { this.lookupObjectIdentitiesWhereClause = lookupObjectIdentitiesWhereClause; } /** * The SQL for the "order by" clause used in both queries. */ - public void setOrderByClause(String orderByClause) { + public final void setOrderByClause(String orderByClause) { this.orderByClause = orderByClause; } @@ -556,13 +572,7 @@ private void convertCurrentResultIntoObject(Map acls, ResultS } boolean entriesInheriting = rs.getBoolean("entries_inheriting"); - Sid owner; - - if (rs.getBoolean("acl_principal")) { - owner = new PrincipalSid(rs.getString("acl_sid")); - } else { - owner = new GrantedAuthoritySid(rs.getString("acl_sid")); - } + Sid owner = createSid(rs.getBoolean("acl_principal"), rs.getString("acl_sid")); acl = new AclImpl(objectIdentity, id, aclAuthorizationStrategy, grantingStrategy, parentAcl, null, entriesInheriting, owner); @@ -574,13 +584,7 @@ private void convertCurrentResultIntoObject(Map acls, ResultS // It is permissible to have no ACEs in an ACL (which is detected by a null ACE_SID) if (rs.getString("ace_sid") != null) { Long aceId = new Long(rs.getLong("ace_id")); - Sid recipient; - - if (rs.getBoolean("ace_principal")) { - recipient = new PrincipalSid(rs.getString("ace_sid")); - } else { - recipient = new GrantedAuthoritySid(rs.getString("ace_sid")); - } + Sid recipient = createSid(rs.getBoolean("ace_principal"), rs.getString("ace_sid")); int mask = rs.getInt("mask"); Permission permission = permissionFactory.buildFromMask(mask); diff --git a/acl/src/main/java/org/springframework/security/acls/jdbc/JdbcMutableAclService.java b/acl/src/main/java/org/springframework/security/acls/jdbc/JdbcMutableAclService.java index 22d86d4cb47..130ab1cb07b 100644 --- a/acl/src/main/java/org/springframework/security/acls/jdbc/JdbcMutableAclService.java +++ b/acl/src/main/java/org/springframework/security/acls/jdbc/JdbcMutableAclService.java @@ -206,6 +206,19 @@ protected Long createOrRetrieveSidPrimaryKey(Sid sid, boolean allowCreate) { throw new IllegalArgumentException("Unsupported implementation of Sid"); } + return createOrRetrieveSidPrimaryKey(sidName, sidIsPrincipal, allowCreate); + } + + /** + * Retrieves the primary key from acl_sid, creating a new row if needed and the allowCreate property is + * true. + * @param sidName name of Sid to find or to create + * @param sidIsPrincipal whether it's a user or granted authority like role + * @param allowCreate true if creation is permitted if not found + * @return the primary key or null if not found + */ + protected Long createOrRetrieveSidPrimaryKey(String sidName, boolean sidIsPrincipal, boolean allowCreate) { + List sidIds = jdbcTemplate.queryForList(selectSidPrimaryKey, new Object[] {Boolean.valueOf(sidIsPrincipal), sidName}, Long.class); diff --git a/acl/src/test/java/org/springframework/security/acls/jdbc/BasicLookupStrategyTests.java b/acl/src/test/java/org/springframework/security/acls/jdbc/BasicLookupStrategyTests.java index a44b35503a3..d76c2b82f92 100644 --- a/acl/src/test/java/org/springframework/security/acls/jdbc/BasicLookupStrategyTests.java +++ b/acl/src/test/java/org/springframework/security/acls/jdbc/BasicLookupStrategyTests.java @@ -9,15 +9,7 @@ import org.springframework.core.io.Resource; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.datasource.SingleConnectionDataSource; -import org.springframework.security.acls.domain.AclAuthorizationStrategy; -import org.springframework.security.acls.domain.AclAuthorizationStrategyImpl; -import org.springframework.security.acls.domain.BasePermission; -import org.springframework.security.acls.domain.ConsoleAuditLogger; -import org.springframework.security.acls.domain.DefaultPermissionFactory; -import org.springframework.security.acls.domain.DefaultPermissionGrantingStrategy; -import org.springframework.security.acls.domain.EhCacheBasedAclCache; -import org.springframework.security.acls.domain.ObjectIdentityImpl; -import org.springframework.security.acls.domain.PrincipalSid; +import org.springframework.security.acls.domain.*; import org.springframework.security.acls.model.Acl; import org.springframework.security.acls.model.AuditableAccessControlEntry; import org.springframework.security.acls.model.MutableAcl; @@ -301,4 +293,20 @@ public void nullOwnerIsNotSupported() { strategy.readAclsById(Arrays.asList(oid), Arrays.asList(BEN_SID)); } + @Test + public void testCreatePrincipalSid() { + Sid result = strategy.createSid(true, "sid"); + + Assert.assertEquals(PrincipalSid.class, result.getClass()); + Assert.assertEquals("sid", ((PrincipalSid)result).getPrincipal()); + } + + @Test + public void testCreateGrantedAuthority() { + Sid result = strategy.createSid(false, "sid"); + + Assert.assertEquals(GrantedAuthoritySid.class, result.getClass()); + Assert.assertEquals("sid", ((GrantedAuthoritySid)result).getGrantedAuthority()); + } + } diff --git a/acl/src/test/java/org/springframework/security/acls/jdbc/JdbcMutableAclServiceTests.java b/acl/src/test/java/org/springframework/security/acls/jdbc/JdbcMutableAclServiceTests.java index 7b0a9066877..a9707cfbf0b 100644 --- a/acl/src/test/java/org/springframework/security/acls/jdbc/JdbcMutableAclServiceTests.java +++ b/acl/src/test/java/org/springframework/security/acls/jdbc/JdbcMutableAclServiceTests.java @@ -15,6 +15,7 @@ package org.springframework.security.acls.jdbc; import static org.junit.Assert.*; +import static org.mockito.Mockito.*; import java.util.Arrays; import java.util.List; @@ -43,6 +44,7 @@ import org.springframework.security.acls.model.ObjectIdentity; import org.springframework.security.acls.model.Permission; import org.springframework.security.acls.model.Sid; +import org.springframework.security.acls.sid.CustomSid; import org.springframework.security.authentication.TestingAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; @@ -488,6 +490,43 @@ public void cumulativePermissions() { assertTrue(topParent.isGranted(Arrays.asList(cm), Arrays.asList(benSid), true)); SecurityContextHolder.clearContext(); - } + } + @Test + public void testProcessingCustomSid() { + CustomJdbcMutableAclService customJdbcMutableAclService = spy(new CustomJdbcMutableAclService(dataSource, + lookupStrategy, aclCache)); + CustomSid customSid = new CustomSid("Custom sid"); + when(customJdbcMutableAclService.createOrRetrieveSidPrimaryKey("Custom sid", false, false)).thenReturn(1L); + + Long result = customJdbcMutableAclService.createOrRetrieveSidPrimaryKey(customSid, false); + + assertEquals(result, new Long(1L)); + } + + /** + * This class needed to show how to extend {@link JdbcMutableAclService} for processing + * custom {@link Sid} implementations + */ + private class CustomJdbcMutableAclService extends JdbcMutableAclService { + + private CustomJdbcMutableAclService(DataSource dataSource, LookupStrategy lookupStrategy, AclCache aclCache) { + super(dataSource, lookupStrategy, aclCache); + } + + @Override + protected Long createOrRetrieveSidPrimaryKey(Sid sid, boolean allowCreate) { + String sidName; + boolean isPrincipal = false; + if (sid instanceof CustomSid) { + sidName = ((CustomSid)sid).getSid(); + } else if (sid instanceof GrantedAuthoritySid) { + sidName = ((GrantedAuthoritySid)sid).getGrantedAuthority(); + } else { + sidName = ((PrincipalSid)sid).getPrincipal(); + isPrincipal = true; + } + return createOrRetrieveSidPrimaryKey(sidName, isPrincipal, allowCreate); + } + } } diff --git a/acl/src/test/java/org/springframework/security/acls/sid/CustomSid.java b/acl/src/test/java/org/springframework/security/acls/sid/CustomSid.java new file mode 100644 index 00000000000..938f0c7d538 --- /dev/null +++ b/acl/src/test/java/org/springframework/security/acls/sid/CustomSid.java @@ -0,0 +1,24 @@ +package org.springframework.security.acls.sid; + +import org.springframework.security.acls.model.Sid; + +/** + * This class is example of custom {@link Sid} implementation + * @author Mikhail Stryzhonok + */ +public class CustomSid implements Sid { + + private String sid; + + public CustomSid(String sid) { + this.sid = sid; + } + + public String getSid() { + return sid; + } + + public void setSid(String sid) { + this.sid = sid; + } +}