All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.acegisecurity.acl.basic.jdbc.JdbcDaoImpl Maven / Gradle / Ivy

There is a newer version: 1.0.7
Show newest version
/* Copyright 2004, 2005, 2006 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.
 * 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.acegisecurity.acl.basic.jdbc;

import org.acegisecurity.acl.basic.AclObjectIdentity;
import org.acegisecurity.acl.basic.BasicAclDao;
import org.acegisecurity.acl.basic.BasicAclEntry;
import org.acegisecurity.acl.basic.NamedEntityObjectIdentity;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.springframework.context.ApplicationContextException;

import org.springframework.jdbc.core.SqlParameter;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
import org.springframework.jdbc.object.MappingSqlQuery;

import org.springframework.util.Assert;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;

import java.util.List;
import java.util.Vector;

import javax.sql.DataSource;


/**
 * 

* Retrieves ACL details from a JDBC location. *

* *

* A default database structure is assumed. This may be overridden by setting * the default query strings to use. If this does not provide enough * flexibility, another strategy would be to subclass this class and override * the {@link MappingSqlQuery} instance used, via the {@link * #initMappingSqlQueries()} extension point. *

*/ public class JdbcDaoImpl extends JdbcDaoSupport implements BasicAclDao { //~ Static fields/initializers ============================================= public static final String RECIPIENT_USED_FOR_INHERITENCE_MARKER = "___INHERITENCE_MARKER_ONLY___"; public static final String DEF_ACLS_BY_OBJECT_IDENTITY_QUERY = "SELECT RECIPIENT, MASK FROM acl_permission WHERE acl_object_identity = ?"; public static final String DEF_OBJECT_PROPERTIES_QUERY = "SELECT CHILD.ID, CHILD.OBJECT_IDENTITY, CHILD.ACL_CLASS, PARENT.OBJECT_IDENTITY as PARENT_OBJECT_IDENTITY FROM acl_object_identity as CHILD LEFT OUTER JOIN acl_object_identity as PARENT ON CHILD.parent_object=PARENT.id WHERE CHILD.object_identity = ?"; private static final Log logger = LogFactory.getLog(JdbcDaoImpl.class); //~ Instance fields ======================================================== protected MappingSqlQuery aclsByObjectIdentity; protected MappingSqlQuery objectProperties; private String aclsByObjectIdentityQuery; private String objectPropertiesQuery; //~ Constructors =========================================================== public JdbcDaoImpl() { aclsByObjectIdentityQuery = DEF_ACLS_BY_OBJECT_IDENTITY_QUERY; objectPropertiesQuery = DEF_OBJECT_PROPERTIES_QUERY; } //~ Methods ================================================================ /** * Responsible for covering a AclObjectIdentity to a * String that can be located in the RDBMS. * * @param aclObjectIdentity to locate * * @return the object identity as a String */ protected String convertAclObjectIdentityToString( AclObjectIdentity aclObjectIdentity) { // Ensure we can process this type of AclObjectIdentity Assert.isInstanceOf(NamedEntityObjectIdentity.class, aclObjectIdentity, "Only aclObjectIdentity of type NamedEntityObjectIdentity supported (was passed: " + aclObjectIdentity + ")"); NamedEntityObjectIdentity neoi = (NamedEntityObjectIdentity) aclObjectIdentity; // Compose the String we expect to find in the RDBMS return neoi.getClassname() + ":" + neoi.getId(); } /** * Constructs an individual BasicAclEntry from the passed * AclDetailsHolders. * *

* Guarantees to never return null (exceptions are thrown in * the event of any issues). *

* * @param propertiesInformation mandatory information about which instance * to create, the object identity, and the parent object identity * (null or empty Strings prohibited for * aclClass and aclObjectIdentity * @param aclInformation optional information about the individual ACL * record (if null only an "inheritence marker" * instance is returned; if not null, it is prohibited * to present null or an empty String for * recipient) * * @return a fully populated instance suitable for use by external objects * * @throws IllegalArgumentException if the indicated ACL class could not be * created */ private BasicAclEntry createBasicAclEntry( AclDetailsHolder propertiesInformation, AclDetailsHolder aclInformation) { BasicAclEntry entry; try { entry = (BasicAclEntry) propertiesInformation.getAclClass() .newInstance(); } catch (InstantiationException ie) { throw new IllegalArgumentException(ie.getMessage()); } catch (IllegalAccessException iae) { throw new IllegalArgumentException(iae.getMessage()); } entry.setAclObjectIdentity(propertiesInformation.getAclObjectIdentity()); entry.setAclObjectParentIdentity(propertiesInformation .getAclObjectParentIdentity()); if (aclInformation == null) { // this is an inheritence marker instance only entry.setMask(0); entry.setRecipient(RECIPIENT_USED_FOR_INHERITENCE_MARKER); } else { // this is an individual ACL entry entry.setMask(aclInformation.getMask()); entry.setRecipient(aclInformation.getRecipient()); } return entry; } /** * Returns the ACLs associated with the requested * AclObjectIdentity. * *

* The {@link BasicAclEntry}s returned by this method will have * String-based recipients. This will not be a problem if you * are using the GrantedAuthorityEffectiveAclsResolver, which * is the default configured against BasicAclProvider. *

* *

* This method will only return ACLs for requests where the * AclObjectIdentity is of type {@link * NamedEntityObjectIdentity}. Of course, you can subclass or replace this * class and support your own custom AclObjectIdentity types. *

* * @param aclObjectIdentity for which ACL information is required (cannot * be null and must be an instance of * NamedEntityObjectIdentity) * * @return the ACLs that apply (without any nulls inside the * array), or null if not found or if an incompatible * AclObjectIdentity was requested */ public BasicAclEntry[] getAcls(AclObjectIdentity aclObjectIdentity) { String aclObjectIdentityString; try { aclObjectIdentityString = convertAclObjectIdentityToString(aclObjectIdentity); } catch (IllegalArgumentException unsupported) { return null; // pursuant to contract described in JavaDocs above } // Lookup the object's main properties from the RDBMS (guaranteed no nulls) List objects = objectProperties.execute(aclObjectIdentityString); if (objects.size() == 0) { // this is an unknown object identity string return null; } // Cast to an object properties holder (there should only be one record) AclDetailsHolder propertiesInformation = (AclDetailsHolder) objects.get(0); // Lookup the object's ACLs from RDBMS (guaranteed no nulls) List acls = aclsByObjectIdentity.execute(propertiesInformation .getForeignKeyId()); if (acls.size() == 0) { // return merely an inheritence marker (as we know about the object but it has no related ACLs) return new BasicAclEntry[] {createBasicAclEntry(propertiesInformation, null)}; } else { // return the individual ACL instances AclDetailsHolder[] aclHolders = (AclDetailsHolder[]) acls.toArray(new AclDetailsHolder[] {}); List toReturnAcls = new Vector(); for (int i = 0; i < aclHolders.length; i++) { toReturnAcls.add(createBasicAclEntry(propertiesInformation, aclHolders[i])); } return (BasicAclEntry[]) toReturnAcls.toArray(new BasicAclEntry[] {}); } } public MappingSqlQuery getAclsByObjectIdentity() { return aclsByObjectIdentity; } public String getAclsByObjectIdentityQuery() { return aclsByObjectIdentityQuery; } public String getObjectPropertiesQuery() { return objectPropertiesQuery; } protected void initDao() throws ApplicationContextException { initMappingSqlQueries(); } /** * Extension point to allow other MappingSqlQuery objects to be substituted * in a subclass */ protected void initMappingSqlQueries() { setAclsByObjectIdentity(new AclsByObjectIdentityMapping(getDataSource())); setObjectProperties(new ObjectPropertiesMapping(getDataSource())); } public void setAclsByObjectIdentity( MappingSqlQuery aclsByObjectIdentityQuery) { this.aclsByObjectIdentity = aclsByObjectIdentityQuery; } /** * Allows the default query string used to retrieve ACLs based on object * identity to be overriden, if default table or column names need to be * changed. The default query is {@link * #DEF_ACLS_BY_OBJECT_IDENTITY_QUERY}; when modifying this query, ensure * that all returned columns are mapped back to the same column names as * in the default query. * * @param queryString The query string to set */ public void setAclsByObjectIdentityQuery(String queryString) { aclsByObjectIdentityQuery = queryString; } public void setObjectProperties(MappingSqlQuery objectPropertiesQuery) { this.objectProperties = objectPropertiesQuery; } public void setObjectPropertiesQuery(String queryString) { objectPropertiesQuery = queryString; } //~ Inner Classes ========================================================== /** * Used to hold details of a domain object instance's properties, or an * individual ACL entry. * *

* Not all properties will be set. The actual properties set will depend on * which MappingSqlQuery creates the object. *

* *

* Does not enforce nulls or empty Strings as * this is performed by the MappingSqlQuery objects (or * preferably the backend RDBMS via schema constraints). *

*/ protected final class AclDetailsHolder { private AclObjectIdentity aclObjectIdentity; private AclObjectIdentity aclObjectParentIdentity; private Class aclClass; private Object recipient; private int mask; private long foreignKeyId; /** * Record details of an individual ACL entry (usually from the * ACL_PERMISSION table) * * @param recipient the recipient * @param mask the integer to be masked */ public AclDetailsHolder(Object recipient, int mask) { this.recipient = recipient; this.mask = mask; } /** * Record details of a domain object instance's properties (usually * from the ACL_OBJECT_IDENTITY table) * * @param foreignKeyId used by the * AclsByObjectIdentityMapping to locate the * individual ACL entries * @param aclObjectIdentity the object identity of the domain object * instance * @param aclObjectParentIdentity the object identity of the domain * object instance's parent * @param aclClass the class of which a new instance which should be * created for each individual ACL entry (or an inheritence * "holder" class if there are no ACL entries) */ public AclDetailsHolder(long foreignKeyId, AclObjectIdentity aclObjectIdentity, AclObjectIdentity aclObjectParentIdentity, Class aclClass) { this.foreignKeyId = foreignKeyId; this.aclObjectIdentity = aclObjectIdentity; this.aclObjectParentIdentity = aclObjectParentIdentity; this.aclClass = aclClass; } public Class getAclClass() { return aclClass; } public AclObjectIdentity getAclObjectIdentity() { return aclObjectIdentity; } public AclObjectIdentity getAclObjectParentIdentity() { return aclObjectParentIdentity; } public long getForeignKeyId() { return foreignKeyId; } public int getMask() { return mask; } public Object getRecipient() { return recipient; } } /** * Query object to look up individual ACL entries. * *

* Returns the generic AclDetailsHolder object. *

* *

* Guarantees to never return null (exceptions are thrown in * the event of any issues). *

* *

* The executed SQL requires the following information be made available * from the indicated placeholders: 1. RECIPIENT, 2. MASK. *

*/ protected class AclsByObjectIdentityMapping extends MappingSqlQuery { protected AclsByObjectIdentityMapping(DataSource ds) { super(ds, aclsByObjectIdentityQuery); declareParameter(new SqlParameter(Types.BIGINT)); compile(); } protected Object mapRow(ResultSet rs, int rownum) throws SQLException { String recipient = rs.getString(1); int mask = rs.getInt(2); Assert.hasText(recipient, "recipient required"); return new AclDetailsHolder(recipient, mask); } } /** * Query object to look up properties for an object identity. * *

* Returns the generic AclDetailsHolder object. *

* *

* Guarantees to never return null (exceptions are thrown in * the event of any issues). *

* *

* The executed SQL requires the following information be made available * from the indicated placeholders: 1. ID, 2. OBJECT_IDENTITY, 3. * ACL_CLASS and 4. PARENT_OBJECT_IDENTITY. *

*/ protected class ObjectPropertiesMapping extends MappingSqlQuery { protected ObjectPropertiesMapping(DataSource ds) { super(ds, objectPropertiesQuery); declareParameter(new SqlParameter(Types.VARCHAR)); compile(); } private AclObjectIdentity buildIdentity(String identity) { if (identity == null) { // Must be an empty parent, so return null return null; } int delim = identity.lastIndexOf(":"); String classname = identity.substring(0, delim); String id = identity.substring(delim + 1); return new NamedEntityObjectIdentity(classname, id); } protected Object mapRow(ResultSet rs, int rownum) throws SQLException { long id = rs.getLong(1); // required String objectIdentity = rs.getString(2); // required String aclClass = rs.getString(3); // required String parentObjectIdentity = rs.getString(4); // optional Assert.hasText(objectIdentity, "required DEF_OBJECT_PROPERTIES_QUERY value (objectIdentity) returned null or empty"); Assert.hasText(aclClass, "required DEF_OBJECT_PROPERTIES_QUERY value (aclClass) returned null or empty"); Class aclClazz; try { aclClazz = this.getClass().getClassLoader().loadClass(aclClass); } catch (ClassNotFoundException cnf) { throw new IllegalArgumentException(cnf.getMessage()); } return new AclDetailsHolder(id, buildIdentity(objectIdentity), buildIdentity(parentObjectIdentity), aclClazz); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy