Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,14 @@
import org.springframework.core.convert.ConversionService;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.ResultSetExtractor;
import org.springframework.security.acls.domain.*;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My apologies, I didn't notice this earlier. Let's please leave package imports exploded. Ideally, import ordering should not change in a PR.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sorry for that, that happens when you press strg+ shift + o too much. Will fix this!

import org.springframework.security.acls.domain.AccessControlEntryImpl;
import org.springframework.security.acls.domain.AclAuthorizationStrategy;
import org.springframework.security.acls.domain.AclImpl;
import org.springframework.security.acls.domain.AuditLogger;
import org.springframework.security.acls.domain.DefaultPermissionFactory;
import org.springframework.security.acls.domain.DefaultPermissionGrantingStrategy;
import org.springframework.security.acls.domain.GrantedAuthoritySid;
import org.springframework.security.acls.domain.ObjectIdentityImpl;
import org.springframework.security.acls.domain.PermissionFactory;
import org.springframework.security.acls.domain.PrincipalSid;
import org.springframework.security.acls.model.AccessControlEntry;
Expand All @@ -51,6 +51,7 @@
import org.springframework.security.acls.model.MutableAcl;
import org.springframework.security.acls.model.NotFoundException;
import org.springframework.security.acls.model.ObjectIdentity;
import org.springframework.security.acls.model.ObjectIdentityGenerator;
import org.springframework.security.acls.model.Permission;
import org.springframework.security.acls.model.PermissionGrantingStrategy;
import org.springframework.security.acls.model.Sid;
Expand Down Expand Up @@ -109,6 +110,8 @@ public class BasicLookupStrategy implements LookupStrategy {

private final AclAuthorizationStrategy aclAuthorizationStrategy;

private ObjectIdentityGenerator objectIdentityGenerator;

private PermissionFactory permissionFactory = new DefaultPermissionFactory();

private final AclCache aclCache;
Expand All @@ -134,6 +137,7 @@ public class BasicLookupStrategy implements LookupStrategy {

private AclClassIdUtils aclClassIdUtils;


/**
* Constructor accepting mandatory arguments
* @param dataSource to access the database
Expand All @@ -152,8 +156,9 @@ public BasicLookupStrategy(DataSource dataSource, AclCache aclCache,
* @param aclAuthorizationStrategy authorization strategy (required)
* @param grantingStrategy the PermissionGrantingStrategy
*/
public BasicLookupStrategy(DataSource dataSource, AclCache aclCache,
AclAuthorizationStrategy aclAuthorizationStrategy, PermissionGrantingStrategy grantingStrategy) {
public BasicLookupStrategy(DataSource dataSource, AclCache aclCache,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding a constructor parameter breaks backward compatibility, so I would recommend that this be moved to a setter.

Additionally, it's quite common in Spring Security for required members to be constructor parameters and optional members to have setters.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This setter should also confirm that the provided strategy is not null.

AclAuthorizationStrategy aclAuthorizationStrategy, PermissionGrantingStrategy grantingStrategy) {

Assert.notNull(dataSource, "DataSource required");
Assert.notNull(aclCache, "AclCache required");
Assert.notNull(aclAuthorizationStrategy, "AclAuthorizationStrategy required");
Expand All @@ -162,6 +167,7 @@ public BasicLookupStrategy(DataSource dataSource, AclCache aclCache,
this.aclCache = aclCache;
this.aclAuthorizationStrategy = aclAuthorizationStrategy;
this.grantingStrategy = grantingStrategy;
this.objectIdentityGenerator = new ObjectIdentityRetrievalStrategyImpl();
this.aclClassIdUtils = new AclClassIdUtils();
this.fieldAces.setAccessible(true);
this.fieldAcl.setAccessible(true);
Expand Down Expand Up @@ -488,6 +494,11 @@ public final void setAclClassIdSupported(boolean aclClassIdSupported) {
}
}

public void setObjectIdentityGenerator(ObjectIdentityGenerator objectIdentityGenerator) {
Assert.notNull(objectIdentityGenerator,"The provided strategy has to be not null!");
this.objectIdentityGenerator = objectIdentityGenerator;
}

public final void setConversionService(ConversionService conversionService) {
this.aclClassIdUtils = new AclClassIdUtils(conversionService);
}
Expand Down Expand Up @@ -569,7 +580,7 @@ private void convertCurrentResultIntoObject(Map<Serializable, Acl> acls, ResultS
// target id type, e.g. UUID.
Serializable identifier = (Serializable) rs.getObject("object_id_identity");
identifier = BasicLookupStrategy.this.aclClassIdUtils.identifierFrom(identifier, rs);
ObjectIdentity objectIdentity = new ObjectIdentityImpl(rs.getString("class"), identifier);
ObjectIdentity objectIdentity = objectIdentityGenerator.createObjectIdentity(identifier,rs.getString("class"));

Acl parentAcl = null;
long parentAclId = rs.getLong("parent_object");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,12 @@
import org.springframework.core.convert.ConversionService;
import org.springframework.jdbc.core.JdbcOperations;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.security.acls.domain.ObjectIdentityImpl;
import org.springframework.security.acls.domain.ObjectIdentityRetrievalStrategyImpl;
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.ObjectIdentityGenerator;
import org.springframework.security.acls.model.Sid;
import org.springframework.util.Assert;

Expand All @@ -50,123 +51,130 @@
*/
public class JdbcAclService implements AclService {

protected static final Log log = LogFactory.getLog(JdbcAclService.class);

private static final String DEFAULT_SELECT_ACL_CLASS_COLUMNS = "class.class as class";

private static final String DEFAULT_SELECT_ACL_CLASS_COLUMNS_WITH_ID_TYPE = DEFAULT_SELECT_ACL_CLASS_COLUMNS
+ ", class.class_id_type as class_id_type";

private static final String DEFAULT_SELECT_ACL_WITH_PARENT_SQL = "select obj.object_id_identity as obj_id, "
+ DEFAULT_SELECT_ACL_CLASS_COLUMNS
+ " from acl_object_identity obj, acl_object_identity parent, acl_class class "
+ "where obj.parent_object = parent.id and obj.object_id_class = class.id "
+ "and parent.object_id_identity = ? and parent.object_id_class = ("
+ "select id FROM acl_class where acl_class.class = ?)";

private static final String DEFAULT_SELECT_ACL_WITH_PARENT_SQL_WITH_CLASS_ID_TYPE = "select obj.object_id_identity as obj_id, "
+ DEFAULT_SELECT_ACL_CLASS_COLUMNS_WITH_ID_TYPE
+ " from acl_object_identity obj, acl_object_identity parent, acl_class class "
+ "where obj.parent_object = parent.id and obj.object_id_class = class.id "
+ "and parent.object_id_identity = ? and parent.object_id_class = ("
+ "select id FROM acl_class where acl_class.class = ?)";

protected final JdbcOperations jdbcOperations;

private final LookupStrategy lookupStrategy;

private boolean aclClassIdSupported;

private String findChildrenSql = DEFAULT_SELECT_ACL_WITH_PARENT_SQL;

private AclClassIdUtils aclClassIdUtils;

public JdbcAclService(DataSource dataSource, LookupStrategy lookupStrategy) {
this(new JdbcTemplate(dataSource), lookupStrategy);
}

public JdbcAclService(JdbcOperations jdbcOperations, LookupStrategy lookupStrategy) {
Assert.notNull(jdbcOperations, "JdbcOperations required");
Assert.notNull(lookupStrategy, "LookupStrategy required");
this.jdbcOperations = jdbcOperations;
this.lookupStrategy = lookupStrategy;
this.aclClassIdUtils = new AclClassIdUtils();
}

@Override
public List<ObjectIdentity> findChildren(ObjectIdentity parentIdentity) {
Object[] args = { parentIdentity.getIdentifier().toString(), parentIdentity.getType() };
List<ObjectIdentity> objects = this.jdbcOperations.query(this.findChildrenSql, args,
(rs, rowNum) -> mapObjectIdentityRow(rs));
return (!objects.isEmpty()) ? objects : null;
}

private ObjectIdentity mapObjectIdentityRow(ResultSet rs) throws SQLException {
String javaType = rs.getString("class");
Serializable identifier = (Serializable) rs.getObject("obj_id");
identifier = this.aclClassIdUtils.identifierFrom(identifier, rs);
return new ObjectIdentityImpl(javaType, identifier);
}

@Override
public Acl readAclById(ObjectIdentity object, List<Sid> sids) throws NotFoundException {
Map<ObjectIdentity, Acl> map = readAclsById(Collections.singletonList(object), sids);
Assert.isTrue(map.containsKey(object),
() -> "There should have been an Acl entry for ObjectIdentity " + object);
return map.get(object);
}

@Override
public Acl readAclById(ObjectIdentity object) throws NotFoundException {
return readAclById(object, null);
}

@Override
public Map<ObjectIdentity, Acl> readAclsById(List<ObjectIdentity> objects) throws NotFoundException {
return readAclsById(objects, null);
}

@Override
public Map<ObjectIdentity, Acl> readAclsById(List<ObjectIdentity> objects, List<Sid> sids)
throws NotFoundException {
Map<ObjectIdentity, Acl> result = this.lookupStrategy.readAclsById(objects, sids);
// Check every requested object identity was found (throw NotFoundException if
// needed)
for (ObjectIdentity oid : objects) {
if (!result.containsKey(oid)) {
throw new NotFoundException("Unable to find ACL information for object identity '" + oid + "'");
}
}
return result;
}

/**
* Allows customization of the SQL query used to find child object identities.
* @param findChildrenSql
*/
public void setFindChildrenQuery(String findChildrenSql) {
this.findChildrenSql = findChildrenSql;
}

public void setAclClassIdSupported(boolean aclClassIdSupported) {
this.aclClassIdSupported = aclClassIdSupported;
if (aclClassIdSupported) {
// Change the default children select if it hasn't been overridden
if (this.findChildrenSql.equals(DEFAULT_SELECT_ACL_WITH_PARENT_SQL)) {
this.findChildrenSql = DEFAULT_SELECT_ACL_WITH_PARENT_SQL_WITH_CLASS_ID_TYPE;
}
else {
log.debug("Find children statement has already been overridden, so not overridding the default");
}
}
}

public void setConversionService(ConversionService conversionService) {
this.aclClassIdUtils = new AclClassIdUtils(conversionService);
}

protected boolean isAclClassIdSupported() {
return this.aclClassIdSupported;
}
protected static final Log log = LogFactory.getLog(JdbcAclService.class);

private static final String DEFAULT_SELECT_ACL_CLASS_COLUMNS = "class.class as class";

private static final String DEFAULT_SELECT_ACL_CLASS_COLUMNS_WITH_ID_TYPE = DEFAULT_SELECT_ACL_CLASS_COLUMNS
+ ", class.class_id_type as class_id_type";

private static final String DEFAULT_SELECT_ACL_WITH_PARENT_SQL = "select obj.object_id_identity as obj_id, "
+ DEFAULT_SELECT_ACL_CLASS_COLUMNS
+ " from acl_object_identity obj, acl_object_identity parent, acl_class class "
+ "where obj.parent_object = parent.id and obj.object_id_class = class.id "
+ "and parent.object_id_identity = ? and parent.object_id_class = ("
+ "select id FROM acl_class where acl_class.class = ?)";

private static final String DEFAULT_SELECT_ACL_WITH_PARENT_SQL_WITH_CLASS_ID_TYPE = "select obj.object_id_identity as obj_id, "
+ DEFAULT_SELECT_ACL_CLASS_COLUMNS_WITH_ID_TYPE
+ " from acl_object_identity obj, acl_object_identity parent, acl_class class "
+ "where obj.parent_object = parent.id and obj.object_id_class = class.id "
+ "and parent.object_id_identity = ? and parent.object_id_class = ("
+ "select id FROM acl_class where acl_class.class = ?)";

protected final JdbcOperations jdbcOperations;

private final LookupStrategy lookupStrategy;

private boolean aclClassIdSupported;

private String findChildrenSql = DEFAULT_SELECT_ACL_WITH_PARENT_SQL;

private AclClassIdUtils aclClassIdUtils;
private ObjectIdentityGenerator objectIdentityGenerator;

public JdbcAclService(DataSource dataSource, LookupStrategy lookupStrategy) {
this(new JdbcTemplate(dataSource), lookupStrategy);
}

public JdbcAclService(JdbcOperations jdbcOperations, LookupStrategy lookupStrategy) {
Assert.notNull(jdbcOperations, "JdbcOperations required");
Assert.notNull(lookupStrategy, "LookupStrategy required");
this.jdbcOperations = jdbcOperations;
this.lookupStrategy = lookupStrategy;
this.objectIdentityGenerator = new ObjectIdentityRetrievalStrategyImpl();
this.aclClassIdUtils = new AclClassIdUtils();
}

@Override
public List<ObjectIdentity> findChildren(ObjectIdentity parentIdentity) {
Object[] args = {parentIdentity.getIdentifier().toString(), parentIdentity.getType()};
List<ObjectIdentity> objects = this.jdbcOperations.query(this.findChildrenSql, args,
(rs, rowNum) -> mapObjectIdentityRow(rs));
return (!objects.isEmpty()) ? objects : null;
}

private ObjectIdentity mapObjectIdentityRow(ResultSet rs) throws SQLException {
String javaType = rs.getString("class");
Serializable identifier = (Serializable) rs.getObject("obj_id");
identifier = this.aclClassIdUtils.identifierFrom(identifier, rs);
return objectIdentityGenerator.createObjectIdentity(identifier, javaType);
}

@Override
public Acl readAclById(ObjectIdentity object, List<Sid> sids) throws NotFoundException {
Map<ObjectIdentity, Acl> map = readAclsById(Collections.singletonList(object), sids);
Assert.isTrue(map.containsKey(object),
() -> "There should have been an Acl entry for ObjectIdentity " + object);
return map.get(object);
}

@Override
public Acl readAclById(ObjectIdentity object) throws NotFoundException {
return readAclById(object, null);
}

@Override
public Map<ObjectIdentity, Acl> readAclsById(List<ObjectIdentity> objects) throws NotFoundException {
return readAclsById(objects, null);
}

@Override
public Map<ObjectIdentity, Acl> readAclsById(List<ObjectIdentity> objects, List<Sid> sids)
throws NotFoundException {
Map<ObjectIdentity, Acl> result = this.lookupStrategy.readAclsById(objects, sids);
// Check every requested object identity was found (throw NotFoundException if
// needed)
for (ObjectIdentity oid : objects) {
if (!result.containsKey(oid)) {
throw new NotFoundException("Unable to find ACL information for object identity '" + oid + "'");
}
}
return result;
}

/**
* Allows customization of the SQL query used to find child object identities.
*
* @param findChildrenSql
*/
public void setFindChildrenQuery(String findChildrenSql) {
this.findChildrenSql = findChildrenSql;
}

public void setAclClassIdSupported(boolean aclClassIdSupported) {
this.aclClassIdSupported = aclClassIdSupported;
if (aclClassIdSupported) {
// Change the default children select if it hasn't been overridden
if (this.findChildrenSql.equals(DEFAULT_SELECT_ACL_WITH_PARENT_SQL)) {
this.findChildrenSql = DEFAULT_SELECT_ACL_WITH_PARENT_SQL_WITH_CLASS_ID_TYPE;
} else {
log.debug("Find children statement has already been overridden, so not overridding the default");
}
}
}

public void setConversionService(ConversionService conversionService) {
this.aclClassIdUtils = new AclClassIdUtils(conversionService);
}

public void setObjectIdentityGenerator(ObjectIdentityGenerator objectIdentityGenerator) {
Assert.notNull(objectIdentityGenerator,"The provided strategy has to be not null!");
this.objectIdentityGenerator = objectIdentityGenerator;
}

protected boolean isAclClassIdSupported() {
return this.aclClassIdSupported;
}

}