diff --git a/acl/src/main/java/org/springframework/security/acls/jdbc/AclClassIdUtils.java b/acl/src/main/java/org/springframework/security/acls/jdbc/AclClassIdUtils.java new file mode 100644 index 00000000000..76d47c6f5fd --- /dev/null +++ b/acl/src/main/java/org/springframework/security/acls/jdbc/AclClassIdUtils.java @@ -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 acl_class.class_id_type. + * @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 Class classIdTypeFrom(ResultSet resultSet) throws SQLException { + return classIdTypeFrom(resultSet.getString(DEFAULT_CLASS_ID_TYPE_COLUMN_NAME)); + } + + private Class 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 boolean canConvertFromStringTo(Class targetType) { + return hasConversionService() && conversionService.canConvert(String.class, targetType); + } + + private T convertFromStringTo(String identifier, Class 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; + } +} 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 0bd358e6115..5b81b36d5b2 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 @@ -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. @@ -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; @@ -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, " @@ -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 = ?)"; @@ -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 // =================================================================================================== @@ -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 @@ -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++; } @@ -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 // ================================================================================================== @@ -602,6 +624,7 @@ public Set 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 acls, ResultSet rs) throws SQLException { @@ -612,9 +635,12 @@ private void convertCurrentResultIntoObject(Map 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"); diff --git a/acl/src/main/java/org/springframework/security/acls/jdbc/JdbcAclService.java b/acl/src/main/java/org/springframework/security/acls/jdbc/JdbcAclService.java index e9e8ed49c74..1032e6da9f5 100644 --- a/acl/src/main/java/org/springframework/security/acls/jdbc/JdbcAclService.java +++ b/acl/src/main/java/org/springframework/security/acls/jdbc/JdbcAclService.java @@ -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. @@ -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; @@ -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 = ?)"; @@ -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 // =================================================================================================== @@ -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 @@ -82,7 +93,8 @@ public List 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); } @@ -138,4 +150,24 @@ public Map readAclsById(List 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; + } } 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 8c990af9acc..6100fe1074b 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 @@ -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. @@ -58,6 +58,8 @@ * @author Johannes Zlattinger */ public class JdbcMutableAclService extends JdbcAclService implements MutableAclService { + private static final String DEFAULT_INSERT_INTO_ACL_CLASS = "insert into acl_class (class) values (?)"; + private static final String DEFAULT_INSERT_INTO_ACL_CLASS_WITH_ID = "insert into acl_class (class, class_id_type) values (?, ?)"; // ~ Instance fields // ================================================================================================ @@ -67,7 +69,7 @@ public class JdbcMutableAclService extends JdbcAclService implements MutableAclS private String deleteObjectIdentityByPrimaryKey = "delete from acl_object_identity where id=?"; private String classIdentityQuery = "call identity()"; private String sidIdentityQuery = "call identity()"; - private String insertClass = "insert into acl_class (class) values (?)"; + private String insertClass = DEFAULT_INSERT_INTO_ACL_CLASS; private String insertEntry = "insert into acl_entry " + "(acl_object_identity, ace_order, sid, mask, granting, audit_success, audit_failure)" + "values (?, ?, ?, ?, ?, ?, ?)"; @@ -167,7 +169,7 @@ public void setValues(PreparedStatement stmt, int i) throws SQLException { */ protected void createObjectIdentity(ObjectIdentity object, Sid owner) { Long sidId = createOrRetrieveSidPrimaryKey(owner, true); - Long classId = createOrRetrieveClassPrimaryKey(object.getType(), true); + Long classId = createOrRetrieveClassPrimaryKey(object.getType(), true, object.getIdentifier().getClass()); jdbcTemplate.update(insertObjectIdentity, classId, object.getIdentifier(), sidId, Boolean.TRUE); } @@ -181,7 +183,7 @@ protected void createObjectIdentity(ObjectIdentity object, Sid owner) { * * @return the primary key or null if not found */ - protected Long createOrRetrieveClassPrimaryKey(String type, boolean allowCreate) { + protected Long createOrRetrieveClassPrimaryKey(String type, boolean allowCreate, Class idType) { List classIds = jdbcTemplate.queryForList(selectClassPrimaryKey, new Object[] { type }, Long.class); @@ -190,7 +192,11 @@ protected Long createOrRetrieveClassPrimaryKey(String type, boolean allowCreate) } if (allowCreate) { - jdbcTemplate.update(insertClass, type); + if (!isAclClassIdSupported()) { + jdbcTemplate.update(insertClass, type); + } else { + jdbcTemplate.update(insertClass, type, idType.getCanonicalName()); + } Assert.isTrue(TransactionSynchronizationManager.isSynchronizationActive(), "Transaction must be running"); return jdbcTemplate.queryForObject(classIdentityQuery, Long.class); @@ -485,4 +491,17 @@ public void setUpdateObjectIdentity(String updateObjectIdentity) { public void setForeignKeysInDatabase(boolean foreignKeysInDatabase) { this.foreignKeysInDatabase = foreignKeysInDatabase; } + + @Override + public void setAclClassIdSupported(boolean aclClassIdSupported) { + super.setAclClassIdSupported(aclClassIdSupported); + if (aclClassIdSupported) { + // Change the default insert if it hasn't been overridden + if (this.insertClass.equals(DEFAULT_INSERT_INTO_ACL_CLASS)) { + this.insertClass = DEFAULT_INSERT_INTO_ACL_CLASS_WITH_ID; + } else { + log.debug("Insert class statement has already been overridden, so not overridding the default"); + } + } + } } diff --git a/acl/src/main/resources/createAclSchemaMySQL.sql b/acl/src/main/resources/createAclSchemaMySQL.sql index 5eb32f4ff23..d50120e2fc0 100644 --- a/acl/src/main/resources/createAclSchemaMySQL.sql +++ b/acl/src/main/resources/createAclSchemaMySQL.sql @@ -21,7 +21,7 @@ CREATE TABLE acl_class ( CREATE TABLE acl_object_identity ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, object_id_class BIGINT UNSIGNED NOT NULL, - object_id_identity BIGINT NOT NULL, + object_id_identity VARCHAR(36) NOT NULL, parent_object BIGINT UNSIGNED, owner_sid BIGINT UNSIGNED, entries_inheriting BOOLEAN NOT NULL, diff --git a/acl/src/main/resources/createAclSchemaOracle.sql b/acl/src/main/resources/createAclSchemaOracle.sql index 9d3fcecebd3..c3fb609bfea 100644 --- a/acl/src/main/resources/createAclSchemaOracle.sql +++ b/acl/src/main/resources/createAclSchemaOracle.sql @@ -43,7 +43,7 @@ END; CREATE TABLE acl_object_identity ( id NUMBER(38) NOT NULL PRIMARY KEY, object_id_class NUMBER(38) NOT NULL, - object_id_identity NUMBER(38) NOT NULL, + object_id_identity NVARCHAR2(36) NOT NULL, parent_object NUMBER(38), owner_sid NUMBER(38), entries_inheriting NUMBER(1) NOT NULL CHECK (entries_inheriting in (0, 1)), diff --git a/acl/src/main/resources/createAclSchemaPostgres.sql b/acl/src/main/resources/createAclSchemaPostgres.sql index 364ebf92d22..ef346ed195b 100644 --- a/acl/src/main/resources/createAclSchemaPostgres.sql +++ b/acl/src/main/resources/createAclSchemaPostgres.sql @@ -15,13 +15,14 @@ create table acl_sid( create table acl_class( id bigserial not null primary key, class varchar(100) not null, + class_id_type varchar(100), constraint unique_uk_2 unique(class) ); create table acl_object_identity( id bigserial primary key, object_id_class bigint not null, - object_id_identity bigint not null, + object_id_identity varchar(36) not null, parent_object bigint, owner_sid bigint, entries_inheriting boolean not null, diff --git a/acl/src/main/resources/createAclSchemaSqlServer.sql b/acl/src/main/resources/createAclSchemaSqlServer.sql index 0c0ded8c827..b7f5ff23fe9 100644 --- a/acl/src/main/resources/createAclSchemaSqlServer.sql +++ b/acl/src/main/resources/createAclSchemaSqlServer.sql @@ -21,7 +21,7 @@ CREATE TABLE acl_class ( CREATE TABLE acl_object_identity ( id BIGINT NOT NULL IDENTITY PRIMARY KEY, object_id_class BIGINT NOT NULL, - object_id_identity BIGINT NOT NULL, + object_id_identity VARCHAR(36) NOT NULL, parent_object BIGINT, owner_sid BIGINT, entries_inheriting BIT NOT NULL, diff --git a/acl/src/main/resources/createAclSchemaWithAclClassIdType.sql b/acl/src/main/resources/createAclSchemaWithAclClassIdType.sql new file mode 100644 index 00000000000..40594f1d148 --- /dev/null +++ b/acl/src/main/resources/createAclSchemaWithAclClassIdType.sql @@ -0,0 +1,47 @@ +-- ACL schema sql used in HSQLDB + +-- drop table acl_entry; +-- drop table acl_object_identity; +-- drop table acl_class; +-- drop table acl_sid; + +create table acl_sid( + id bigint generated by default as identity(start with 100) not null primary key, + principal boolean not null, + sid varchar_ignorecase(100) not null, + constraint unique_uk_1 unique(sid,principal) +); + +create table acl_class( + id bigint generated by default as identity(start with 100) not null primary key, + class varchar_ignorecase(100) not null, + class_id_type varchar_ignorecase(100), + constraint unique_uk_2 unique(class) +); + +create table acl_object_identity( + id bigint generated by default as identity(start with 100) not null primary key, + object_id_class bigint not null, + object_id_identity varchar_ignorecase(36) not null, + parent_object bigint, + owner_sid bigint, + entries_inheriting boolean not null, + constraint unique_uk_3 unique(object_id_class,object_id_identity), + constraint foreign_fk_1 foreign key(parent_object)references acl_object_identity(id), + constraint foreign_fk_2 foreign key(object_id_class)references acl_class(id), + constraint foreign_fk_3 foreign key(owner_sid)references acl_sid(id) +); + +create table acl_entry( + id bigint generated by default as identity(start with 100) not null primary key, + acl_object_identity bigint not null, + ace_order int not null, + sid bigint not null, + mask integer not null, + granting boolean not null, + audit_success boolean not null, + audit_failure boolean not null, + constraint unique_uk_4 unique(acl_object_identity,ace_order), + constraint foreign_fk_4 foreign key(acl_object_identity) references acl_object_identity(id), + constraint foreign_fk_5 foreign key(sid) references acl_sid(id) +); diff --git a/acl/src/test/java/org/springframework/security/acls/TargetObjectWithUUID.java b/acl/src/test/java/org/springframework/security/acls/TargetObjectWithUUID.java new file mode 100644 index 00000000000..c3148699c44 --- /dev/null +++ b/acl/src/test/java/org/springframework/security/acls/TargetObjectWithUUID.java @@ -0,0 +1,36 @@ +/* + * 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; + +import java.util.UUID; + +/** + * Dummy domain object class with a {@link UUID} for the Id. + * + * @author Luke Taylor + */ +public final class TargetObjectWithUUID { + + private UUID id; + + public UUID getId() { + return id; + } + + public void setId(UUID id) { + this.id = id; + } +} diff --git a/acl/src/test/java/org/springframework/security/acls/jdbc/AbstractBasicLookupStrategyTests.java b/acl/src/test/java/org/springframework/security/acls/jdbc/AbstractBasicLookupStrategyTests.java new file mode 100644 index 00000000000..2e564535629 --- /dev/null +++ b/acl/src/test/java/org/springframework/security/acls/jdbc/AbstractBasicLookupStrategyTests.java @@ -0,0 +1,341 @@ +/* + * 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 static org.assertj.core.api.Assertions.*; + +import net.sf.ehcache.Cache; +import net.sf.ehcache.CacheManager; +import net.sf.ehcache.Ehcache; + +import org.junit.*; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.security.acls.TargetObject; +import org.springframework.security.acls.TargetObjectWithUUID; +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; +import org.springframework.security.acls.model.NotFoundException; +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.core.authority.SimpleGrantedAuthority; + +import java.util.*; + +import javax.sql.DataSource; + +/** + * Tests {@link BasicLookupStrategy} + * + * @author Andrei Stefan + */ +public abstract class AbstractBasicLookupStrategyTests { + + protected static final Sid BEN_SID = new PrincipalSid("ben"); + protected static final String TARGET_CLASS = TargetObject.class.getName(); + protected static final String TARGET_CLASS_WITH_UUID = TargetObjectWithUUID.class.getName(); + protected static final UUID OBJECT_IDENTITY_UUID = UUID.randomUUID(); + protected static final Long OBJECT_IDENTITY_LONG_AS_UUID = 110L; + + // ~ Instance fields + // ================================================================================================ + + private BasicLookupStrategy strategy; + private static CacheManager cacheManager; + + // ~ Methods + // ======================================================================================================== + + public abstract JdbcTemplate getJdbcTemplate(); + + public abstract DataSource getDataSource(); + + @BeforeClass + public static void initCacheManaer() { + cacheManager = CacheManager.create(); + cacheManager.addCache(new Cache("basiclookuptestcache", 500, false, false, 30, 30)); + } + + @AfterClass + public static void shutdownCacheManager() { + cacheManager.removalAll(); + cacheManager.shutdown(); + } + + @Before + public void populateDatabase() { + String query = "INSERT INTO acl_sid(ID,PRINCIPAL,SID) VALUES (1,1,'ben');" + + "INSERT INTO acl_class(ID,CLASS) VALUES (2,'" + TARGET_CLASS + "');" + + "INSERT INTO acl_object_identity(ID,OBJECT_ID_CLASS,OBJECT_ID_IDENTITY,PARENT_OBJECT,OWNER_SID,ENTRIES_INHERITING) VALUES (1,2,100,null,1,1);" + + "INSERT INTO acl_object_identity(ID,OBJECT_ID_CLASS,OBJECT_ID_IDENTITY,PARENT_OBJECT,OWNER_SID,ENTRIES_INHERITING) VALUES (2,2,101,1,1,1);" + + "INSERT INTO acl_object_identity(ID,OBJECT_ID_CLASS,OBJECT_ID_IDENTITY,PARENT_OBJECT,OWNER_SID,ENTRIES_INHERITING) VALUES (3,2,102,2,1,1);" + + "INSERT INTO acl_entry(ID,ACL_OBJECT_IDENTITY,ACE_ORDER,SID,MASK,GRANTING,AUDIT_SUCCESS,AUDIT_FAILURE) VALUES (1,1,0,1,1,1,0,0);" + + "INSERT INTO acl_entry(ID,ACL_OBJECT_IDENTITY,ACE_ORDER,SID,MASK,GRANTING,AUDIT_SUCCESS,AUDIT_FAILURE) VALUES (2,1,1,1,2,0,0,0);" + + "INSERT INTO acl_entry(ID,ACL_OBJECT_IDENTITY,ACE_ORDER,SID,MASK,GRANTING,AUDIT_SUCCESS,AUDIT_FAILURE) VALUES (3,2,0,1,8,1,0,0);" + + "INSERT INTO acl_entry(ID,ACL_OBJECT_IDENTITY,ACE_ORDER,SID,MASK,GRANTING,AUDIT_SUCCESS,AUDIT_FAILURE) VALUES (4,3,0,1,8,0,0,0);"; + getJdbcTemplate().execute(query); + } + + @Before + public void initializeBeans() { + strategy = new BasicLookupStrategy(getDataSource(), aclCache(), aclAuthStrategy(), + new DefaultPermissionGrantingStrategy(new ConsoleAuditLogger())); + strategy.setPermissionFactory(new DefaultPermissionFactory()); + } + + protected AclAuthorizationStrategy aclAuthStrategy() { + return new AclAuthorizationStrategyImpl( + new SimpleGrantedAuthority("ROLE_ADMINISTRATOR")); + } + + protected EhCacheBasedAclCache aclCache() { + return new EhCacheBasedAclCache(getCache(), + new DefaultPermissionGrantingStrategy(new ConsoleAuditLogger()), + new AclAuthorizationStrategyImpl(new SimpleGrantedAuthority("ROLE_USER"))); + } + + + @After + public void emptyDatabase() { + String query = "DELETE FROM acl_entry;" + "DELETE FROM acl_object_identity WHERE ID = 9;" + + "DELETE FROM acl_object_identity WHERE ID = 8;" + "DELETE FROM acl_object_identity WHERE ID = 7;" + + "DELETE FROM acl_object_identity WHERE ID = 6;" + "DELETE FROM acl_object_identity WHERE ID = 5;" + + "DELETE FROM acl_object_identity WHERE ID = 4;" + "DELETE FROM acl_object_identity WHERE ID = 3;" + + "DELETE FROM acl_object_identity WHERE ID = 2;" + "DELETE FROM acl_object_identity WHERE ID = 1;" + + "DELETE FROM acl_class;" + "DELETE FROM acl_sid;"; + getJdbcTemplate().execute(query); + } + + protected Ehcache getCache() { + Ehcache cache = cacheManager.getCache("basiclookuptestcache"); + cache.removeAll(); + return cache; + } + + @Test + public void testAclsRetrievalWithDefaultBatchSize() throws Exception { + ObjectIdentity topParentOid = new ObjectIdentityImpl(TARGET_CLASS, new Long(100)); + ObjectIdentity middleParentOid = new ObjectIdentityImpl(TARGET_CLASS, new Long(101)); + // Deliberately use an integer for the child, to reproduce bug report in SEC-819 + ObjectIdentity childOid = new ObjectIdentityImpl(TARGET_CLASS, Integer.valueOf(102)); + + Map map = this.strategy + .readAclsById(Arrays.asList(topParentOid, middleParentOid, childOid), null); + checkEntries(topParentOid, middleParentOid, childOid, map); + } + + @Test + public void testAclsRetrievalFromCacheOnly() throws Exception { + ObjectIdentity topParentOid = new ObjectIdentityImpl(TARGET_CLASS, Integer.valueOf(100)); + ObjectIdentity middleParentOid = new ObjectIdentityImpl(TARGET_CLASS, new Long(101)); + ObjectIdentity childOid = new ObjectIdentityImpl(TARGET_CLASS, new Long(102)); + + // Objects were put in cache + strategy.readAclsById(Arrays.asList(topParentOid, middleParentOid, childOid), null); + + // Let's empty the database to force acls retrieval from cache + emptyDatabase(); + Map map = this.strategy + .readAclsById(Arrays.asList(topParentOid, middleParentOid, childOid), null); + + checkEntries(topParentOid, middleParentOid, childOid, map); + } + + @Test + public void testAclsRetrievalWithCustomBatchSize() throws Exception { + ObjectIdentity topParentOid = new ObjectIdentityImpl(TARGET_CLASS, new Long(100)); + ObjectIdentity middleParentOid = new ObjectIdentityImpl(TARGET_CLASS, Integer.valueOf(101)); + ObjectIdentity childOid = new ObjectIdentityImpl(TARGET_CLASS, new Long(102)); + + // Set a batch size to allow multiple database queries in order to retrieve all + // acls + this.strategy.setBatchSize(1); + Map map = this.strategy + .readAclsById(Arrays.asList(topParentOid, middleParentOid, childOid), null); + checkEntries(topParentOid, middleParentOid, childOid, map); + } + + private void checkEntries(ObjectIdentity topParentOid, ObjectIdentity middleParentOid, ObjectIdentity childOid, + Map map) throws Exception { + assertThat(map).hasSize(3); + + MutableAcl topParent = (MutableAcl) map.get(topParentOid); + MutableAcl middleParent = (MutableAcl) map.get(middleParentOid); + MutableAcl child = (MutableAcl) map.get(childOid); + + // Check the retrieved versions has IDs + assertThat(topParent.getId()).isNotNull(); + assertThat(middleParent.getId()).isNotNull(); + assertThat(child.getId()).isNotNull(); + + // Check their parents were correctly retrieved + assertThat(topParent.getParentAcl()).isNull(); + assertThat(middleParent.getParentAcl().getObjectIdentity()).isEqualTo(topParentOid); + assertThat(child.getParentAcl().getObjectIdentity()).isEqualTo(middleParentOid); + + // Check their ACEs were correctly retrieved + assertThat(topParent.getEntries()).hasSize(2); + assertThat(middleParent.getEntries()).hasSize(1); + assertThat(child.getEntries()).hasSize(1); + + // Check object identities were correctly retrieved + assertThat(topParent.getObjectIdentity()).isEqualTo(topParentOid); + assertThat(middleParent.getObjectIdentity()).isEqualTo(middleParentOid); + assertThat(child.getObjectIdentity()).isEqualTo(childOid); + + // Check each entry + assertThat(topParent.isEntriesInheriting()).isTrue(); + assertThat(Long.valueOf(1)).isEqualTo(topParent.getId()); + assertThat(new PrincipalSid("ben")).isEqualTo(topParent.getOwner()); + assertThat(Long.valueOf(1)).isEqualTo(topParent.getEntries().get(0).getId()); + assertThat(topParent.getEntries().get(0).getPermission()).isEqualTo(BasePermission.READ); + assertThat(topParent.getEntries().get(0).getSid()).isEqualTo(new PrincipalSid("ben")); + assertThat(((AuditableAccessControlEntry) topParent.getEntries().get(0)).isAuditFailure()).isFalse(); + assertThat(((AuditableAccessControlEntry) topParent.getEntries().get(0)).isAuditSuccess()).isFalse(); + assertThat((topParent.getEntries().get(0)).isGranting()).isTrue(); + + assertThat(Long.valueOf(2)).isEqualTo(topParent.getEntries().get(1).getId()); + assertThat(topParent.getEntries().get(1).getPermission()).isEqualTo(BasePermission.WRITE); + assertThat(topParent.getEntries().get(1).getSid()).isEqualTo(new PrincipalSid("ben")); + assertThat(((AuditableAccessControlEntry) topParent.getEntries().get(1)).isAuditFailure()).isFalse(); + assertThat(((AuditableAccessControlEntry) topParent.getEntries().get(1)).isAuditSuccess()).isFalse(); + assertThat(topParent.getEntries().get(1).isGranting()).isFalse(); + + assertThat(middleParent.isEntriesInheriting()).isTrue(); + assertThat(Long.valueOf(2)).isEqualTo(middleParent.getId()); + assertThat(new PrincipalSid("ben")).isEqualTo(middleParent.getOwner()); + assertThat(Long.valueOf(3)).isEqualTo(middleParent.getEntries().get(0).getId()); + assertThat(middleParent.getEntries().get(0).getPermission()).isEqualTo(BasePermission.DELETE); + assertThat(middleParent.getEntries().get(0).getSid()).isEqualTo(new PrincipalSid("ben")); + assertThat(((AuditableAccessControlEntry) middleParent.getEntries().get(0)).isAuditFailure()).isFalse(); + assertThat(((AuditableAccessControlEntry) middleParent.getEntries().get(0)).isAuditSuccess()).isFalse(); + assertThat(middleParent.getEntries().get(0).isGranting()).isTrue(); + + assertThat(child.isEntriesInheriting()).isTrue(); + assertThat(Long.valueOf(3)).isEqualTo(child.getId()); + assertThat(new PrincipalSid("ben")).isEqualTo(child.getOwner()); + assertThat(Long.valueOf(4)).isEqualTo(child.getEntries().get(0).getId()); + assertThat(child.getEntries().get(0).getPermission()).isEqualTo(BasePermission.DELETE); + assertThat(new PrincipalSid("ben")).isEqualTo(child.getEntries().get(0).getSid()); + assertThat(((AuditableAccessControlEntry) child.getEntries().get(0)).isAuditFailure()).isFalse(); + assertThat(((AuditableAccessControlEntry) child.getEntries().get(0)).isAuditSuccess()).isFalse(); + assertThat((child.getEntries().get(0)).isGranting()).isFalse(); + } + + @Test + public void testAllParentsAreRetrievedWhenChildIsLoaded() throws Exception { + String query = "INSERT INTO acl_object_identity(ID,OBJECT_ID_CLASS,OBJECT_ID_IDENTITY,PARENT_OBJECT,OWNER_SID,ENTRIES_INHERITING) VALUES (6,2,103,1,1,1);"; + getJdbcTemplate().execute(query); + + ObjectIdentity topParentOid = new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(100)); + ObjectIdentity middleParentOid = new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(101)); + ObjectIdentity childOid = new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(102)); + ObjectIdentity middleParent2Oid = new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(103)); + + // Retrieve the child + Map map = this.strategy.readAclsById(Arrays.asList(childOid), null); + + // Check that the child and all its parents were retrieved + assertThat(map.get(childOid)).isNotNull(); + assertThat(map.get(childOid).getObjectIdentity()).isEqualTo(childOid); + assertThat(map.get(middleParentOid)).isNotNull(); + assertThat(map.get(middleParentOid).getObjectIdentity()).isEqualTo(middleParentOid); + assertThat(map.get(topParentOid)).isNotNull(); + assertThat(map.get(topParentOid).getObjectIdentity()).isEqualTo(topParentOid); + + // The second parent shouldn't have been retrieved + assertThat(map.get(middleParent2Oid)).isNull(); + } + + /** + * Test created from SEC-590. + */ + @Test + public void testReadAllObjectIdentitiesWhenLastElementIsAlreadyCached() throws Exception { + String query = "INSERT INTO acl_object_identity(ID,OBJECT_ID_CLASS,OBJECT_ID_IDENTITY,PARENT_OBJECT,OWNER_SID,ENTRIES_INHERITING) VALUES (6,2,105,null,1,1);" + + "INSERT INTO acl_object_identity(ID,OBJECT_ID_CLASS,OBJECT_ID_IDENTITY,PARENT_OBJECT,OWNER_SID,ENTRIES_INHERITING) VALUES (7,2,106,6,1,1);" + + "INSERT INTO acl_object_identity(ID,OBJECT_ID_CLASS,OBJECT_ID_IDENTITY,PARENT_OBJECT,OWNER_SID,ENTRIES_INHERITING) VALUES (8,2,107,6,1,1);" + + "INSERT INTO acl_object_identity(ID,OBJECT_ID_CLASS,OBJECT_ID_IDENTITY,PARENT_OBJECT,OWNER_SID,ENTRIES_INHERITING) VALUES (9,2,108,7,1,1);" + + "INSERT INTO acl_entry(ID,ACL_OBJECT_IDENTITY,ACE_ORDER,SID,MASK,GRANTING,AUDIT_SUCCESS,AUDIT_FAILURE) VALUES (7,6,0,1,1,1,0,0)"; + getJdbcTemplate().execute(query); + + ObjectIdentity grandParentOid = new ObjectIdentityImpl(TARGET_CLASS, new Long(104)); + ObjectIdentity parent1Oid = new ObjectIdentityImpl(TARGET_CLASS, new Long(105)); + ObjectIdentity parent2Oid = new ObjectIdentityImpl(TARGET_CLASS, Integer.valueOf(106)); + ObjectIdentity childOid = new ObjectIdentityImpl(TARGET_CLASS, Integer.valueOf(107)); + + // First lookup only child, thus populating the cache with grandParent, + // parent1 + // and child + List checkPermission = Arrays.asList(BasePermission.READ); + List sids = Arrays.asList(BEN_SID); + List childOids = Arrays.asList(childOid); + + strategy.setBatchSize(6); + Map foundAcls = strategy.readAclsById(childOids, sids); + + Acl foundChildAcl = foundAcls.get(childOid); + assertThat(foundChildAcl).isNotNull(); + assertThat(foundChildAcl.isGranted(checkPermission, sids, false)).isTrue(); + + // Search for object identities has to be done in the following order: + // last + // element have to be one which + // is already in cache and the element before it must not be stored in + // cache + List allOids = Arrays.asList(grandParentOid, parent1Oid, parent2Oid, childOid); + try { + foundAcls = strategy.readAclsById(allOids, sids); + + } catch (NotFoundException notExpected) { + fail("It shouldn't have thrown NotFoundException"); + } + + Acl foundParent2Acl = foundAcls.get(parent2Oid); + assertThat(foundParent2Acl).isNotNull(); + assertThat(foundParent2Acl.isGranted(checkPermission, sids, false)).isTrue(); + } + + @Test(expected = IllegalArgumentException.class) + public void nullOwnerIsNotSupported() { + String query = "INSERT INTO acl_object_identity(ID,OBJECT_ID_CLASS,OBJECT_ID_IDENTITY,PARENT_OBJECT,OWNER_SID,ENTRIES_INHERITING) VALUES (6,2,104,null,null,1);"; + + getJdbcTemplate().execute(query); + + ObjectIdentity oid = new ObjectIdentityImpl(TARGET_CLASS, new Long(104)); + + strategy.readAclsById(Arrays.asList(oid), Arrays.asList(BEN_SID)); + } + + @Test + public void testCreatePrincipalSid() { + Sid result = strategy.createSid(true, "sid"); + + assertThat(result.getClass()).isEqualTo(PrincipalSid.class); + assertThat(((PrincipalSid) result).getPrincipal()).isEqualTo("sid"); + } + + @Test + public void testCreateGrantedAuthority() { + Sid result = strategy.createSid(false, "sid"); + + assertThat(result.getClass()).isEqualTo(GrantedAuthoritySid.class); + assertThat(((GrantedAuthoritySid) result).getGrantedAuthority()).isEqualTo("sid"); + } + +} diff --git a/acl/src/test/java/org/springframework/security/acls/jdbc/AclClassIdUtilsTest.java b/acl/src/test/java/org/springframework/security/acls/jdbc/AclClassIdUtilsTest.java new file mode 100644 index 00000000000..59bb3ea4006 --- /dev/null +++ b/acl/src/test/java/org/springframework/security/acls/jdbc/AclClassIdUtilsTest.java @@ -0,0 +1,160 @@ +/* + * 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 static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.BDDMockito.given; + +import java.io.Serializable; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.UUID; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; +import org.springframework.core.convert.ConversionService; + +/** + * Tests for {@link AclClassIdUtils}. + * @author paulwheeler + */ +@RunWith(MockitoJUnitRunner.class) +public class AclClassIdUtilsTest { + + private static final Long DEFAULT_IDENTIFIER = 999L; + private static final String DEFAULT_IDENTIFIER_AS_STRING = DEFAULT_IDENTIFIER.toString(); + + @Mock + private ResultSet resultSet; + @Mock + private ConversionService conversionService; + @InjectMocks + private AclClassIdUtils aclClassIdUtils; + + @Before + public void setUp() throws Exception { + given(conversionService.canConvert(String.class, Long.class)).willReturn(true); + given(conversionService.convert(DEFAULT_IDENTIFIER, Long.class)).willReturn(new Long(DEFAULT_IDENTIFIER)); + given(conversionService.convert(DEFAULT_IDENTIFIER_AS_STRING, Long.class)).willReturn(new Long(DEFAULT_IDENTIFIER)); + } + + @Test + public void shouldReturnLongIfIdentifierIsNotStringAndNoConversionService() throws SQLException { + // given + AclClassIdUtils aclClassIdUtilsWithoutConversionSvc = new AclClassIdUtils(); + + // when + Serializable newIdentifier = aclClassIdUtilsWithoutConversionSvc.identifierFrom(DEFAULT_IDENTIFIER, resultSet); + + // then + assertThat(newIdentifier).isEqualTo(DEFAULT_IDENTIFIER); + } + + @Test + public void shouldReturnLongIfIdentifierIsNotString() throws SQLException { + // given + Long prevIdentifier = 999L; + + // when + Serializable newIdentifier = aclClassIdUtils.identifierFrom(prevIdentifier, resultSet); + + // then + assertThat(newIdentifier).isEqualTo(prevIdentifier); + } + + @Test + public void shouldReturnLongIfClassIdTypeIsNull() throws SQLException { + // given + given(resultSet.getString("class_id_type")).willReturn(null); + + // when + Serializable newIdentifier = aclClassIdUtils.identifierFrom(DEFAULT_IDENTIFIER_AS_STRING, resultSet); + + // then + assertThat(newIdentifier).isEqualTo(DEFAULT_IDENTIFIER); + } + + @Test + public void shouldReturnLongIfNoClassIdTypeColumn() throws SQLException { + // given + given(resultSet.getString("class_id_type")).willThrow(SQLException.class); + + // when + Serializable newIdentifier = aclClassIdUtils.identifierFrom(DEFAULT_IDENTIFIER_AS_STRING, resultSet); + + // then + assertThat(newIdentifier).isEqualTo(DEFAULT_IDENTIFIER); + } + + @Test + public void shouldReturnLongIfTypeClassNotFound() throws SQLException { + // given + given(resultSet.getString("class_id_type")).willReturn("com.example.UnknownType"); + + // when + Serializable newIdentifier = aclClassIdUtils.identifierFrom(DEFAULT_IDENTIFIER_AS_STRING, resultSet); + + // then + assertThat(newIdentifier).isEqualTo(DEFAULT_IDENTIFIER); + } + + @Test + public void shouldReturnLongIfTypeClassCannotBeConverted() throws SQLException { + // given + given(resultSet.getString("class_id_type")).willReturn("java.lang.Long"); + given(conversionService.canConvert(String.class, Long.class)).willReturn(false); + + // when + Serializable newIdentifier = aclClassIdUtils.identifierFrom(DEFAULT_IDENTIFIER_AS_STRING, resultSet); + + // then + assertThat(newIdentifier).isEqualTo(DEFAULT_IDENTIFIER); + } + + @Test + public void shouldReturnLongWhenLongClassIdType() throws SQLException { + // given + given(resultSet.getString("class_id_type")).willReturn("java.lang.Long"); + + // when + Serializable newIdentifier = aclClassIdUtils.identifierFrom(DEFAULT_IDENTIFIER_AS_STRING, resultSet); + + // then + assertThat(newIdentifier).isEqualTo(DEFAULT_IDENTIFIER); + } + + @Test + public void shouldReturnUUIDWhenUUIDClassIdType() throws SQLException { + // given + UUID identifier = UUID.randomUUID(); + String identifierAsString = identifier.toString(); + given(resultSet.getString("class_id_type")).willReturn("java.util.UUID"); + given(conversionService.canConvert(String.class, UUID.class)).willReturn(true); + given(conversionService.convert(identifierAsString, UUID.class)).willReturn(UUID.fromString(identifierAsString)); + + // when + Serializable newIdentifier = aclClassIdUtils.identifierFrom(identifier.toString(), resultSet); + + // then + assertThat(newIdentifier).isEqualTo(identifier); + } + +} 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 069f999b8d2..8557177dc64 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 @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * 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. @@ -15,330 +15,39 @@ */ package org.springframework.security.acls.jdbc; -import static org.assertj.core.api.Assertions.*; +import javax.sql.DataSource; -import net.sf.ehcache.Cache; -import net.sf.ehcache.CacheManager; -import net.sf.ehcache.Ehcache; -import org.junit.*; -import org.springframework.core.io.ClassPathResource; -import org.springframework.core.io.Resource; +import org.junit.AfterClass; +import org.junit.BeforeClass; import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.jdbc.datasource.SingleConnectionDataSource; -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; -import org.springframework.security.acls.model.NotFoundException; -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.core.authority.SimpleGrantedAuthority; -import org.springframework.util.FileCopyUtils; - -import java.util.*; /** - * Tests {@link BasicLookupStrategy} + * Tests {@link BasicLookupStrategy} with Acl Class type id not specified. * * @author Andrei Stefan + * @author Paul Wheeler */ -public class BasicLookupStrategyTests { - - private static final Sid BEN_SID = new PrincipalSid("ben"); - private static final String TARGET_CLASS = "org.springframework.security.acls.TargetObject"; - - // ~ Instance fields - // ================================================================================================ - - private static JdbcTemplate jdbcTemplate; - private BasicLookupStrategy strategy; - private static SingleConnectionDataSource dataSource; - private static CacheManager cacheManager; +public class BasicLookupStrategyTests extends AbstractBasicLookupStrategyTests { + private static final BasicLookupStrategyTestsDbHelper DATABASE_HELPER = new BasicLookupStrategyTestsDbHelper(); - // ~ Methods - // ======================================================================================================== - @BeforeClass - public static void initCacheManaer() { - cacheManager = CacheManager.create(); - cacheManager.addCache(new Cache("basiclookuptestcache", 500, false, false, 30, 30)); - } @BeforeClass public static void createDatabase() throws Exception { - dataSource = new SingleConnectionDataSource("jdbc:hsqldb:mem:lookupstrategytest", "sa", "", true); - dataSource.setDriverClassName("org.hsqldb.jdbcDriver"); - jdbcTemplate = new JdbcTemplate(dataSource); - - Resource resource = new ClassPathResource("createAclSchema.sql"); - String sql = new String(FileCopyUtils.copyToByteArray(resource.getInputStream())); - jdbcTemplate.execute(sql); + DATABASE_HELPER.createDatabase(); } @AfterClass public static void dropDatabase() throws Exception { - dataSource.destroy(); - } - - @AfterClass - public static void shutdownCacheManager() { - cacheManager.removalAll(); - cacheManager.shutdown(); - } - - @Before - public void populateDatabase() { - String query = "INSERT INTO acl_sid(ID,PRINCIPAL,SID) VALUES (1,1,'ben');" - + "INSERT INTO acl_class(ID,CLASS) VALUES (2,'" + TARGET_CLASS + "');" - + "INSERT INTO acl_object_identity(ID,OBJECT_ID_CLASS,OBJECT_ID_IDENTITY,PARENT_OBJECT,OWNER_SID,ENTRIES_INHERITING) VALUES (1,2,100,null,1,1);" - + "INSERT INTO acl_object_identity(ID,OBJECT_ID_CLASS,OBJECT_ID_IDENTITY,PARENT_OBJECT,OWNER_SID,ENTRIES_INHERITING) VALUES (2,2,101,1,1,1);" - + "INSERT INTO acl_object_identity(ID,OBJECT_ID_CLASS,OBJECT_ID_IDENTITY,PARENT_OBJECT,OWNER_SID,ENTRIES_INHERITING) VALUES (3,2,102,2,1,1);" - + "INSERT INTO acl_entry(ID,ACL_OBJECT_IDENTITY,ACE_ORDER,SID,MASK,GRANTING,AUDIT_SUCCESS,AUDIT_FAILURE) VALUES (1,1,0,1,1,1,0,0);" - + "INSERT INTO acl_entry(ID,ACL_OBJECT_IDENTITY,ACE_ORDER,SID,MASK,GRANTING,AUDIT_SUCCESS,AUDIT_FAILURE) VALUES (2,1,1,1,2,0,0,0);" - + "INSERT INTO acl_entry(ID,ACL_OBJECT_IDENTITY,ACE_ORDER,SID,MASK,GRANTING,AUDIT_SUCCESS,AUDIT_FAILURE) VALUES (3,2,0,1,8,1,0,0);" - + "INSERT INTO acl_entry(ID,ACL_OBJECT_IDENTITY,ACE_ORDER,SID,MASK,GRANTING,AUDIT_SUCCESS,AUDIT_FAILURE) VALUES (4,3,0,1,8,0,0,0);"; - jdbcTemplate.execute(query); - } - - @Before - public void initializeBeans() { - EhCacheBasedAclCache cache = new EhCacheBasedAclCache(getCache(), - new DefaultPermissionGrantingStrategy(new ConsoleAuditLogger()), - new AclAuthorizationStrategyImpl(new SimpleGrantedAuthority("ROLE_USER"))); - AclAuthorizationStrategy authorizationStrategy = new AclAuthorizationStrategyImpl( - new SimpleGrantedAuthority("ROLE_ADMINISTRATOR")); - strategy = new BasicLookupStrategy(dataSource, cache, authorizationStrategy, - new DefaultPermissionGrantingStrategy(new ConsoleAuditLogger())); - strategy.setPermissionFactory(new DefaultPermissionFactory()); - } - - @After - public void emptyDatabase() { - String query = "DELETE FROM acl_entry;" + "DELETE FROM acl_object_identity WHERE ID = 7;" - + "DELETE FROM acl_object_identity WHERE ID = 6;" + "DELETE FROM acl_object_identity WHERE ID = 5;" - + "DELETE FROM acl_object_identity WHERE ID = 4;" + "DELETE FROM acl_object_identity WHERE ID = 3;" - + "DELETE FROM acl_object_identity WHERE ID = 2;" + "DELETE FROM acl_object_identity WHERE ID = 1;" - + "DELETE FROM acl_class;" + "DELETE FROM acl_sid;"; - jdbcTemplate.execute(query); + DATABASE_HELPER.getDataSource().destroy(); } - private Ehcache getCache() { - Ehcache cache = cacheManager.getCache("basiclookuptestcache"); - cache.removeAll(); - return cache; + @Override + public JdbcTemplate getJdbcTemplate() { + return DATABASE_HELPER.getJdbcTemplate(); } - @Test - public void testAclsRetrievalWithDefaultBatchSize() throws Exception { - ObjectIdentity topParentOid = new ObjectIdentityImpl(TARGET_CLASS, new Long(100)); - ObjectIdentity middleParentOid = new ObjectIdentityImpl(TARGET_CLASS, new Long(101)); - // Deliberately use an integer for the child, to reproduce bug report in - // SEC-819 - ObjectIdentity childOid = new ObjectIdentityImpl(TARGET_CLASS, Integer.valueOf(102)); - - Map map = this.strategy - .readAclsById(Arrays.asList(topParentOid, middleParentOid, childOid), null); - checkEntries(topParentOid, middleParentOid, childOid, map); - } - - @Test - public void testAclsRetrievalFromCacheOnly() throws Exception { - ObjectIdentity topParentOid = new ObjectIdentityImpl(TARGET_CLASS, Integer.valueOf(100)); - ObjectIdentity middleParentOid = new ObjectIdentityImpl(TARGET_CLASS, new Long(101)); - ObjectIdentity childOid = new ObjectIdentityImpl(TARGET_CLASS, new Long(102)); - - // Objects were put in cache - strategy.readAclsById(Arrays.asList(topParentOid, middleParentOid, childOid), null); - - // Let's empty the database to force acls retrieval from cache - emptyDatabase(); - Map map = this.strategy - .readAclsById(Arrays.asList(topParentOid, middleParentOid, childOid), null); - - checkEntries(topParentOid, middleParentOid, childOid, map); - } - - @Test - public void testAclsRetrievalWithCustomBatchSize() throws Exception { - ObjectIdentity topParentOid = new ObjectIdentityImpl(TARGET_CLASS, new Long(100)); - ObjectIdentity middleParentOid = new ObjectIdentityImpl(TARGET_CLASS, Integer.valueOf(101)); - ObjectIdentity childOid = new ObjectIdentityImpl(TARGET_CLASS, new Long(102)); - - // Set a batch size to allow multiple database queries in order to - // retrieve all - // acls - this.strategy.setBatchSize(1); - Map map = this.strategy - .readAclsById(Arrays.asList(topParentOid, middleParentOid, childOid), null); - checkEntries(topParentOid, middleParentOid, childOid, map); + @Override + public DataSource getDataSource() { + return DATABASE_HELPER.getDataSource(); } - - private void checkEntries(ObjectIdentity topParentOid, ObjectIdentity middleParentOid, ObjectIdentity childOid, - Map map) throws Exception { - assertThat(map).hasSize(3); - - MutableAcl topParent = (MutableAcl) map.get(topParentOid); - MutableAcl middleParent = (MutableAcl) map.get(middleParentOid); - MutableAcl child = (MutableAcl) map.get(childOid); - - // Check the retrieved versions has IDs - assertThat(topParent.getId()).isNotNull(); - assertThat(middleParent.getId()).isNotNull(); - assertThat(child.getId()).isNotNull(); - - // Check their parents were correctly retrieved - assertThat(topParent.getParentAcl()).isNull(); - assertThat(middleParent.getParentAcl().getObjectIdentity()).isEqualTo(topParentOid); - assertThat(child.getParentAcl().getObjectIdentity()).isEqualTo(middleParentOid); - - // Check their ACEs were correctly retrieved - assertThat(topParent.getEntries()).hasSize(2); - assertThat(middleParent.getEntries()).hasSize(1); - assertThat(child.getEntries()).hasSize(1); - - // Check object identities were correctly retrieved - assertThat(topParent.getObjectIdentity()).isEqualTo(topParentOid); - assertThat(middleParent.getObjectIdentity()).isEqualTo(middleParentOid); - assertThat(child.getObjectIdentity()).isEqualTo(childOid); - - // Check each entry - assertThat(topParent.isEntriesInheriting()).isTrue(); - assertThat(Long.valueOf(1)).isEqualTo(topParent.getId()); - assertThat(new PrincipalSid("ben")).isEqualTo(topParent.getOwner()); - assertThat(Long.valueOf(1)).isEqualTo(topParent.getEntries().get(0).getId()); - assertThat(topParent.getEntries().get(0).getPermission()).isEqualTo(BasePermission.READ); - assertThat(topParent.getEntries().get(0).getSid()).isEqualTo(new PrincipalSid("ben")); - assertThat(((AuditableAccessControlEntry) topParent.getEntries().get(0)).isAuditFailure()).isFalse(); - assertThat(((AuditableAccessControlEntry) topParent.getEntries().get(0)).isAuditSuccess()).isFalse(); - assertThat((topParent.getEntries().get(0)).isGranting()).isTrue(); - - assertThat(Long.valueOf(2)).isEqualTo(topParent.getEntries().get(1).getId()); - assertThat(topParent.getEntries().get(1).getPermission()).isEqualTo(BasePermission.WRITE); - assertThat(topParent.getEntries().get(1).getSid()).isEqualTo(new PrincipalSid("ben")); - assertThat(((AuditableAccessControlEntry) topParent.getEntries().get(1)).isAuditFailure()).isFalse(); - assertThat(((AuditableAccessControlEntry) topParent.getEntries().get(1)).isAuditSuccess()).isFalse(); - assertThat(topParent.getEntries().get(1).isGranting()).isFalse(); - - assertThat(middleParent.isEntriesInheriting()).isTrue(); - assertThat(Long.valueOf(2)).isEqualTo(middleParent.getId()); - assertThat(new PrincipalSid("ben")).isEqualTo(middleParent.getOwner()); - assertThat(Long.valueOf(3)).isEqualTo(middleParent.getEntries().get(0).getId()); - assertThat(middleParent.getEntries().get(0).getPermission()).isEqualTo(BasePermission.DELETE); - assertThat(middleParent.getEntries().get(0).getSid()).isEqualTo(new PrincipalSid("ben")); - assertThat(((AuditableAccessControlEntry) middleParent.getEntries().get(0)).isAuditFailure()).isFalse(); - assertThat(((AuditableAccessControlEntry) middleParent.getEntries().get(0)).isAuditSuccess()).isFalse(); - assertThat(middleParent.getEntries().get(0).isGranting()).isTrue(); - - assertThat(child.isEntriesInheriting()).isTrue(); - assertThat(Long.valueOf(3)).isEqualTo(child.getId()); - assertThat(new PrincipalSid("ben")).isEqualTo(child.getOwner()); - assertThat(Long.valueOf(4)).isEqualTo(child.getEntries().get(0).getId()); - assertThat(child.getEntries().get(0).getPermission()).isEqualTo(BasePermission.DELETE); - assertThat(new PrincipalSid("ben")).isEqualTo(child.getEntries().get(0).getSid()); - assertThat(((AuditableAccessControlEntry) child.getEntries().get(0)).isAuditFailure()).isFalse(); - assertThat(((AuditableAccessControlEntry) child.getEntries().get(0)).isAuditSuccess()).isFalse(); - assertThat((child.getEntries().get(0)).isGranting()).isFalse(); - } - - @Test - public void testAllParentsAreRetrievedWhenChildIsLoaded() throws Exception { - String query = "INSERT INTO acl_object_identity(ID,OBJECT_ID_CLASS,OBJECT_ID_IDENTITY,PARENT_OBJECT,OWNER_SID,ENTRIES_INHERITING) VALUES (4,2,103,1,1,1);"; - jdbcTemplate.execute(query); - - ObjectIdentity topParentOid = new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(100)); - ObjectIdentity middleParentOid = new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(101)); - ObjectIdentity childOid = new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(102)); - ObjectIdentity middleParent2Oid = new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(103)); - - // Retrieve the child - Map map = this.strategy.readAclsById(Arrays.asList(childOid), null); - - // Check that the child and all its parents were retrieved - assertThat(map.get(childOid)).isNotNull(); - assertThat(map.get(childOid).getObjectIdentity()).isEqualTo(childOid); - assertThat(map.get(middleParentOid)).isNotNull(); - assertThat(map.get(middleParentOid).getObjectIdentity()).isEqualTo(middleParentOid); - assertThat(map.get(topParentOid)).isNotNull(); - assertThat(map.get(topParentOid).getObjectIdentity()).isEqualTo(topParentOid); - - // The second parent shouldn't have been retrieved - assertThat(map.get(middleParent2Oid)).isNull(); - } - - /** - * Test created from SEC-590. - */ - @Test - public void testReadAllObjectIdentitiesWhenLastElementIsAlreadyCached() throws Exception { - String query = "INSERT INTO acl_object_identity(ID,OBJECT_ID_CLASS,OBJECT_ID_IDENTITY,PARENT_OBJECT,OWNER_SID,ENTRIES_INHERITING) VALUES (4,2,104,null,1,1);" - + "INSERT INTO acl_object_identity(ID,OBJECT_ID_CLASS,OBJECT_ID_IDENTITY,PARENT_OBJECT,OWNER_SID,ENTRIES_INHERITING) VALUES (5,2,105,4,1,1);" - + "INSERT INTO acl_object_identity(ID,OBJECT_ID_CLASS,OBJECT_ID_IDENTITY,PARENT_OBJECT,OWNER_SID,ENTRIES_INHERITING) VALUES (6,2,106,4,1,1);" - + "INSERT INTO acl_object_identity(ID,OBJECT_ID_CLASS,OBJECT_ID_IDENTITY,PARENT_OBJECT,OWNER_SID,ENTRIES_INHERITING) VALUES (7,2,107,5,1,1);" - + "INSERT INTO acl_entry(ID,ACL_OBJECT_IDENTITY,ACE_ORDER,SID,MASK,GRANTING,AUDIT_SUCCESS,AUDIT_FAILURE) VALUES (5,4,0,1,1,1,0,0)"; - jdbcTemplate.execute(query); - - ObjectIdentity grandParentOid = new ObjectIdentityImpl(TARGET_CLASS, new Long(104)); - ObjectIdentity parent1Oid = new ObjectIdentityImpl(TARGET_CLASS, new Long(105)); - ObjectIdentity parent2Oid = new ObjectIdentityImpl(TARGET_CLASS, Integer.valueOf(106)); - ObjectIdentity childOid = new ObjectIdentityImpl(TARGET_CLASS, Integer.valueOf(107)); - - // First lookup only child, thus populating the cache with grandParent, - // parent1 - // and child - List checkPermission = Arrays.asList(BasePermission.READ); - List sids = Arrays.asList(BEN_SID); - List childOids = Arrays.asList(childOid); - - strategy.setBatchSize(6); - Map foundAcls = strategy.readAclsById(childOids, sids); - - Acl foundChildAcl = foundAcls.get(childOid); - assertThat(foundChildAcl).isNotNull(); - assertThat(foundChildAcl.isGranted(checkPermission, sids, false)).isTrue(); - - // Search for object identities has to be done in the following order: - // last - // element have to be one which - // is already in cache and the element before it must not be stored in - // cache - List allOids = Arrays.asList(grandParentOid, parent1Oid, parent2Oid, childOid); - try { - foundAcls = strategy.readAclsById(allOids, sids); - - } catch (NotFoundException notExpected) { - fail("It shouldn't have thrown NotFoundException"); - } - - Acl foundParent2Acl = foundAcls.get(parent2Oid); - assertThat(foundParent2Acl).isNotNull(); - assertThat(foundParent2Acl.isGranted(checkPermission, sids, false)).isTrue(); - } - - @Test(expected = IllegalArgumentException.class) - public void nullOwnerIsNotSupported() { - String query = "INSERT INTO acl_object_identity(ID,OBJECT_ID_CLASS,OBJECT_ID_IDENTITY,PARENT_OBJECT,OWNER_SID,ENTRIES_INHERITING) VALUES (4,2,104,null,null,1);"; - - jdbcTemplate.execute(query); - - ObjectIdentity oid = new ObjectIdentityImpl(TARGET_CLASS, new Long(104)); - - strategy.readAclsById(Arrays.asList(oid), Arrays.asList(BEN_SID)); - } - - @Test - public void testCreatePrincipalSid() { - Sid result = strategy.createSid(true, "sid"); - - assertThat(result.getClass()).isEqualTo(PrincipalSid.class); - assertThat(((PrincipalSid) result).getPrincipal()).isEqualTo("sid"); - } - - @Test - public void testCreateGrantedAuthority() { - Sid result = strategy.createSid(false, "sid"); - - assertThat(result.getClass()).isEqualTo(GrantedAuthoritySid.class); - assertThat(((GrantedAuthoritySid) result).getGrantedAuthority()).isEqualTo("sid"); - } - } diff --git a/acl/src/test/java/org/springframework/security/acls/jdbc/BasicLookupStrategyTestsDbHelper.java b/acl/src/test/java/org/springframework/security/acls/jdbc/BasicLookupStrategyTestsDbHelper.java new file mode 100644 index 00000000000..5615c71c04f --- /dev/null +++ b/acl/src/test/java/org/springframework/security/acls/jdbc/BasicLookupStrategyTestsDbHelper.java @@ -0,0 +1,72 @@ +/* + * 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 org.springframework.core.io.ClassPathResource; +import org.springframework.core.io.Resource; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.datasource.SingleConnectionDataSource; +import org.springframework.util.FileCopyUtils; + +/** + * Helper class to initialize the database for BasicLookupStrategyTests. + * @author Andrei Stefan + * @author Paul Wheeler + */ +public class BasicLookupStrategyTestsDbHelper { + private static final String ACL_SCHEMA_SQL_FILE = "createAclSchema.sql"; + private static final String ACL_SCHEMA_SQL_FILE_WITH_ACL_CLASS_ID = "createAclSchemaWithAclClassIdType.sql"; + + private SingleConnectionDataSource dataSource; + private JdbcTemplate jdbcTemplate; + private boolean withAclClassIdType; + + public BasicLookupStrategyTestsDbHelper() { + } + + public BasicLookupStrategyTestsDbHelper(boolean withAclClassIdType) { + this.withAclClassIdType = withAclClassIdType; + } + + public void createDatabase() throws Exception { + // Use a different connection url so the tests can run in parallel + String connectionUrl; + String sqlClassPathResource; + if (!withAclClassIdType) { + connectionUrl = "jdbc:hsqldb:mem:lookupstrategytest"; + sqlClassPathResource = ACL_SCHEMA_SQL_FILE; + } else { + connectionUrl = "jdbc:hsqldb:mem:lookupstrategytestWithAclClassIdType"; + sqlClassPathResource = ACL_SCHEMA_SQL_FILE_WITH_ACL_CLASS_ID; + + } + dataSource = new SingleConnectionDataSource(connectionUrl, "sa", "", true); + dataSource.setDriverClassName("org.hsqldb.jdbcDriver"); + jdbcTemplate = new JdbcTemplate(dataSource); + + Resource resource = new ClassPathResource(sqlClassPathResource); + String sql = new String(FileCopyUtils.copyToByteArray(resource.getInputStream())); + jdbcTemplate.execute(sql); + } + + public JdbcTemplate getJdbcTemplate() { + return jdbcTemplate; + } + + public SingleConnectionDataSource getDataSource() { + return dataSource; + } +} diff --git a/acl/src/test/java/org/springframework/security/acls/jdbc/BasicLookupStrategyWithAclClassTypeTests.java b/acl/src/test/java/org/springframework/security/acls/jdbc/BasicLookupStrategyWithAclClassTypeTests.java new file mode 100644 index 00000000000..b84d606c222 --- /dev/null +++ b/acl/src/test/java/org/springframework/security/acls/jdbc/BasicLookupStrategyWithAclClassTypeTests.java @@ -0,0 +1,117 @@ +/* + * 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.util.Arrays; +import java.util.Map; + +import javax.sql.DataSource; + +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.springframework.core.convert.ConversionFailedException; +import org.springframework.core.convert.support.DefaultConversionService; +import org.springframework.jdbc.core.JdbcTemplate; +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.ObjectIdentityImpl; +import org.springframework.security.acls.model.Acl; +import org.springframework.security.acls.model.ObjectIdentity; + +import junit.framework.Assert; + +/** + * Tests {@link BasicLookupStrategy} with Acl Class type id set to UUID. + * + * @author Paul Wheeler + */ +public class BasicLookupStrategyWithAclClassTypeTests extends AbstractBasicLookupStrategyTests { + + private static final BasicLookupStrategyTestsDbHelper DATABASE_HELPER = new BasicLookupStrategyTestsDbHelper(true); + + private BasicLookupStrategy uuidEnabledStrategy; + + @Override + public JdbcTemplate getJdbcTemplate() { + return DATABASE_HELPER.getJdbcTemplate(); + } + + @Override + public DataSource getDataSource() { + return DATABASE_HELPER.getDataSource(); + } + + @BeforeClass + public static void createDatabase() throws Exception { + DATABASE_HELPER.createDatabase(); + } + + @AfterClass + public static void dropDatabase() throws Exception { + DATABASE_HELPER.getDataSource().destroy(); + } + + @Before + public void initializeBeans() { + super.initializeBeans(); + AclClassIdUtils aclClassIdUtils = new AclClassIdUtils(); + aclClassIdUtils.setConversionService(new DefaultConversionService()); + uuidEnabledStrategy = new BasicLookupStrategy(getDataSource(), aclCache(), aclAuthStrategy(), + new DefaultPermissionGrantingStrategy(new ConsoleAuditLogger())); + uuidEnabledStrategy.setPermissionFactory(new DefaultPermissionFactory()); + uuidEnabledStrategy.setAclClassIdSupported(true); + uuidEnabledStrategy.setAclClassIdUtils(aclClassIdUtils); + } + + @Before + public void populateDatabaseForAclClassTypeTests() { + String query = "INSERT INTO acl_class(ID,CLASS,CLASS_ID_TYPE) VALUES (3,'" + + TARGET_CLASS_WITH_UUID + + "', 'java.util.UUID');" + + "INSERT INTO acl_object_identity(ID,OBJECT_ID_CLASS,OBJECT_ID_IDENTITY,PARENT_OBJECT,OWNER_SID,ENTRIES_INHERITING) VALUES (4,3,'" + + OBJECT_IDENTITY_UUID.toString() + "',null,1,1);" + + "INSERT INTO acl_object_identity(ID,OBJECT_ID_CLASS,OBJECT_ID_IDENTITY,PARENT_OBJECT,OWNER_SID,ENTRIES_INHERITING) VALUES (5,3,'" + + OBJECT_IDENTITY_LONG_AS_UUID + "',null,1,1);" + + "INSERT INTO acl_entry(ID,ACL_OBJECT_IDENTITY,ACE_ORDER,SID,MASK,GRANTING,AUDIT_SUCCESS,AUDIT_FAILURE) VALUES (5,4,0,1,8,0,0,0);" + + "INSERT INTO acl_entry(ID,ACL_OBJECT_IDENTITY,ACE_ORDER,SID,MASK,GRANTING,AUDIT_SUCCESS,AUDIT_FAILURE) VALUES (6,5,0,1,8,0,0,0);"; + DATABASE_HELPER.getJdbcTemplate().execute(query); + } + + @Test + public void testReadObjectIdentityUsingUuidType() { + ObjectIdentity oid = new ObjectIdentityImpl(TARGET_CLASS_WITH_UUID, OBJECT_IDENTITY_UUID); + Map foundAcls = uuidEnabledStrategy.readAclsById(Arrays.asList(oid), Arrays.asList(BEN_SID)); + Assert.assertEquals(1, foundAcls.size()); + Assert.assertNotNull(foundAcls.get(oid)); + } + + @Test + public void testReadObjectIdentityUsingLongTypeWithConversionServiceEnabled() { + ObjectIdentity oid = new ObjectIdentityImpl(TARGET_CLASS, new Long(100)); + Map foundAcls = uuidEnabledStrategy.readAclsById(Arrays.asList(oid), Arrays.asList(BEN_SID)); + Assert.assertEquals(1, foundAcls.size()); + Assert.assertNotNull(foundAcls.get(oid)); + } + + @Test(expected = ConversionFailedException.class) + public void testReadObjectIdentityUsingNonUuidInDatabase() { + ObjectIdentity oid = new ObjectIdentityImpl(TARGET_CLASS_WITH_UUID, OBJECT_IDENTITY_LONG_AS_UUID); + uuidEnabledStrategy.readAclsById(Arrays.asList(oid), Arrays.asList(BEN_SID)); + } +} 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 fcc6f8098bd..0f47daa5c60 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 @@ -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. @@ -98,10 +98,30 @@ public class JdbcMutableAclServiceTests extends // ~ Methods // ======================================================================================================== + protected String getSqlClassPathResource() { + return "createAclSchema.sql"; + } + + protected ObjectIdentity getTopParentOid() { + return topParentOid; + } + + protected ObjectIdentity getMiddleParentOid() { + return middleParentOid; + } + + protected ObjectIdentity getChildOid() { + return childOid; + } + + protected String getTargetClass() { + return TARGET_CLASS; + } + @BeforeTransaction public void createTables() throws Exception { try { - new DatabaseSeeder(dataSource, new ClassPathResource("createAclSchema.sql")); + new DatabaseSeeder(dataSource, new ClassPathResource(getSqlClassPathResource())); // new DatabaseSeeder(dataSource, new // ClassPathResource("createAclSchemaPostgres.sql")); } @@ -126,9 +146,9 @@ public void clearContextAndData() throws Exception { public void testLifecycle() { SecurityContextHolder.getContext().setAuthentication(auth); - MutableAcl topParent = jdbcMutableAclService.createAcl(topParentOid); - MutableAcl middleParent = jdbcMutableAclService.createAcl(middleParentOid); - MutableAcl child = jdbcMutableAclService.createAcl(childOid); + MutableAcl topParent = jdbcMutableAclService.createAcl(getTopParentOid()); + MutableAcl middleParent = jdbcMutableAclService.createAcl(getMiddleParentOid()); + MutableAcl child = jdbcMutableAclService.createAcl(getChildOid()); // Specify the inheritance hierarchy middleParent.setParent(topParent); @@ -147,13 +167,13 @@ public void testLifecycle() { // Let's check if we can read them back correctly Map map = jdbcMutableAclService.readAclsById(Arrays.asList( - topParentOid, middleParentOid, childOid)); + getTopParentOid(), getMiddleParentOid(), getChildOid())); assertThat(map).hasSize(3); // Replace our current objects with their retrieved versions - topParent = (MutableAcl) map.get(topParentOid); - middleParent = (MutableAcl) map.get(middleParentOid); - child = (MutableAcl) map.get(childOid); + topParent = (MutableAcl) map.get(getTopParentOid()); + middleParent = (MutableAcl) map.get(getMiddleParentOid()); + child = (MutableAcl) map.get(getChildOid()); // Check the retrieved versions has IDs assertThat(topParent.getId()).isNotNull(); @@ -162,8 +182,8 @@ public void testLifecycle() { // Check their parents were correctly persisted assertThat(topParent.getParentAcl()).isNull(); - assertThat(middleParent.getParentAcl().getObjectIdentity()).isEqualTo(topParentOid); - assertThat(child.getParentAcl().getObjectIdentity()).isEqualTo(middleParentOid); + assertThat(middleParent.getParentAcl().getObjectIdentity()).isEqualTo(getTopParentOid()); + assertThat(child.getParentAcl().getObjectIdentity()).isEqualTo(getMiddleParentOid()); // Check their ACEs were correctly persisted assertThat(topParent.getEntries()).hasSize(2); @@ -197,7 +217,7 @@ public void testLifecycle() { // Next change the child so it doesn't inherit permissions from above child.setEntriesInheriting(false); jdbcMutableAclService.updateAcl(child); - child = (MutableAcl) jdbcMutableAclService.readAclById(childOid); + child = (MutableAcl) jdbcMutableAclService.readAclById(getChildOid()); assertThat(child.isEntriesInheriting()).isFalse(); // Check the child permissions no longer inherit @@ -228,7 +248,7 @@ public void testLifecycle() { // Save the changed child jdbcMutableAclService.updateAcl(child); - child = (MutableAcl) jdbcMutableAclService.readAclById(childOid); + child = (MutableAcl) jdbcMutableAclService.readAclById(getChildOid()); assertThat(child.getEntries()).hasSize(3); // Output permissions @@ -268,38 +288,38 @@ public void testLifecycle() { public void deleteAclAlsoDeletesChildren() throws Exception { SecurityContextHolder.getContext().setAuthentication(auth); - jdbcMutableAclService.createAcl(topParentOid); - MutableAcl middleParent = jdbcMutableAclService.createAcl(middleParentOid); - MutableAcl child = jdbcMutableAclService.createAcl(childOid); + jdbcMutableAclService.createAcl(getTopParentOid()); + MutableAcl middleParent = jdbcMutableAclService.createAcl(getMiddleParentOid()); + MutableAcl child = jdbcMutableAclService.createAcl(getChildOid()); child.setParent(middleParent); jdbcMutableAclService.updateAcl(middleParent); jdbcMutableAclService.updateAcl(child); // Check the childOid really is a child of middleParentOid - Acl childAcl = jdbcMutableAclService.readAclById(childOid); + Acl childAcl = jdbcMutableAclService.readAclById(getChildOid()); - assertThat(childAcl.getParentAcl().getObjectIdentity()).isEqualTo(middleParentOid); + assertThat(childAcl.getParentAcl().getObjectIdentity()).isEqualTo(getMiddleParentOid()); // Delete the mid-parent and test if the child was deleted, as well - jdbcMutableAclService.deleteAcl(middleParentOid, true); + jdbcMutableAclService.deleteAcl(getMiddleParentOid(), true); try { - jdbcMutableAclService.readAclById(middleParentOid); + jdbcMutableAclService.readAclById(getMiddleParentOid()); fail("It should have thrown NotFoundException"); } catch (NotFoundException expected) { } try { - jdbcMutableAclService.readAclById(childOid); + jdbcMutableAclService.readAclById(getChildOid()); fail("It should have thrown NotFoundException"); } catch (NotFoundException expected) { } - Acl acl = jdbcMutableAclService.readAclById(topParentOid); + Acl acl = jdbcMutableAclService.readAclById(getTopParentOid()); assertThat(acl).isNotNull(); - assertThat(topParentOid).isEqualTo(((MutableAcl) acl).getObjectIdentity()); + assertThat(getTopParentOid()).isEqualTo(((MutableAcl) acl).getObjectIdentity()); } @Test @@ -367,8 +387,8 @@ public void deleteAclRejectsNullParameters() throws Exception { @Transactional public void deleteAclWithChildrenThrowsException() throws Exception { SecurityContextHolder.getContext().setAuthentication(auth); - MutableAcl parent = jdbcMutableAclService.createAcl(topParentOid); - MutableAcl child = jdbcMutableAclService.createAcl(middleParentOid); + MutableAcl parent = jdbcMutableAclService.createAcl(getTopParentOid()); + MutableAcl child = jdbcMutableAclService.createAcl(getMiddleParentOid()); // Specify the inheritance hierarchy child.setParent(parent); @@ -378,7 +398,7 @@ public void deleteAclWithChildrenThrowsException() throws Exception { jdbcMutableAclService.setForeignKeysInDatabase(false); // switch on FK // checking in the // class, not database - jdbcMutableAclService.deleteAcl(topParentOid, false); + jdbcMutableAclService.deleteAcl(getTopParentOid(), false); fail("It should have thrown ChildrenExistException"); } catch (ChildrenExistException expected) { @@ -393,21 +413,21 @@ public void deleteAclWithChildrenThrowsException() throws Exception { @Transactional public void deleteAclRemovesRowsFromDatabase() throws Exception { SecurityContextHolder.getContext().setAuthentication(auth); - MutableAcl child = jdbcMutableAclService.createAcl(childOid); + MutableAcl child = jdbcMutableAclService.createAcl(getChildOid()); child.insertAce(0, BasePermission.DELETE, new PrincipalSid(auth), false); jdbcMutableAclService.updateAcl(child); // Remove the child and check all related database rows were removed accordingly - jdbcMutableAclService.deleteAcl(childOid, false); + jdbcMutableAclService.deleteAcl(getChildOid(), false); assertThat( jdbcTemplate.queryForList(SELECT_ALL_CLASSES, - new Object[] { TARGET_CLASS })).hasSize(1); + new Object[] { getTargetClass() })).hasSize(1); assertThat(jdbcTemplate.queryForList("select * from acl_object_identity") ).isEmpty(); assertThat(jdbcTemplate.queryForList("select * from acl_entry")).isEmpty(); // Check the cache - assertThat(aclCache.getFromCache(childOid)).isNull(); + assertThat(aclCache.getFromCache(getChildOid())).isNull(); assertThat(aclCache.getFromCache(Long.valueOf(102))).isNull(); } @@ -573,4 +593,11 @@ else if (sid instanceof GrantedAuthoritySid) { } } + protected Authentication getAuth() { + return auth; + } + + protected JdbcMutableAclService getJdbcMutableAclService() { + return jdbcMutableAclService; + } } diff --git a/acl/src/test/java/org/springframework/security/acls/jdbc/JdbcMutableAclServiceTestsWithAclClassId.java b/acl/src/test/java/org/springframework/security/acls/jdbc/JdbcMutableAclServiceTestsWithAclClassId.java new file mode 100644 index 00000000000..21ca2a4e6b5 --- /dev/null +++ b/acl/src/test/java/org/springframework/security/acls/jdbc/JdbcMutableAclServiceTestsWithAclClassId.java @@ -0,0 +1,83 @@ +/* + * 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 static org.assertj.core.api.Assertions.assertThat; + +import java.util.UUID; + +import org.junit.Test; +import org.springframework.security.acls.TargetObjectWithUUID; +import org.springframework.security.acls.domain.ObjectIdentityImpl; +import org.springframework.security.acls.model.ObjectIdentity; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.transaction.annotation.Transactional; + +/** + * Integration tests the ACL system using ACL class id type of UUID and using an in-memory database. + * @author Paul Wheeler + */ +@ContextConfiguration(locations = {"/jdbcMutableAclServiceTestsWithAclClass-context.xml"}) +public class JdbcMutableAclServiceTestsWithAclClassId extends JdbcMutableAclServiceTests { + + private static final String TARGET_CLASS_WITH_UUID = TargetObjectWithUUID.class.getName(); + + private final ObjectIdentity topParentOid = new ObjectIdentityImpl(TARGET_CLASS_WITH_UUID, + UUID.randomUUID()); + private final ObjectIdentity middleParentOid = new ObjectIdentityImpl(TARGET_CLASS_WITH_UUID, + UUID.randomUUID()); + private final ObjectIdentity childOid = new ObjectIdentityImpl(TARGET_CLASS_WITH_UUID, + UUID.randomUUID()); + + @Override + protected String getSqlClassPathResource() { + return "createAclSchemaWithAclClassIdType.sql"; + } + + @Override + protected ObjectIdentity getTopParentOid() { + return topParentOid; + } + + @Override + protected ObjectIdentity getMiddleParentOid() { + return middleParentOid; + } + + @Override + protected ObjectIdentity getChildOid() { + return childOid; + } + + @Override + protected String getTargetClass() { + return TARGET_CLASS_WITH_UUID; + } + + @Test + @Transactional + public void identityWithUuidIdIsSupportedByCreateAcl() throws Exception { + SecurityContextHolder.getContext().setAuthentication(getAuth()); + + UUID id = UUID.randomUUID(); + ObjectIdentity oid = new ObjectIdentityImpl(TARGET_CLASS_WITH_UUID, id); + getJdbcMutableAclService().createAcl(oid); + + assertThat(getJdbcMutableAclService().readAclById(new ObjectIdentityImpl( + TARGET_CLASS_WITH_UUID, id))).isNotNull(); + } +} diff --git a/acl/src/test/resources/jdbcMutableAclServiceTests-context.xml b/acl/src/test/resources/jdbcMutableAclServiceTests-context.xml index 4c1802536a6..e748cb7d925 100644 --- a/acl/src/test/resources/jdbcMutableAclServiceTests-context.xml +++ b/acl/src/test/resources/jdbcMutableAclServiceTests-context.xml @@ -17,7 +17,10 @@ - + + + + diff --git a/acl/src/test/resources/jdbcMutableAclServiceTestsWithAclClass-context.xml b/acl/src/test/resources/jdbcMutableAclServiceTestsWithAclClass-context.xml new file mode 100644 index 00000000000..e0baeede48a --- /dev/null +++ b/acl/src/test/resources/jdbcMutableAclServiceTestsWithAclClass-context.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/manual/src/docs/asciidoc/index.adoc b/docs/manual/src/docs/asciidoc/index.adoc index 21cba98a0a7..7464b9238d3 100644 --- a/docs/manual/src/docs/asciidoc/index.adoc +++ b/docs/manual/src/docs/asciidoc/index.adoc @@ -7279,7 +7279,7 @@ create table acl_class( create table acl_object_identity( id bigint generated by default as identity(start with 100) not null primary key, object_id_class bigint not null, - object_id_identity bigint not null, + object_id_identity varchar_ignorecase(36) not null, parent_object bigint, owner_sid bigint, entries_inheriting boolean not null, @@ -7323,7 +7323,7 @@ create table acl_class( create table acl_object_identity( id bigserial primary key, object_id_class bigint not null, - object_id_identity bigint not null, + object_id_identity varchar(36) not null, parent_object bigint, owner_sid bigint, entries_inheriting boolean not null, @@ -7372,7 +7372,7 @@ CREATE TABLE acl_class ( CREATE TABLE acl_object_identity ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, object_id_class BIGINT UNSIGNED NOT NULL, - object_id_identity BIGINT NOT NULL, + object_id_identity VARCHAR(36) NOT NULL, parent_object BIGINT UNSIGNED, owner_sid BIGINT UNSIGNED, entries_inheriting BOOLEAN NOT NULL, @@ -7416,7 +7416,7 @@ CREATE TABLE acl_class ( CREATE TABLE acl_object_identity ( id BIGINT NOT NULL IDENTITY PRIMARY KEY, object_id_class BIGINT NOT NULL, - object_id_identity BIGINT NOT NULL, + object_id_identity VARCHAR(36) NOT NULL, parent_object BIGINT, owner_sid BIGINT, entries_inheriting BIT NOT NULL, @@ -7474,7 +7474,7 @@ END; CREATE TABLE acl_object_identity ( id NUMBER(38) NOT NULL PRIMARY KEY, object_id_class NUMBER(38) NOT NULL, - object_id_identity NUMBER(38) NOT NULL, + object_id_identity NVARCHAR2(36) NOT NULL, parent_object NUMBER(38), owner_sid NUMBER(38), entries_inheriting NUMBER(1) NOT NULL CHECK (entries_inheriting in (0, 1)), diff --git a/samples/xml/contacts/src/main/java/sample/contact/DataSourcePopulator.java b/samples/xml/contacts/src/main/java/sample/contact/DataSourcePopulator.java index dce00eff5d2..559e564c2d4 100644 --- a/samples/xml/contacts/src/main/java/sample/contact/DataSourcePopulator.java +++ b/samples/xml/contacts/src/main/java/sample/contact/DataSourcePopulator.java @@ -95,11 +95,12 @@ public void afterPropertiesSet() throws Exception { template.execute("CREATE TABLE ACL_CLASS(" + "ID BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 100) NOT NULL PRIMARY KEY," + "CLASS VARCHAR_IGNORECASE(100) NOT NULL," + + "CLASS_ID_TYPE VARCHAR_IGNORECASE(100)," + "CONSTRAINT UNIQUE_UK_2 UNIQUE(CLASS));"); template.execute("CREATE TABLE ACL_OBJECT_IDENTITY(" + "ID BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 100) NOT NULL PRIMARY KEY," + "OBJECT_ID_CLASS BIGINT NOT NULL," - + "OBJECT_ID_IDENTITY BIGINT NOT NULL," + + "OBJECT_ID_IDENTITY VARCHAR_IGNORECASE(36) NOT NULL," + "PARENT_OBJECT BIGINT," + "OWNER_SID BIGINT," + "ENTRIES_INHERITING BOOLEAN NOT NULL," diff --git a/samples/xml/dms/src/main/java/sample/dms/DataSourcePopulator.java b/samples/xml/dms/src/main/java/sample/dms/DataSourcePopulator.java index 9da9b052e99..fa355d96d0c 100755 --- a/samples/xml/dms/src/main/java/sample/dms/DataSourcePopulator.java +++ b/samples/xml/dms/src/main/java/sample/dms/DataSourcePopulator.java @@ -48,8 +48,8 @@ public DataSourcePopulator(DataSource dataSource, DocumentDao documentDao) { public void afterPropertiesSet() throws Exception { // ACL tables template.execute("CREATE TABLE ACL_SID(ID BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 100) NOT NULL PRIMARY KEY,PRINCIPAL BOOLEAN NOT NULL,SID VARCHAR_IGNORECASE(100) NOT NULL,CONSTRAINT UNIQUE_UK_1 UNIQUE(SID,PRINCIPAL));"); - template.execute("CREATE TABLE ACL_CLASS(ID BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 100) NOT NULL PRIMARY KEY,CLASS VARCHAR_IGNORECASE(100) NOT NULL,CONSTRAINT UNIQUE_UK_2 UNIQUE(CLASS));"); - template.execute("CREATE TABLE ACL_OBJECT_IDENTITY(ID BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 100) NOT NULL PRIMARY KEY,OBJECT_ID_CLASS BIGINT NOT NULL,OBJECT_ID_IDENTITY BIGINT NOT NULL,PARENT_OBJECT BIGINT,OWNER_SID BIGINT,ENTRIES_INHERITING BOOLEAN NOT NULL,CONSTRAINT UNIQUE_UK_3 UNIQUE(OBJECT_ID_CLASS,OBJECT_ID_IDENTITY),CONSTRAINT FOREIGN_FK_1 FOREIGN KEY(PARENT_OBJECT)REFERENCES ACL_OBJECT_IDENTITY(ID),CONSTRAINT FOREIGN_FK_2 FOREIGN KEY(OBJECT_ID_CLASS)REFERENCES ACL_CLASS(ID),CONSTRAINT FOREIGN_FK_3 FOREIGN KEY(OWNER_SID)REFERENCES ACL_SID(ID));"); + template.execute("CREATE TABLE ACL_CLASS(ID BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 100) NOT NULL PRIMARY KEY,CLASS VARCHAR_IGNORECASE(100) NOT NULL,CLASS_ID_TYPE VARCHAR_IGNORECASE(100),CONSTRAINT UNIQUE_UK_2 UNIQUE(CLASS));"); + template.execute("CREATE TABLE ACL_OBJECT_IDENTITY(ID BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 100) NOT NULL PRIMARY KEY,OBJECT_ID_CLASS BIGINT NOT NULL,OBJECT_ID_IDENTITY VARCHAR_IGNORECASE(36) NOT NULL,PARENT_OBJECT BIGINT,OWNER_SID BIGINT,ENTRIES_INHERITING BOOLEAN NOT NULL,CONSTRAINT UNIQUE_UK_3 UNIQUE(OBJECT_ID_CLASS,OBJECT_ID_IDENTITY),CONSTRAINT FOREIGN_FK_1 FOREIGN KEY(PARENT_OBJECT)REFERENCES ACL_OBJECT_IDENTITY(ID),CONSTRAINT FOREIGN_FK_2 FOREIGN KEY(OBJECT_ID_CLASS)REFERENCES ACL_CLASS(ID),CONSTRAINT FOREIGN_FK_3 FOREIGN KEY(OWNER_SID)REFERENCES ACL_SID(ID));"); template.execute("CREATE TABLE ACL_ENTRY(ID BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 100) NOT NULL PRIMARY KEY,ACL_OBJECT_IDENTITY BIGINT NOT NULL,ACE_ORDER INT NOT NULL,SID BIGINT NOT NULL,MASK INTEGER NOT NULL,GRANTING BOOLEAN NOT NULL,AUDIT_SUCCESS BOOLEAN NOT NULL,AUDIT_FAILURE BOOLEAN NOT NULL,CONSTRAINT UNIQUE_UK_4 UNIQUE(ACL_OBJECT_IDENTITY,ACE_ORDER),CONSTRAINT FOREIGN_FK_4 FOREIGN KEY(ACL_OBJECT_IDENTITY) REFERENCES ACL_OBJECT_IDENTITY(ID),CONSTRAINT FOREIGN_FK_5 FOREIGN KEY(SID) REFERENCES ACL_SID(ID));"); // Normal authentication tables