001: /* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
002: *
003: * Licensed under the Apache License, Version 2.0 (the "License");
004: * you may not use this file except in compliance with the License.
005: * You may obtain a copy of the License at
006: *
007: * http://www.apache.org/licenses/LICENSE-2.0
008: *
009: * Unless required by applicable law or agreed to in writing, software
010: * distributed under the License is distributed on an "AS IS" BASIS,
011: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012: * See the License for the specific language governing permissions and
013: * limitations under the License.
014: */
015:
016: package org.acegisecurity.acl.basic.jdbc;
017:
018: import org.acegisecurity.acl.basic.AclObjectIdentity;
019: import org.acegisecurity.acl.basic.BasicAclEntry;
020: import org.acegisecurity.acl.basic.BasicAclEntryCache;
021: import org.acegisecurity.acl.basic.BasicAclExtendedDao;
022: import org.acegisecurity.acl.basic.cache.NullAclEntryCache;
023:
024: import org.apache.commons.logging.Log;
025: import org.apache.commons.logging.LogFactory;
026:
027: import org.springframework.context.ApplicationContextException;
028:
029: import org.springframework.dao.DataAccessException;
030: import org.springframework.dao.DataIntegrityViolationException;
031: import org.springframework.dao.DataRetrievalFailureException;
032:
033: import org.springframework.jdbc.core.SqlParameter;
034: import org.springframework.jdbc.object.MappingSqlQuery;
035: import org.springframework.jdbc.object.SqlUpdate;
036:
037: import org.springframework.util.Assert;
038:
039: import java.sql.ResultSet;
040: import java.sql.SQLException;
041: import java.sql.Types;
042:
043: import java.util.Iterator;
044: import java.util.List;
045:
046: import javax.sql.DataSource;
047:
048: /**
049: * <p>Extension of the base {@link JdbcDaoImpl}, which implements {@link BasicAclExtendedDao}.</p>
050: * <p>A default database structure is assumed. This may be overridden by setting the default query strings to use.</p>
051: * <p>If you are using a cache with <code>BasicAclProvider</code>, you should specify that cache via {@link
052: * #setBasicAclEntryCache(BasicAclEntryCache)}. This will cause cache evictions (removals) to take place whenever a
053: * DAO mutator method is called.</p>
054: * <p>This implementation works with <code>String</code> based recipients and {@link
055: * org.acegisecurity.acl.basic.NamedEntityObjectIdentity} only. The latter can be changed by overriding {@link
056: * #convertAclObjectIdentityToString(AclObjectIdentity)}.</p>
057: *
058: * @author Ben Alex
059: * @version $Id: JdbcExtendedDaoImpl.java 1784 2007-02-24 21:00:24Z luke_t $
060: */
061: public class JdbcExtendedDaoImpl extends JdbcDaoImpl implements
062: BasicAclExtendedDao {
063: //~ Static fields/initializers =====================================================================================
064:
065: private static final Log logger = LogFactory
066: .getLog(JdbcExtendedDaoImpl.class);
067: public static final String DEF_ACL_OBJECT_IDENTITY_DELETE_STATEMENT = "DELETE FROM acl_object_identity WHERE id = ?";
068: public static final String DEF_ACL_OBJECT_IDENTITY_INSERT_STATEMENT = "INSERT INTO acl_object_identity (object_identity, parent_object, acl_class) VALUES (?, ?, ?)";
069: public static final String DEF_ACL_PERMISSION_DELETE_STATEMENT = "DELETE FROM acl_permission WHERE acl_object_identity = ? AND recipient = ?";
070: public static final String DEF_ACL_PERMISSION_INSERT_STATEMENT = "INSERT INTO acl_permission (acl_object_identity, recipient, mask) VALUES (?, ?, ?)";
071: public static final String DEF_ACL_PERMISSION_UPDATE_STATEMENT = "UPDATE acl_permission SET mask = ? WHERE id = ?";
072: public static final String DEF_LOOKUP_PERMISSION_ID_QUERY = "SELECT id FROM acl_permission WHERE acl_object_identity = ? AND recipient = ?";
073:
074: //~ Instance fields ================================================================================================
075:
076: private AclObjectIdentityDelete aclObjectIdentityDelete;
077: private AclObjectIdentityInsert aclObjectIdentityInsert;
078: private AclPermissionDelete aclPermissionDelete;
079: private AclPermissionInsert aclPermissionInsert;
080: private AclPermissionUpdate aclPermissionUpdate;
081: private BasicAclEntryCache basicAclEntryCache = new NullAclEntryCache();
082: private MappingSqlQuery lookupPermissionIdMapping;
083: private String aclObjectIdentityDeleteStatement;
084: private String aclObjectIdentityInsertStatement;
085: private String aclPermissionDeleteStatement;
086: private String aclPermissionInsertStatement;
087: private String aclPermissionUpdateStatement;
088: private String lookupPermissionIdQuery;
089:
090: //~ Constructors ===================================================================================================
091:
092: public JdbcExtendedDaoImpl() {
093: aclObjectIdentityDeleteStatement = DEF_ACL_OBJECT_IDENTITY_DELETE_STATEMENT;
094: aclObjectIdentityInsertStatement = DEF_ACL_OBJECT_IDENTITY_INSERT_STATEMENT;
095: aclPermissionDeleteStatement = DEF_ACL_PERMISSION_DELETE_STATEMENT;
096: aclPermissionInsertStatement = DEF_ACL_PERMISSION_INSERT_STATEMENT;
097: aclPermissionUpdateStatement = DEF_ACL_PERMISSION_UPDATE_STATEMENT;
098: lookupPermissionIdQuery = DEF_LOOKUP_PERMISSION_ID_QUERY;
099: }
100:
101: //~ Methods ========================================================================================================
102:
103: public void changeMask(AclObjectIdentity aclObjectIdentity,
104: Object recipient, Integer newMask)
105: throws DataAccessException {
106: basicAclEntryCache.removeEntriesFromCache(aclObjectIdentity);
107:
108: // Retrieve acl_object_identity record details
109: AclDetailsHolder aclDetailsHolder = lookupAclDetailsHolder(aclObjectIdentity);
110:
111: // Retrieve applicable acl_permission.id
112: long permissionId = lookupPermissionId(aclDetailsHolder
113: .getForeignKeyId(), recipient.toString());
114:
115: if (permissionId == -1) {
116: throw new DataRetrievalFailureException(
117: "Could not locate existing acl_permission for aclObjectIdentity: "
118: + aclObjectIdentity + ", recipient: "
119: + recipient.toString());
120: }
121:
122: // Change permission
123: aclPermissionUpdate.update(new Long(permissionId), newMask);
124: }
125:
126: public void create(BasicAclEntry basicAclEntry)
127: throws DataAccessException {
128: // Create acl_object_identity record if required
129: createAclObjectIdentityIfRequired(basicAclEntry);
130:
131: // Only continue if a recipient is specifed (null recipient indicates
132: // just wanted to ensure the acl_object_identity was created)
133: if (basicAclEntry.getRecipient() == null) {
134: return;
135: }
136:
137: // Retrieve acl_object_identity record details
138: AclDetailsHolder aclDetailsHolder = lookupAclDetailsHolder(basicAclEntry
139: .getAclObjectIdentity());
140:
141: // Ensure there isn't an existing record for this recipient
142: long permissionId = lookupPermissionId(aclDetailsHolder
143: .getForeignKeyId(), basicAclEntry.getRecipient());
144:
145: if (permissionId != -1) {
146: throw new DataIntegrityViolationException("Recipient '"
147: + basicAclEntry.getRecipient()
148: + "' already exists for aclObjectIdentity ID "
149: + aclDetailsHolder.getForeignKeyId()
150: + " (permission ID " + ")");
151: }
152:
153: // Create acl_permission
154: aclPermissionInsert.insert(new Long(aclDetailsHolder
155: .getForeignKeyId()), basicAclEntry.getRecipient()
156: .toString(), new Integer(basicAclEntry.getMask()));
157: }
158:
159: /**
160: * Convenience method that creates an acl_object_identity record if required.
161: *
162: * @param basicAclEntry containing the <code>AclObjectIdentity</code> to create
163: *
164: * @throws DataAccessException
165: */
166: private void createAclObjectIdentityIfRequired(
167: BasicAclEntry basicAclEntry) throws DataAccessException {
168: basicAclEntryCache.removeEntriesFromCache(basicAclEntry
169: .getAclObjectIdentity());
170:
171: String aclObjectIdentityString = convertAclObjectIdentityToString(basicAclEntry
172: .getAclObjectIdentity());
173:
174: // Lookup the object's main properties from the RDBMS (guaranteed no nulls)
175: List objects = objectProperties
176: .execute(aclObjectIdentityString);
177:
178: if (objects.size() == 0) {
179: if (basicAclEntry.getAclObjectParentIdentity() != null) {
180: AclDetailsHolder parentDetails = lookupAclDetailsHolder(basicAclEntry
181: .getAclObjectParentIdentity());
182:
183: // Must create the acl_object_identity record
184: aclObjectIdentityInsert.insert(aclObjectIdentityString,
185: new Long(parentDetails.getForeignKeyId()),
186: basicAclEntry.getClass().getName());
187: } else {
188: // Must create the acl_object_identity record
189: aclObjectIdentityInsert.insert(aclObjectIdentityString,
190: null, basicAclEntry.getClass().getName());
191: }
192: }
193: }
194:
195: public void delete(AclObjectIdentity aclObjectIdentity)
196: throws DataAccessException {
197: basicAclEntryCache.removeEntriesFromCache(aclObjectIdentity);
198:
199: // Retrieve acl_object_identity record details
200: AclDetailsHolder aclDetailsHolder = lookupAclDetailsHolder(aclObjectIdentity);
201:
202: // Retrieve all acl_permissions applying to this acl_object_identity
203: Iterator acls = aclsByObjectIdentity.execute(
204: aclDetailsHolder.getForeignKeyId()).iterator();
205:
206: // Delete all existing acl_permissions applying to this acl_object_identity
207: while (acls.hasNext()) {
208: AclDetailsHolder permission = (AclDetailsHolder) acls
209: .next();
210: delete(aclObjectIdentity, permission.getRecipient());
211: }
212:
213: // Delete acl_object_identity
214: aclObjectIdentityDelete.delete(new Long(aclDetailsHolder
215: .getForeignKeyId()));
216: }
217:
218: public void delete(AclObjectIdentity aclObjectIdentity,
219: Object recipient) throws DataAccessException {
220: basicAclEntryCache.removeEntriesFromCache(aclObjectIdentity);
221:
222: // Retrieve acl_object_identity record details
223: AclDetailsHolder aclDetailsHolder = lookupAclDetailsHolder(aclObjectIdentity);
224:
225: // Delete acl_permission
226: aclPermissionDelete.delete(new Long(aclDetailsHolder
227: .getForeignKeyId()), recipient.toString());
228: }
229:
230: public AclObjectIdentityDelete getAclObjectIdentityDelete() {
231: return aclObjectIdentityDelete;
232: }
233:
234: public String getAclObjectIdentityDeleteStatement() {
235: return aclObjectIdentityDeleteStatement;
236: }
237:
238: public AclObjectIdentityInsert getAclObjectIdentityInsert() {
239: return aclObjectIdentityInsert;
240: }
241:
242: public String getAclObjectIdentityInsertStatement() {
243: return aclObjectIdentityInsertStatement;
244: }
245:
246: public AclPermissionDelete getAclPermissionDelete() {
247: return aclPermissionDelete;
248: }
249:
250: public String getAclPermissionDeleteStatement() {
251: return aclPermissionDeleteStatement;
252: }
253:
254: public AclPermissionInsert getAclPermissionInsert() {
255: return aclPermissionInsert;
256: }
257:
258: public String getAclPermissionInsertStatement() {
259: return aclPermissionInsertStatement;
260: }
261:
262: public AclPermissionUpdate getAclPermissionUpdate() {
263: return aclPermissionUpdate;
264: }
265:
266: public String getAclPermissionUpdateStatement() {
267: return aclPermissionUpdateStatement;
268: }
269:
270: public BasicAclEntryCache getBasicAclEntryCache() {
271: return basicAclEntryCache;
272: }
273:
274: public MappingSqlQuery getLookupPermissionIdMapping() {
275: return lookupPermissionIdMapping;
276: }
277:
278: public String getLookupPermissionIdQuery() {
279: return lookupPermissionIdQuery;
280: }
281:
282: protected void initDao() throws ApplicationContextException {
283: super .initDao();
284: lookupPermissionIdMapping = new LookupPermissionIdMapping(
285: getDataSource());
286: aclPermissionInsert = new AclPermissionInsert(getDataSource());
287: aclObjectIdentityInsert = new AclObjectIdentityInsert(
288: getDataSource());
289: aclPermissionDelete = new AclPermissionDelete(getDataSource());
290: aclObjectIdentityDelete = new AclObjectIdentityDelete(
291: getDataSource());
292: aclPermissionUpdate = new AclPermissionUpdate(getDataSource());
293: }
294:
295: /**
296: * Convenience method that obtains a given acl_object_identity record.
297: *
298: * @param aclObjectIdentity to lookup
299: *
300: * @return details of the record
301: *
302: * @throws DataRetrievalFailureException if record could not be found
303: */
304: private AclDetailsHolder lookupAclDetailsHolder(
305: AclObjectIdentity aclObjectIdentity)
306: throws DataRetrievalFailureException {
307: String aclObjectIdentityString = convertAclObjectIdentityToString(aclObjectIdentity);
308:
309: // Lookup the object's main properties from the RDBMS (guaranteed no nulls)
310: List objects = objectProperties
311: .execute(aclObjectIdentityString);
312:
313: if (objects.size() == 0) {
314: throw new DataRetrievalFailureException(
315: "aclObjectIdentity not found: "
316: + aclObjectIdentityString);
317: }
318:
319: // Should only be one record
320: return (AclDetailsHolder) objects.get(0);
321: }
322:
323: /**
324: * Convenience method to lookup the acl_permission applying to a given acl_object_identity.id and
325: * acl_permission.recipient.
326: *
327: * @param aclObjectIdentityId to locate
328: * @param recipient to locate
329: *
330: * @return the acl_permission.id of the record, or -1 if not found
331: *
332: * @throws DataAccessException DOCUMENT ME!
333: */
334: private long lookupPermissionId(long aclObjectIdentityId,
335: Object recipient) throws DataAccessException {
336: List list = lookupPermissionIdMapping.execute(new Object[] {
337: new Long(aclObjectIdentityId), recipient });
338:
339: if (list.size() == 0) {
340: return -1;
341: }
342:
343: return ((Long) list.get(0)).longValue();
344: }
345:
346: public void setAclObjectIdentityDelete(
347: AclObjectIdentityDelete aclObjectIdentityDelete) {
348: this .aclObjectIdentityDelete = aclObjectIdentityDelete;
349: }
350:
351: public void setAclObjectIdentityDeleteStatement(
352: String aclObjectIdentityDeleteStatement) {
353: this .aclObjectIdentityDeleteStatement = aclObjectIdentityDeleteStatement;
354: }
355:
356: public void setAclObjectIdentityInsert(
357: AclObjectIdentityInsert aclObjectIdentityInsert) {
358: this .aclObjectIdentityInsert = aclObjectIdentityInsert;
359: }
360:
361: public void setAclObjectIdentityInsertStatement(
362: String aclObjectIdentityInsertStatement) {
363: this .aclObjectIdentityInsertStatement = aclObjectIdentityInsertStatement;
364: }
365:
366: public void setAclPermissionDelete(
367: AclPermissionDelete aclPermissionDelete) {
368: this .aclPermissionDelete = aclPermissionDelete;
369: }
370:
371: public void setAclPermissionDeleteStatement(
372: String aclPermissionDeleteStatement) {
373: this .aclPermissionDeleteStatement = aclPermissionDeleteStatement;
374: }
375:
376: public void setAclPermissionInsert(
377: AclPermissionInsert aclPermissionInsert) {
378: this .aclPermissionInsert = aclPermissionInsert;
379: }
380:
381: public void setAclPermissionInsertStatement(
382: String aclPermissionInsertStatement) {
383: this .aclPermissionInsertStatement = aclPermissionInsertStatement;
384: }
385:
386: public void setAclPermissionUpdate(
387: AclPermissionUpdate aclPermissionUpdate) {
388: this .aclPermissionUpdate = aclPermissionUpdate;
389: }
390:
391: public void setAclPermissionUpdateStatement(
392: String aclPermissionUpdateStatement) {
393: this .aclPermissionUpdateStatement = aclPermissionUpdateStatement;
394: }
395:
396: public void setBasicAclEntryCache(
397: BasicAclEntryCache basicAclEntryCache) {
398: Assert.notNull(basicAclEntryCache,
399: "Cache cannot be set to null");
400: this .basicAclEntryCache = basicAclEntryCache;
401: }
402:
403: public void setLookupPermissionIdMapping(
404: MappingSqlQuery lookupPermissionIdMapping) {
405: this .lookupPermissionIdMapping = lookupPermissionIdMapping;
406: }
407:
408: public void setLookupPermissionIdQuery(
409: String lookupPermissionIdQuery) {
410: this .lookupPermissionIdQuery = lookupPermissionIdQuery;
411: }
412:
413: //~ Inner Classes ==================================================================================================
414:
415: protected class AclObjectIdentityDelete extends SqlUpdate {
416: protected AclObjectIdentityDelete(DataSource ds) {
417: super (ds, aclObjectIdentityDeleteStatement);
418: declareParameter(new SqlParameter(Types.BIGINT));
419: compile();
420: }
421:
422: protected void delete(Long aclObjectIdentity)
423: throws DataAccessException {
424: super .update(aclObjectIdentity.intValue());
425: }
426: }
427:
428: protected class AclObjectIdentityInsert extends SqlUpdate {
429: protected AclObjectIdentityInsert(DataSource ds) {
430: super (ds, aclObjectIdentityInsertStatement);
431: declareParameter(new SqlParameter(Types.VARCHAR));
432: declareParameter(new SqlParameter(Types.BIGINT));
433: declareParameter(new SqlParameter(Types.VARCHAR));
434: compile();
435: }
436:
437: protected void insert(String objectIdentity,
438: Long parentAclObjectIdentity, String aclClass)
439: throws DataAccessException {
440: Object[] objs = new Object[] { objectIdentity,
441: parentAclObjectIdentity, aclClass };
442: super .update(objs);
443: }
444: }
445:
446: protected class AclPermissionDelete extends SqlUpdate {
447: protected AclPermissionDelete(DataSource ds) {
448: super (ds, aclPermissionDeleteStatement);
449: declareParameter(new SqlParameter(Types.BIGINT));
450: declareParameter(new SqlParameter(Types.VARCHAR));
451: compile();
452: }
453:
454: protected void delete(Long aclObjectIdentity, String recipient)
455: throws DataAccessException {
456: super .update(new Object[] { aclObjectIdentity, recipient });
457: }
458: }
459:
460: protected class AclPermissionInsert extends SqlUpdate {
461: protected AclPermissionInsert(DataSource ds) {
462: super (ds, aclPermissionInsertStatement);
463: declareParameter(new SqlParameter(Types.BIGINT));
464: declareParameter(new SqlParameter(Types.VARCHAR));
465: declareParameter(new SqlParameter(Types.INTEGER));
466: compile();
467: }
468:
469: protected void insert(Long aclObjectIdentity, String recipient,
470: Integer mask) throws DataAccessException {
471: Object[] objs = new Object[] { aclObjectIdentity,
472: recipient, mask };
473: super .update(objs);
474: }
475: }
476:
477: protected class AclPermissionUpdate extends SqlUpdate {
478: protected AclPermissionUpdate(DataSource ds) {
479: super (ds, aclPermissionUpdateStatement);
480: declareParameter(new SqlParameter(Types.BIGINT));
481: declareParameter(new SqlParameter(Types.INTEGER));
482: compile();
483: }
484:
485: protected void update(Long aclPermissionId, Integer newMask)
486: throws DataAccessException {
487: super
488: .update(newMask.intValue(), aclPermissionId
489: .intValue());
490: }
491: }
492:
493: protected class LookupPermissionIdMapping extends MappingSqlQuery {
494: protected LookupPermissionIdMapping(DataSource ds) {
495: super (ds, lookupPermissionIdQuery);
496: declareParameter(new SqlParameter(Types.BIGINT));
497: declareParameter(new SqlParameter(Types.VARCHAR));
498: compile();
499: }
500:
501: protected Object mapRow(ResultSet rs, int rownum)
502: throws SQLException {
503: return new Long(rs.getLong(1));
504: }
505: }
506: }
|