diff --git a/exist-core/pom.xml b/exist-core/pom.xml
index 880bda2491..d5a60ccc24 100644
--- a/exist-core/pom.xml
+++ b/exist-core/pom.xml
@@ -1344,6 +1344,7 @@
src/test/java/org/exist/xquery/functions/securitymanager/GroupManagementFunctionRemoveGroupTest.java
src/test/java/org/exist/xquery/functions/securitymanager/GroupMembershipFunctionRemoveGroupMemberTest.java
src/test/java/org/exist/xquery/functions/securitymanager/IdFunctionTest.java
+ src/main/java/org/exist/xquery/functions/securitymanager/PermissionsFunction.java
src/test/java/org/exist/xquery/functions/securitymanager/PermissionsFunctionChmodTest.java
src/test/java/org/exist/xquery/functions/securitymanager/PermissionsFunctionChownTest.java
src/test/java/org/exist/xquery/functions/system/GetRunningXQueriesTest.java
@@ -2144,6 +2145,7 @@
src/test/java/org/exist/xquery/functions/securitymanager/GroupManagementFunctionRemoveGroupTest.java
src/test/java/org/exist/xquery/functions/securitymanager/GroupMembershipFunctionRemoveGroupMemberTest.java
src/test/java/org/exist/xquery/functions/securitymanager/IdFunctionTest.java
+ src/main/java/org/exist/xquery/functions/securitymanager/PermissionsFunction.java
src/test/java/org/exist/xquery/functions/securitymanager/PermissionsFunctionChmodTest.java
src/test/java/org/exist/xquery/functions/securitymanager/PermissionsFunctionChownTest.java
src/test/java/org/exist/xquery/functions/securitymanager/SecurityManagerTestUtil.java
diff --git a/exist-core/src/main/java/org/exist/xquery/functions/securitymanager/PermissionsFunction.java b/exist-core/src/main/java/org/exist/xquery/functions/securitymanager/PermissionsFunction.java
index 7d6cc2cfdb..216f37569b 100644
--- a/exist-core/src/main/java/org/exist/xquery/functions/securitymanager/PermissionsFunction.java
+++ b/exist-core/src/main/java/org/exist/xquery/functions/securitymanager/PermissionsFunction.java
@@ -1,4 +1,28 @@
/*
+ * Elemental
+ * Copyright (C) 2024, Evolved Binary Ltd
+ *
+ * admin@evolvedbinary.com
+ * https://www.evolvedbinary.com | https://www.elemental.xyz
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; version 2.1.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * NOTE: Parts of this file contain code from 'The eXist-db Authors'.
+ * The original license header is included below.
+ *
+ * =====================================================================
+ *
* eXist-db Open Source Native XML Database
* Copyright (C) 2001 The eXist-db Authors
*
@@ -22,15 +46,17 @@
package org.exist.xquery.functions.securitymanager;
import org.exist.collections.Collection;
-import org.exist.dom.persistent.DocumentImpl;
import org.exist.dom.QName;
import org.exist.dom.memtree.MemTreeBuilder;
+import org.exist.dom.persistent.LockedDocument;
import org.exist.security.*;
import org.exist.security.ACLPermission.ACE_ACCESS_TYPE;
import org.exist.security.ACLPermission.ACE_TARGET;
import org.exist.storage.DBBroker;
+import org.exist.storage.lock.Lock;
import org.exist.storage.txn.TransactionException;
import org.exist.storage.txn.Txn;
+import org.exist.util.LockException;
import org.exist.util.SyntaxException;
import org.exist.xmldb.XmldbURI;
import org.exist.xquery.BasicFunction;
@@ -52,7 +78,7 @@
/**
*
- * @author Adam Retter
+ * @author Adam Retter
*/
public class PermissionsFunction extends BasicFunction {
@@ -409,20 +435,49 @@ private Sequence functionOctalToMode(final String octal) {
}
private Permission getPermissions(final XmldbURI pathUri) throws XPathException, PermissionDeniedException {
- final Permission permissions;
- final Collection col = context.getBroker().getCollection(pathUri);
- if(col != null) {
- permissions = col.getPermissionsNoLock();
+ final DBBroker broker = context.getBroker();
+ if (pathUri.equals(XmldbURI.DB)) {
+ // Can only be a Collection (i.e. `/db`)
+ try (final Collection rootCollection = broker.openCollection(pathUri, Lock.LockMode.READ_LOCK)) {
+ return rootCollection.getPermissions();
+ }
+
} else {
- final DocumentImpl doc = context.getBroker().getResource(pathUri, Permission.READ);
- if(doc != null) {
- permissions = doc.getPermissions();
- } else {
- throw new XPathException(this, "Resource or collection '" + pathUri.toString() + "' does not exist.");
+ // Interrogate the parent Collection so that we can efficiently work out if this URI indicates a Collection or Document
+ final XmldbURI parentCollectionUri = pathUri.removeLastSegment();
+ final XmldbURI resourceName = pathUri.lastSegment();
+
+ try (final Collection parentCollection = broker.openCollection(parentCollectionUri, Lock.LockMode.READ_LOCK)) {
+
+ if (parentCollection.hasChildCollectionNoLock(broker, resourceName)) {
+ // URI indicates a Collection
+
+ try (final Collection collection = broker.openCollection(pathUri, Lock.LockMode.READ_LOCK)) {
+
+ // asymmetrical locking - unlock parent
+ parentCollection.close();
+
+ return collection.getPermissions();
+ }
+ } else {
+ // URI indicates a Document
+
+ try (final LockedDocument lockedDocument = parentCollection.getDocumentWithLock(broker, resourceName, Lock.LockMode.READ_LOCK)) {
+
+ // asymmetrical locking - unlock parent
+ parentCollection.close();
+
+ if (lockedDocument != null) {
+ return lockedDocument.getDocument().getPermissions();
+ }
+ } catch (final LockException e) {
+ throw new PermissionDeniedException(e.getMessage(), e);
+ }
+ }
}
- }
- return permissions;
+ throw new XPathException(this, "Resource or collection '" + pathUri + "' does not exist.");
+ }
}
private org.exist.dom.memtree.DocumentImpl permissionsToXml(final Permission permission) {