diff --git a/jackrabbit-jcr-server/src/main/java/org/apache/jackrabbit/server/jcr/JCRWebdavServer.java b/jackrabbit-jcr-server/src/main/java/org/apache/jackrabbit/server/jcr/JCRWebdavServer.java
index 461b5818701..6de04427f79 100644
--- a/jackrabbit-jcr-server/src/main/java/org/apache/jackrabbit/server/jcr/JCRWebdavServer.java
+++ b/jackrabbit-jcr-server/src/main/java/org/apache/jackrabbit/server/jcr/JCRWebdavServer.java
@@ -227,6 +227,10 @@ private DavSession get(WebdavRequest request)
}
}
+ if (session != null) {
+ session = checkSameUser(session, request);
+ }
+
// no cached session present -> create new one.
if (session == null) {
Session repSession = getRepositorySession(request);
@@ -242,7 +246,34 @@ private DavSession get(WebdavRequest request)
return session;
}
- /**
+ private DavSession checkSameUser(DavSession cached, WebdavRequest request)
+ throws DavException {
+ Session repSession = getRepositorySession(request);
+ String userId = repSession.getUserID();
+ String cachedUserId = null;
+ try {
+ Session cachedSession = DavSessionImpl.getRepositorySession(cached);
+ if (cachedSession != null) {
+ cachedUserId = cachedSession.getUserID();
+ }
+ } catch (DavException e) {
+ // should not occur, since only DavSessionImpl instances
+ // are put into the cache.
+ log.error("Unexpected error: {}", e.getMessage(), e.getCause());
+ }
+ if (cachedUserId != null && cachedUserId.equals(userId)) {
+ // same user: reuse the cached session and release the
+ // session that was only needed to authenticate the request.
+ sessionProvider.releaseSession(repSession);
+ return cached;
+ } else {
+ log.warn("Request of user '{}' presented a token referencing a session of user '{}' - token ignored.", userId, cachedUserId);
+ DavSession session = new DavSessionImpl(repSession);
+ sessionMap.put(session, new HashSet<>());
+ return session;
+ }
+ }
+ /**
* Add a references to the specified DavSession.
*
* @param session
diff --git a/jackrabbit-jcr-server/src/main/java/org/apache/jackrabbit/webdav/jcr/DefaultItemCollection.java b/jackrabbit-jcr-server/src/main/java/org/apache/jackrabbit/webdav/jcr/DefaultItemCollection.java
index b6e3efff1bb..a762ed6918f 100644
--- a/jackrabbit-jcr-server/src/main/java/org/apache/jackrabbit/webdav/jcr/DefaultItemCollection.java
+++ b/jackrabbit-jcr-server/src/main/java/org/apache/jackrabbit/webdav/jcr/DefaultItemCollection.java
@@ -62,6 +62,7 @@
import org.apache.jackrabbit.webdav.DavResourceLocator;
import org.apache.jackrabbit.webdav.DavServletResponse;
import org.apache.jackrabbit.webdav.MultiStatusResponse;
+import org.apache.jackrabbit.webdav.jcr.lock.LockTokenMapper;
import org.apache.jackrabbit.webdav.jcr.property.JcrDavPropertyNameSet;
import org.apache.jackrabbit.webdav.jcr.security.JcrUserPrivilegesProperty;
import org.apache.jackrabbit.webdav.jcr.security.JcrSupportedPrivilegesProperty;
@@ -695,6 +696,10 @@ public ActiveLock lock(LockInfo reqLockInfo) throws DavException {
javax.jcr.lock.LockManager lockMgr = getRepositorySession().getWorkspace().getLockManager();
Lock jcrLock = lockMgr.lock((item).getPath(), reqLockInfo.isDeep(),
sessionScoped, timeout, reqLockInfo.getOwner());
+ if (sessionScoped) {
+ // discard any mapping of a previous lock on this node
+ LockTokenMapper.releaseSessionScopedToken(jcrLock.getNode().getIdentifier());
+ }
ActiveLock lock = new JcrActiveLock(jcrLock);
// add reference to DAVSession for this lock
getSession().addReference(lock.getToken());
@@ -767,8 +772,12 @@ public void unlock(String lockToken) throws DavException {
ActiveLock lock = getWriteLock();
if (lock != null && lockToken.equals(lock.getToken())) {
try {
- ((Node) item).unlock();
- getSession().removeReference(lock.getToken());
+ Node n = (Node) item;
+ n.unlock();
+ getSession().removeReference(lockToken);
+ // the lock is gone: discard its DAV token secret so it can
+ // not be replayed against a future lock on this node.
+ LockTokenMapper.releaseSessionScopedToken(n.getIdentifier());
} catch (RepositoryException e) {
throw new JcrDavException(e);
}
diff --git a/jackrabbit-jcr-server/src/main/java/org/apache/jackrabbit/webdav/jcr/lock/LockTokenMapper.java b/jackrabbit-jcr-server/src/main/java/org/apache/jackrabbit/webdav/jcr/lock/LockTokenMapper.java
index 2daba0a0828..2aed70264fe 100644
--- a/jackrabbit-jcr-server/src/main/java/org/apache/jackrabbit/webdav/jcr/lock/LockTokenMapper.java
+++ b/jackrabbit-jcr-server/src/main/java/org/apache/jackrabbit/webdav/jcr/lock/LockTokenMapper.java
@@ -19,15 +19,20 @@
import javax.jcr.RepositoryException;
import javax.jcr.lock.Lock;
+import org.apache.commons.collections4.map.LRUMap;
import org.apache.jackrabbit.util.Text;
+import java.util.Collections;
+import java.util.Map;
+import java.util.UUID;
+
/**
* Maps between WebDAV lock tokens and JCR lock tokens.
*
* The following notations are used: * *
- * opaquelocktoken:SESSIONSCOPED:NODEIDENTIFIER + * urn:uuid:UUID of mapping * opaquelocktoken:OPENSCOPED:JCRLOCKTOKEN ** @@ -44,22 +49,34 @@ public class LockTokenMapper { private static final String OL = "opaquelocktoken:"; - private static final String SESSIONSCOPED = "4403ef44-4124-11e1-b965-00059a3c7a00"; private static final String OPENSCOPED = "dccce564-412e-11e1-b969-00059a3c7a00"; - private static final String SESSPREFIX = OL + SESSIONSCOPED + ":"; + private static final String SESSPREFIX = "urn:uuid:"; private static final String OPENPREFIX = OL + OPENSCOPED + ":"; + // map node identifiers to randomized session locks, 4k mappings should be enough for everyone + private static final Map