1 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 ; 41 42 import EDU.oswego.cs.dl.util.concurrent.Mutex; 43 44 48 public class LocalAclStrategy implements AclStrategy, AclEvaluationContext { 49 53 private Mutex mutex = new Mutex(); 54 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 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 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 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 stmt.close(); 106 lastModified = new Date (); 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 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 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 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 getAclName(long id) { 178 if (id == 1) 179 return "live"; 180 else 181 return "staging"; 182 } 183 184 private AclSubjectType subjectTypeFromString(String 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 ("Got unrecognized subject type string from database: " + subjectType); 193 } 194 195 private AclActionType actionTypeFromString(String 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 ("Got unrecognized action type string from database: " + actionType); 204 } 205 206 private String 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 ("Unrecognized ACL subject type: " + subjectType); 215 } 216 217 private String 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 ("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 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 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 lastModified = new Date (); 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 createInitialAclRecord(conn, aclInt.getId(), new Date (), 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 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 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 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 throw new RuntimeException ("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 aclInt.setLastModified(lastModified); 346 aclInt.setLastModifier(lastModifier); 347 aclInt.setUpdateCount(acl.getUpdateCount() + 1); 348 349 invalidateCachedAcl(aclInt.getId()); 351 352 } catch (Throwable 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 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 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 (id)); 389 } 390 391 private AclImpl getCachedAcl(long id) throws RepositoryException { 392 Long key = new Long (id); 393 AclImpl acl = (AclImpl)acls.get(key); 394 if (acl == null) { 395 synchronized(acls) { 396 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 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 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 compileObjectExpression(String 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 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 } catch (DocumentVariantNotFoundException e) { 518 } 520 } 521 522 return (VariantKey[])result.toArray(new VariantKey[result.size()]); 523 } 524 } 525 | Popular Tags |