You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by kw...@apache.org on 2023/04/18 06:59:03 UTC
[jackrabbit-filevault] 02/03: Add method to persist the principal ACLs
This is an automated email from the ASF dual-hosted git repository.
kwin pushed a commit to branch bugfix/JCRVLT-683
in repository https://gitbox.apache.org/repos/asf/jackrabbit-filevault.git
commit 1abee0280b1902009bb6b11cfd154130470298ed
Author: Konrad Windszus <kw...@apache.org>
AuthorDate: Sun Apr 16 12:28:34 2023 +0200
Add method to persist the principal ACLs
---
.../vault/fs/impl/io/DocViewImporter.java | 7 +-
.../jackrabbit/vault/fs/spi/ACLManagement.java | 16 +++
.../spi/impl/jcr20/JackrabbitUserManagement.java | 28 +++--
.../vault/fs/spi/impl/jcr20/JcrACLManagement.java | 140 +++++++++++++++++++++
.../META-INF/vault/filter.xml | 2 +-
.../spi/impl/AccessControlValidator.java | 3 +-
6 files changed, 185 insertions(+), 11 deletions(-)
diff --git a/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/DocViewImporter.java b/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/DocViewImporter.java
index 7ac3f3ad..dfb92644 100644
--- a/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/DocViewImporter.java
+++ b/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/DocViewImporter.java
@@ -54,6 +54,7 @@ import javax.jcr.nodetype.NoSuchNodeTypeException;
import javax.jcr.nodetype.NodeDefinition;
import javax.jcr.nodetype.NodeType;
import javax.jcr.nodetype.PropertyDefinition;
+import javax.jcr.security.AccessControlManager;
import javax.jcr.version.VersionException;
import org.apache.jackrabbit.spi.Name;
@@ -481,6 +482,10 @@ public class DocViewImporter implements DocViewParserHandler {
}
}
}
+
+ // TODO: how to find authorizables in the quickest way?
+ aclManagement.getPrincipalAcls(child);
+
if (shouldRemoveChild) {
importInfo.onDeleted(path);
child.remove();
@@ -736,7 +741,7 @@ public class DocViewImporter implements DocViewParserHandler {
}
// just import the authorizable node
- log.trace("Authorizable element detected. starting sysview transformation {}", newPath);
+ log.trace("Authorizable element detected. Starting sysview transformation {}", newPath);
stack = stack.push();
stack.adapter = new JcrSysViewTransformer(node, wspFilter.getImportMode(newPath));
stack.adapter.startNode(docViewNode);
diff --git a/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/spi/ACLManagement.java b/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/spi/ACLManagement.java
index 90703db5..faa57c54 100644
--- a/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/spi/ACLManagement.java
+++ b/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/spi/ACLManagement.java
@@ -17,9 +17,14 @@
package org.apache.jackrabbit.vault.fs.spi;
+import java.util.List;
+import java.util.Map;
+
import javax.jcr.Node;
import javax.jcr.RepositoryException;
+import javax.jcr.security.AccessControlPolicy;
+import org.jetbrains.annotations.NotNull;
import org.osgi.annotation.versioning.ProviderType;
/**
@@ -70,4 +75,15 @@ public interface ACLManagement {
* @throws RepositoryException if an error occurs
*/
void clearACL(Node node) throws RepositoryException;
+
+ /**
+ *
+ * @param node the start node from where to collect principal policies
+ * @return all collected principal access control policies per principal name inside the given node (even nested ones)
+ * @throws RepositoryException in case some error occurred while collecting the principal policies
+ * @see <a href="https://jackrabbit.apache.org/archive/wiki/JCR/AccessControl_115513330.html">Access Control</a>
+ * @since 3.6.10
+ */
+ @NotNull
+ Map<String, List<AccessControlPolicy>> getPrincipalAcls(Node node) throws RepositoryException;
}
\ No newline at end of file
diff --git a/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/spi/impl/jcr20/JackrabbitUserManagement.java b/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/spi/impl/jcr20/JackrabbitUserManagement.java
index 2c1086be..04c313c9 100644
--- a/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/spi/impl/jcr20/JackrabbitUserManagement.java
+++ b/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/spi/impl/jcr20/JackrabbitUserManagement.java
@@ -55,21 +55,33 @@ public class JackrabbitUserManagement implements UserManagement {
* {@inheritDoc}
*/
public boolean isAuthorizableNodeType(String ntName) {
- return ntName.equals("rep:Group") || ntName.equals("rep:User");
+ return ntName.equals("rep:Group") || ntName.equals("rep:User") || ntName.equals("rep:SystemUser");
}
/**
* {@inheritDoc}
*/
- public String getAuthorizablePath(Session session, String name) {
- // currently we rely on the implementation detail to keep the API dependency to jackrabbit < 2.3.
+ public String getAuthorizablePath(Session session, String id) {
+ UserManager uMgr;
try {
- UUID uuid = UUID.nameUUIDFromBytes(name.toLowerCase().getBytes("UTF-8"));
- return session.getNodeByIdentifier(uuid.toString()).getPath();
- } catch (Exception e) {
- // ignore
+ uMgr = ((JackrabbitSession) session).getUserManager();
+ } catch (RepositoryException e) {
+ log.warn("Unable to get authorizable path of {}. Error while retrieving user manager.", id, e);
+ return null;
+ }
+ Authorizable authorizable;
+ try {
+ authorizable = uMgr.getAuthorizable(id);
+ if (authorizable == null) {
+ log.debug("No existing authorizable with id {} found", id);
+ return null;
+ }
+ return authorizable.getPath();
+ } catch (RepositoryException e) {
+ log.warn("Unable to get authorizable path of {}: {}", id, e.getMessage(), e);
+ return null;
}
- return null;
+
}
@Override
diff --git a/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/spi/impl/jcr20/JcrACLManagement.java b/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/spi/impl/jcr20/JcrACLManagement.java
index bd2ab782..0090cd0f 100644
--- a/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/spi/impl/jcr20/JcrACLManagement.java
+++ b/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/spi/impl/jcr20/JcrACLManagement.java
@@ -17,12 +17,32 @@
package org.apache.jackrabbit.vault.fs.spi.impl.jcr20;
+import java.security.Principal;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import java.util.function.Consumer;
+
import javax.jcr.Node;
import javax.jcr.RepositoryException;
+import javax.jcr.Session;
import javax.jcr.security.AccessControlManager;
import javax.jcr.security.AccessControlPolicy;
+import org.apache.jackrabbit.api.JackrabbitSession;
+import org.apache.jackrabbit.api.security.JackrabbitAccessControlManager;
+import org.apache.jackrabbit.api.security.user.Authorizable;
+import org.apache.jackrabbit.api.security.user.Group;
+import org.apache.jackrabbit.api.security.user.User;
+import org.apache.jackrabbit.api.security.user.UserManager;
+import org.apache.jackrabbit.commons.JcrUtils;
+import org.apache.jackrabbit.util.Text;
import org.apache.jackrabbit.vault.fs.spi.ACLManagement;
+import org.apache.jackrabbit.vault.fs.spi.UserManagement;
+import org.jetbrains.annotations.NotNull;
/**
* {@code JcrACLManagement}...
@@ -30,6 +50,34 @@ import org.apache.jackrabbit.vault.fs.spi.ACLManagement;
*/
public class JcrACLManagement implements ACLManagement {
+ private String groupsRootPath;
+ private String usersRootPath;
+ private final UserManagement userManagement;
+
+ public JcrACLManagement() {
+ // figure out the root paths of users and groups
+ // how to get the securityProvider in Oak (https://issues.apache.org/jira/browse/OAK-9416) was never implemented
+ userManagement = new JackrabbitUserManagement();
+ }
+
+ /**
+ * Determines the authorizable root paths (as Jackrabbit/Oak stores authorizables inside the repo below a dedicated root path)
+ * @param session
+ * @throws RepositoryException
+ */
+ private synchronized void determineAuthorizableRootPaths(Session session) throws RepositoryException {
+ JackrabbitSession jrSession = (JackrabbitSession)session;
+ UserManager userMgr = jrSession.getUserManager();
+ // userMgr.autoSave(false) is not supported by Oak
+ String testAuthorizableId = UUID.randomUUID().toString();
+ Group group = userMgr.createGroup(new SimplePrincipal(testAuthorizableId), "intermediate");
+ groupsRootPath = Text.getRelativeParent(group.getPath(), 2);
+ group.remove();
+ User user = userMgr.createUser(testAuthorizableId, "test", new SimplePrincipal(testAuthorizableId), "intermediate");
+ usersRootPath = Text.getRelativeParent(user.getPath(), 2);
+ user.remove();
+ }
+
/**
* {@inheritDoc}
*/
@@ -102,4 +150,96 @@ public class JcrACLManagement implements ACLManagement {
private static boolean isRootNode(Node node) throws RepositoryException {
return node.getDepth() == 0;
}
+
+ private boolean areAuthorizablesAllowedBelowPath(Session session, String nodePath) throws RepositoryException {
+ if (usersRootPath == null || groupsRootPath == null) {
+ determineAuthorizableRootPaths(session);
+ }
+ return nodePath.startsWith(usersRootPath) || nodePath.startsWith(groupsRootPath);
+ }
+
+ @Override
+ public @NotNull Map<String, List<AccessControlPolicy>> getPrincipalAcls(Node node) throws RepositoryException {
+ // first do a quick check if path may contain principal ACLs at all before triggering expensive traversal
+ if (!areAuthorizablesAllowedBelowPath(node.getSession(), node.getPath())) {
+ // TODO: Oak does not allow principal based authorizables everywhere, so we may restrict further
+ return Collections.emptyMap();
+ }
+ JackrabbitSession jrSession = (JackrabbitSession)node.getSession();
+ AccessControlManager acMgr = jrSession.getAccessControlManager();
+ if (!(acMgr instanceof JackrabbitAccessControlManager)) {
+ throw new RepositoryException("The access control manager returned is no JackrabbitAccessControlManager, this is probably not a Jackrabbit/Oak repository");
+ }
+ JackrabbitAccessControlManager jrAcMgr = (JackrabbitAccessControlManager) acMgr;
+ PrincipalAccessControlPolicyCollector policiesCollector = new PrincipalAccessControlPolicyCollector(jrAcMgr);
+ findPrincipalsRecursively(jrSession.getUserManager(), node, policiesCollector);
+ return policiesCollector.getPoliciesPerPrincipal();
+ }
+
+ private static final class SimplePrincipal implements Principal {
+ private final String name;
+
+ SimplePrincipal(String name) {
+ if(name == null) {
+ throw new IllegalArgumentException("Name cannot be null");
+ }
+ this.name = name;
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return (
+ other instanceof SimplePrincipal)
+ && (this.name.equals(((SimplePrincipal)other).name));
+ }
+
+ @Override
+ public int hashCode() {
+ return name.hashCode();
+ }
+ }
+
+ private static final class PrincipalAccessControlPolicyCollector implements Consumer<Principal> {
+
+ private final JackrabbitAccessControlManager jrAcMgr;
+ private final Map<String, List<AccessControlPolicy>> policiesPerPrincipal;
+
+ public PrincipalAccessControlPolicyCollector(JackrabbitAccessControlManager jrAcMgr) {
+ super();
+ this.jrAcMgr = jrAcMgr;
+ this.policiesPerPrincipal = new HashMap<>();
+ }
+
+ public Map<String, List<AccessControlPolicy>> getPoliciesPerPrincipal() {
+ return policiesPerPrincipal;
+ }
+
+ @Override
+ public void accept(Principal principal) {
+ try {
+ policiesPerPrincipal.put(principal.getName(), Arrays.asList(jrAcMgr.getPolicies(principal)));
+ } catch (RepositoryException e) {
+ //throw new UncheckedRepositoryException()
+ }
+ }
+ }
+
+ private void findPrincipalsRecursively(UserManager userMgr, Node node, Consumer<Principal> principalConsumer) throws RepositoryException {
+ if (userManagement.isAuthorizableNodeType(node.getPrimaryNodeType().getName())) {
+ // what is this authorizable's name?
+ Authorizable authorizable = userMgr.getAuthorizableByPath(node.getPath());
+ if (authorizable != null) {
+ principalConsumer.accept(authorizable.getPrincipal());
+ }
+ } else {
+ for (Node child : JcrUtils.in(node.getNodes())) {
+ findPrincipalsRecursively(userMgr, child, principalConsumer);
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/vault-core/src/test/resources/test-packages/principalbased_nopolicy.zip/META-INF/vault/filter.xml b/vault-core/src/test/resources/test-packages/principalbased_nopolicy.zip/META-INF/vault/filter.xml
index 44bdf212..87f5c26f 100644
--- a/vault-core/src/test/resources/test-packages/principalbased_nopolicy.zip/META-INF/vault/filter.xml
+++ b/vault-core/src/test/resources/test-packages/principalbased_nopolicy.zip/META-INF/vault/filter.xml
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<workspaceFilter version="1.0">
- <filter root="/home/users/system/intermediate" mode="update"/>
+ <filter root="/home/users/system/intermediate" mode="update_properties"/>
</workspaceFilter>
diff --git a/vault-validation/src/main/java/org/apache/jackrabbit/vault/validation/spi/impl/AccessControlValidator.java b/vault-validation/src/main/java/org/apache/jackrabbit/vault/validation/spi/impl/AccessControlValidator.java
index dcfda887..9b93d6db 100644
--- a/vault-validation/src/main/java/org/apache/jackrabbit/vault/validation/spi/impl/AccessControlValidator.java
+++ b/vault-validation/src/main/java/org/apache/jackrabbit/vault/validation/spi/impl/AccessControlValidator.java
@@ -20,6 +20,7 @@ import java.util.Collection;
import java.util.Collections;
import org.apache.jackrabbit.vault.fs.io.AccessControlHandling;
+import org.apache.jackrabbit.vault.fs.spi.ACLManagement;
import org.apache.jackrabbit.vault.fs.spi.impl.jcr20.JcrACLManagement;
import org.apache.jackrabbit.vault.util.DocViewNode2;
import org.apache.jackrabbit.vault.validation.spi.DocumentViewXmlValidator;
@@ -34,7 +35,7 @@ import org.jetbrains.annotations.Nullable;
*/
public class AccessControlValidator implements DocumentViewXmlValidator {
- protected static final JcrACLManagement ACL_MANAGEMENT = new JcrACLManagement();
+ protected static final ACLManagement ACL_MANAGEMENT = new JcrACLManagement();
protected static final String MESSAGE_IGNORED_ACCESS_CONTROL_LIST = "Found an access control list, but it is never considered during installation as the property 'acHandling' is set to '%s'!";
protected static final String MESSAGE_INEFFECTIVE_ACCESS_CONTROL_LIST = "Found no access control list, but there is supposed to be one contained as the property 'acHandling' is set to '%s'!";
private final ValidationMessageSeverity severity;