diff --git a/modeshape-jcr/src/main/java/org/modeshape/jcr/JcrLockManager.java b/modeshape-jcr/src/main/java/org/modeshape/jcr/JcrLockManager.java index cdd4d9fca1..ce288305f1 100644 --- a/modeshape-jcr/src/main/java/org/modeshape/jcr/JcrLockManager.java +++ b/modeshape-jcr/src/main/java/org/modeshape/jcr/JcrLockManager.java @@ -141,7 +141,7 @@ Set lockTokens() { @Override public String[] getLockTokens() { - Set tokens = new HashSet(); + Set tokens = new HashSet(); for (String token : lockTokens) { ModeShapeLock lock = lockManager.findLockByToken(token); if (lock != null && !lock.isSessionScoped()) { @@ -161,7 +161,8 @@ public boolean isLocked( AbstractJcrNode node ) throws PathNotFoundException, Re } @Override - public Lock getLock( String absPath ) throws PathNotFoundException, LockException, AccessDeniedException, RepositoryException { + public Lock getLock( String absPath ) + throws PathNotFoundException, LockException, AccessDeniedException, RepositoryException { ModeShapeLock lock = getLowestLockAlongPath(session.node(session.absolutePathFor(absPath)), false); if (lock != null) return lock.lockFor(session); throw new LockException(JcrI18n.notLocked.text(absPath)); @@ -178,10 +179,11 @@ public Lock getLockIfExists( AbstractJcrNode node ) throws AccessDeniedException return lock == null ? null : lock.lockFor(session); } - private ModeShapeLock getLowestLockAlongPath(final AbstractJcrNode node, boolean skipExpiredLocks) + private ModeShapeLock getLowestLockAlongPath( final AbstractJcrNode node, + boolean skipExpiredLocks ) throws PathNotFoundException, AccessDeniedException, RepositoryException { session.checkLive(); - + SessionCache sessionCache = session.cache(); NodeCache cache = sessionCache; NodeKey nodeKey = node.key(); @@ -262,10 +264,13 @@ public Lock lock( AbstractJcrNode node, if (!node.isLockable()) { throw new LockException(JcrI18n.nodeNotLockable.text(node.location())); } + + session.checkPermission(session.workspaceName(), node.path(), ModeShapePermissions.LOCK_MANAGEMENT); + if (node.isLocked()) { - throw new LockException(JcrI18n.alreadyLocked.text(node.location())); + throw new LockException(JcrI18n.alreadyLocked.text(node.location())); } - if (node.isNew()|| node.isModified()) { + if (node.isNew() || node.isModified()) { throw new InvalidItemStateException(JcrI18n.changedNodeCannotBeLocked.text(node.location())); } @@ -286,6 +291,8 @@ public void unlock( String absPath ) public void unlock( AbstractJcrNode node ) throws PathNotFoundException, LockException, AccessDeniedException, InvalidItemStateException, RepositoryException { + session.checkPermission(session.workspaceName(), node.path(), ModeShapePermissions.LOCK_MANAGEMENT); + if (node.isModified()) { throw new InvalidItemStateException(JcrI18n.changedNodeCannotBeUnlocked.text(node.getPath())); } @@ -296,7 +303,7 @@ public void unlock( AbstractJcrNode node ) try { session.checkPermission(session.workspaceName(), node.path(), ModeShapePermissions.UNLOCK_ANY); } catch (AccessDeniedException e) { - //expected by the TCK + // expected by the TCK throw new LockException(e); } } diff --git a/modeshape-jcr/src/main/java/org/modeshape/jcr/JcrObservationManager.java b/modeshape-jcr/src/main/java/org/modeshape/jcr/JcrObservationManager.java index 5167e39c59..46c4e9b81b 100644 --- a/modeshape-jcr/src/main/java/org/modeshape/jcr/JcrObservationManager.java +++ b/modeshape-jcr/src/main/java/org/modeshape/jcr/JcrObservationManager.java @@ -599,7 +599,7 @@ public String toString() { sb.append(" from ").append(info.get(MOVE_FROM_KEY)).append(" to ").append(info.get(MOVE_TO_KEY)); } else { sb.append("Node reordered"); - String destination = info.get(ORDER_DEST_KEY).toString(); + String destination = (String)info.get(ORDER_DEST_KEY); if (destination == null) { destination = " at the end of the children list"; } diff --git a/modeshape-jcr/src/main/java/org/modeshape/jcr/ModeShapePermissions.java b/modeshape-jcr/src/main/java/org/modeshape/jcr/ModeShapePermissions.java index 7e77c808d0..c19e5ac711 100644 --- a/modeshape-jcr/src/main/java/org/modeshape/jcr/ModeShapePermissions.java +++ b/modeshape-jcr/src/main/java/org/modeshape/jcr/ModeShapePermissions.java @@ -136,11 +136,23 @@ public interface ModeShapePermissions { */ public static final String MODIFY_ACCESS_CONTROL = "modify_access_control"; + /** + * The permission that allows the user ability to lock and unlock nodes + * related to access control. + */ + public static final String LOCK_MANAGEMENT = "lock_management"; + + /** + * The permission that allows the user ability to perform versioning operations on a node + * related to access control. + */ + public static final String VERSION_MANAGEMENT = "version_management"; + static final String[] ALL_CHANGE_PERMISSIONS = new String[] {REGISTER_NAMESPACE, REGISTER_TYPE, UNLOCK_ANY, ADD_NODE, - SET_PROPERTY, REMOVE, CREATE_WORKSPACE, DELETE_WORKSPACE, INDEX_WORKSPACE, BACKUP, RESTORE}; + SET_PROPERTY, REMOVE, CREATE_WORKSPACE, DELETE_WORKSPACE, INDEX_WORKSPACE, BACKUP, RESTORE, LOCK_MANAGEMENT, VERSION_MANAGEMENT}; static final String[] ALL_PERMISSIONS = new String[] {REGISTER_NAMESPACE, REGISTER_TYPE, UNLOCK_ANY, ADD_NODE, SET_PROPERTY, - REMOVE, READ, CREATE_WORKSPACE, DELETE_WORKSPACE, INDEX_WORKSPACE, MONITOR, BACKUP, RESTORE}; + REMOVE, READ, CREATE_WORKSPACE, DELETE_WORKSPACE, INDEX_WORKSPACE, MONITOR, BACKUP, RESTORE, LOCK_MANAGEMENT, VERSION_MANAGEMENT}; static final List READONLY_EXTERNAL_PATH_PERMISSIONS = Arrays.asList(READ, INDEX_WORKSPACE); } diff --git a/modeshape-jcr/src/main/java/org/modeshape/jcr/security/acl/Privileges.java b/modeshape-jcr/src/main/java/org/modeshape/jcr/security/acl/Privileges.java index 2f61ae74dc..1128b94433 100644 --- a/modeshape-jcr/src/main/java/org/modeshape/jcr/security/acl/Privileges.java +++ b/modeshape-jcr/src/main/java/org/modeshape/jcr/security/acl/Privileges.java @@ -154,6 +154,8 @@ public Privileges(JcrSession session) { actions.put(ModeShapePermissions.READ_ACCESS_CONTROL, readAccessControl); actions.put(ModeShapePermissions.REMOVE_CHILD_NODES, removeChildNodes); actions.put(ModeShapePermissions.REMOVE, removeNode); + actions.put(ModeShapePermissions.LOCK_MANAGEMENT, lockManagement); + actions.put(ModeShapePermissions.VERSION_MANAGEMENT, versionManagement); } /** diff --git a/modeshape-jcr/src/test/java/org/modeshape/jcr/security/AccessControlManagerTest.java b/modeshape-jcr/src/test/java/org/modeshape/jcr/security/AccessControlManagerTest.java index 5c3c5f80fb..ae7253b30f 100644 --- a/modeshape-jcr/src/test/java/org/modeshape/jcr/security/AccessControlManagerTest.java +++ b/modeshape-jcr/src/test/java/org/modeshape/jcr/security/AccessControlManagerTest.java @@ -29,6 +29,8 @@ import javax.jcr.RepositoryException; import javax.jcr.Session; import javax.jcr.UnsupportedRepositoryOperationException; +import javax.jcr.Workspace; +import javax.jcr.lock.LockManager; import javax.jcr.nodetype.ConstraintViolationException; import javax.jcr.security.AccessControlList; import javax.jcr.security.AccessControlManager; @@ -48,6 +50,7 @@ public class AccessControlManagerTest extends MultiUseAbstractTest { private AccessControlManager acm; private Privileges privileges; + private LockManager lm; @BeforeClass public static final void beforeAll() throws Exception { @@ -62,7 +65,7 @@ public static final void beforeAll() throws Exception { setPolicy("/Cars/Luxury/", Privilege.JCR_READ, Privilege.JCR_MODIFY_ACCESS_CONTROL, Privilege.JCR_READ_ACCESS_CONTROL); setPolicy("/Cars/Sports/", Privilege.JCR_READ, Privilege.JCR_WRITE, Privilege.JCR_MODIFY_ACCESS_CONTROL); setPolicy("/Cars/Utility/Ford F-150/", Privilege.JCR_MODIFY_ACCESS_CONTROL, Privilege.JCR_READ_ACCESS_CONTROL); - setPolicy("/Cars/Utility/", Privilege.JCR_READ_ACCESS_CONTROL); + setPolicy("/Cars/Utility/", Privilege.JCR_READ_ACCESS_CONTROL, Privilege.JCR_LOCK_MANAGEMENT); } @@ -77,6 +80,8 @@ public void beforeEach() throws Exception { super.beforeEach(); acm = session.getAccessControlManager(); privileges = new Privileges(session); + Workspace ws = session.getWorkspace(); + lm = ws.getLockManager(); } @Test @@ -151,6 +156,39 @@ public void shouldDenyAdd() throws RepositoryException { } } + @FixFor("MODE-2724") + @Test + public void shouldGrantLockWhenHasAllPrivileges() throws Exception { + try { + lm.lock("/Cars", true, true, 60, "anonymous"); + lm.unlock("/Cars"); + } catch (AccessDeniedException e) { + fail("Should grant lock management"); + } + } + + @FixFor("MODE-2724") + @Test + public void shouldGrantLockWhenHasLockManagementPrivilege() throws Exception { + try { + lm.lock("/Cars/Utility", true, true, 60, "anonymous"); + lm.unlock("/Cars/Utility"); + } catch (AccessDeniedException e) { + fail("Should grant lock management"); + } + } + + @FixFor("MODE-2724") + @Test + public void shouldDenyLock() throws RepositoryException { + try { + lm.lock("/Cars/Luxury/Lexus IS350", true, true, 60, "anonymous"); + fail("Should deny locking node"); + } catch (AccessDeniedException e) { + //expected + } + } + @Test public void shouldGrantModify() throws RepositoryException { Node infinity = session.getNode("/Cars/Sports/Infiniti G37"); diff --git a/modeshape-jcr/src/test/resources/io/cars-system-view-with-uuids.xml b/modeshape-jcr/src/test/resources/io/cars-system-view-with-uuids.xml index 2d015eacda..0b6174f238 100644 --- a/modeshape-jcr/src/test/resources/io/cars-system-view-with-uuids.xml +++ b/modeshape-jcr/src/test/resources/io/cars-system-view-with-uuids.xml @@ -13,7 +13,8 @@ nt:unstructured - mix:referenceable + mix:referenceable + mix:lockable e41075cb-a09a-4910-87b1-90ce8b4ca9dd @@ -289,7 +290,8 @@ car:Car - mix:referenceable + mix:referenceable + mix:lockable 7c513b8e-e77e-40f2-bf4d-147419037a75 @@ -326,6 +328,7 @@ mix:referenceable + mix:lockable 30568ebe-6351-4e74-9502-71b136387142