View Javadoc

    1   /* Copyright 2004, 2005 Acegi Technology Pty Limited
    2    *
    3    * Licensed under the Apache License, Version 2.0 (the "License");
    4    * you may not use this file except in compliance with the License.
    5    * You may obtain a copy of the License at
    6    *
    7    *     http://www.apache.org/licenses/LICENSE-2.0
    8    *
    9    * Unless required by applicable law or agreed to in writing, software
    10   * distributed under the License is distributed on an "AS IS" BASIS,
    11   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12   * See the License for the specific language governing permissions and
    13   * limitations under the License.
    14   */
    15
    16  package org.acegisecurity.acl.basic.jdbc;
    17
    18  import org.acegisecurity.acl.basic.AclObjectIdentity;
    19  import org.acegisecurity.acl.basic.BasicAclEntry;
    20  import org.acegisecurity.acl.basic.BasicAclExtendedDao;
    21
    22  import org.apache.commons.logging.Log;
    23  import org.apache.commons.logging.LogFactory;
    24
    25  import org.springframework.context.ApplicationContextException;
    26
    27  import org.springframework.dao.DataAccessException;
    28  import org.springframework.dao.DataIntegrityViolationException;
    29  import org.springframework.dao.DataRetrievalFailureException;
    30
    31  import org.springframework.jdbc.core.SqlParameter;
    32  import org.springframework.jdbc.object.MappingSqlQuery;
    33  import org.springframework.jdbc.object.SqlUpdate;
    34
    35  import java.sql.ResultSet;
    36  import java.sql.SQLException;
    37  import java.sql.Types;
    38
    39  import java.util.Iterator;
    40  import java.util.List;
    41
    42  import javax.sql.DataSource;
    43
    44
    45  /***
    46   * <p>
    47   * Extension of the base {@link JdbcDaoImpl}, which implements {@link
    48   * BasicAclExtendedDao}.
    49   * </p>
    50   *
    51   * <p>
    52   * A default database structure is assumed. This may be overridden by setting
    53   * the default query strings to use.
    54   * </p>
    55   *
    56   * <p>
    57   * This implementation works with <code>String</code> based recipients and
    58   * {@link org.acegisecurity.acl.basic.NamedEntityObjectIdentity} only. The
    59   * latter can be changed by overriding {@link
    60   * #convertAclObjectIdentityToString(AclObjectIdentity)}.
    61   * </p>
    62   *
    63   * @author Ben Alex
    64   * @version $Id: JdbcExtendedDaoImpl.java,v 1.7 2005/11/17 00:56:10 benalex Exp $
    65   */
    66  public class JdbcExtendedDaoImpl extends JdbcDaoImpl
    67      implements BasicAclExtendedDao {
    68      //~ Static fields/initializers =============================================
    69
    70      private static final Log logger = LogFactory.getLog(JdbcExtendedDaoImpl.class);
    71      public static final String DEF_ACL_OBJECT_IDENTITY_DELETE_STATEMENT = "DELETE FROM acl_object_identity WHERE id = ?";
    72      public static final String DEF_ACL_OBJECT_IDENTITY_INSERT_STATEMENT = "INSERT INTO acl_object_identity (object_identity, parent_object, acl_class) VALUES (?, ?, ?)";
    73      public static final String DEF_ACL_PERMISSION_DELETE_STATEMENT = "DELETE FROM acl_permission WHERE acl_object_identity = ? AND recipient = ?";
    74      public static final String DEF_ACL_PERMISSION_INSERT_STATEMENT = "INSERT INTO acl_permission (acl_object_identity, recipient, mask) VALUES (?, ?, ?)";
    75      public static final String DEF_ACL_PERMISSION_UPDATE_STATEMENT = "UPDATE acl_permission SET mask = ? WHERE id = ?";
    76      public static final String DEF_LOOKUP_PERMISSION_ID_QUERY = "SELECT id FROM acl_permission WHERE acl_object_identity = ? AND recipient = ?";
    77
    78      //~ Instance fields ========================================================
    79
    80      private AclObjectIdentityDelete aclObjectIdentityDelete;
    81      private AclObjectIdentityInsert aclObjectIdentityInsert;
    82      private AclPermissionDelete aclPermissionDelete;
    83      private AclPermissionInsert aclPermissionInsert;
    84      private AclPermissionUpdate aclPermissionUpdate;
    85      private MappingSqlQuery lookupPermissionIdMapping;
    86      private String aclObjectIdentityDeleteStatement;
    87      private String aclObjectIdentityInsertStatement;
    88      private String aclPermissionDeleteStatement;
    89      private String aclPermissionInsertStatement;
    90      private String aclPermissionUpdateStatement;
    91      private String lookupPermissionIdQuery;
    92
    93      //~ Constructors ===========================================================
    94
    95      public JdbcExtendedDaoImpl() {
    96          aclObjectIdentityDeleteStatement = DEF_ACL_OBJECT_IDENTITY_DELETE_STATEMENT;
    97          aclObjectIdentityInsertStatement = DEF_ACL_OBJECT_IDENTITY_INSERT_STATEMENT;
    98          aclPermissionDeleteStatement = DEF_ACL_PERMISSION_DELETE_STATEMENT;
    99          aclPermissionInsertStatement = DEF_ACL_PERMISSION_INSERT_STATEMENT;
    100         aclPermissionUpdateStatement = DEF_ACL_PERMISSION_UPDATE_STATEMENT;
    101         lookupPermissionIdQuery = DEF_LOOKUP_PERMISSION_ID_QUERY;
    102     }
    103
    104     //~ Methods ================================================================
    105
    106     public void setAclObjectIdentityDelete(
    107         AclObjectIdentityDelete aclObjectIdentityDelete) {
    108         this.aclObjectIdentityDelete = aclObjectIdentityDelete;
    109     }
    110
    111     public AclObjectIdentityDelete getAclObjectIdentityDelete() {
    112         return aclObjectIdentityDelete;
    113     }
    114
    115     public void setAclObjectIdentityDeleteStatement(
    116         String aclObjectIdentityDeleteStatement) {
    117         this.aclObjectIdentityDeleteStatement = aclObjectIdentityDeleteStatement;
    118     }
    119
    120     public String getAclObjectIdentityDeleteStatement() {
    121         return aclObjectIdentityDeleteStatement;
    122     }
    123
    124     public void setAclObjectIdentityInsert(
    125         AclObjectIdentityInsert aclObjectIdentityInsert) {
    126         this.aclObjectIdentityInsert = aclObjectIdentityInsert;
    127     }
    128
    129     public AclObjectIdentityInsert getAclObjectIdentityInsert() {
    130         return aclObjectIdentityInsert;
    131     }
    132
    133     public void setAclObjectIdentityInsertStatement(
    134         String aclObjectIdentityInsertStatement) {
    135         this.aclObjectIdentityInsertStatement = aclObjectIdentityInsertStatement;
    136     }
    137
    138     public String getAclObjectIdentityInsertStatement() {
    139         return aclObjectIdentityInsertStatement;
    140     }
    141
    142     public void setAclPermissionDelete(AclPermissionDelete aclPermissionDelete) {
    143         this.aclPermissionDelete = aclPermissionDelete;
    144     }
    145
    146     public AclPermissionDelete getAclPermissionDelete() {
    147         return aclPermissionDelete;
    148     }
    149
    150     public void setAclPermissionDeleteStatement(
    151         String aclPermissionDeleteStatement) {
    152         this.aclPermissionDeleteStatement = aclPermissionDeleteStatement;
    153     }
    154
    155     public String getAclPermissionDeleteStatement() {
    156         return aclPermissionDeleteStatement;
    157     }
    158
    159     public void setAclPermissionInsert(AclPermissionInsert aclPermissionInsert) {
    160         this.aclPermissionInsert = aclPermissionInsert;
    161     }
    162
    163     public AclPermissionInsert getAclPermissionInsert() {
    164         return aclPermissionInsert;
    165     }
    166
    167     public void setAclPermissionInsertStatement(
    168         String aclPermissionInsertStatement) {
    169         this.aclPermissionInsertStatement = aclPermissionInsertStatement;
    170     }
    171
    172     public String getAclPermissionInsertStatement() {
    173         return aclPermissionInsertStatement;
    174     }
    175
    176     public void setAclPermissionUpdate(AclPermissionUpdate aclPermissionUpdate) {
    177         this.aclPermissionUpdate = aclPermissionUpdate;
    178     }
    179
    180     public AclPermissionUpdate getAclPermissionUpdate() {
    181         return aclPermissionUpdate;
    182     }
    183
    184     public void setAclPermissionUpdateStatement(
    185         String aclPermissionUpdateStatement) {
    186         this.aclPermissionUpdateStatement = aclPermissionUpdateStatement;
    187     }
    188
    189     public String getAclPermissionUpdateStatement() {
    190         return aclPermissionUpdateStatement;
    191     }
    192
    193     public void setLookupPermissionIdMapping(
    194         MappingSqlQuery lookupPermissionIdMapping) {
    195         this.lookupPermissionIdMapping = lookupPermissionIdMapping;
    196     }
    197
    198     public MappingSqlQuery getLookupPermissionIdMapping() {
    199         return lookupPermissionIdMapping;
    200     }
    201
    202     public void setLookupPermissionIdQuery(String lookupPermissionIdQuery) {
    203         this.lookupPermissionIdQuery = lookupPermissionIdQuery;
    204     }
    205
    206     public String getLookupPermissionIdQuery() {
    207         return lookupPermissionIdQuery;
    208     }
    209
    210     public void changeMask(AclObjectIdentity aclObjectIdentity,
    211         Object recipient, Integer newMask) throws DataAccessException {
    212         // Retrieve acl_object_identity record details
    213         AclDetailsHolder aclDetailsHolder = lookupAclDetailsHolder(aclObjectIdentity);
    214
    215         // Retrieve applicable acl_permission.id
    216         long permissionId = lookupPermissionId(aclDetailsHolder.getForeignKeyId(),
    217                 recipient.toString());
    218
    219         if (permissionId == -1) {
    220             throw new DataRetrievalFailureException(
    221                 "Could not locate existing acl_permission for aclObjectIdentity: "
    222                 + aclObjectIdentity + ", recipient: " + recipient.toString());
    223         }
    224
    225         // Change permission
    226         aclPermissionUpdate.update(new Long(permissionId), newMask);
    227     }
    228
    229     public void create(BasicAclEntry basicAclEntry) throws DataAccessException {
    230         // Create acl_object_identity record if required
    231         createAclObjectIdentityIfRequired(basicAclEntry);
    232
    233         // Only continue if a recipient is specifed (null recipient indicates
    234         // just wanted to ensure the acl_object_identity was created)
    235         if (basicAclEntry.getRecipient() == null) {
    236             return;
    237         }
    238
    239         // Retrieve acl_object_identity record details
    240         AclDetailsHolder aclDetailsHolder = lookupAclDetailsHolder(basicAclEntry
    241                 .getAclObjectIdentity());
    242
    243         // Ensure there isn't an existing record for this recipient
    244         if (lookupPermissionId(aclDetailsHolder.getForeignKeyId(),
    245                 basicAclEntry.getRecipient()) != -1) {
    246             throw new DataIntegrityViolationException(
    247                 "This recipient already exists for this aclObjectIdentity");
    248         }
    249
    250         // Create acl_permission
    251         aclPermissionInsert.insert(new Long(aclDetailsHolder.getForeignKeyId()),
    252             basicAclEntry.getRecipient().toString(),
    253             new Integer(basicAclEntry.getMask()));
    254     }
    255
    256     public void delete(AclObjectIdentity aclObjectIdentity)
    257         throws DataAccessException {
    258         // Retrieve acl_object_identity record details
    259         AclDetailsHolder aclDetailsHolder = lookupAclDetailsHolder(aclObjectIdentity);
    260
    261         // Retrieve all acl_permissions applying to this acl_object_identity
    262         Iterator acls = aclsByObjectIdentity.execute(aclDetailsHolder
    263                 .getForeignKeyId()).iterator();
    264
    265         // Delete all existing acl_permissions applying to this acl_object_identity
    266         while (acls.hasNext()) {
    267             AclDetailsHolder permission = (AclDetailsHolder) acls.next();
    268             delete(aclObjectIdentity, permission.getRecipient());
    269         }
    270
    271         // Delete acl_object_identity
    272         aclObjectIdentityDelete.delete(new Long(
    273                 aclDetailsHolder.getForeignKeyId()));
    274     }
    275
    276     public void delete(AclObjectIdentity aclObjectIdentity, Object recipient)
    277         throws DataAccessException {
    278         // Retrieve acl_object_identity record details
    279         AclDetailsHolder aclDetailsHolder = lookupAclDetailsHolder(aclObjectIdentity);
    280
    281         // Delete acl_permission
    282         aclPermissionDelete.delete(new Long(aclDetailsHolder.getForeignKeyId()),
    283             recipient.toString());
    284     }
    285
    286     protected void initDao() throws ApplicationContextException {
    287         super.initDao();
    288         lookupPermissionIdMapping = new LookupPermissionIdMapping(getDataSource());
    289         aclPermissionInsert = new AclPermissionInsert(getDataSource());
    290         aclObjectIdentityInsert = new AclObjectIdentityInsert(getDataSource());
    291         aclPermissionDelete = new AclPermissionDelete(getDataSource());
    292         aclObjectIdentityDelete = new AclObjectIdentityDelete(getDataSource());
    293         aclPermissionUpdate = new AclPermissionUpdate(getDataSource());
    294     }
    295
    296     /***
    297      * Convenience method that creates an acl_object_identity record if
    298      * required.
    299      *
    300      * @param basicAclEntry containing the <code>AclObjectIdentity</code> to
    301      *        create
    302      *
    303      * @throws DataAccessException
    304      */
    305     private void createAclObjectIdentityIfRequired(BasicAclEntry basicAclEntry)
    306         throws DataAccessException {
    307         String aclObjectIdentityString = convertAclObjectIdentityToString(basicAclEntry
    308                 .getAclObjectIdentity());
    309
    310         // Lookup the object's main properties from the RDBMS (guaranteed no nulls)
    311         List objects = objectProperties.execute(aclObjectIdentityString);
    312
    313         if (objects.size() == 0) {
    314             if (basicAclEntry.getAclObjectParentIdentity() != null) {
    315                 AclDetailsHolder parentDetails = lookupAclDetailsHolder(basicAclEntry
    316                         .getAclObjectParentIdentity());
    317
    318                 // Must create the acl_object_identity record
    319                 aclObjectIdentityInsert.insert(aclObjectIdentityString,
    320                     new Long(parentDetails.getForeignKeyId()),
    321                     basicAclEntry.getClass().getName());
    322             } else {
    323                 // Must create the acl_object_identity record
    324                 aclObjectIdentityInsert.insert(aclObjectIdentityString, null,
    325                     basicAclEntry.getClass().getName());
    326             }
    327         }
    328     }
    329
    330     /***
    331      * Convenience method that obtains a given acl_object_identity record.
    332      *
    333      * @param aclObjectIdentity to lookup
    334      *
    335      * @return details of the record
    336      *
    337      * @throws DataRetrievalFailureException if record could not be found
    338      */
    339     private AclDetailsHolder lookupAclDetailsHolder(
    340         AclObjectIdentity aclObjectIdentity)
    341         throws DataRetrievalFailureException {
    342         String aclObjectIdentityString = convertAclObjectIdentityToString(aclObjectIdentity);
    343
    344         // Lookup the object's main properties from the RDBMS (guaranteed no nulls)
    345         List objects = objectProperties.execute(aclObjectIdentityString);
    346
    347         if (objects.size() == 0) {
    348             throw new DataRetrievalFailureException(
    349                 "aclObjectIdentity not found: " + aclObjectIdentityString);
    350         }
    351
    352         // Should only be one record
    353         return (AclDetailsHolder) objects.get(0);
    354     }
    355
    356     /***
    357      * Convenience method to lookup the acl_permission applying to a given
    358      * acl_object_identity.id and acl_permission.recipient.
    359      *
    360      * @param aclObjectIdentityId to locate
    361      * @param recipient to locate
    362      *
    363      * @return the acl_permission.id of the record, or -1 if not found
    364      *
    365      * @throws DataAccessException DOCUMENT ME!
    366      */
    367     private long lookupPermissionId(long aclObjectIdentityId, Object recipient)
    368         throws DataAccessException {
    369         List list = lookupPermissionIdMapping.execute(new Object[] {new Long(
    370                         aclObjectIdentityId), recipient});
    371
    372         if (list.size() == 0) {
    373             return -1;
    374         }
    375
    376         return ((Long) list.get(0)).longValue();
    377     }
    378
    379     //~ Inner Classes ==========================================================
    380
    381     protected class AclObjectIdentityDelete extends SqlUpdate {
    382         protected AclObjectIdentityDelete(DataSource ds) {
    383             super(ds, aclObjectIdentityDeleteStatement);
    384             declareParameter(new SqlParameter(Types.BIGINT));
    385             compile();
    386         }
    387
    388         protected void delete(Long aclObjectIdentity)
    389             throws DataAccessException {
    390             super.update(aclObjectIdentity.intValue());
    391         }
    392     }
    393
    394     protected class AclObjectIdentityInsert extends SqlUpdate {
    395         protected AclObjectIdentityInsert(DataSource ds) {
    396             super(ds, aclObjectIdentityInsertStatement);
    397             declareParameter(new SqlParameter(Types.VARCHAR));
    398             declareParameter(new SqlParameter(Types.BIGINT));
    399             declareParameter(new SqlParameter(Types.VARCHAR));
    400             compile();
    401         }
    402
    403         protected void insert(String objectIdentity,
    404             Long parentAclObjectIdentity, String aclClass)
    405             throws DataAccessException {
    406             Object[] objs = new Object[] {objectIdentity, parentAclObjectIdentity, aclClass};
    407             super.update(objs);
    408         }
    409     }
    410
    411     protected class AclPermissionDelete extends SqlUpdate {
    412         protected AclPermissionDelete(DataSource ds) {
    413             super(ds, aclPermissionDeleteStatement);
    414             declareParameter(new SqlParameter(Types.BIGINT));
    415             declareParameter(new SqlParameter(Types.VARCHAR));
    416             compile();
    417         }
    418
    419         protected void delete(Long aclObjectIdentity, String recipient)
    420             throws DataAccessException {
    421             super.update(new Object[] {aclObjectIdentity, recipient});
    422         }
    423     }
    424
    425     protected class AclPermissionInsert extends SqlUpdate {
    426         protected AclPermissionInsert(DataSource ds) {
    427             super(ds, aclPermissionInsertStatement);
    428             declareParameter(new SqlParameter(Types.BIGINT));
    429             declareParameter(new SqlParameter(Types.VARCHAR));
    430             declareParameter(new SqlParameter(Types.INTEGER));
    431             compile();
    432         }
    433
    434         protected void insert(Long aclObjectIdentity, String recipient,
    435             Integer mask) throws DataAccessException {
    436             Object[] objs = new Object[] {aclObjectIdentity, recipient, mask};
    437             super.update(objs);
    438         }
    439     }
    440
    441     protected class AclPermissionUpdate extends SqlUpdate {
    442         protected AclPermissionUpdate(DataSource ds) {
    443             super(ds, aclPermissionUpdateStatement);
    444             declareParameter(new SqlParameter(Types.BIGINT));
    445             declareParameter(new SqlParameter(Types.INTEGER));
    446             compile();
    447         }
    448
    449         protected void update(Long aclPermissionId, Integer newMask)
    450             throws DataAccessException {
    451             super.update(newMask.intValue(), aclPermissionId.intValue());
    452         }
    453     }
    454
    455     protected class LookupPermissionIdMapping extends MappingSqlQuery {
    456         protected LookupPermissionIdMapping(DataSource ds) {
    457             super(ds, lookupPermissionIdQuery);
    458             declareParameter(new SqlParameter(Types.BIGINT));
    459             declareParameter(new SqlParameter(Types.VARCHAR));
    460             compile();
    461         }
    462
    463         protected Object mapRow(ResultSet rs, int rownum)
    464             throws SQLException {
    465             return new Long(rs.getLong(1));
    466         }
    467     }
    468 }