Added: 
jackrabbit/oak/trunk/oak-authorization-principalbased/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/principalbased/impl/PrincipalPolicyValidatorProvider.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-authorization-principalbased/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/principalbased/impl/PrincipalPolicyValidatorProvider.java?rev=1857551&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-authorization-principalbased/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/principalbased/impl/PrincipalPolicyValidatorProvider.java
 (added)
+++ 
jackrabbit/oak/trunk/oak-authorization-principalbased/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/principalbased/impl/PrincipalPolicyValidatorProvider.java
 Mon Apr 15 07:16:49 2019
@@ -0,0 +1,270 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package 
org.apache.jackrabbit.oak.spi.security.authorization.principalbased.impl;
+
+import com.google.common.base.Strings;
+import com.google.common.collect.Iterables;
+import org.apache.jackrabbit.JcrConstants;
+import org.apache.jackrabbit.api.security.authorization.PrivilegeManager;
+import org.apache.jackrabbit.oak.api.CommitFailedException;
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.api.Root;
+import org.apache.jackrabbit.oak.api.Tree;
+import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.commons.PathUtils;
+import org.apache.jackrabbit.oak.namepath.NamePathMapper;
+import org.apache.jackrabbit.oak.plugins.nodetype.TypePredicate;
+import org.apache.jackrabbit.oak.plugins.tree.TreeUtil;
+import org.apache.jackrabbit.oak.spi.commit.CommitInfo;
+import org.apache.jackrabbit.oak.spi.commit.DefaultValidator;
+import org.apache.jackrabbit.oak.spi.commit.Validator;
+import org.apache.jackrabbit.oak.spi.commit.ValidatorProvider;
+import org.apache.jackrabbit.oak.spi.commit.VisibleValidator;
+import org.apache.jackrabbit.oak.spi.nodetype.NodeTypeConstants;
+import 
org.apache.jackrabbit.oak.spi.security.authorization.AuthorizationConfiguration;
+import 
org.apache.jackrabbit.oak.spi.security.authorization.permission.PermissionProvider;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.apache.jackrabbit.oak.spi.state.NodeStateUtils;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.jcr.RepositoryException;
+import javax.jcr.security.AccessControlException;
+import javax.jcr.security.Privilege;
+import java.security.Principal;
+import java.util.Set;
+
+import static com.google.common.base.Preconditions.checkState;
+import static org.apache.jackrabbit.oak.api.CommitFailedException.ACCESS;
+import static 
org.apache.jackrabbit.oak.api.CommitFailedException.ACCESS_CONTROL;
+import static org.apache.jackrabbit.oak.api.CommitFailedException.CONSTRAINT;
+import static org.apache.jackrabbit.oak.api.CommitFailedException.OAK;
+
+class PrincipalPolicyValidatorProvider extends ValidatorProvider implements 
Constants {
+
+    private final MgrProvider mgrProvider;
+    private final Set<Principal> principals;
+    private final String workspaceName;
+
+    private PermissionProvider permissionProvider;
+    private TypePredicate isMixPrincipalBased;
+
+    PrincipalPolicyValidatorProvider(@NotNull MgrProvider mgrProvider, 
@NotNull Set<Principal> principals, @NotNull String workspaceName) {
+        this.mgrProvider = mgrProvider;
+        this.principals = principals;
+        this.workspaceName = workspaceName;
+    }
+
+    @Override
+    protected PolicyValidator getRootValidator(NodeState before, NodeState 
after, CommitInfo info) {
+        Root rootBefore = 
mgrProvider.getRootProvider().createReadOnlyRoot(before);
+        permissionProvider = 
mgrProvider.getSecurityProvider().getConfiguration(AuthorizationConfiguration.class).getPermissionProvider(rootBefore,
 workspaceName, principals);
+        isMixPrincipalBased = new TypePredicate(after, 
MIX_REP_PRINCIPAL_BASED_MIXIN);
+        return new PolicyValidator(before, after);
+    }
+
+    private final class PolicyValidator extends DefaultValidator {
+
+        private final Tree parentBefore;
+        private final Tree parentAfter;
+        private final boolean isNodetypeTree;
+
+        private PolicyValidator(@NotNull NodeState rootStateBefore, @NotNull 
NodeState rootState) {
+            
mgrProvider.reset(mgrProvider.getRootProvider().createReadOnlyRoot(rootState), 
NamePathMapper.DEFAULT);
+            this.parentBefore = 
mgrProvider.getTreeProvider().createReadOnlyTree(rootStateBefore);
+            this.parentAfter = 
mgrProvider.getTreeProvider().createReadOnlyTree(rootState);
+            this.isNodetypeTree = false;
+        }
+
+        private PolicyValidator(@NotNull PolicyValidator parentValidator, 
@NotNull Tree before, @NotNull Tree after) {
+            this.parentBefore = before;
+            this.parentAfter = after;
+            if (parentValidator.isNodetypeTree) {
+                this.isNodetypeTree = true;
+            } else {
+                this.isNodetypeTree = 
NodeTypeConstants.JCR_NODE_TYPES.equals(after.getName()) && 
NodeTypeConstants.JCR_SYSTEM.equals(parentValidator.getName());
+            }
+        }
+
+        private PolicyValidator(@NotNull PolicyValidator parentValidator, 
@NotNull Tree tree, boolean isAfter) {
+            this.parentBefore = (isAfter) ? null : tree;
+            this.parentAfter = (isAfter) ? tree : null;
+            if (parentValidator.isNodetypeTree) {
+                this.isNodetypeTree = true;
+            } else {
+                this.isNodetypeTree = 
NodeTypeConstants.JCR_NODE_TYPES.equals(tree.getName()) && 
NodeTypeConstants.JCR_SYSTEM.equals(parentValidator.getName());
+            }
+        }
+
+        @NotNull
+        private String getName() {
+            return (parentBefore == null) ? 
verifyNotNull(parentAfter).getName() : parentBefore.getName();
+        }
+
+        //------------------------------------------------------< Validator 
>---
+        @Override
+        public void propertyAdded(PropertyState after) throws 
CommitFailedException {
+            String propertyName = after.getName();
+            if (JcrConstants.JCR_PRIMARYTYPE.equals(propertyName)) {
+                if (NT_REP_PRINCIPAL_POLICY.equals(after.getValue(Type.NAME)) 
&& !REP_PRINCIPAL_POLICY.equals(verifyNotNull(parentAfter).getName())) {
+                    throw accessControlViolation(30, "Attempt create policy 
node with different name than '"+REP_PRINCIPAL_POLICY+"'.");
+                }
+            }
+        }
+
+        @Override
+        public void propertyChanged(PropertyState before, PropertyState after) 
throws CommitFailedException {
+            String name = after.getName();
+            if (JcrConstants.JCR_PRIMARYTYPE.equals(name)) {
+                if 
(NT_REP_PRINCIPAL_POLICY.equals(before.getValue(Type.STRING)) || 
NT_REP_PRINCIPAL_POLICY.equals(after.getValue(Type.STRING))) {
+                    throw accessControlViolation(31, "Attempt to change 
primary type of/to rep:PrincipalPolicy.");
+                }
+            }
+        }
+
+        @Override
+        public Validator childNodeAdded(String name, NodeState after) throws 
CommitFailedException {
+            if (!isNodetypeTree) {
+                if (REP_PRINCIPAL_POLICY.equals(name)) {
+                    validatePolicyNode(verifyNotNull(parentAfter), after);
+                } else if (REP_RESTRICTIONS.equals(name)) {
+                    validateRestrictions(after);
+                } else if 
(NT_REP_PRINCIPAL_ENTRY.equals(NodeStateUtils.getPrimaryTypeName(after))) {
+                    validateEntry(name, after);
+                }
+            }
+            return new VisibleValidator(nextValidator(name, after, true), 
true, true);
+        }
+
+        @Override
+        public Validator childNodeChanged(String name, NodeState before, 
NodeState after) throws CommitFailedException {
+            if (!isNodetypeTree) {
+                if (after.hasChildNode(REP_PRINCIPAL_POLICY)) {
+                    Tree parent = 
mgrProvider.getTreeProvider().createReadOnlyTree(verifyNotNull(parentAfter), 
name, after);
+                    validatePolicyNode(parent, 
after.getChildNode(REP_PRINCIPAL_POLICY));
+                } else if (REP_RESTRICTIONS.equals(name)) {
+                    validateRestrictions(after);
+                } else if 
(NT_REP_PRINCIPAL_ENTRY.equals(NodeStateUtils.getPrimaryTypeName(after))) {
+                    validateEntry(name, after);
+                }
+            }
+            return new VisibleValidator(nextValidator(name, before, after), 
true, true);
+        }
+
+        @Override
+        public Validator childNodeDeleted(String name, NodeState before) 
throws CommitFailedException {
+            if (!isNodetypeTree) {
+                PropertyState effectivePath = null;
+                if (REP_RESTRICTIONS.equals(name)) {
+                    effectivePath = 
verifyNotNull(parentBefore).getProperty(REP_EFFECTIVE_PATH);
+                } else if 
(NT_REP_PRINCIPAL_ENTRY.equals(NodeStateUtils.getPrimaryTypeName(before))) {
+                    effectivePath = before.getProperty(REP_EFFECTIVE_PATH);
+                }
+                if (effectivePath != null && 
!Utils.hasModAcPermission(permissionProvider, 
effectivePath.getValue(Type.PATH))) {
+                    throw new CommitFailedException(ACCESS, 3, "Access 
denied");
+                }
+            }
+            return new VisibleValidator(nextValidator(name, before, false), 
true, true);        }
+
+        
//----------------------------------------------------------------------
+        private void validatePolicyNode(@NotNull Tree parent, @NotNull 
NodeState nodeState) throws CommitFailedException {
+            if 
(!NT_REP_PRINCIPAL_POLICY.equals(NodeStateUtils.getPrimaryTypeName(nodeState))) 
{
+                throw accessControlViolation(32, "Reserved node name 
'rep:principalPolicy' must only be used for nodes of type 
'rep:PrincipalPolicy'.");
+            }
+            if (!isMixPrincipalBased.apply(parent)) {
+                throw accessControlViolation(33, "Parent node not of mixin 
type 'rep:PrincipalBasedMixin'.");
+            }
+        }
+
+        private void validateRestrictions(@NotNull NodeState nodeState) throws 
CommitFailedException {
+            if 
(!NT_REP_RESTRICTIONS.equals(NodeStateUtils.getPrimaryTypeName(nodeState))) {
+                throw accessControlViolation(34, "Reserved node name 
'rep:restrictions' must only be used for nodes of type 'rep:Restrictions'.");
+            }
+            Tree parent = verifyNotNull(parentAfter);
+            if 
(NT_REP_PRINCIPAL_ENTRY.equals(TreeUtil.getPrimaryTypeName(parent))) {
+                try {
+                    String oakPath = 
Strings.emptyToNull(TreeUtil.getString(parent, REP_EFFECTIVE_PATH));
+                    
mgrProvider.getRestrictionProvider().validateRestrictions(oakPath, parent);
+                } catch (AccessControlException e) {
+                    throw new CommitFailedException(ACCESS_CONTROL, 35, 
"Invalid restrictions", e);
+                } catch (RepositoryException e) {
+                    throw new CommitFailedException(OAK, 13, "Internal error", 
e);
+                }
+            } else {
+                // assert the restrictions node resides within access control 
content
+                if (!mgrProvider.getContext().definesTree(parent)) {
+                    throw new CommitFailedException(ACCESS_CONTROL, 2, 
"Expected access control entry parent (isolated restriction).");
+                }
+            }
+        }
+
+        private void validateEntry(@NotNull String name, @NotNull NodeState 
nodeState) throws CommitFailedException {
+            Tree parent = verifyNotNull(parentAfter);
+            String entryPath = PathUtils.concat(parent.getPath(), name);
+            if (!REP_PRINCIPAL_POLICY.equals(parent.getName())) {
+                throw accessControlViolation(36, "Isolated entry of principal 
policy at " + entryPath);
+            }
+            Iterable<String> privilegeNames = 
nodeState.getNames(REP_PRIVILEGES);
+            if (Iterables.isEmpty(privilegeNames)) {
+                throw accessControlViolation(37, "Empty rep:privileges 
property at " + entryPath);
+            }
+            PrivilegeManager privilegeManager = 
mgrProvider.getPrivilegeManager();
+            for (String privilegeName : privilegeNames) {
+                try {
+                    Privilege privilege = 
privilegeManager.getPrivilege(privilegeName);
+                    if (privilege.isAbstract()) {
+                        throw accessControlViolation(38, "Abstract privilege " 
+ privilegeName + " at " + entryPath);
+                    }
+                } catch (AccessControlException e) {
+                    throw accessControlViolation(39, "Invalid privilege " + 
privilegeName + " at " + entryPath);
+                } catch (RepositoryException e) {
+                    throw new CommitFailedException(OAK, 13, "Internal error", 
e);
+                }
+            }
+            // check mod-access-control permission on the effective path
+            PropertyState effectivePath = 
nodeState.getProperty(REP_EFFECTIVE_PATH);
+            if (effectivePath == null) {
+                throw new CommitFailedException(CONSTRAINT, 21, "Missing 
mandatory rep:effectivePath property at " + entryPath);
+            }
+            if (!Utils.hasModAcPermission(permissionProvider, 
effectivePath.getValue(Type.PATH))) {
+                throw new CommitFailedException(ACCESS, 3, "Access denied");
+            }
+        }
+
+        private CommitFailedException accessControlViolation(int code, String 
message) {
+            return new CommitFailedException(ACCESS_CONTROL, code, message);
+        }
+
+        private PolicyValidator nextValidator(@NotNull String name, @NotNull 
NodeState beforeState, @NotNull NodeState afterState) {
+            Tree before = 
mgrProvider.getTreeProvider().createReadOnlyTree(verifyNotNull(parentBefore), 
name, beforeState);
+            Tree after = 
mgrProvider.getTreeProvider().createReadOnlyTree(verifyNotNull(parentAfter), 
name, afterState);
+            return new PolicyValidator(this, before, after);
+        }
+
+        private PolicyValidator nextValidator(@NotNull String name, @NotNull 
NodeState nodeState, boolean isAfter) {
+            Tree parent = (isAfter) ? parentAfter : parentBefore;
+            Tree tree = 
mgrProvider.getTreeProvider().createReadOnlyTree(verifyNotNull(parent), name, 
nodeState);
+            return new PolicyValidator(this, tree, isAfter);
+        }
+
+        @NotNull
+        private Tree verifyNotNull(@Nullable Tree tree) {
+            checkState(tree != null);
+            return tree;
+        }
+    }
+}

Propchange: 
jackrabbit/oak/trunk/oak-authorization-principalbased/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/principalbased/impl/PrincipalPolicyValidatorProvider.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: 
jackrabbit/oak/trunk/oak-authorization-principalbased/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/principalbased/impl/Utils.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-authorization-principalbased/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/principalbased/impl/Utils.java?rev=1857551&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-authorization-principalbased/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/principalbased/impl/Utils.java
 (added)
+++ 
jackrabbit/oak/trunk/oak-authorization-principalbased/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/principalbased/impl/Utils.java
 Mon Apr 15 07:16:49 2019
@@ -0,0 +1,124 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package 
org.apache.jackrabbit.oak.spi.security.authorization.principalbased.impl;
+
+import com.google.common.base.Predicates;
+import com.google.common.base.Strings;
+import com.google.common.collect.Collections2;
+import org.apache.jackrabbit.api.security.authorization.PrivilegeManager;
+import org.apache.jackrabbit.oak.api.Tree;
+import org.apache.jackrabbit.oak.namepath.NamePathMapper;
+import org.apache.jackrabbit.oak.plugins.tree.TreeUtil;
+import 
org.apache.jackrabbit.oak.spi.security.authorization.permission.PermissionProvider;
+import 
org.apache.jackrabbit.oak.spi.security.authorization.permission.Permissions;
+import 
org.apache.jackrabbit.oak.spi.security.authorization.principalbased.Filter;
+import org.apache.jackrabbit.oak.spi.xml.ImportBehavior;
+import org.jetbrains.annotations.NotNull;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jcr.RepositoryException;
+import javax.jcr.security.AccessControlException;
+import javax.jcr.security.Privilege;
+import java.security.Principal;
+import java.util.Collections;
+import java.util.Set;
+
+final class Utils implements Constants {
+
+    private static final Logger log = LoggerFactory.getLogger(Utils.class);
+
+    private Utils() {}
+
+    /**
+     * Returns {@code true} if the given tree exists and represents a valid 
principal policy node, i.e. name equals to
+     * {@link #REP_PRINCIPAL_POLICY} and primary type name equals to {@link 
#NT_REP_PRINCIPAL_POLICY}. Otherwise this
+     * method returns {@code false}.
+     *
+     * @param tree The tree to be tested.
+     * @return {@code true} if the given tree exists and represents a valid 
principal policy node, i.e. name equals to
+     * {@link #REP_PRINCIPAL_POLICY} and primary type name equals to {@link 
#NT_REP_PRINCIPAL_POLICY}; otherwise
+     * returns {@code false}.
+     */
+    public static boolean isPrincipalPolicyTree(@NotNull Tree tree) {
+        return tree.exists() && REP_PRINCIPAL_POLICY.equals(tree.getName()) && 
NT_REP_PRINCIPAL_POLICY.equals(TreeUtil.getPrimaryTypeName(tree));
+    }
+
+    public static boolean isPrincipalEntry(@NotNull Tree tree) {
+        return 
NT_REP_PRINCIPAL_ENTRY.equals(TreeUtil.getPrimaryTypeName(tree));
+    }
+
+    /**
+     * Validate the specified {@code principal} taking the configured
+     * {@link ImportBehavior} into account.
+     *
+     * @param principal The principal to validate.
+     * @return if the principal can be handled by the filter
+     * @throws AccessControlException If the principal has an invalid name or
+     * if {@link ImportBehavior#ABORT} is configured and this principal cannot 
be handled by the filter.
+     */
+    public static boolean canHandle(@NotNull Principal principal, @NotNull 
Filter filter, int importBehavior) throws AccessControlException {
+        String name = principal.getName();
+        if (Strings.isNullOrEmpty(name)) {
+            throw new AccessControlException("Invalid principal " + name);
+        }
+
+        boolean canHandle = filter.canHandle(Collections.singleton(principal));
+        switch (importBehavior) {
+            case ImportBehavior.ABORT:
+                if (!canHandle) {
+                    throw new AccessControlException("Unsupported principal " 
+ name);
+                }
+                break;
+            case ImportBehavior.IGNORE:
+            case ImportBehavior.BESTEFFORT:
+                log.debug("Ignoring unsupported principal {}", name);
+                break;
+            default:
+                throw new IllegalArgumentException("Unsupported import 
behavior " + importBehavior);
+        }
+        return canHandle;
+    }
+
+    /**
+     * Returns an array of privileges from the given Oak names. Note that 
{@link RepositoryException} thrown by
+     * {@link PrivilegeManager#getPrivilege(String)} will be swallowed but 
notified in the error log.
+     *
+     * @param privilegeNames The Oak names of privileges as stored in the 
repository.
+     * @param privilegeManager The {@link PrivilegeManager} to retrieve the 
privileges.
+     * @param namePathMapper The {@link NamePathMapper} to convert the Oak 
names to JCR names.
+     * @return An array of {@link Privilege} for the given names.
+     */
+    public static Privilege[] privilegesFromOakNames(@NotNull Set<String> 
privilegeNames, @NotNull PrivilegeManager privilegeManager, @NotNull 
NamePathMapper namePathMapper) {
+        return Collections2.filter(Collections2.transform(privilegeNames, 
privilegeName -> {
+            try {
+                return 
privilegeManager.getPrivilege(namePathMapper.getJcrName(privilegeName));
+            } catch (RepositoryException e) {
+                log.error("Unknown privilege in access control entry : {}", 
privilegeName);
+                return null;
+            }
+        }), Predicates.notNull()).toArray(new Privilege[0]);
+    }
+
+    public static boolean hasModAcPermission(@NotNull PermissionProvider 
permissionProvider, @NotNull String effectivePath) {
+        if (REPOSITORY_PERMISSION_PATH.equals(effectivePath)) {
+            return 
permissionProvider.getRepositoryPermission().isGranted(Permissions.MODIFY_ACCESS_CONTROL);
+        } else {
+            return permissionProvider.isGranted(effectivePath, 
Permissions.getString(Permissions.MODIFY_ACCESS_CONTROL));
+        }
+    }
+}
\ No newline at end of file

Propchange: 
jackrabbit/oak/trunk/oak-authorization-principalbased/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/principalbased/impl/Utils.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: 
jackrabbit/oak/trunk/oak-authorization-principalbased/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/principalbased/package-info.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-authorization-principalbased/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/principalbased/package-info.java?rev=1857551&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-authorization-principalbased/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/principalbased/package-info.java
 (added)
+++ 
jackrabbit/oak/trunk/oak-authorization-principalbased/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/principalbased/package-info.java
 Mon Apr 15 07:16:49 2019
@@ -0,0 +1,20 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+@Version("1.0.0")
+package org.apache.jackrabbit.oak.spi.security.authorization.principalbased;
+
+import org.osgi.annotation.versioning.Version;

Propchange: 
jackrabbit/oak/trunk/oak-authorization-principalbased/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/principalbased/package-info.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: 
jackrabbit/oak/trunk/oak-authorization-principalbased/src/main/resources/org/apache/jackrabbit/oak/spi/security/authorization/principalbased/impl/nodetypes.cnd
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-authorization-principalbased/src/main/resources/org/apache/jackrabbit/oak/spi/security/authorization/principalbased/impl/nodetypes.cnd?rev=1857551&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-authorization-principalbased/src/main/resources/org/apache/jackrabbit/oak/spi/security/authorization/principalbased/impl/nodetypes.cnd
 (added)
+++ 
jackrabbit/oak/trunk/oak-authorization-principalbased/src/main/resources/org/apache/jackrabbit/oak/spi/security/authorization/principalbased/impl/nodetypes.cnd
 Mon Apr 15 07:16:49 2019
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+<rep='internal'>
+<jcr='http://www.jcp.org/jcr/1.0'>
+<nt='http://www.jcp.org/jcr/nt/1.0'>
+<mix='http://www.jcp.org/jcr/mix/1.0'>
+<oak='http://jackrabbit.apache.org/oak/ns/1.0'>
+
+// 
-----------------------------------------------------------------------------
+// Authorization: Principal Based
+// 
-----------------------------------------------------------------------------
+/**
+ * @since oak 1.14
+ */
+[rep:PrincipalBasedMixin]
+  mixin
+  + rep:principalPolicy (rep:PrincipalPolicy) protected IGNORE
+
+/**
+ * @since oak 1.14
+ */
+[rep:PrincipalPolicy] > rep:Policy
+  orderable
+  - rep:principalName (STRING) protected mandatory IGNORE
+  + * (rep:PrincipalEntry) = rep:PrincipalEntry protected IGNORE
+
+/**
+ * @since oak 1.14
+ */
+[rep:PrincipalEntry]
+  - rep:effectivePath (PATH) protected mandatory
+  - rep:privileges (NAME) multiple protected mandatory multiple
+  + rep:restrictions (rep:Restrictions) = rep:Restrictions protected

Propchange: 
jackrabbit/oak/trunk/oak-authorization-principalbased/src/main/resources/org/apache/jackrabbit/oak/spi/security/authorization/principalbased/impl/nodetypes.cnd
------------------------------------------------------------------------------
    svn:eol-style = native

Added: 
jackrabbit/oak/trunk/oak-authorization-principalbased/src/test/java/org/apache/jackrabbit/oak/spi/security/authorization/principalbased/impl/AbstractPrincipalBasedTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-authorization-principalbased/src/test/java/org/apache/jackrabbit/oak/spi/security/authorization/principalbased/impl/AbstractPrincipalBasedTest.java?rev=1857551&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-authorization-principalbased/src/test/java/org/apache/jackrabbit/oak/spi/security/authorization/principalbased/impl/AbstractPrincipalBasedTest.java
 (added)
+++ 
jackrabbit/oak/trunk/oak-authorization-principalbased/src/test/java/org/apache/jackrabbit/oak/spi/security/authorization/principalbased/impl/AbstractPrincipalBasedTest.java
 Mon Apr 15 07:16:49 2019
@@ -0,0 +1,227 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package 
org.apache.jackrabbit.oak.spi.security.authorization.principalbased.impl;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.ObjectArrays;
+import org.apache.jackrabbit.api.security.JackrabbitAccessControlList;
+import org.apache.jackrabbit.api.security.JackrabbitAccessControlManager;
+import org.apache.jackrabbit.api.security.JackrabbitAccessControlPolicy;
+import org.apache.jackrabbit.api.security.user.User;
+import 
org.apache.jackrabbit.commons.jackrabbit.authorization.AccessControlUtils;
+import org.apache.jackrabbit.oak.AbstractSecurityTest;
+import org.apache.jackrabbit.oak.api.Root;
+import org.apache.jackrabbit.oak.api.Tree;
+import org.apache.jackrabbit.oak.commons.PathUtils;
+import org.apache.jackrabbit.oak.namepath.impl.LocalNameMapper;
+import org.apache.jackrabbit.oak.namepath.impl.NamePathMapperImpl;
+import org.apache.jackrabbit.oak.plugins.tree.TreeUtil;
+import org.apache.jackrabbit.oak.security.internal.SecurityProviderHelper;
+import org.apache.jackrabbit.oak.spi.mount.Mounts;
+import org.apache.jackrabbit.oak.spi.security.SecurityProvider;
+import 
org.apache.jackrabbit.oak.spi.security.authorization.AuthorizationConfiguration;
+import 
org.apache.jackrabbit.oak.spi.security.authorization.permission.PermissionProvider;
+import 
org.apache.jackrabbit.oak.spi.security.authorization.principalbased.FilterProvider;
+import org.apache.jackrabbit.oak.spi.security.user.UserConstants;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.junit.Before;
+
+import javax.jcr.RepositoryException;
+import javax.jcr.Value;
+import javax.jcr.security.AccessControlManager;
+import javax.jcr.security.Privilege;
+import java.security.Principal;
+import java.util.Collections;
+import java.util.Map;
+import java.util.UUID;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static 
org.apache.jackrabbit.oak.spi.nodetype.NodeTypeConstants.NT_OAK_UNSTRUCTURED;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class AbstractPrincipalBasedTest extends AbstractSecurityTest {
+
+    static final String INTERMEDIATE_PATH = 
UserConstants.DEFAULT_SYSTEM_RELATIVE_PATH + "/test";
+    static final String SUPPORTED_PATH = 
PathUtils.concat(UserConstants.DEFAULT_USER_PATH, INTERMEDIATE_PATH);
+
+    static final String TEST_OAK_PATH = 
"/oak:content/child/grandchild/oak:subtree";
+
+    static final Map<String, String> LOCAL_NAME_MAPPINGS = ImmutableMap.of(
+            "a","internal",
+            "b","http://www.jcp.org/jcr/1.0";,
+            "c","http://jackrabbit.apache.org/oak/ns/1.0";
+    );
+
+    private User testSystemUser;
+    private MgrProvider mgrProvider;
+    private PrincipalBasedAuthorizationConfiguration 
principalBasedAuthorizationConfiguration;
+
+    String testJcrPath;
+    String testContentJcrPath;
+
+    @Before
+    public void before() throws Exception {
+        super.before();
+        namePathMapper = new NamePathMapperImpl(new LocalNameMapper(root, 
LOCAL_NAME_MAPPINGS));
+        testJcrPath =  getNamePathMapper().getJcrPath(TEST_OAK_PATH);
+        testContentJcrPath = PathUtils.getAncestorPath(testJcrPath, 3);
+    }
+
+    @Override
+    public void after() throws Exception {
+        try {
+            root.refresh();
+            if (testSystemUser != null) {
+                testSystemUser.remove();
+                root.commit();
+            }
+        } finally {
+            super.after();
+        }
+    }
+
+    @Override
+    @NotNull
+    protected SecurityProvider initSecurityProvider() {
+        SecurityProvider sp = super.initSecurityProvider();
+        principalBasedAuthorizationConfiguration = new 
PrincipalBasedAuthorizationConfiguration();
+        
principalBasedAuthorizationConfiguration.bindFilterProvider(getFilterProvider());
+        
principalBasedAuthorizationConfiguration.bindMountInfoProvider(Mounts.defaultMountInfoProvider());
+        SecurityProviderHelper.updateConfig(sp, 
principalBasedAuthorizationConfiguration, AuthorizationConfiguration.class);
+        return sp;
+    }
+
+    @Override
+    @NotNull
+    protected Privilege[] privilegesFromNames(@NotNull String... 
privilegeNames) throws RepositoryException {
+        Iterable<String> pn = 
Iterables.transform(ImmutableSet.copyOf(privilegeNames), privName -> 
getNamePathMapper().getJcrName(privName));
+        return super.privilegesFromNames(pn);
+    }
+
+    @NotNull
+    User getTestSystemUser() throws Exception {
+        if (testSystemUser == null) {
+            String uid = "testSystemUser" + UUID.randomUUID();
+            testSystemUser = getUserManager(root).createSystemUser(uid, 
INTERMEDIATE_PATH);
+            root.commit();
+        }
+        return testSystemUser;
+
+    }
+
+    void setupContentTrees(@NotNull String oakPath) throws Exception {
+        setupContentTrees(NT_OAK_UNSTRUCTURED, oakPath);
+    }
+
+    void setupContentTrees(@NotNull String ntName, @NotNull String... 
oakPaths) throws Exception {
+        Tree rootTree = root.getTree(PathUtils.ROOT_PATH);
+        for (String absPath : oakPaths) {
+            Tree t = rootTree;
+            for (String element : PathUtils.elements(absPath)) {
+                t = TreeUtil.getOrAddChild(t, element, ntName);
+            }
+        }
+    }
+
+    @NotNull
+    PrincipalPolicyImpl getPrincipalPolicyImpl(@NotNull Principal 
testPrincipal, @NotNull JackrabbitAccessControlManager acMgr) throws Exception {
+        for (JackrabbitAccessControlPolicy policy : 
ObjectArrays.concat(acMgr.getApplicablePolicies(testPrincipal), 
acMgr.getPolicies(testPrincipal), JackrabbitAccessControlPolicy.class)) {
+            if (policy instanceof PrincipalPolicyImpl) {
+                return (PrincipalPolicyImpl) policy;
+            }
+        }
+        throw new IllegalStateException("unable to obtain 
PrincipalPolicyImpl");
+    }
+
+    @NotNull
+    PrincipalPolicyImpl setupPrincipalBasedAccessControl(@NotNull Principal 
testPrincipal, @Nullable String effectivePath, @NotNull String... privNames) 
throws Exception {
+        // set principal-based policy for 'testPrincipal'
+        JackrabbitAccessControlManager jacm = getAccessControlManager(root);
+        PrincipalPolicyImpl policy = getPrincipalPolicyImpl(testPrincipal, 
jacm);
+        policy.addEntry(effectivePath, privilegesFromNames(privNames));
+        jacm.setPolicy(policy.getPath(), policy);
+        return policy;
+    }
+
+    boolean addPrincipalBasedEntry(@NotNull PrincipalPolicyImpl policy, 
@Nullable String effectivePath, @NotNull String... privNames) throws Exception {
+        boolean mod = policy.addEntry(effectivePath, 
privilegesFromNames(privNames));
+        getAccessControlManager(root).setPolicy(policy.getPath(), policy);
+        return mod;
+    }
+
+    boolean addDefaultEntry(@Nullable String path, @NotNull Principal 
principal, @NotNull String... privNames) throws Exception {
+        return addDefaultEntry(path, principal, null, null, privNames);
+    }
+
+    boolean addDefaultEntry(@Nullable String path, @NotNull Principal 
principal, @Nullable Map<String, Value> restr, @Nullable Map<String, Value[]> 
mvRestr, @NotNull String... privNames) throws Exception {
+        JackrabbitAccessControlManager jacm = getAccessControlManager(root);
+        JackrabbitAccessControlList acl = 
AccessControlUtils.getAccessControlList(jacm, path);
+        checkNotNull(acl);
+
+        boolean mod = acl.addEntry(principal, privilegesFromNames(privNames), 
true, restr, mvRestr);
+        jacm.setPolicy(acl.getPath(), acl);
+        return mod;
+    }
+
+    @NotNull
+    PrincipalBasedPermissionProvider createPermissionProvider(@NotNull Root 
root, @NotNull Principal... principals) {
+        PermissionProvider pp = 
principalBasedAuthorizationConfiguration.getPermissionProvider(root, 
root.getContentSession().getWorkspaceName(), ImmutableSet.copyOf(principals));
+        if (pp instanceof PrincipalBasedPermissionProvider) {
+            return (PrincipalBasedPermissionProvider) pp;
+        } else {
+            throw new IllegalStateException("not a 
PrincipalBasedPermissionProvider");
+        }
+    }
+
+    PrincipalBasedAccessControlManager createAccessControlManager(@NotNull 
Root root) {
+        AccessControlManager acMgr = 
principalBasedAuthorizationConfiguration.getAccessControlManager(root, 
getNamePathMapper());
+        if (acMgr instanceof PrincipalBasedAccessControlManager) {
+            return (PrincipalBasedAccessControlManager) acMgr;
+        } else {
+            throw new IllegalStateException("not a 
PrincipalBasedAccessControlManager");
+        }
+    }
+
+    @NotNull
+    FilterProvider getFilterProvider() {
+        return createFilterProviderImpl(SUPPORTED_PATH);
+    }
+
+    @NotNull
+    static FilterProviderImpl createFilterProviderImpl(@NotNull final String 
path) {
+        FilterProviderImpl fp = new FilterProviderImpl();
+        
fp.activate(when(mock(FilterProviderImpl.Configuration.class).path()).thenReturn(path).getMock(),
 Collections.emptyMap());
+        return fp;
+    }
+
+    @NotNull
+    MgrProvider getMgrProvider(Root root) {
+        if (mgrProvider == null) {
+            mgrProvider = new 
MgrProviderImpl(principalBasedAuthorizationConfiguration, root, 
getNamePathMapper());
+        }
+        return mgrProvider;
+    }
+
+    @NotNull
+    PrincipalBasedAuthorizationConfiguration 
getPrincipalBasedAuthorizationConfiguration() {
+        return principalBasedAuthorizationConfiguration;
+    }
+}
\ No newline at end of file

Propchange: 
jackrabbit/oak/trunk/oak-authorization-principalbased/src/test/java/org/apache/jackrabbit/oak/spi/security/authorization/principalbased/impl/AbstractPrincipalBasedTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: 
jackrabbit/oak/trunk/oak-authorization-principalbased/src/test/java/org/apache/jackrabbit/oak/spi/security/authorization/principalbased/impl/AbstractTreePermissionTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-authorization-principalbased/src/test/java/org/apache/jackrabbit/oak/spi/security/authorization/principalbased/impl/AbstractTreePermissionTest.java?rev=1857551&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-authorization-principalbased/src/test/java/org/apache/jackrabbit/oak/spi/security/authorization/principalbased/impl/AbstractTreePermissionTest.java
 (added)
+++ 
jackrabbit/oak/trunk/oak-authorization-principalbased/src/test/java/org/apache/jackrabbit/oak/spi/security/authorization/principalbased/impl/AbstractTreePermissionTest.java
 Mon Apr 15 07:16:49 2019
@@ -0,0 +1,145 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package 
org.apache.jackrabbit.oak.spi.security.authorization.principalbased.impl;
+
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.api.Tree;
+import org.apache.jackrabbit.oak.plugins.tree.TreeType;
+import 
org.apache.jackrabbit.oak.spi.security.authorization.permission.Permissions;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.jetbrains.annotations.NotNull;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertSame;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+public class AbstractTreePermissionTest {
+
+    private Tree tree;
+    private PrincipalBasedPermissionProvider pp;
+
+    @Before
+    public void before() {
+        tree = mock(Tree.class);
+        pp = mock(PrincipalBasedPermissionProvider.class);
+
+    }
+
+    private AbstractTreePermission createAbstractTreePermission(@NotNull Tree 
tree, @NotNull TreeType type, @NotNull PrincipalBasedPermissionProvider pp) {
+        return new AbstractTreePermission(tree, type) {
+            @Override
+            PrincipalBasedPermissionProvider getPermissionProvider() {
+                return pp;
+            }
+        };
+    }
+
+    @Test
+    public void testGetTree() {
+        AbstractTreePermission atp = createAbstractTreePermission(tree, 
TreeType.DEFAULT, pp);
+        assertEquals(tree, atp.getTree());
+    }
+
+    @Test
+    public void testGetType() {
+        AbstractTreePermission atp = createAbstractTreePermission(tree, 
TreeType.INTERNAL, pp);
+        assertSame(TreeType.INTERNAL, atp.getType());
+    }
+
+    @Test
+    public void testGetChildPermission() {
+        NodeState childState = mock(NodeState.class);
+        AbstractTreePermission atp = createAbstractTreePermission(tree, 
TreeType.HIDDEN, pp);
+        atp.getChildPermission("childName", childState);
+
+        verify(pp, times(1)).getTreePermission("childName", childState, atp);
+    }
+
+    @Test
+    public void testCanRead() {
+        AbstractTreePermission atp = createAbstractTreePermission(tree, 
TreeType.DEFAULT, pp);
+        atp.canRead();
+
+        verify(pp, times(1)).isGranted(tree, null, Permissions.READ_NODE);
+    }
+
+    @Test
+    public void testCanReadAcType() {
+        AbstractTreePermission atp = createAbstractTreePermission(tree, 
TreeType.ACCESS_CONTROL, pp);
+        atp.canRead();
+
+        verify(pp, times(1)).isGranted(tree, null, 
Permissions.READ_ACCESS_CONTROL);
+    }
+
+    @Test
+    public void testCanReadWithProperty() {
+        PropertyState ps = mock(PropertyState.class);
+
+        AbstractTreePermission atp = createAbstractTreePermission(tree, 
TreeType.VERSION, pp);
+        atp.canRead(ps);
+
+        verify(pp, times(1)).isGranted(tree, ps, Permissions.READ_PROPERTY);
+    }
+
+    @Test
+    public void testCanReadWithPropertyAcType() {
+        PropertyState ps = mock(PropertyState.class);
+
+        AbstractTreePermission atp = createAbstractTreePermission(tree, 
TreeType.ACCESS_CONTROL, pp);
+        atp.canRead(ps);
+
+        verify(pp, times(1)).isGranted(tree, ps, 
Permissions.READ_ACCESS_CONTROL);
+    }
+
+    @Test
+    public void testCanReadAll() {
+        AbstractTreePermission atp = createAbstractTreePermission(tree, 
TreeType.DEFAULT, pp);
+        assertFalse(atp.canReadAll());
+    }
+
+    @Test
+    public void testCanReadProperties() {
+        AbstractTreePermission atp = createAbstractTreePermission(tree, 
TreeType.DEFAULT, pp);
+        assertFalse(atp.canReadProperties());
+    }
+
+    @Test
+    public void testIsGranted() {
+        AbstractTreePermission atp = createAbstractTreePermission(tree, 
TreeType.ACCESS_CONTROL, pp);
+        atp.isGranted(Permissions.ALL);
+
+        verify(pp, times(1)).isGranted(tree, null, Permissions.ALL);
+    }
+
+    @Test
+    public void testIsGrantedWithProperty() {
+        PropertyState ps = mock(PropertyState.class);
+
+        AbstractTreePermission atp = createAbstractTreePermission(tree, 
TreeType.VERSION, pp);
+        atp.isGranted(Permissions.SET_PROPERTY|Permissions.VERSION_MANAGEMENT, 
ps);
+
+        verify(pp, times(1)).isGranted(tree, ps, 
Permissions.SET_PROPERTY|Permissions.VERSION_MANAGEMENT);
+        verify(pp, never()).isGranted(tree, null, 
Permissions.SET_PROPERTY|Permissions.VERSION_MANAGEMENT);
+    }
+
+}
\ No newline at end of file

Propchange: 
jackrabbit/oak/trunk/oak-authorization-principalbased/src/test/java/org/apache/jackrabbit/oak/spi/security/authorization/principalbased/impl/AbstractTreePermissionTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: 
jackrabbit/oak/trunk/oak-authorization-principalbased/src/test/java/org/apache/jackrabbit/oak/spi/security/authorization/principalbased/impl/AccessControlManagerLimitedSystemUserTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-authorization-principalbased/src/test/java/org/apache/jackrabbit/oak/spi/security/authorization/principalbased/impl/AccessControlManagerLimitedSystemUserTest.java?rev=1857551&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-authorization-principalbased/src/test/java/org/apache/jackrabbit/oak/spi/security/authorization/principalbased/impl/AccessControlManagerLimitedSystemUserTest.java
 (added)
+++ 
jackrabbit/oak/trunk/oak-authorization-principalbased/src/test/java/org/apache/jackrabbit/oak/spi/security/authorization/principalbased/impl/AccessControlManagerLimitedSystemUserTest.java
 Mon Apr 15 07:16:49 2019
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package 
org.apache.jackrabbit.oak.spi.security.authorization.principalbased.impl;
+
+import com.google.common.collect.ImmutableSet;
+import org.apache.jackrabbit.api.security.user.Authorizable;
+import org.apache.jackrabbit.api.security.user.User;
+import org.apache.jackrabbit.oak.api.AuthInfo;
+import org.apache.jackrabbit.oak.api.CommitFailedException;
+import org.apache.jackrabbit.oak.api.Root;
+import org.apache.jackrabbit.oak.spi.security.authentication.AuthInfoImpl;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.junit.Test;
+
+import javax.jcr.AccessDeniedException;
+import javax.security.auth.Subject;
+import java.security.Principal;
+import java.security.PrivilegedExceptionAction;
+import java.util.Collections;
+import java.util.Set;
+import java.util.UUID;
+
+/**
+ * Tests for PrincipalBasedAccessControlManager where the editing session 
(based on a system user with both default and
+ * principal-based permission evaluation) lacks permissions to read/modify 
access control on the target system-principal.
+ */
+public class AccessControlManagerLimitedSystemUserTest extends 
AccessControlManagerLimitedUserTest {
+
+    private final String UID = "testSystemSession" + UUID.randomUUID();
+
+    @Override
+    public void after() throws Exception {
+        try {
+            Authorizable a = getUserManager(root).getAuthorizable(UID);
+            if (a != null) {
+                a.remove();
+                root.commit();
+            }
+        } finally {
+            super.after();
+        }
+    }
+
+    Principal createTestPrincipal() throws Exception {
+        User testUser = getUserManager(root).createSystemUser(UID, 
INTERMEDIATE_PATH);
+        root.commit();
+        return testUser.getPrincipal();
+    }
+
+    Root createTestRoot() throws Exception {
+        Set<Principal> principals = ImmutableSet.of(testPrincipal);
+        AuthInfo authInfo = new AuthInfoImpl(UID, Collections.<String, 
Object>emptyMap(), principals);
+        Subject subject = new Subject(true, principals, 
ImmutableSet.of(authInfo), ImmutableSet.of());
+        return Subject.doAsPrivileged(subject, 
(PrivilegedExceptionAction<Root>) () -> getContentRepository().login(null, 
null).getLatestRoot(), null);
+    }
+
+    void grant(@NotNull Principal principal, @Nullable String path, @NotNull 
String... privNames) throws Exception {
+        super.grant(principal, path, privNames);
+        setupPrincipalBasedAccessControl(principal, path, privNames);
+    }
+
+}
\ No newline at end of file

Propchange: 
jackrabbit/oak/trunk/oak-authorization-principalbased/src/test/java/org/apache/jackrabbit/oak/spi/security/authorization/principalbased/impl/AccessControlManagerLimitedSystemUserTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: 
jackrabbit/oak/trunk/oak-authorization-principalbased/src/test/java/org/apache/jackrabbit/oak/spi/security/authorization/principalbased/impl/AccessControlManagerLimitedUserTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-authorization-principalbased/src/test/java/org/apache/jackrabbit/oak/spi/security/authorization/principalbased/impl/AccessControlManagerLimitedUserTest.java?rev=1857551&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-authorization-principalbased/src/test/java/org/apache/jackrabbit/oak/spi/security/authorization/principalbased/impl/AccessControlManagerLimitedUserTest.java
 (added)
+++ 
jackrabbit/oak/trunk/oak-authorization-principalbased/src/test/java/org/apache/jackrabbit/oak/spi/security/authorization/principalbased/impl/AccessControlManagerLimitedUserTest.java
 Mon Apr 15 07:16:49 2019
@@ -0,0 +1,769 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package 
org.apache.jackrabbit.oak.spi.security.authorization.principalbased.impl;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import org.apache.jackrabbit.api.security.JackrabbitAccessControlList;
+import org.apache.jackrabbit.api.security.JackrabbitAccessControlManager;
+import org.apache.jackrabbit.api.security.JackrabbitAccessControlPolicy;
+import org.apache.jackrabbit.api.security.user.User;
+import org.apache.jackrabbit.oak.api.CommitFailedException;
+import org.apache.jackrabbit.oak.api.Root;
+import org.apache.jackrabbit.oak.api.Tree;
+import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.commons.PathUtils;
+import org.apache.jackrabbit.oak.plugins.tree.TreeUtil;
+import org.apache.jackrabbit.oak.spi.nodetype.NodeTypeConstants;
+import 
org.apache.jackrabbit.oak.spi.security.authorization.accesscontrol.ImmutableACL;
+import 
org.apache.jackrabbit.oak.spi.security.authorization.principalbased.PrincipalPolicy;
+import org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeBits;
+import org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeConstants;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.junit.Before;
+import org.junit.Test;
+
+import javax.jcr.AccessDeniedException;
+import javax.jcr.RepositoryException;
+import javax.jcr.SimpleCredentials;
+import javax.jcr.security.AccessControlEntry;
+import javax.jcr.security.AccessControlException;
+import javax.jcr.security.AccessControlPolicy;
+import javax.jcr.security.Privilege;
+import java.security.Principal;
+
+import static 
org.apache.jackrabbit.oak.spi.security.authorization.principalbased.impl.Constants.NT_REP_PRINCIPAL_ENTRY;
+import static 
org.apache.jackrabbit.oak.spi.security.authorization.principalbased.impl.Constants.REP_EFFECTIVE_PATH;
+import static 
org.apache.jackrabbit.oak.spi.security.authorization.principalbased.impl.Constants.REP_PRINCIPAL_POLICY;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Tests for PrincipalBasedAccessControlManager where the editing session 
(based on a regular user with default
+ * permission evaluation) lacks permissions to read/modify access control on 
the target system-principal.
+ */
+public class AccessControlManagerLimitedUserTest extends 
AbstractPrincipalBasedTest implements PrivilegeConstants {
+
+    Principal systemPrincipal;
+    String systemPrincipalPath;
+
+    Principal testPrincipal;
+    Root testRoot;
+    JackrabbitAccessControlManager testAcMgr;
+
+    @Before
+    public void before() throws Exception {
+        super.before();
+
+        User systemUser = getTestSystemUser();
+        systemPrincipalPath = systemUser.getPath();
+        systemPrincipal = getTestSystemUser().getPrincipal();
+
+        testPrincipal = createTestPrincipal();
+
+        setupContentTrees(TEST_OAK_PATH);
+
+        // grant test-user full read access (but not read-access control!)
+        grant(testPrincipal, PathUtils.ROOT_PATH, JCR_READ);
+
+        // trigger creation of principal policy with testPrincipal with 2 
random entries
+        PrincipalPolicyImpl policy = 
setupPrincipalBasedAccessControl(systemPrincipal, testContentJcrPath, 
JCR_NODE_TYPE_MANAGEMENT);
+        addPrincipalBasedEntry(policy, null, JCR_NAMESPACE_MANAGEMENT);
+        root.commit();
+
+        testRoot = createTestRoot();
+        testAcMgr = createAccessControlManager(testRoot);
+    }
+
+    @Override
+    public void after() throws Exception {
+        try {
+            if (testRoot != null) {
+                testRoot.getContentSession().close();
+            }
+        } finally {
+            super.after();
+        }
+    }
+
+    Principal createTestPrincipal() throws Exception {
+        return getTestUser().getPrincipal();
+    }
+
+    Root createTestRoot() throws Exception {
+        User testUser = getTestUser();
+        return login(new SimpleCredentials(testUser.getID(), 
testUser.getID().toCharArray())).getLatestRoot();
+    }
+
+    void grant(@NotNull Principal principal, @Nullable String path, @NotNull 
String... privNames) throws Exception {
+        addDefaultEntry(path, principal, privNames);
+    }
+
+    private static void assertEmptyPolicies(@NotNull AccessControlPolicy[] 
policies) {
+        assertEquals(0, policies.length);
+    }
+
+    private static void assertPolicies(@NotNull AccessControlPolicy[] 
policies, Class<? extends JackrabbitAccessControlList> expectedClass, int 
expectedSize, int expectedEntrySize) {
+        assertEquals(expectedSize, policies.length);
+        if (expectedSize > 0) {
+            assertTrue(expectedClass.isAssignableFrom(policies[0].getClass()));
+            assertEquals(expectedEntrySize, ((JackrabbitAccessControlList) 
policies[0]).size());
+        }
+    }
+
+    private static void assertEntry(@NotNull PrincipalPolicyImpl.EntryImpl 
entry, @Nullable String effectivePath, @NotNull PrivilegeBits expectedBits) {
+        assertEquals(expectedBits, entry.getPrivilegeBits());
+        if (effectivePath == null) {
+            assertNull(entry.getEffectivePath());
+        } else {
+            assertEquals(effectivePath, entry.getEffectivePath());
+        }
+    }
+
+    @NotNull
+    private String getPolicyPath() throws Exception {
+        JackrabbitAccessControlPolicy[] policies = 
createAccessControlManager(root).getPolicies(systemPrincipal);
+        assertEquals(1, policies.length);
+        return PathUtils.concat(((PrincipalPolicyImpl) 
policies[0]).getOakPath(), REP_PRINCIPAL_POLICY);
+    }
+
+    @NotNull
+    private String getEntryPath() throws Exception {
+        Tree policyTree = root.getTree(getPolicyPath());
+        assertTrue(policyTree.exists());
+
+        for (Tree child : policyTree.getChildren()) {
+            if (Utils.isPrincipalEntry(child)) {
+                return child.getPath();
+            }
+        }
+        throw new RepositoryException("unable to locate policy entry");
+    }
+
+    @Test(expected = AccessDeniedException.class)
+    public void testGetApplicableByPath() throws RepositoryException {
+        testAcMgr.getApplicablePolicies(testJcrPath);
+    }
+
+    @Test(expected = AccessDeniedException.class)
+    public void testGetPoliciesByPath() throws RepositoryException {
+        testAcMgr.getPolicies(testJcrPath);
+    }
+
+    @Test(expected = AccessDeniedException.class)
+    public void testGetEffectiveByPathNoAccess() throws RepositoryException {
+        testAcMgr.getEffectivePolicies(testJcrPath);
+    }
+
+    @Test(expected = AccessDeniedException.class)
+    public void testGetEffectiveByNullPath() throws RepositoryException {
+        testAcMgr.getEffectivePolicies((String) null);
+    }
+
+    @Test(expected = AccessDeniedException.class)
+    public void testGetEffectiveByRooyPath() throws RepositoryException {
+        testAcMgr.getEffectivePolicies(PathUtils.ROOT_PATH);
+    }
+
+    @Test(expected = AccessDeniedException.class)
+    public void testGetEffectiveByPathReadAccessControlOnPrincipal() throws 
Exception {
+        // grant testuser read-access control on testPrincipal-path but NOT on 
effective paths null and /oak:content
+        grant(testPrincipal, systemPrincipalPath, JCR_READ_ACCESS_CONTROL);
+        root.commit();
+        testRoot.refresh();
+
+        // since default permission evaluation is in charge for 'testUser' -> 
access to full principal policy is now
+        // granted
+        AccessControlPolicy[] effective = 
testAcMgr.getEffectivePolicies((String) testJcrPath);
+        assertEquals(1, effective.length);
+        assertTrue(effective[0] instanceof PrincipalPolicyImpl);
+    }
+
+    @Test
+    public void testGetEffectiveByPathMissingReadAccessControlOnPrincipal() 
throws Exception {
+        // test-user: granted read-access-control on effective null-path
+        grant(testPrincipal, null, JCR_READ_ACCESS_CONTROL);
+        // test-user: granted read-access-control on effective /oak:content
+        grant(testPrincipal, PathUtils.getAncestorPath(testJcrPath, 3), 
JCR_READ_ACCESS_CONTROL);
+        root.commit();
+        testRoot.refresh();
+
+        assertEmptyPolicies(testAcMgr.getEffectivePolicies((String) null));
+        assertEmptyPolicies(testAcMgr.getEffectivePolicies(testJcrPath));
+    }
+
+    @Test(expected = AccessDeniedException.class)
+    public void testGetApplicableByPrincipalNoAccess() throws 
RepositoryException {
+        testAcMgr.getApplicablePolicies(systemPrincipal);
+    }
+
+    @Test(expected = AccessDeniedException.class)
+    public void testGetPoliciesByPrincipalNoAccess() throws 
RepositoryException {
+        testAcMgr.getPolicies(systemPrincipal);
+    }
+
+    @Test(expected = AccessDeniedException.class)
+    public void testGetEffectiveByPrincipalNoAccess() throws 
RepositoryException {
+        testAcMgr.getEffectivePolicies(ImmutableSet.of(systemPrincipal));
+    }
+
+    @Test
+    public void testGetPoliciesByPrincipal() throws Exception {
+        // grant testuser read-access control on testPrincipal-path but NOT on 
effective paths null and /oak:content
+        grant(testPrincipal, systemPrincipalPath, JCR_READ_ACCESS_CONTROL);
+        root.commit();
+        testRoot.refresh();
+
+        // no read-ac permission on effective paths
+        assertPolicies(testAcMgr.getPolicies(systemPrincipal), 
PrincipalPolicyImpl.class, 1, 2);
+
+        // grant testuser read-access control on /oak:content
+        grant(testPrincipal, testContentJcrPath, JCR_READ_ACCESS_CONTROL);
+        root.commit();
+        testRoot.refresh();
+        assertPolicies(testAcMgr.getPolicies(systemPrincipal), 
PrincipalPolicyImpl.class, 1, 2);
+
+        // additionally grant testuser read-access control on null
+        grant(testPrincipal, null, JCR_READ_ACCESS_CONTROL);
+        root.commit();
+        testRoot.refresh();
+        assertPolicies(testAcMgr.getPolicies(systemPrincipal), 
PrincipalPolicyImpl.class, 1, 2);
+    }
+
+    @Test
+    public void testGetEffectiveByPrincipal() throws Exception {
+        // grant testuser read-access control on testPrincipal-path but NOT on 
effective paths null and /oak:content
+        grant(testPrincipal, systemPrincipalPath, JCR_READ_ACCESS_CONTROL);
+        root.commit();
+        testRoot.refresh();
+
+        // read-access-control is only granted for the principalpolicy itself
+        
assertPolicies(testAcMgr.getEffectivePolicies(ImmutableSet.of(systemPrincipal)),
 ImmutableACL.class, 1, 2);
+
+        // grant testuser read-access control on /oak:content
+        grant(testPrincipal, testContentJcrPath, JCR_READ_ACCESS_CONTROL);
+        root.commit();
+        testRoot.refresh();
+        
assertPolicies(testAcMgr.getEffectivePolicies(ImmutableSet.of(systemPrincipal)),
 ImmutableACL.class, 1, 2);
+
+        // additionally grant testuser read-access control on null
+        grant(testPrincipal, null, JCR_READ_ACCESS_CONTROL);
+        root.commit();
+        testRoot.refresh();
+        
assertPolicies(testAcMgr.getEffectivePolicies(ImmutableSet.of(systemPrincipal)),
 ImmutableACL.class, 1, 2);
+    }
+
+    @Test(expected = AccessDeniedException.class)
+    public void testSetPolicyMissingModifyAccessControlOnPrincipal() throws 
Exception {
+        grant(testPrincipal, systemPrincipalPath, JCR_READ_ACCESS_CONTROL);
+        root.commit();
+        testRoot.refresh();
+
+        PrincipalPolicyImpl policy = (PrincipalPolicyImpl) 
testAcMgr.getPolicies(systemPrincipal)[0];
+        policy.addEntry(null, privilegesFromNames(JCR_WORKSPACE_MANAGEMENT));
+
+        testAcMgr.setPolicy(policy.getPath(), policy);
+    }
+
+    @Test(expected = AccessDeniedException.class)
+    public void testSetPolicyMissingModifyAccessControlOnEffectivePath() 
throws Exception {
+        grant(testPrincipal, systemPrincipalPath, JCR_READ_ACCESS_CONTROL, 
JCR_MODIFY_ACCESS_CONTROL);
+        root.commit();
+        testRoot.refresh();
+
+        PrincipalPolicyImpl policy = (PrincipalPolicyImpl) 
testAcMgr.getPolicies(systemPrincipal)[0];
+        policy.addEntry(null, privilegesFromNames(JCR_WORKSPACE_MANAGEMENT));
+        policy.addEntry(testJcrPath, privilegesFromNames(JCR_READ));
+
+        testAcMgr.setPolicy(policy.getPath(), policy);
+    }
+
+    @Test(expected = AccessDeniedException.class)
+    public void testSetPolicyMissingModifyAccessControlOnEffectivePath2() 
throws Exception {
+        grant(testPrincipal, systemPrincipalPath, JCR_READ_ACCESS_CONTROL, 
JCR_MODIFY_ACCESS_CONTROL);
+        grant(testPrincipal, testContentJcrPath, JCR_READ_ACCESS_CONTROL, 
JCR_MODIFY_ACCESS_CONTROL);
+        root.commit();
+        testRoot.refresh();
+
+        PrincipalPolicyImpl policy = (PrincipalPolicyImpl) 
testAcMgr.getPolicies(systemPrincipal)[0];
+        policy.addEntry(null, privilegesFromNames(JCR_WORKSPACE_MANAGEMENT));
+        policy.addEntry(testJcrPath, privilegesFromNames(JCR_READ));
+
+        testAcMgr.setPolicy(policy.getPath(), policy);
+    }
+
+    @Test
+    public void testSetPolicy() throws Exception {
+        grant(testPrincipal, systemPrincipalPath, JCR_READ_ACCESS_CONTROL, 
JCR_MODIFY_ACCESS_CONTROL);
+        grant(testPrincipal, testContentJcrPath, JCR_READ_ACCESS_CONTROL, 
JCR_MODIFY_ACCESS_CONTROL);
+        grant(testPrincipal, null, JCR_READ_ACCESS_CONTROL, 
JCR_MODIFY_ACCESS_CONTROL);
+        root.commit();
+        testRoot.refresh();
+
+        PrincipalPolicyImpl policy = (PrincipalPolicyImpl) 
testAcMgr.getPolicies(systemPrincipal)[0];
+        policy.addEntry(null, privilegesFromNames(JCR_WORKSPACE_MANAGEMENT));
+        policy.addEntry(testJcrPath, privilegesFromNames(JCR_READ));
+
+        testAcMgr.setPolicy(policy.getPath(), policy);
+        testRoot.commit();
+    }
+
+    @Test(expected = AccessDeniedException.class)
+    public void testRemovePolicyMissingModifyAccessControlOnPrincipal() throws 
Exception {
+        grant(testPrincipal, systemPrincipalPath, JCR_READ_ACCESS_CONTROL);
+        root.commit();
+        testRoot.refresh();
+
+        PrincipalPolicyImpl policy = (PrincipalPolicyImpl) 
testAcMgr.getPolicies(systemPrincipal)[0];
+        testAcMgr.removePolicy(policy.getPath(), policy);
+    }
+
+    @Test(expected = AccessDeniedException.class)
+    public void testRemovePolicyMissingModifyAccessControlOnEffectivePath() 
throws Exception {
+        grant(testPrincipal, systemPrincipalPath, JCR_READ_ACCESS_CONTROL, 
JCR_MODIFY_ACCESS_CONTROL);
+        root.commit();
+        testRoot.refresh();
+
+        PrincipalPolicyImpl policy = (PrincipalPolicyImpl) 
testAcMgr.getPolicies(systemPrincipal)[0];
+        testAcMgr.removePolicy(policy.getPath(), policy);
+    }
+
+    @Test(expected = AccessDeniedException.class)
+    public void testRemovePolicyMissingModifyAccessControlOnEffectivePath2() 
throws Exception {
+        grant(testPrincipal, systemPrincipalPath, JCR_READ_ACCESS_CONTROL, 
JCR_MODIFY_ACCESS_CONTROL);
+        grant(testPrincipal, testContentJcrPath, JCR_READ_ACCESS_CONTROL, 
JCR_MODIFY_ACCESS_CONTROL);
+        root.commit();
+        testRoot.refresh();
+
+        PrincipalPolicyImpl policy = (PrincipalPolicyImpl) 
testAcMgr.getPolicies(systemPrincipal)[0];
+        testAcMgr.removePolicy(policy.getPath(), policy);
+    }
+
+    @Test
+    public void testRemovePolicy() throws Exception {
+        grant(testPrincipal, systemPrincipalPath, JCR_READ_ACCESS_CONTROL, 
JCR_MODIFY_ACCESS_CONTROL);
+        grant(testPrincipal, null, JCR_READ_ACCESS_CONTROL, 
JCR_MODIFY_ACCESS_CONTROL);
+        grant(testPrincipal, testContentJcrPath, JCR_READ_ACCESS_CONTROL, 
JCR_MODIFY_ACCESS_CONTROL);
+        root.commit();
+        testRoot.refresh();
+
+        PrincipalPolicyImpl policy = (PrincipalPolicyImpl) 
testAcMgr.getPolicies(systemPrincipal)[0];
+        testAcMgr.removePolicy(policy.getPath(), policy);
+        testRoot.commit();
+    }
+
+    @Test
+    public void testRemovePolicyWithNonEntryChild() throws Exception {
+        // clear all entries from policy
+        PrincipalPolicyImpl policy = getPrincipalPolicyImpl(systemPrincipal, 
getAccessControlManager(root));
+        for (AccessControlEntry entry : policy.getAccessControlEntries()) {
+            policy.removeAccessControlEntry(entry);
+        }
+        getAccessControlManager(root).setPolicy(policy.getPath(), policy);
+        // grant permission to read/modify policy
+        grant(testPrincipal, systemPrincipalPath, JCR_READ_ACCESS_CONTROL, 
JCR_MODIFY_ACCESS_CONTROL);
+        root.commit();
+        testRoot.refresh();
+
+        // transiently add non-entry tree
+        Tree policyTree = testRoot.getTree(getPolicyPath());
+        assertFalse(policyTree.getChildren().iterator().hasNext());
+        TreeUtil.addChild(policyTree, "nonEntry", 
NodeTypeConstants.NT_OAK_UNSTRUCTURED);
+
+        policy = getPrincipalPolicyImpl(systemPrincipal, testAcMgr);
+        testAcMgr.removePolicy(policy.getPath(), policy);
+        testRoot.commit();
+    }
+
+    @Test(expected = AccessControlException.class)
+    public void testRemovePolicyMissingEffectivePaths() throws Exception {
+        // grant permission to read/modify policy
+        grant(testPrincipal, systemPrincipalPath, JCR_READ_ACCESS_CONTROL, 
JCR_MODIFY_ACCESS_CONTROL);
+        root.commit();
+        testRoot.refresh();
+
+        // first read policy
+        PrincipalPolicyImpl policy = getPrincipalPolicyImpl(systemPrincipal, 
testAcMgr);
+
+        // transiently remove rep:effectivePath properties from all entries.
+        Tree policyTree = testRoot.getTree(getPolicyPath());
+        for (Tree child : policyTree.getChildren()) {
+            child.removeProperty(REP_EFFECTIVE_PATH);
+        }
+
+        // removing policy must fail, because effective paths cannot be 
evaluated
+        testAcMgr.removePolicy(policy.getPath(), policy);
+    }
+
+    @Test(expected = AccessDeniedException.class)
+    public void testHasPrivilegeSystemUser() throws Exception {
+        // test session has no access
+        testAcMgr.hasPrivileges(testContentJcrPath, 
ImmutableSet.of(systemPrincipal), 
privilegesFromNames(JCR_NODE_TYPE_MANAGEMENT));
+    }
+
+    @Test(expected = AccessDeniedException.class)
+    public void testHasPrivilegeSystemUserWithPartialReadAc() throws Exception 
{
+        // grant read-ac access on principal policy (but not on targetPath)
+        grant(testPrincipal, systemPrincipalPath, JCR_READ_ACCESS_CONTROL);
+        root.commit();
+        testRoot.refresh();
+
+        testAcMgr.hasPrivileges(testContentJcrPath, 
ImmutableSet.of(systemPrincipal), 
privilegesFromNames(JCR_NODE_TYPE_MANAGEMENT));
+    }
+
+    @Test
+    public void testHasPrivilegeSystemUserWithPartialReadAc2() throws 
Exception {
+        // grant read-ac access on effective path -> no entries accessible
+        grant(testPrincipal, testContentJcrPath, JCR_READ_ACCESS_CONTROL);
+        root.commit();
+        testRoot.refresh();
+
+        assertFalse(testAcMgr.hasPrivileges(testContentJcrPath, 
ImmutableSet.of(systemPrincipal), 
privilegesFromNames(JCR_NODE_TYPE_MANAGEMENT)));
+    }
+
+    @Test
+    public void testHasPrivilegeSystemUserWithReadAc() throws Exception {
+        // grant read-ac access on effective path and on principal policy
+        grant(testPrincipal, testContentJcrPath, JCR_READ_ACCESS_CONTROL);
+        grant(testPrincipal, systemPrincipalPath, JCR_READ_ACCESS_CONTROL);
+        root.commit();
+        testRoot.refresh();
+
+        // default model lacks jcr:nodeTypeManagement privilege -> not granted
+        assertFalse(testAcMgr.hasPrivileges(testContentJcrPath, 
ImmutableSet.of(systemPrincipal), 
privilegesFromNames(JCR_NODE_TYPE_MANAGEMENT)));
+
+        addDefaultEntry(testContentJcrPath, systemPrincipal, JCR_READ, 
JCR_NODE_TYPE_MANAGEMENT);
+        root.commit();
+        testRoot.refresh();
+
+        // once default model grants permissions as well -> granted
+        assertTrue(testAcMgr.hasPrivileges(testContentJcrPath, 
ImmutableSet.of(systemPrincipal), 
privilegesFromNames(JCR_NODE_TYPE_MANAGEMENT)));
+
+        // but combination read/nt-mgt is not granted because jcr:read is 
missing on principal-based setup.
+        assertFalse(testAcMgr.hasPrivileges(testContentJcrPath, 
ImmutableSet.of(systemPrincipal), privilegesFromNames(JCR_READ, 
JCR_NODE_TYPE_MANAGEMENT)));
+    }
+
+    @Test(expected = AccessDeniedException.class)
+    public void testGetPrivilegeSystemUser() throws Exception {
+        // test session has no access
+        testAcMgr.getPrivileges(null, ImmutableSet.of(systemPrincipal));
+    }
+
+    @Test(expected = AccessDeniedException.class)
+    public void testGetPrivilegeSystemUserWithPartialReadAc() throws Exception 
{
+        // grant read ac on principal policy but not on target path
+        grant(testPrincipal, systemPrincipalPath, JCR_READ_ACCESS_CONTROL);
+        root.commit();
+        testRoot.refresh();
+
+        testAcMgr.getPrivileges(null, ImmutableSet.of(systemPrincipal));
+    }
+
+    @Test
+    public void testGetPrivilegeSystemUserWithPartialReadAc2() throws 
Exception {
+        // grant read-ac access on target path (but not on principal policy)
+        grant(testPrincipal, null, JCR_READ_ACCESS_CONTROL);
+        root.commit();
+        testRoot.refresh();
+
+        assertEquals(0, testAcMgr.getPrivileges(null, 
ImmutableSet.of(systemPrincipal)).length);
+    }
+
+    @Test
+    public void testGetPrivilegeSystemUserWithWithReadAc() throws Exception {
+        // grant read-ac access on effective path and on principal policy
+        grant(testPrincipal, null, JCR_READ_ACCESS_CONTROL);
+        grant(testPrincipal, systemPrincipalPath, JCR_READ_ACCESS_CONTROL);
+        root.commit();
+        testRoot.refresh();
+
+        // default model lacks jcr:nodeTypeManagement privilege -> not granted
+        assertArrayEquals(new Privilege[0], testAcMgr.getPrivileges(null, 
ImmutableSet.of(systemPrincipal)));
+
+        addDefaultEntry(null, systemPrincipal, JCR_NAMESPACE_MANAGEMENT, 
REP_PRIVILEGE_MANAGEMENT);
+        root.commit();
+        testRoot.refresh();
+
+        // once default model grants namespace-mgt privilege as well -> granted
+        // but not rep:privilegeMgt because the principal-based model doesn't 
grant that one
+        assertArrayEquals(privilegesFromNames(JCR_NAMESPACE_MANAGEMENT), 
testAcMgr.getPrivileges(null, ImmutableSet.of(systemPrincipal)));
+    }
+
+    @Test
+    public void testReadPolicyTree() throws Exception {
+        Tree policyTree = testRoot.getTree(getPolicyPath());
+        assertNotNull(policyTree);
+        assertFalse(policyTree.exists());
+    }
+
+    @Test
+    public void testReadPolicyTreeWithReadAc() throws Exception {
+        // grant read-ac access and check again
+        grant(testPrincipal, systemPrincipalPath, JCR_READ_ACCESS_CONTROL);
+        root.commit();
+        testRoot.refresh();
+
+        Tree policyTree = testRoot.getTree(getPolicyPath());
+        assertNotNull(policyTree);
+        assertTrue(policyTree.exists());
+    }
+
+    @Test
+    public void testReadEntryTree() throws Exception {
+        Tree entryTree = testRoot.getTree(getEntryPath());
+        assertNotNull(entryTree);
+        assertFalse(entryTree.exists());
+    }
+
+    @Test
+    public void testReadEntryTreeWithReadAc() throws Exception {
+        // grant read-ac access and check again
+        grant(testPrincipal, systemPrincipalPath, JCR_READ_ACCESS_CONTROL);
+        root.commit();
+        testRoot.refresh();
+
+        Tree entryTree = testRoot.getTree(getEntryPath());
+        assertNotNull(entryTree);
+        assertTrue(entryTree.exists());
+    }
+
+    @Test(expected = CommitFailedException.class)
+    public void testAddEntryTree() throws Exception {
+        // grant read-ac access on principal policy
+        grant(testPrincipal, systemPrincipalPath, JCR_READ_ACCESS_CONTROL);
+        root.commit();
+        testRoot.refresh();
+
+        try {
+            Tree policyTree = testRoot.getTree(getPolicyPath());
+            Tree entry = TreeUtil.addChild(policyTree, "entry", 
NT_REP_PRINCIPAL_ENTRY);
+            entry.setProperty(REP_EFFECTIVE_PATH, TEST_OAK_PATH, Type.PATH);
+            entry.setProperty(Constants.REP_PRIVILEGES, 
ImmutableList.of(JCR_ADD_CHILD_NODES), Type.NAMES);
+            testRoot.commit();
+        } catch (CommitFailedException e) {
+            assertEquals(CommitFailedException.ACCESS, e.getType());
+            assertEquals(3, e.getCode());
+            throw e;
+        }
+    }
+
+    @Test(expected = CommitFailedException.class)
+    public void testAddEntryTreeModAcOnSystemPrincipal() throws Exception {
+        // grant read-ac + mod-ac access on principal policy
+        grant(testPrincipal, systemPrincipalPath, JCR_READ_ACCESS_CONTROL, 
JCR_MODIFY_ACCESS_CONTROL);
+        root.commit();
+        testRoot.refresh();
+
+        try {
+            Tree policyTree = testRoot.getTree(getPolicyPath());
+            Tree entry = TreeUtil.addChild(policyTree, "entry", 
NT_REP_PRINCIPAL_ENTRY);
+            entry.setProperty(REP_EFFECTIVE_PATH, TEST_OAK_PATH, Type.PATH);
+            entry.setProperty(Constants.REP_PRIVILEGES, 
ImmutableList.of(JCR_ADD_CHILD_NODES), Type.NAMES);
+            testRoot.commit();
+        } catch (CommitFailedException e) {
+            assertEquals(CommitFailedException.ACCESS, e.getType());
+            assertEquals(3, e.getCode());
+            throw e;
+        }
+    }
+
+    @Test(expected = IllegalStateException.class)
+    public void testAddEntryTreeModAcOnEffectivePath() throws Exception {
+        // grant read-ac + mod-ac access on effective path only -> cannot read 
principal policy
+        grant(testPrincipal, testJcrPath, JCR_READ_ACCESS_CONTROL, 
JCR_MODIFY_ACCESS_CONTROL);
+        root.commit();
+        testRoot.refresh();
+
+        Tree policyTree = testRoot.getTree(getPolicyPath());
+        Tree entry = TreeUtil.addChild(policyTree, "entry", 
NT_REP_PRINCIPAL_ENTRY);
+
+    }
+
+    @Test
+    public void testAddEntryTreeFullModAc() throws Exception {
+        grant(testPrincipal, testJcrPath, JCR_READ_ACCESS_CONTROL, 
JCR_MODIFY_ACCESS_CONTROL);
+        grant(testPrincipal, systemPrincipalPath, JCR_READ_ACCESS_CONTROL, 
JCR_MODIFY_ACCESS_CONTROL);
+        root.commit();
+        testRoot.refresh();
+
+        Tree policyTree = testRoot.getTree(getPolicyPath());
+        Tree entry = TreeUtil.addChild(policyTree, "entry", 
NT_REP_PRINCIPAL_ENTRY);
+        entry.setProperty(REP_EFFECTIVE_PATH, TEST_OAK_PATH, Type.PATH);
+        entry.setProperty(Constants.REP_PRIVILEGES, 
ImmutableList.of(JCR_ADD_CHILD_NODES), Type.NAMES);
+        testRoot.commit();
+    }
+
+    @Test(expected = CommitFailedException.class)
+    public void testRemovePolicyTree() throws Exception {
+        // grant read-ac access on principal policy
+        grant(testPrincipal, systemPrincipalPath, JCR_READ_ACCESS_CONTROL);
+        root.commit();
+        testRoot.refresh();
+
+        try {
+            testRoot.getTree(getPolicyPath()).remove();
+            testRoot.commit();
+        } catch (CommitFailedException e) {
+            assertEquals(CommitFailedException.ACCESS, e.getType());
+            assertEquals(0, e.getCode());
+            throw e;
+        }
+    }
+
+    @Test(expected = CommitFailedException.class)
+    public void testRemovePolicyTreeWithModAcOnSystemPrincipal() throws 
Exception {
+        // grant read-ac + mod-ac access on principal policy but not on target 
paths
+        grant(testPrincipal, systemPrincipalPath, JCR_READ_ACCESS_CONTROL, 
JCR_MODIFY_ACCESS_CONTROL);
+        root.commit();
+        testRoot.refresh();
+
+        try {
+            testRoot.getTree(getPolicyPath()).remove();
+            testRoot.commit();
+        } catch (CommitFailedException e) {
+            assertEquals(CommitFailedException.ACCESS, e.getType());
+            assertEquals(3, e.getCode());
+            throw e;
+        }
+    }
+
+    @Test(expected = CommitFailedException.class)
+    public void testRemovePolicyTreeWithModAcOnOneEffectivePath() throws 
Exception {
+        // grant read-ac + mod-ac access on principal policy and on 
testcontent target paths (but not on null path)
+        grant(testPrincipal, systemPrincipalPath, JCR_READ_ACCESS_CONTROL, 
JCR_MODIFY_ACCESS_CONTROL);
+        grant(testPrincipal, testContentJcrPath, JCR_MODIFY_ACCESS_CONTROL);
+        root.commit();
+        testRoot.refresh();
+
+        try {
+            testRoot.getTree(getPolicyPath()).remove();
+            testRoot.commit();
+        } catch (CommitFailedException e) {
+            assertEquals(CommitFailedException.ACCESS, e.getType());
+            assertEquals(3, e.getCode());
+            throw e;
+        }
+    }
+
+    @Test(expected = CommitFailedException.class)
+    public void testRemovePolicyTreeWithModAcOnOneEffectivePath2() throws 
Exception {
+        // grant read-ac + mod-ac access on principal policy and on nul target 
paths (but not on testcontent path)
+        grant(testPrincipal, systemPrincipalPath, JCR_READ_ACCESS_CONTROL, 
JCR_MODIFY_ACCESS_CONTROL);
+        grant(testPrincipal, null, JCR_MODIFY_ACCESS_CONTROL);
+        root.commit();
+        testRoot.refresh();
+
+        try {
+            testRoot.getTree(getPolicyPath()).remove();
+            testRoot.commit();
+        } catch (CommitFailedException e) {
+            assertEquals(CommitFailedException.ACCESS, e.getType());
+            assertEquals(3, e.getCode());
+            throw e;
+        }
+    }
+
+    @Test(expected = CommitFailedException.class)
+    public void testRemoveEmptyPolicyTree() throws Exception {
+        PrincipalPolicy policy = getPrincipalPolicyImpl(systemPrincipal, 
getAccessControlManager(root));
+        for (AccessControlEntry entry : ((PrincipalPolicyImpl) 
policy).getEntries()) {
+            policy.removeAccessControlEntry(entry);
+        }
+        getAccessControlManager(root).setPolicy(policy.getPath(), policy);
+        // grant permission to read policy
+        grant(testPrincipal, systemPrincipalPath, JCR_READ_ACCESS_CONTROL);
+
+        root.commit();
+        testRoot.refresh();
+
+        Tree policyTree = testRoot.getTree(getPolicyPath());
+        assertTrue(policyTree.exists());
+        policyTree.remove();
+        testRoot.commit();
+    }
+
+    @Test(expected = CommitFailedException.class)
+    public void testRemoveEntryTree() throws Exception {
+        // grant read-ac access on principal policy
+        grant(testPrincipal, systemPrincipalPath, JCR_READ_ACCESS_CONTROL);
+        root.commit();
+        testRoot.refresh();
+
+        try {
+            testRoot.getTree(getEntryPath()).remove();
+            testRoot.commit();
+        } catch (CommitFailedException e) {
+            assertEquals(CommitFailedException.ACCESS, e.getType());
+            assertEquals(3, e.getCode());
+            throw e;
+        }
+    }
+
+    @Test(expected = CommitFailedException.class)
+    public void testRemoveEntryTreeModAcOnSystemPrincipal() throws Exception {
+        // grant read-ac + mod-ac access on principal policy but not on target 
path
+        grant(testPrincipal, systemPrincipalPath, JCR_READ_ACCESS_CONTROL, 
JCR_MODIFY_ACCESS_CONTROL);
+        root.commit();
+        testRoot.refresh();
+
+        try {
+            testRoot.getTree(getEntryPath()).remove();
+            testRoot.commit();
+        } catch (CommitFailedException e) {
+            assertEquals(CommitFailedException.ACCESS, e.getType());
+            assertEquals(3, e.getCode());
+            throw e;
+        }
+    }
+
+    @Test(expected = CommitFailedException.class)
+    public void testRemoveEntryTreeModAcOnEffectivePath() throws Exception {
+        // grant read-ac on principal policy but not mod-ac
+        // on target path grant mod-ac
+        grant(testPrincipal, systemPrincipalPath, JCR_READ_ACCESS_CONTROL);
+        grant(testPrincipal, testContentJcrPath, JCR_MODIFY_ACCESS_CONTROL);
+        root.commit();
+        testRoot.refresh();
+
+        try {
+            testRoot.getTree(getEntryPath()).remove();
+            testRoot.commit();
+        } catch (CommitFailedException e) {
+            assertEquals(CommitFailedException.ACCESS, e.getType());
+            assertEquals(0, e.getCode());
+            throw e;
+        }
+    }
+
+    @Test
+    public void testRemoveEntryTreeFullModAc() throws Exception {
+        // grant read-ac and mod-ac on principal policy
+        // on target path grant mod-ac
+        grant(testPrincipal, systemPrincipalPath, JCR_READ_ACCESS_CONTROL, 
JCR_MODIFY_ACCESS_CONTROL);
+        grant(testPrincipal, testContentJcrPath, JCR_MODIFY_ACCESS_CONTROL);
+        root.commit();
+        testRoot.refresh();
+
+        testRoot.getTree(getEntryPath()).remove();
+        testRoot.commit();
+    }
+}
\ No newline at end of file

Propchange: 
jackrabbit/oak/trunk/oak-authorization-principalbased/src/test/java/org/apache/jackrabbit/oak/spi/security/authorization/principalbased/impl/AccessControlManagerLimitedUserTest.java
------------------------------------------------------------------------------
    svn:eol-style = native


Reply via email to