KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > outerj > daisy > repository > serverimpl > acl > LocalAclStrategy


1 /*
2  * Copyright 2004 Outerthought bvba and Schaubroeck nv
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16 package org.outerj.daisy.repository.serverimpl.acl;
17
18 import org.outerj.daisy.repository.commonimpl.acl.AclStrategy;
19 import org.outerj.daisy.repository.commonimpl.acl.AclImpl;
20 import org.outerj.daisy.repository.commonimpl.acl.AclEvaluationContext;
21 import org.outerj.daisy.repository.commonimpl.AuthenticatedUser;
22 import org.outerj.daisy.repository.commonimpl.CommonRepository;
23 import org.outerj.daisy.repository.commonimpl.RepositoryImpl;
24 import org.outerj.daisy.repository.*;
25 import org.outerj.daisy.repository.query.QueryException;
26 import org.outerj.daisy.repository.query.EvaluationContext;
27 import org.outerj.daisy.repository.serverimpl.LocalRepositoryManager;
28 import org.outerj.daisy.jdbcutil.JdbcHelper;
29 import org.outerj.daisy.repository.serverimpl.EventHelper;
30 import org.outerj.daisy.repository.acl.*;
31 import org.outerj.daisy.query.model.PredicateExpr;
32 import org.outerj.daisy.query.model.AclConditionViolation;
33 import org.outerj.daisy.query.ExtQueryContext;
34 import org.apache.commons.collections.primitives.ArrayLongList;
35 import org.outerx.daisy.x10.AclUpdatedDocument;
36 import org.outerx.daisy.x10.AclDocument;
37
38 import java.sql.*;
39 import java.util.*;
40 import java.util.Date JavaDoc;
41
42 import EDU.oswego.cs.dl.util.concurrent.Mutex;
43
44 /**
45  * Server-side implementation of the AclStrategy interface.
46  *
47  */

48 public class LocalAclStrategy implements AclStrategy, AclEvaluationContext {
49     /**
50      * A Mutex controlling who (which thread) can read or write ACLs from/to the database.
51      * This is used to serialize all database access (related to ACLs).
52      */

53     private Mutex mutex = new Mutex();
54     /**
55      * Map containing cached copies of the ACLs which are used for ACL-evaluation.
56      */

57     private Map acls = new HashMap();
58     private AuthenticatedUser systemUser;
59     private LocalRepositoryManager.Context context;
60     private JdbcHelper jdbcHelper;
61     private EventHelper eventHelper;
62
63     public LocalAclStrategy(LocalRepositoryManager.Context context, AuthenticatedUser systemUser, JdbcHelper jdbcHelper) {
64         this.context = context;
65         this.systemUser = systemUser;
66         this.jdbcHelper = jdbcHelper;
67         this.eventHelper = new EventHelper(context, jdbcHelper);
68     }
69
70     public AclImpl loadAcl(long id, AuthenticatedUser user) throws RepositoryException {
71         try {
72             mutex.acquire();
73         } catch (InterruptedException JavaDoc e) {
74             throw new RepositoryException("Could not get an ACL lock.", e);
75         }
76         try {
77             return loadAclInternal(id, user);
78         } finally {
79             mutex.release();
80         }
81     }
82
83     /**
84      * Loads an ACL, assuming the mutex has already been acquired by the caller.
85      */

86     private AclImpl loadAclInternal(long id, AuthenticatedUser user) throws RepositoryException {
87         if (id != 1 && id != 2)
88             throw new RepositoryException("Invalid ACL id: " + id);
89
90         Connection conn = null;
91         PreparedStatement stmt = null;
92         PreparedStatement stmtEntries = null;
93         try {
94             conn = context.getDataSource().getConnection();
95             Date JavaDoc lastModified;
96             long lastModifier;
97             long updateCount;
98
99             stmt = conn.prepareStatement("select last_modified, last_modifier, updatecount from acls where id = ?");
100             stmt.setLong(1, id);
101             ResultSet rs = stmt.executeQuery();
102
103             if (!rs.next()) {
104                 // record for this ACL did not yet exist, make it now
105
stmt.close();
106                 lastModified = new Date JavaDoc();
107                 lastModifier = systemUser.getId();
108                 updateCount = 1;
109                 createInitialAclRecord(conn, id, lastModified, lastModifier);
110             } else {
111                 lastModified = rs.getTimestamp("last_modified");
112                 lastModifier = rs.getLong("last_modifier");
113                 updateCount = rs.getLong("updatecount");
114                 stmt.close();
115             }
116
117             AclImpl acl = new AclImpl(this, lastModified, lastModifier, id, user, updateCount);
118
119             stmtEntries = conn.prepareStatement("select subject_user_id, subject_role_id, subject_type, perm_read_live, perm_read, perm_write, perm_publish, perm_delete from acl_entries where acl_object_id= ? and acl_id = ? order by id");
120             stmtEntries.setLong(2, id);
121             stmt = conn.prepareStatement("select id, objectspec from acl_objects where acl_id = ? order by id");
122             stmt.setLong(1, id);
123             rs = stmt.executeQuery();
124
125             while (rs.next()) {
126                 long objectId = rs.getLong(1);
127                 String JavaDoc objectExpr = rs.getString(2);
128                 AclObject aclObject = acl.createNewObject(objectExpr);
129
130                 stmtEntries.setLong(1, objectId);
131                 ResultSet rsEntries = stmtEntries.executeQuery();
132                 while (rsEntries.next()) {
133                     AclSubjectType subjectType = subjectTypeFromString(rsEntries.getString("subject_type"));
134                     long subjectValue = -1;
135                     if (subjectType == AclSubjectType.USER) {
136                         subjectValue = rsEntries.getLong("subject_user_id");
137                     } else if (subjectType == AclSubjectType.ROLE) {
138                         subjectValue = rsEntries.getLong("subject_role_id");
139                     }
140                     AclEntry aclEntry = aclObject.createNewEntry(subjectType, subjectValue);
141                     aclEntry.set(AclPermission.READ_LIVE, actionTypeFromString(rsEntries.getString("perm_read_live")));
142                     aclEntry.set(AclPermission.READ, actionTypeFromString(rsEntries.getString("perm_read")));
143                     aclEntry.set(AclPermission.WRITE, actionTypeFromString(rsEntries.getString("perm_write")));
144                     aclEntry.set(AclPermission.PUBLISH, actionTypeFromString(rsEntries.getString("perm_publish")));
145                     aclEntry.set(AclPermission.DELETE, actionTypeFromString(rsEntries.getString("perm_delete")));
146                     aclObject.add(aclEntry);
147                 }
148                 rsEntries.close();
149
150                 acl.add(aclObject);
151             }
152             return acl;
153         } catch (Throwable JavaDoc e) {
154             throw new RepositoryException("Error loading ACL.", e);
155         } finally {
156             jdbcHelper.closeStatement(stmt);
157             jdbcHelper.closeStatement(stmtEntries);
158             jdbcHelper.closeConnection(conn);
159         }
160     }
161
162     private void createInitialAclRecord(Connection conn, long id, Date JavaDoc lastModified, long lastModifier) throws SQLException {
163         PreparedStatement stmt = null;
164         try {
165             stmt = conn.prepareStatement("insert into acls(id, \"name\", last_modified, last_modifier, updatecount) values (?,?,?,?,?)");
166             stmt.setLong(1, id);
167             stmt.setString(2, getAclName(id));
168             stmt.setTimestamp(3, new Timestamp(lastModified.getTime()));
169             stmt.setLong(4, lastModifier);
170             stmt.setLong(5, 1L);
171             stmt.execute();
172         } finally {
173             jdbcHelper.closeStatement(stmt);
174         }
175     }
176
177     private String JavaDoc getAclName(long id) {
178         if (id == 1)
179             return "live";
180         else
181             return "staging";
182     }
183
184     private AclSubjectType subjectTypeFromString(String JavaDoc subjectType) {
185         if (subjectType.equals("U"))
186             return AclSubjectType.USER;
187         else if (subjectType.equals("R"))
188             return AclSubjectType.ROLE;
189         else if (subjectType.equals("E"))
190             return AclSubjectType.EVERYONE;
191         else
192             throw new RuntimeException JavaDoc("Got unrecognized subject type string from database: " + subjectType);
193     }
194
195     private AclActionType actionTypeFromString(String JavaDoc actionType) {
196         if (actionType.equals("G"))
197             return AclActionType.GRANT;
198         else if (actionType.equals("D"))
199             return AclActionType.DENY;
200         else if (actionType.equals("N"))
201             return AclActionType.DO_NOTHING;
202         else
203             throw new RuntimeException JavaDoc("Got unrecognized action type string from database: " + actionType);
204     }
205
206     private String JavaDoc stringFromSubjectType(AclSubjectType subjectType) {
207         if (subjectType == AclSubjectType.USER)
208             return "U";
209         else if (subjectType == AclSubjectType.ROLE)
210             return "R";
211         else if (subjectType == AclSubjectType.EVERYONE)
212             return "E";
213         else
214             throw new RuntimeException JavaDoc("Unrecognized ACL subject type: " + subjectType);
215     }
216
217     private String JavaDoc stringFromActionType(AclActionType actionType) {
218         if (actionType == AclActionType.DENY)
219             return "D";
220         else if (actionType == AclActionType.GRANT)
221             return "G";
222         else if (actionType == AclActionType.DO_NOTHING)
223             return "N";
224         else
225             throw new RuntimeException JavaDoc("Unrecognized ACL action type: " + actionType);
226     }
227
228     public void storeAcl(AclImpl acl) throws RepositoryException {
229         if (!acl.getIntimateAccess(this).getCurrentModifier().isInAdministratorRole())
230             throw new RepositoryException("Only the Administrator can store ACLs.");
231
232         try {
233             mutex.acquire();
234         } catch (InterruptedException JavaDoc e) {
235             throw new RepositoryException("Could not get an ACL lock.", e);
236         }
237         try {
238             storeAclInternal(acl, false);
239         } finally {
240             mutex.release();
241         }
242     }
243
244     /**
245      * Stores an ACL, assuming the mutex has already been acquired by the caller.
246      */

247     private void storeAclInternal(AclImpl acl, boolean forceOverwrite) throws RepositoryException {
248         Connection conn = null;
249         PreparedStatement stmt = null;
250         PreparedStatement stmtInsertObject = null;
251         PreparedStatement stmtInsertEntry = null;
252         ResultSet rs;
253         AclImpl.IntimateAccess aclInt = acl.getIntimateAccess(this);
254         long newUpdateCount;
255         try {
256
257             Date JavaDoc lastModified = new Date JavaDoc();
258             long lastModifier = aclInt.getCurrentModifier().getId();
259
260             conn = context.getDataSource().getConnection();
261             jdbcHelper.startTransaction(conn);
262
263             AclImpl oldAcl = loadAclInternal(aclInt.getId(), aclInt.getCurrentModifier());
264
265             stmt = conn.prepareStatement("select updatecount from acls where id = ?");
266             stmt.setLong(1, aclInt.getId());
267             rs = stmt.executeQuery();
268
269             if (!rs.next()) {
270                 // Normally you will only be able to save an ACL if you first retrieved it, so at first
271
// it would appear that this situation can't occur, but it can occur via copyStagingToLive
272
createInitialAclRecord(conn, aclInt.getId(), new Date JavaDoc(), systemUser.getId());
273             } else if (!forceOverwrite && acl.getUpdateCount() != rs.getLong(1)) {
274                 throw new RepositoryException("The ACL has been concurrently modified by someone else.");
275             }
276
277             stmt.close();
278
279             newUpdateCount = acl.getUpdateCount() + 1;
280
281             // update lastmodifier/lastmodified information
282
stmt = conn.prepareStatement("update acls set last_modified=?, last_modifier=?, updatecount=? where id=?");
283             stmt.setTimestamp(1, new Timestamp(lastModified.getTime()));
284             stmt.setLong(2, lastModifier);
285             stmt.setLong(3, newUpdateCount);
286             stmt.setLong(4, aclInt.getId());
287             stmt.execute();
288             stmt.close();
289
290             // drop existing records for this ACL
291
stmt = conn.prepareStatement("delete from acl_objects where acl_id = ?");
292             stmt.setLong(1, aclInt.getId());
293             stmt.execute();
294             stmt.close();
295             stmt = conn.prepareStatement("delete from acl_entries where acl_id = ?");
296             stmt.setLong(1, aclInt.getId());
297             stmt.execute();
298             stmt.close();
299
300             stmtInsertObject = conn.prepareStatement("insert into acl_objects(acl_id, id, objectspec) values(?,?,?)");
301             stmtInsertEntry = conn.prepareStatement("insert into acl_entries(acl_id, acl_object_id, id, subject_user_id, subject_role_id, subject_type, perm_read_live, perm_read, perm_write, perm_publish, perm_delete) values(?,?,?,?,?,?,?,?,?,?,?)");
302             // insert the new records
303
for (int i = 0; i < acl.size(); i++) {
304                 AclObject aclObject = acl.get(i);
305
306                 stmtInsertObject.setLong(1, aclInt.getId());
307                 stmtInsertObject.setLong(2, i);
308                 stmtInsertObject.setString(3, aclObject.getObjectExpr());
309                 stmtInsertObject.execute();
310
311                 for (int j = 0; j < aclObject.size(); j++) {
312                     AclEntry aclEntry = aclObject.get(j);
313
314                     stmtInsertEntry.setLong(1, aclInt.getId());
315                     stmtInsertEntry.setLong(2, i);
316                     stmtInsertEntry.setLong(3, j);
317                     if (aclEntry.getSubjectType() == AclSubjectType.USER) {
318                         stmtInsertEntry.setLong(4, aclEntry.getSubjectValue());
319                         stmtInsertEntry.setNull(5, Types.BIGINT);
320                     } else if (aclEntry.getSubjectType() == AclSubjectType.ROLE) {
321                         stmtInsertEntry.setNull(4, Types.BIGINT);
322                         stmtInsertEntry.setLong(5, aclEntry.getSubjectValue());
323                     } else if (aclEntry.getSubjectType() == AclSubjectType.EVERYONE) {
324                         stmtInsertEntry.setNull(4, Types.BIGINT);
325                         stmtInsertEntry.setNull(5, Types.BIGINT);
326                     } else {
327                         // This situation should never occur
328
throw new RuntimeException JavaDoc("Encountered unexpected value for AclSubjectType: " + aclEntry.getSubjectType());
329                     }
330                     stmtInsertEntry.setString(6, stringFromSubjectType(aclEntry.getSubjectType()));
331                     stmtInsertEntry.setString(7, stringFromActionType(aclEntry.get(AclPermission.READ_LIVE)));
332                     stmtInsertEntry.setString(8, stringFromActionType(aclEntry.get(AclPermission.READ)));
333                     stmtInsertEntry.setString(9, stringFromActionType(aclEntry.get(AclPermission.WRITE)));
334                     stmtInsertEntry.setString(10, stringFromActionType(aclEntry.get(AclPermission.PUBLISH)));
335                     stmtInsertEntry.setString(11, stringFromActionType(aclEntry.get(AclPermission.DELETE)));
336                     stmtInsertEntry.execute();
337                 }
338             }
339
340             eventHelper.createEvent(createAclUpdatedEvent(oldAcl, acl, lastModified, newUpdateCount), "AclUpdated", conn);
341
342             conn.commit();
343
344             // update object state
345
aclInt.setLastModified(lastModified);
346             aclInt.setLastModifier(lastModifier);
347             aclInt.setUpdateCount(acl.getUpdateCount() + 1);
348
349             // invalidate our cached copy of this ACL.
350
invalidateCachedAcl(aclInt.getId());
351
352         } catch (Throwable JavaDoc e) {
353             jdbcHelper.rollback(conn);
354             throw new RepositoryException("Error storing ACL.", e);
355         } finally {
356             jdbcHelper.closeStatement(stmt);
357             jdbcHelper.closeStatement(stmtInsertObject);
358             jdbcHelper.closeStatement(stmtInsertEntry);
359             jdbcHelper.closeConnection(conn);
360         }
361
362         context.getCommonRepository().fireRepositoryEvent(RepositoryEventType.ACL_UPDATED, aclInt.getId(), newUpdateCount);
363     }
364
365     private AclUpdatedDocument createAclUpdatedEvent(AclImpl oldAcl, AclImpl newAcl, Date JavaDoc lastModified, long newUpdateCount) {
366         AclUpdatedDocument aclUpdatedDocument = AclUpdatedDocument.Factory.newInstance();
367         AclUpdatedDocument.AclUpdated aclUpdated = aclUpdatedDocument.addNewAclUpdated();
368
369         aclUpdated.addNewOldAcl().setAcl(oldAcl.getXml().getAcl());
370
371         AclDocument.Acl newAclXml = newAcl.getXml().getAcl();
372         newAclXml.setLastModified(getCalendar(lastModified));
373         newAclXml.setLastModifier(newAcl.getIntimateAccess(this).getCurrentModifier().getId());
374         newAclXml.setUpdateCount(newUpdateCount);
375
376         aclUpdated.addNewNewAcl().setAcl(newAclXml);
377
378         return aclUpdatedDocument;
379     }
380
381     private Calendar getCalendar(Date JavaDoc date) {
382         GregorianCalendar calendar = new GregorianCalendar();
383         calendar.setTime(date);
384         return calendar;
385     }
386
387     private void invalidateCachedAcl(long id) {
388         acls.remove(new Long JavaDoc(id));
389     }
390
391     private AclImpl getCachedAcl(long id) throws RepositoryException {
392         Long JavaDoc key = new Long JavaDoc(id);
393         AclImpl acl = (AclImpl)acls.get(key);
394         if (acl == null) {
395             synchronized(acls) {
396                 // yes, I know double-checking isn't foolproof but that can't cause harm here
397
if (!acls.containsKey(key)) {
398                     acl = loadAcl(id, systemUser);
399                     acl.makeReadOnly();
400                     acls.put(key, acl);
401                 } else {
402                     acl = (AclImpl)acls.get(key);
403                 }
404             }
405         }
406         return acl;
407     }
408
409     public void copyStagingToLive(AuthenticatedUser user) throws RepositoryException {
410         if (!user.isInAdministratorRole())
411             throw new RepositoryException("Only Administrators can copy the staging ACL to the live ACL.");
412
413         try {
414             mutex.acquire();
415         } catch (InterruptedException JavaDoc e) {
416             throw new RepositoryException("Could not get an ACL lock.", e);
417         }
418         try {
419             AclImpl stagingAcl = loadAclInternal(STAGING_ACL_ID, user);
420             AclImpl liveAcl = loadAclInternal(LIVE_ACL_ID, user);
421             AclImpl.IntimateAccess stagingAclInt = stagingAcl.getIntimateAccess(this);
422             stagingAclInt.setId(LIVE_ACL_ID);
423             stagingAclInt.setUpdateCount(liveAcl.getUpdateCount());
424             storeAclInternal(stagingAcl, true);
425         } finally {
426             mutex.release();
427         }
428     }
429
430     public void copyLiveToStaging(AuthenticatedUser user) throws RepositoryException {
431         if (!user.isInAdministratorRole())
432             throw new RepositoryException("Only Administrators can copy the live ACL to the staging ACL.");
433
434         try {
435             mutex.acquire();
436         } catch (InterruptedException JavaDoc e) {
437             throw new RepositoryException("Could not get an ACL lock.", e);
438         }
439         try {
440             AclImpl liveAcl = loadAclInternal(LIVE_ACL_ID, user);
441             AclImpl stagingAcl = loadAclInternal(STAGING_ACL_ID, user);
442             AclImpl.IntimateAccess liveAclInt = liveAcl.getIntimateAccess(this);
443             liveAclInt.setId(STAGING_ACL_ID);
444             liveAclInt.setUpdateCount(stagingAcl.getUpdateCount());
445             storeAclInternal(liveAcl, true);
446         } finally {
447             mutex.release();
448         }
449     }
450
451     public Object JavaDoc compileObjectExpression(String JavaDoc expression) throws RepositoryException {
452         try {
453             PredicateExpr expr = context.getQueryFactory().parsePredicateExpression(expression);
454             expr.prepare(new ExtQueryContext(new RepositoryImpl(context.getCommonRepository(), systemUser)));
455             AclConditionViolation violation = expr.isAclAllowed();
456             if (violation != null)
457                 throw new RepositoryException(violation.getMessage());
458             return expr;
459         } catch (QueryException e) {
460             throw new RepositoryException("Error compiling object selection expression.", e);
461         }
462     }
463
464     public boolean checkObjectExpression(Object JavaDoc compiledExpression, Document document) throws RepositoryException {
465         try {
466             return ((PredicateExpr)compiledExpression).evaluate(document, null, new EvaluationContext());
467         } catch (QueryException e) {
468             throw new RepositoryException("Error checking object selection expression.", e);
469         }
470     }
471
472     public AclResultInfo getAclInfo(AuthenticatedUser user, long id, long userId, long[] roleIds, Document document) throws RepositoryException {
473         if (user.getId() != userId && !user.isInAdministratorRole())
474             throw new RepositoryException("User " + user.getId() + " is not an Administrator and hence cannot access ACL info of user " + userId);
475
476         AclImpl acl = getCachedAcl(id);
477         AclEvaluator aclEvaluator = new AclEvaluator(acl, this, this);
478         return aclEvaluator.getAclInfo(userId, roleIds, document);
479     }
480
481     public AclResultInfo getAclInfo(AuthenticatedUser user, long id, long userId, long[] roleIds, long documentId, long branchId, long languageId) throws RepositoryException {
482         if (user.getId() != userId && !user.isInAdministratorRole())
483             throw new RepositoryException("User " + user.getId() + " is not an Administrator and hence cannot access ACL info of user " + userId);
484
485         Document document = context.getCommonRepository().getDocument(documentId, branchId, languageId, false, user);
486         return getAclInfo(user, id, userId, roleIds, document);
487     }
488
489     public long[] filterDocumentTypes(AuthenticatedUser user, long[] documentTypeIds, long collectionId) throws RepositoryException {
490         AclImpl acl = getCachedAcl(AclStrategy.LIVE_ACL_ID);
491         ArrayLongList result = new ArrayLongList(documentTypeIds.length);
492         AclEvaluator aclEvaluator = new AclEvaluator(acl, this, this);
493
494         for (int i = 0; i < documentTypeIds.length; i++) {
495             boolean success = aclEvaluator.hasPotentialWriteAccess(user.getId(), user.getActiveRoleIds(), documentTypeIds[i], collectionId);
496             if (success)
497                 result.add(documentTypeIds[i]);
498         }
499
500         return result.toArray();
501     }
502
503     public VariantKey[] filterDocuments(AuthenticatedUser user, VariantKey[] variantKeys, AclPermission permission) throws RepositoryException {
504         AclImpl acl = getCachedAcl(AclStrategy.LIVE_ACL_ID);
505         List result = new ArrayList(variantKeys.length);
506         AclEvaluator aclEvaluator = new AclEvaluator(acl, this, this);
507
508         CommonRepository repository = context.getCommonRepository();
509         for (int i = 0; i < variantKeys.length; i++) {
510             try {
511                 Document document = repository.getDocument(variantKeys[i].getDocumentId(), variantKeys[i].getBranchId(), variantKeys[i].getLanguageId(), false, systemUser);
512                 AclResultInfo aclResultInfo = aclEvaluator.getAclInfo(user.getId(), user.getActiveRoleIds(), document);
513                 if (aclResultInfo.isAllowed(permission))
514                     result.add(variantKeys[i]);
515             } catch (DocumentNotFoundException e) {
516                 // skip non-existing documents
517
} catch (DocumentVariantNotFoundException e) {
518                 // skip non-existing document variants
519
}
520         }
521
522         return (VariantKey[])result.toArray(new VariantKey[result.size()]);
523     }
524 }
525
Popular Tags