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 }