Skip to content

Allow use of non-numeric (e.g. UUID) values for ObjectIdentity.getIdentifier() #4424

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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
@@ -0,0 +1,125 @@
/*
* Copyright 2002-2017 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
*
* http://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.jdbc;

import java.io.Serializable;
import java.sql.ResultSet;
import java.sql.SQLException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.convert.ConversionService;
import org.springframework.security.acls.model.ObjectIdentity;

/**
* Utility class for helping convert database representations of {@link ObjectIdentity#getIdentifier()} into
* the correct Java type as specified by <code>acl_class.class_id_type</code>.
* @author paulwheeler
*/
class AclClassIdUtils {
private static final String DEFAULT_CLASS_ID_TYPE_COLUMN_NAME = "class_id_type";
private static final Log log = LogFactory.getLog(AclClassIdUtils.class);

private ConversionService conversionService;

public AclClassIdUtils() {
}

/**
* Converts the raw type from the database into the right Java type. For most applications the 'raw type' will be Long, for some applications
* it could be String.
* @param identifier The identifier from the database
* @param resultSet Result set of the query
* @return The identifier in the appropriate target Java type. Typically Long or UUID.
* @throws SQLException
*/
Serializable identifierFrom(Serializable identifier, ResultSet resultSet) throws SQLException {
if (isString(identifier) && hasValidClassIdType(resultSet)
&& canConvertFromStringTo(classIdTypeFrom(resultSet))) {

identifier = convertFromStringTo((String) identifier, classIdTypeFrom(resultSet));
} else {
// Assume it should be a Long type
identifier = convertToLong(identifier);
}

return identifier;
}

private boolean hasValidClassIdType(ResultSet resultSet) throws SQLException {
boolean hasClassIdType = false;
try {
hasClassIdType = classIdTypeFrom(resultSet) != null;
} catch (SQLException e) {
log.debug("Unable to obtain the class id type", e);
}
return hasClassIdType;
}

private <T extends Serializable> Class<T> classIdTypeFrom(ResultSet resultSet) throws SQLException {
return classIdTypeFrom(resultSet.getString(DEFAULT_CLASS_ID_TYPE_COLUMN_NAME));
}

private <T extends Serializable> Class<T> classIdTypeFrom(String className) {
Class targetType = null;
if (className != null) {
try {
targetType = Class.forName(className);
} catch (ClassNotFoundException e) {
log.debug("Unable to find class id type on classpath", e);
}
}
return targetType;
}

private <T> boolean canConvertFromStringTo(Class<T> targetType) {
return hasConversionService() && conversionService.canConvert(String.class, targetType);
}

private <T extends Serializable> T convertFromStringTo(String identifier, Class<T> targetType) {
return conversionService.convert(identifier, targetType);
}

private boolean hasConversionService() {
return conversionService != null;
}

/**
* Converts to a {@link Long}, attempting to use the {@link ConversionService} if available.
* @param identifier The identifier
* @return Long version of the identifier
* @throws NumberFormatException if the string cannot be parsed to a long.
* @throws org.springframework.core.convert.ConversionException if a conversion exception occurred
* @throws IllegalArgumentException if targetType is null
*/
private Long convertToLong(Serializable identifier) {
Long idAsLong;
if (hasConversionService()) {
idAsLong = conversionService.convert(identifier, Long.class);
} else {
idAsLong = Long.valueOf(identifier.toString());
}
return idAsLong;
}

private boolean isString(Serializable object) {
return object.getClass().isAssignableFrom(String.class);
}

public void setConversionService(ConversionService conversionService) {
this.conversionService = conversionService;
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
* Copyright 2004, 2005, 2006, 2017 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.
Expand Down Expand Up @@ -30,6 +30,9 @@

import javax.sql.DataSource;

import org.springframework.core.convert.ConversionException;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.PreparedStatementSetter;
import org.springframework.jdbc.core.ResultSetExtractor;
Expand Down Expand Up @@ -78,7 +81,7 @@
*/
public class BasicLookupStrategy implements LookupStrategy {

public final static String DEFAULT_SELECT_CLAUSE = "select acl_object_identity.object_id_identity, "
private final static String DEFAULT_SELECT_CLAUSE_COLUMNS = "select acl_object_identity.object_id_identity, "
+ "acl_entry.ace_order, "
+ "acl_object_identity.id as acl_id, "
+ "acl_object_identity.parent_object, "
Expand All @@ -92,13 +95,19 @@ public class BasicLookupStrategy implements LookupStrategy {
+ "acl_sid.sid as ace_sid, "
+ "acli_sid.principal as acl_principal, "
+ "acli_sid.sid as acl_sid, "
+ "acl_class.class "
+ "from acl_object_identity "
+ "acl_class.class ";
private final static String DEFAULT_SELECT_CLAUSE_ACL_CLASS_ID_TYPE_COLUMN = ", acl_class.class_id_type ";
private final static String DEFAULT_SELECT_CLAUSE_FROM = "from acl_object_identity "
+ "left join acl_sid acli_sid on acli_sid.id = acl_object_identity.owner_sid "
+ "left join acl_class on acl_class.id = acl_object_identity.object_id_class "
+ "left join acl_entry on acl_object_identity.id = acl_entry.acl_object_identity "
+ "left join acl_sid on acl_entry.sid = acl_sid.id " + "where ( ";

public final static String DEFAULT_SELECT_CLAUSE = DEFAULT_SELECT_CLAUSE_COLUMNS + DEFAULT_SELECT_CLAUSE_FROM;

public final static String DEFAULT_ACL_CLASS_ID_SELECT_CLAUSE = DEFAULT_SELECT_CLAUSE_COLUMNS +
DEFAULT_SELECT_CLAUSE_ACL_CLASS_ID_TYPE_COLUMN + DEFAULT_SELECT_CLAUSE_FROM;

private final static String DEFAULT_LOOKUP_KEYS_WHERE_CLAUSE = "(acl_object_identity.id = ?)";

private final static String DEFAULT_LOOKUP_IDENTITIES_WHERE_CLAUSE = "(acl_object_identity.object_id_identity = ? and acl_class.class = ?)";
Expand Down Expand Up @@ -126,6 +135,8 @@ public class BasicLookupStrategy implements LookupStrategy {
private String lookupObjectIdentitiesWhereClause = DEFAULT_LOOKUP_IDENTITIES_WHERE_CLAUSE;
private String orderByClause = DEFAULT_ORDER_BY_CLAUSE;

private AclClassIdUtils aclClassIdUtils;

// ~ Constructors
// ===================================================================================================

Expand Down Expand Up @@ -161,9 +172,9 @@ public BasicLookupStrategy(DataSource dataSource, AclCache aclCache,
this.aclCache = aclCache;
this.aclAuthorizationStrategy = aclAuthorizationStrategy;
this.grantingStrategy = grantingStrategy;
this.aclClassIdUtils = new AclClassIdUtils();
fieldAces.setAccessible(true);
fieldAcl.setAccessible(true);

}

// ~ Methods
Expand Down Expand Up @@ -383,10 +394,9 @@ public void setValues(PreparedStatement ps) throws SQLException {
// No need to check for nulls, as guaranteed non-null by
// ObjectIdentity.getIdentifier() interface contract
String identifier = oid.getIdentifier().toString();
long id = (Long.valueOf(identifier)).longValue();

// Inject values
ps.setLong((2 * i) + 1, id);
ps.setString((2 * i) + 1, identifier);
ps.setString((2 * i) + 2, type);
i++;
}
Expand Down Expand Up @@ -537,6 +547,18 @@ public final void setOrderByClause(String orderByClause) {
this.orderByClause = orderByClause;
}

public final void setAclClassIdSupported(boolean aclClassIdSupported) {
if (aclClassIdSupported) {
Assert.isTrue(this.selectClause.equals(DEFAULT_SELECT_CLAUSE), "Cannot set aclClassIdSupported and override the select clause; "
+ "just override the select clause");
this.selectClause = DEFAULT_ACL_CLASS_ID_SELECT_CLAUSE;
}
}

public final void setAclClassIdUtils(AclClassIdUtils aclClassIdUtils) {
this.aclClassIdUtils = aclClassIdUtils;
}

// ~ Inner Classes
// ==================================================================================================

Expand Down Expand Up @@ -602,6 +624,7 @@ public Set<Long> extractData(ResultSet rs) throws SQLException {
* @param rs the ResultSet focused on a current row
*
* @throws SQLException if something goes wrong converting values
* @throws ConversionException if can't convert to the desired Java type
*/
private void convertCurrentResultIntoObject(Map<Serializable, Acl> acls,
ResultSet rs) throws SQLException {
Expand All @@ -612,9 +635,12 @@ private void convertCurrentResultIntoObject(Map<Serializable, Acl> acls,

if (acl == null) {
// Make an AclImpl and pop it into the Map

// If the Java type is a String, check to see if we can convert it to the target id type, e.g. UUID.
Serializable identifier = (Serializable) rs.getObject("object_id_identity");
identifier = aclClassIdUtils.identifierFrom(identifier, rs);
ObjectIdentity objectIdentity = new ObjectIdentityImpl(
rs.getString("class"), Long.valueOf(rs
.getLong("object_id_identity")));
rs.getString("class"), identifier);

Acl parentAcl = null;
long parentAclId = rs.getLong("parent_object");
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
* Copyright 2004, 2005, 2006, 2017 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.
Expand All @@ -15,6 +15,7 @@
*/
package org.springframework.security.acls.jdbc;

import java.io.Serializable;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Arrays;
Expand Down Expand Up @@ -49,8 +50,15 @@ public class JdbcAclService implements AclService {
// =====================================================================================

protected static final Log log = LogFactory.getLog(JdbcAclService.class);
private static final String DEFAULT_SELECT_ACL_WITH_PARENT_SQL = "select obj.object_id_identity as obj_id, class.class as class "
+ "from acl_object_identity obj, acl_object_identity parent, acl_class 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 = ?)";
Expand All @@ -60,7 +68,9 @@ public class JdbcAclService implements AclService {

protected final JdbcTemplate jdbcTemplate;
private final LookupStrategy lookupStrategy;
private boolean aclClassIdSupported;
private String findChildrenSql = DEFAULT_SELECT_ACL_WITH_PARENT_SQL;
private AclClassIdUtils aclClassIdUtils;

// ~ Constructors
// ===================================================================================================
Expand All @@ -70,6 +80,7 @@ public JdbcAclService(DataSource dataSource, LookupStrategy lookupStrategy) {
Assert.notNull(lookupStrategy, "LookupStrategy required");
this.jdbcTemplate = new JdbcTemplate(dataSource);
this.lookupStrategy = lookupStrategy;
this.aclClassIdUtils = new AclClassIdUtils();
}

// ~ Methods
Expand All @@ -82,7 +93,8 @@ public List<ObjectIdentity> findChildren(ObjectIdentity parentIdentity) {
public ObjectIdentity mapRow(ResultSet rs, int rowNum)
throws SQLException {
String javaType = rs.getString("class");
Long identifier = new Long(rs.getLong("obj_id"));
Serializable identifier = (Serializable) rs.getObject("obj_id");
identifier = aclClassIdUtils.identifierFrom(identifier, rs);

return new ObjectIdentityImpl(javaType, identifier);
}
Expand Down Expand Up @@ -138,4 +150,24 @@ public Map<ObjectIdentity, Acl> readAclsById(List<ObjectIdentity> objects,
public void setFindChildrenQuery(String findChildrenSql) {
this.findChildrenSql = findChildrenSql;
}

public void setAclClassIdSupported(boolean aclClassIdSupported) {
this.aclClassIdSupported = aclClassIdSupported;
if (aclClassIdSupported) {
// Change the default insert 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 setAclClassIdUtils(AclClassIdUtils aclClassIdUtils) {
this.aclClassIdUtils = aclClassIdUtils;
}

protected boolean isAclClassIdSupported() {
return aclClassIdSupported;
}
}
Loading