Added: 
jackrabbit/oak/trunk/oak-authorization-principalbased/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/principalbased/impl/PrincipalBasedAccessControlManager.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/PrincipalBasedAccessControlManager.java?rev=1857551&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-authorization-principalbased/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/principalbased/impl/PrincipalBasedAccessControlManager.java
 (added)
+++ 
jackrabbit/oak/trunk/oak-authorization-principalbased/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/principalbased/impl/PrincipalBasedAccessControlManager.java
 Mon Apr 15 07:16:49 2019
@@ -0,0 +1,392 @@
+/*
+ * 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 com.google.common.collect.Lists;
+import org.apache.jackrabbit.api.security.JackrabbitAccessControlEntry;
+import org.apache.jackrabbit.api.security.JackrabbitAccessControlPolicy;
+import org.apache.jackrabbit.api.security.principal.PrincipalManager;
+import 
org.apache.jackrabbit.commons.iterator.AccessControlPolicyIteratorAdapter;
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.api.QueryEngine;
+import org.apache.jackrabbit.oak.api.Result;
+import org.apache.jackrabbit.oak.api.ResultRow;
+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.commons.QueryUtils;
+import org.apache.jackrabbit.oak.namepath.NamePathMapper;
+import org.apache.jackrabbit.oak.plugins.tree.TreeUtil;
+import org.apache.jackrabbit.oak.spi.nodetype.NodeTypeConstants;
+import org.apache.jackrabbit.oak.spi.query.QueryConstants;
+import org.apache.jackrabbit.oak.spi.security.authorization.accesscontrol.ACE;
+import 
org.apache.jackrabbit.oak.spi.security.authorization.accesscontrol.AbstractAccessControlManager;
+import 
org.apache.jackrabbit.oak.spi.security.authorization.accesscontrol.AccessControlConstants;
+import 
org.apache.jackrabbit.oak.spi.security.authorization.accesscontrol.ImmutableACL;
+import 
org.apache.jackrabbit.oak.spi.security.authorization.accesscontrol.PolicyOwner;
+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.security.authorization.principalbased.FilterProvider;
+import 
org.apache.jackrabbit.oak.spi.security.authorization.restriction.Restriction;
+import 
org.apache.jackrabbit.oak.spi.security.authorization.restriction.RestrictionProvider;
+import org.apache.jackrabbit.oak.spi.security.principal.PrincipalImpl;
+import org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeBits;
+import org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeBitsProvider;
+import org.apache.jackrabbit.oak.spi.xml.ImportBehavior;
+import org.apache.jackrabbit.oak.spi.xml.ProtectedItemImporter;
+import org.apache.jackrabbit.util.ISO9075;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jcr.AccessDeniedException;
+import javax.jcr.RepositoryException;
+import javax.jcr.query.Query;
+import javax.jcr.security.AccessControlException;
+import javax.jcr.security.AccessControlPolicy;
+import javax.jcr.security.AccessControlPolicyIterator;
+import javax.jcr.security.Privilege;
+import java.security.Principal;
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+
+/**
+ * Implementation of the {@link 
org.apache.jackrabbit.api.security.JackrabbitAccessControlManager}
+ * interface that allows to create, modify and remove closed user group 
policies.
+ */
+class PrincipalBasedAccessControlManager extends AbstractAccessControlManager 
implements PolicyOwner, Constants {
+
+    private static final Logger log = 
LoggerFactory.getLogger(PrincipalBasedAccessControlManager.class);
+
+    private final MgrProvider mgrProvider;
+    private final int importBehavior;
+
+    private final PrincipalManager principalManager;
+    private final PrivilegeBitsProvider privilegeBitsProvider;
+
+    private final FilterProvider filterProvider;
+    private final Filter filter;
+
+    PrincipalBasedAccessControlManager(@NotNull MgrProvider mgrProvider,
+                                       @NotNull FilterProvider filterProvider) 
{
+        super(mgrProvider.getRoot(), mgrProvider.getNamePathMapper(), 
mgrProvider.getSecurityProvider());
+
+        this.mgrProvider = mgrProvider;
+
+        importBehavior = 
ImportBehavior.valueFromString(getConfig().getParameters().getConfigValue(ProtectedItemImporter.PARAM_IMPORT_BEHAVIOR,
 ImportBehavior.NAME_ABORT));
+
+        principalManager = mgrProvider.getPrincipalManager();
+        privilegeBitsProvider = mgrProvider.getPrivilegeBitsProvider();
+
+        this.filterProvider = filterProvider;
+        filter = filterProvider.getFilter(mgrProvider.getSecurityProvider(), 
mgrProvider.getRoot(), mgrProvider.getNamePathMapper());
+    }
+
+    //-------------------------------------< JackrabbitAccessControlManager 
>---
+
+    @Override
+    public JackrabbitAccessControlPolicy[] getApplicablePolicies(Principal 
principal) throws RepositoryException {
+        if (canHandle(principal)) {
+            String oakPath = filter.getOakPath(principal);
+            Tree tree = getTree(oakPath, Permissions.READ_ACCESS_CONTROL, 
true);
+            if (!tree.hasChild(REP_PRINCIPAL_POLICY)) {
+                return new JackrabbitAccessControlPolicy[]{new 
PrincipalPolicyImpl(principal, oakPath, mgrProvider)};
+            }
+        }
+        return new JackrabbitAccessControlPolicy[0];
+    }
+
+    @Override
+    public JackrabbitAccessControlPolicy[] getPolicies(Principal principal) 
throws RepositoryException {
+        JackrabbitAccessControlPolicy policy = null;
+        if (canHandle(principal)) {
+            policy = createPolicy(principal, false);
+        }
+        return (policy == null) ? new JackrabbitAccessControlPolicy[0] : new 
JackrabbitAccessControlPolicy[]{policy};
+    }
+
+    @Override
+    public AccessControlPolicy[] getEffectivePolicies(Set<Principal> 
principals) throws RepositoryException {
+        // this implementation only takes effect if the complete set of 
principals can be handled. see also
+        // PrincipalBasedAuthorizationConfiguration.getPermissionProvider
+        if (canHandle(principals)) {
+            Set<AccessControlPolicy> effective = new 
HashSet<>(principals.size());
+            for (Principal principal : principals) {
+                AccessControlPolicy policy = createPolicy(principal, true);
+                if (policy != null) {
+                    effective.add(policy);
+                }
+            }
+            return effective.toArray(new AccessControlPolicy[0]);
+        } else {
+            return new JackrabbitAccessControlPolicy[0];
+        }
+    }
+
+    //-----------------------------------------------< AccessControlManager 
>---
+
+    @Override
+    public AccessControlPolicy[] getPolicies(String absPath) throws 
RepositoryException {
+        getTree(getOakPath(absPath), Permissions.READ_ACCESS_CONTROL, true);
+
+        log.debug("Editing access control policies by path is not supported. 
Use JackrabbitAccessControlManager.getPolicies(Principal principal)");
+        return new AccessControlPolicy[0];
+    }
+
+    @Override
+    public AccessControlPolicy[] getEffectivePolicies(String absPath) throws 
RepositoryException {
+        String oakPath = getOakPath(absPath);
+        getTree(oakPath, Permissions.READ_ACCESS_CONTROL, true);
+
+        StringBuilder stmt = new 
StringBuilder(QueryConstants.SEARCH_ROOT_PATH);
+        stmt.append(filterProvider.getFilterRoot());
+        
stmt.append("//element(*,").append(NT_REP_PRINCIPAL_ENTRY).append(")[");
+        String cond = "";
+        // list of effective paths not empty at this point and will at least 
contain the oakPath
+        for (String effectivePath : getEffectivePaths(oakPath)) {
+            stmt.append(cond);
+            stmt.append("@").append(ISO9075.encode(REP_EFFECTIVE_PATH));
+            stmt.append("='").append(QueryUtils.escapeForQuery(effectivePath));
+            stmt.append("'");
+            cond = " or ";
+
+        }
+        stmt.append("] order by jcr:path option (traversal ok)");
+
+        try {
+            // run query on persisted content omitting any transient 
modifications
+            QueryEngine queryEngine = getLatestRoot().getQueryEngine();
+            Result result = queryEngine.executeQuery(stmt.toString(), 
Query.XPATH, QueryEngine.NO_BINDINGS, QueryEngine.NO_MAPPINGS);
+
+            Map<String, List<JackrabbitAccessControlEntry>> m = new 
TreeMap<>();
+            for (ResultRow row : result.getRows()) {
+                Tree entryTree = row.getTree(null);
+                String effectivePath = 
row.getValue(REP_EFFECTIVE_PATH).getValue(Type.STRING);
+                List<JackrabbitAccessControlEntry> entries = 
m.computeIfAbsent(effectivePath, s -> new ArrayList<>());
+                entries.add(createEffectiveEntry(entryTree, effectivePath));
+            }
+            Iterable<ImmutableACL> acls = Iterables.transform(m.entrySet(), 
entry -> new ImmutableACL(entry.getKey(), entry.getValue(), 
mgrProvider.getRestrictionProvider(), getNamePathMapper()));
+            return Iterables.toArray(acls, ImmutableACL.class);
+        } catch (ParseException e) {
+            String msg = "Error while collecting effective policies at " 
+absPath;
+            log.error(msg, e);
+            throw new RepositoryException(msg, e);
+        }
+    }
+
+    @Override
+    public AccessControlPolicyIterator getApplicablePolicies(String absPath) 
throws RepositoryException {
+        getTree(getOakPath(absPath), Permissions.READ_ACCESS_CONTROL, true);
+
+        log.debug("Editing access control policies by path is not supported. 
Use JackrabbitAccessControlManager.getApplicablePolicies(Principal principal)");
+        return AccessControlPolicyIteratorAdapter.EMPTY;
+    }
+
+    @Override
+    public void setPolicy(String absPath, AccessControlPolicy policy) throws 
RepositoryException {
+        PrincipalPolicyImpl pp = checkValidPolicy(absPath, policy);
+        String oakPath = pp.getOakPath();
+        Tree tree = getTree(oakPath, Permissions.MODIFY_ACCESS_CONTROL, true);
+
+        Tree policyTree = getPolicyTree(tree);
+        if (policyTree.exists()) {
+            policyTree.remove();
+        }
+
+        // make sure parent has mixin set and policy node is properly 
initialized
+        TreeUtil.addMixin(tree, MIX_REP_PRINCIPAL_BASED_MIXIN, 
getRoot().getTree(NodeTypeConstants.NODE_TYPES_PATH), 
getRoot().getContentSession().getAuthInfo().getUserID());
+        policyTree = TreeUtil.addChild(tree, REP_PRINCIPAL_POLICY, 
NT_REP_PRINCIPAL_POLICY);
+        policyTree.setOrderableChildren(true);
+        policyTree.setProperty(Constants.REP_PRINCIPAL_NAME, 
pp.getPrincipal().getName());
+
+        int i = 0;
+        RestrictionProvider restrictionProvider = 
mgrProvider.getRestrictionProvider();
+        for (PrincipalPolicyImpl.EntryImpl entry : pp.getEntries()) {
+            String effectiveOakPath = Strings.nullToEmpty(entry.getOakPath());
+            Tree entryTree = TreeUtil.addChild(policyTree, "entry" + i++, 
NT_REP_PRINCIPAL_ENTRY);
+            if (!Utils.hasModAcPermission(getPermissionProvider(), 
effectiveOakPath)) {
+                throw new AccessDeniedException("Access denied.");
+            }
+            entryTree.setProperty(REP_EFFECTIVE_PATH, effectiveOakPath, 
Type.PATH);
+            entryTree.setProperty(Constants.REP_PRIVILEGES, 
privilegeBitsProvider.getPrivilegeNames(entry.getPrivilegeBits()), Type.NAMES);
+            restrictionProvider.writeRestrictions(oakPath, entryTree, 
entry.getRestrictions());
+        }
+    }
+
+    @Override
+    public void removePolicy(String absPath, AccessControlPolicy policy) 
throws RepositoryException {
+        PrincipalPolicyImpl pp = checkValidPolicy(absPath, policy);
+        Tree policyTree = getPolicyTree(getTree(pp.getOakPath(), 
Permissions.MODIFY_ACCESS_CONTROL, true));
+        if (policyTree.exists()) {
+            for (Tree child : policyTree.getChildren()) {
+                if (Utils.isPrincipalEntry(child)) {
+                    PropertyState effectivePath = 
child.getProperty(REP_EFFECTIVE_PATH);
+                    if (effectivePath == null) {
+                        throw new AccessControlException("Missing mandatory 
property rep:effectivePath; cannot validate permissions to modify policy.");
+                    } else if 
(!Utils.hasModAcPermission(getPermissionProvider(), 
effectivePath.getValue(Type.PATH))) {
+                        throw new AccessDeniedException("Access denied.");
+                    }
+                }
+            }
+            policyTree.remove();
+        } else {
+            throw new AccessControlException("No policy to remove at " + 
absPath);
+        }
+    }
+
+    //--------------------------------------------------------< PolicyOwner 
>---
+    @Override
+    public boolean defines(@Nullable String absPath, @NotNull 
AccessControlPolicy accessControlPolicy) {
+        String oakPath = (absPath == null) ? null : 
getNamePathMapper().getOakPath(absPath);
+        if (oakPath == null || !filterProvider.handlesPath(oakPath) ||  
!(accessControlPolicy instanceof PrincipalPolicyImpl)) {
+            return false;
+        }
+        return oakPath.equals(((PrincipalPolicyImpl) 
accessControlPolicy).getOakPath());
+    }
+
+    
//--------------------------------------------------------------------------
+
+    /**
+     * 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.
+     */
+    private boolean canHandle(@Nullable Principal principal) throws 
AccessControlException {
+        String name = (principal == null) ? null : principal.getName();
+        if (Strings.isNullOrEmpty(name)) {
+            throw new AccessControlException("Invalid principal " + name);
+        }
+        if (importBehavior ==  ImportBehavior.ABORT || importBehavior == 
ImportBehavior.IGNORE) {
+            principal = principalManager.getPrincipal(name);
+            if (principal == null) {
+                if (importBehavior == ImportBehavior.IGNORE) {
+                    log.debug("Ignoring unknown principal {}", name);
+                    return false;
+                } else {
+                    // abort
+                    throw new AccessControlException("Unsupported principal " 
+ name);
+                }
+            }
+        }
+        return filter.canHandle(Collections.singleton(principal));
+    }
+
+    private boolean canHandle(@NotNull Collection<Principal> principals) 
throws AccessControlException {
+        for (Principal principal : principals) {
+            if (!canHandle(principal)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private PrincipalPolicyImpl checkValidPolicy(@Nullable String absPath, 
@NotNull AccessControlPolicy policy) throws AccessControlException {
+        if (!defines(absPath, policy)) {
+            throw new AccessControlException("Invalid policy "+ policy + " at 
path " + absPath);
+        }
+        return (PrincipalPolicyImpl) policy;
+    }
+
+    @NotNull
+    private static Tree getPolicyTree(@NotNull Tree accessControlledTree) {
+        return accessControlledTree.getChild(Constants.REP_PRINCIPAL_POLICY);
+    }
+
+    @Nullable
+    private JackrabbitAccessControlPolicy createPolicy(@NotNull Principal 
principal,
+                                                       boolean 
isEffectivePolicy) throws RepositoryException {
+        String oakPath = filter.getOakPath(principal);
+        Tree tree = getTree(oakPath, Permissions.READ_ACCESS_CONTROL, true);
+        if (isEffectivePolicy) {
+            Root r = getRoot().getContentSession().getLatestRoot();
+            tree = r.getTree(tree.getPath());
+        }
+
+        if (!isAccessControlled(tree)) {
+            return null;
+        }
+
+        PrincipalPolicyImpl policy = null;
+        Tree policyTree = getPolicyTree(tree);
+        if (Utils.isPrincipalPolicyTree(policyTree)) {
+            policy = new PrincipalPolicyImpl(principal, oakPath, mgrProvider);
+            for (Tree child : policyTree.getChildren()) {
+                if (Utils.isPrincipalEntry(child)) {
+                    policy.addEntry(child);
+                }
+            }
+        }
+        if (isEffectivePolicy && policy != null) {
+            return (policy.isEmpty()) ? null : new ImmutableACL(policy);
+        } else {
+            return policy;
+        }
+    }
+
+    private boolean isAccessControlled(@NotNull Tree tree) {
+        Tree typeRoot = getRoot().getTree(NodeTypeConstants.NODE_TYPES_PATH);
+        return tree.exists() && TreeUtil.isNodeType(tree, 
MIX_REP_PRINCIPAL_BASED_MIXIN, typeRoot);
+    }
+
+    private Iterable<String> getEffectivePaths(@Nullable String oakPath) {
+        // read-access-control permission has already been check for 'oakPath'
+        List<String> paths = Lists.newArrayList();
+        paths.add(Strings.nullToEmpty(oakPath));
+
+        String effectivePath = oakPath;
+        while (effectivePath != null && !PathUtils.denotesRoot(effectivePath)) 
{
+            effectivePath = PathUtils.getParentPath(effectivePath);
+            paths.add(effectivePath);
+        }
+        return paths;
+    }
+
+    @NotNull
+    private JackrabbitAccessControlEntry createEffectiveEntry(@NotNull Tree 
entryTree, @NotNull String effectivePath) throws AccessControlException {
+        String principalName = TreeUtil.getString(entryTree.getParent(), 
AccessControlConstants.REP_PRINCIPAL_NAME);
+        PrivilegeBits bits = 
privilegeBitsProvider.getBits(entryTree.getProperty(Constants.REP_PRIVILEGES).getValue(Type.NAMES));
+        Set<Restriction> restrictions = 
mgrProvider.getRestrictionProvider().readRestrictions(effectivePath, entryTree);
+        return new EffectiveEntry(new PrincipalImpl(principalName), bits, 
true, restrictions, getNamePathMapper());
+    }
+
+    private final class EffectiveEntry extends ACE {
+        private EffectiveEntry(Principal principal, PrivilegeBits 
privilegeBits, boolean isAllow, Set<Restriction> restrictions, NamePathMapper 
namePathMapper) throws AccessControlException {
+            super(principal, privilegeBits, isAllow, restrictions, 
namePathMapper);
+        }
+
+        @Override
+        public Privilege[] getPrivileges() {
+            Set<String> names =  
privilegeBitsProvider.getPrivilegeNames(getPrivilegeBits());
+            return Utils.privilegesFromOakNames(names, 
mgrProvider.getPrivilegeManager(), getNamePathMapper());
+        }
+    }
+}

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

Added: 
jackrabbit/oak/trunk/oak-authorization-principalbased/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/principalbased/impl/PrincipalBasedAuthorizationConfiguration.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/PrincipalBasedAuthorizationConfiguration.java?rev=1857551&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-authorization-principalbased/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/principalbased/impl/PrincipalBasedAuthorizationConfiguration.java
 (added)
+++ 
jackrabbit/oak/trunk/oak-authorization-principalbased/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/principalbased/impl/PrincipalBasedAuthorizationConfiguration.java
 Mon Apr 15 07:16:49 2019
@@ -0,0 +1,235 @@
+/*
+ * 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.Iterables;
+import org.apache.jackrabbit.oak.api.Root;
+import org.apache.jackrabbit.oak.api.Tree;
+import org.apache.jackrabbit.oak.namepath.NamePathMapper;
+import org.apache.jackrabbit.oak.plugins.memory.MemoryNodeStore;
+import org.apache.jackrabbit.oak.plugins.nodetype.ReadOnlyNodeTypeManager;
+import org.apache.jackrabbit.oak.plugins.nodetype.write.NodeTypeRegistry;
+import org.apache.jackrabbit.oak.spi.commit.CommitHook;
+import org.apache.jackrabbit.oak.spi.commit.MoveTracker;
+import org.apache.jackrabbit.oak.spi.commit.ValidatorProvider;
+import org.apache.jackrabbit.oak.spi.lifecycle.RepositoryInitializer;
+import org.apache.jackrabbit.oak.spi.mount.Mount;
+import org.apache.jackrabbit.oak.spi.mount.MountInfoProvider;
+import org.apache.jackrabbit.oak.spi.nodetype.NodeTypeConstants;
+import org.apache.jackrabbit.oak.spi.security.ConfigurationBase;
+import org.apache.jackrabbit.oak.spi.security.ConfigurationParameters;
+import org.apache.jackrabbit.oak.spi.security.Context;
+import org.apache.jackrabbit.oak.spi.security.SecurityConfiguration;
+import 
org.apache.jackrabbit.oak.spi.security.authorization.AuthorizationConfiguration;
+import 
org.apache.jackrabbit.oak.spi.security.authorization.accesscontrol.AccessControlConstants;
+import 
org.apache.jackrabbit.oak.spi.security.authorization.permission.EmptyPermissionProvider;
+import 
org.apache.jackrabbit.oak.spi.security.authorization.permission.PermissionProvider;
+import 
org.apache.jackrabbit.oak.spi.security.authorization.principalbased.Filter;
+import 
org.apache.jackrabbit.oak.spi.security.authorization.principalbased.FilterProvider;
+import 
org.apache.jackrabbit.oak.spi.security.authorization.restriction.RestrictionProvider;
+import org.apache.jackrabbit.oak.spi.state.ApplyDiff;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.apache.jackrabbit.oak.spi.state.NodeStore;
+import org.apache.jackrabbit.oak.spi.xml.ProtectedItemImporter;
+import org.jetbrains.annotations.NotNull;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Modified;
+import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.component.annotations.ReferenceCardinality;
+import org.osgi.service.metatype.annotations.AttributeDefinition;
+import org.osgi.service.metatype.annotations.Designate;
+import org.osgi.service.metatype.annotations.ObjectClassDefinition;
+
+import javax.jcr.RepositoryException;
+import javax.jcr.security.AccessControlManager;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.Principal;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import static 
org.apache.jackrabbit.oak.spi.security.RegistrationConstants.OAK_SECURITY_NAME;
+
+@Component(
+        service = {AuthorizationConfiguration.class, 
SecurityConfiguration.class},
+        property = OAK_SECURITY_NAME + 
"=org.apache.jackrabbit.oak.spi.security.authorization.principalbased.impl.PrincipalBasedAuthorizationConfiguration")
+@Designate(ocd = PrincipalBasedAuthorizationConfiguration.Configuration.class)
+public class PrincipalBasedAuthorizationConfiguration extends 
ConfigurationBase implements AuthorizationConfiguration {
+
+    @ObjectClassDefinition(name = "Apache Jackrabbit Oak Principal Based 
AuthorizationConfiguration")
+    @interface Configuration {
+        @AttributeDefinition(
+                name = "Ranking",
+                description = "Ranking of this configuration in a setup with 
multiple authorization configurations.")
+        int configurationRanking() default 500;
+    }
+
+    /**
+     * Reference to service implementing {@link FilterProvider} to define the 
principals for which this module should take effect.
+     */
+    private FilterProvider filterProvider;
+
+    /**
+     * Reference to service implementing {@link MountInfoProvider}
+     */
+    private MountInfoProvider mountInfoProvider;
+
+    @SuppressWarnings("UnusedDeclaration")
+    public PrincipalBasedAuthorizationConfiguration() {
+        super();
+    }
+
+    @NotNull
+    @Override
+    public AccessControlManager getAccessControlManager(@NotNull Root root, 
@NotNull NamePathMapper namePathMapper) {
+        return new PrincipalBasedAccessControlManager(new 
MgrProviderImpl(this, root, namePathMapper), filterProvider);
+    }
+
+    @NotNull
+    @Override
+    public RestrictionProvider getRestrictionProvider() {
+        return 
getParameters().getConfigValue(AccessControlConstants.PARAM_RESTRICTION_PROVIDER,
 RestrictionProvider.EMPTY, RestrictionProvider.class);
+    }
+
+    @NotNull
+    @Override
+    public PermissionProvider getPermissionProvider(@NotNull Root root, 
@NotNull String workspaceName, @NotNull Set<Principal> principals) {
+        Filter f = filterProvider.getFilter(getSecurityProvider(), 
getRootProvider().createReadOnlyRoot(root), NamePathMapper.DEFAULT);
+        if (!f.canHandle(principals)) {
+            return EmptyPermissionProvider.getInstance();
+        } else {
+            Iterable<String> principalPaths = Iterables.transform(principals, 
principal -> f.getOakPath(principal));
+            return new PrincipalBasedPermissionProvider(root, workspaceName, 
principalPaths, this);
+        }
+    }
+
+    @NotNull
+    @Override
+    public String getName() {
+        return AuthorizationConfiguration.NAME;
+    }
+
+    @NotNull
+    @Override
+    public RepositoryInitializer getRepositoryInitializer() {
+        return builder -> {
+            NodeState base = builder.getNodeState();
+            NodeStore store = new MemoryNodeStore(base);
+
+            Root root = getRootProvider().createSystemRoot(store, null);
+            if (registerNodeTypes(root)) {
+                NodeState target = store.getRoot();
+                target.compareAgainstBaseState(base, new ApplyDiff(builder));
+            }
+        };
+    }
+
+    @NotNull
+    @Override
+    public List<? extends CommitHook> getCommitHooks(@NotNull String 
workspaceName) {
+        return Collections.emptyList();
+    }
+
+    @NotNull
+    @Override
+    public List<? extends ValidatorProvider> getValidators(@NotNull String 
workspaceName, @NotNull Set<Principal> principals, @NotNull MoveTracker 
moveTracker) {
+        return ImmutableList.of(new PrincipalPolicyValidatorProvider(new 
MgrProviderImpl(this), principals, workspaceName));
+    }
+
+    @NotNull
+    @Override
+    public List<ProtectedItemImporter> getProtectedItemImporters() {
+        return Collections.<ProtectedItemImporter>singletonList(new 
PrincipalPolicyImporter(filterProvider, new MgrProviderImpl(this)));
+    }
+
+    @NotNull
+    @Override
+    public Context getContext() {
+        return ContextImpl.INSTANCE;
+    }
+
+    //----------------------------------------------------< SCR Integration 
>---
+    @Activate
+    public void activate(@NotNull Configuration configuration, @NotNull 
Map<String, Object> properties) {
+        checkConflictingMount();
+        setParameters(ConfigurationParameters.of(properties));
+    }
+
+    @Modified
+    public void modified(@NotNull Configuration configuration, @NotNull 
Map<String, Object> properties) {
+        activate(configuration, properties);
+    }
+
+    @Reference(name = "filterProvider", cardinality = 
ReferenceCardinality.MANDATORY)
+    public void bindFilterProvider(@NotNull FilterProvider filterProvider) {
+        this.filterProvider = filterProvider;
+    }
+
+    public void unbindFilterProvider(@NotNull FilterProvider filterProvider) {
+        this.filterProvider = null;
+    }
+
+    @Reference(name = "mountInfoProvider", cardinality = 
ReferenceCardinality.MANDATORY)
+    public void bindMountInfoProvider(@NotNull MountInfoProvider 
mountInfoProvider) {
+        this.mountInfoProvider = mountInfoProvider;
+    }
+
+    public void unbindMountInfoProvider(@NotNull MountInfoProvider 
mountInfoProvider) {
+        // set to null (and not default) to comply with OSGi lifecycle,
+        // if the reference is unset it means the service is being deactivated
+        this.mountInfoProvider = null;
+    }
+
+    
//--------------------------------------------------------------------------
+    /**
+     * While it is perfectly valid if the filter root is the start of or 
located below a mount, it's illegal if a given
+     * mount would start somewhere in the subtree of the filter root 
distributing the principal based policies between
+     * different mounts.
+     */
+    private void checkConflictingMount() {
+        String filterRoot = filterProvider.getFilterRoot();
+        for (Mount mount : mountInfoProvider.getNonDefaultMounts()) {
+            if (mount.isUnder(filterRoot)) {
+                throw new IllegalStateException("Mount found below filter root 
" + filterRoot);
+            }
+        }
+    }
+
+    private static boolean registerNodeTypes(@NotNull final Root root) {
+        try {
+            ReadOnlyNodeTypeManager ntMgr = new ReadOnlyNodeTypeManager() {
+                @Override
+                protected Tree getTypes() {
+                    return root.getTree(NodeTypeConstants.NODE_TYPES_PATH);
+                }
+            };
+            if (!ntMgr.hasNodeType(Constants.NT_REP_PRINCIPAL_POLICY)) {
+                try (InputStream stream = 
PrincipalBasedAuthorizationConfiguration.class.getResourceAsStream("nodetypes.cnd"))
 {
+                    NodeTypeRegistry.register(root, stream, "node types for 
principal based authorization");
+                    return true;
+                }
+            }
+        } catch (IOException | RepositoryException e) {
+            throw new IllegalStateException("Unable to read node types for 
principal based authorization", e);
+        }
+        return false;
+    }
+}

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

Added: 
jackrabbit/oak/trunk/oak-authorization-principalbased/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/principalbased/impl/PrincipalBasedPermissionProvider.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/PrincipalBasedPermissionProvider.java?rev=1857551&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-authorization-principalbased/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/principalbased/impl/PrincipalBasedPermissionProvider.java
 (added)
+++ 
jackrabbit/oak/trunk/oak-authorization-principalbased/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/principalbased/impl/PrincipalBasedPermissionProvider.java
 Mon Apr 15 07:16:49 2019
@@ -0,0 +1,466 @@
+/*
+ * 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.Predicate;
+import com.google.common.base.Predicates;
+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.namepath.NamePathMapper;
+import org.apache.jackrabbit.oak.plugins.tree.ReadOnly;
+import org.apache.jackrabbit.oak.plugins.tree.TreeLocation;
+import org.apache.jackrabbit.oak.plugins.tree.TreeType;
+import org.apache.jackrabbit.oak.plugins.tree.TreeTypeProvider;
+import org.apache.jackrabbit.oak.plugins.version.ReadOnlyVersionManager;
+import 
org.apache.jackrabbit.oak.spi.security.authorization.permission.AggregatedPermissionProvider;
+import 
org.apache.jackrabbit.oak.spi.security.authorization.permission.Permissions;
+import 
org.apache.jackrabbit.oak.spi.security.authorization.permission.RepositoryPermission;
+import 
org.apache.jackrabbit.oak.spi.security.authorization.permission.TreePermission;
+import org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeBits;
+import org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeBitsProvider;
+import org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeConstants;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.apache.jackrabbit.oak.spi.state.NodeStateUtils;
+import org.apache.jackrabbit.oak.spi.version.VersionConstants;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Iterator;
+import java.util.Set;
+
+import static 
org.apache.jackrabbit.oak.spi.security.authorization.permission.TreePermission.ALL;
+import static 
org.apache.jackrabbit.oak.spi.security.authorization.permission.TreePermission.EMPTY;
+
+class PrincipalBasedPermissionProvider implements 
AggregatedPermissionProvider, Constants {
+
+    private static final Logger log = 
LoggerFactory.getLogger(PrincipalBasedPermissionProvider.class);
+
+    private final Root root;
+    private final String workspaceName;
+    private final Iterable principalPaths;
+    private final MgrProvider mgrProvider;
+    private final TreeTypeProvider typeProvider;
+
+    private final PrivilegeBits modAcBits;
+
+    private Root immutableRoot;
+    private RepositoryPermissionImpl repositoryPermission;
+    private EntryCache entryCache;
+
+    PrincipalBasedPermissionProvider(@NotNull Root root,
+                                     @NotNull String workspaceName,
+                                     @NotNull Iterable<String> principalPaths,
+                                     @NotNull 
PrincipalBasedAuthorizationConfiguration authorizationConfiguration) {
+        this.root = root;
+        this.workspaceName = workspaceName;
+        this.principalPaths = principalPaths;
+
+        immutableRoot = 
authorizationConfiguration.getRootProvider().createReadOnlyRoot(root);
+        mgrProvider = new MgrProviderImpl(authorizationConfiguration, 
immutableRoot, NamePathMapper.DEFAULT);
+        typeProvider = new TreeTypeProvider(mgrProvider.getContext());
+        modAcBits = 
mgrProvider.getPrivilegeBitsProvider().getBits(PrivilegeConstants.JCR_MODIFY_ACCESS_CONTROL);
+
+        entryCache = new EntryCache(immutableRoot, principalPaths, 
mgrProvider.getRestrictionProvider());
+    }
+
+    //-------------------------------------------------< PermissionProvider 
>---
+
+    @Override
+    public void refresh() {
+        immutableRoot = mgrProvider.getRootProvider().createReadOnlyRoot(root);
+        mgrProvider.reset(immutableRoot, NamePathMapper.DEFAULT);
+
+        entryCache = new EntryCache(immutableRoot, principalPaths, 
mgrProvider.getRestrictionProvider());
+        if (repositoryPermission != null) {
+            repositoryPermission.refresh();
+        }
+    }
+
+    @NotNull
+    @Override
+    public Set<String> getPrivileges(@Nullable Tree tree) {
+        return 
mgrProvider.getPrivilegeBitsProvider().getPrivilegeNames(getGrantedPrivilegeBits(tree));
+    }
+
+    @Override
+    public boolean hasPrivileges(@Nullable Tree tree, @NotNull String... 
privilegeNames) {
+        return 
getGrantedPrivilegeBits(tree).includes(mgrProvider.getPrivilegeBitsProvider().getBits(privilegeNames));
+    }
+
+    @Override
+    public @NotNull RepositoryPermission getRepositoryPermission() {
+        if (repositoryPermission == null) {
+            repositoryPermission = new RepositoryPermissionImpl();
+        }
+        return repositoryPermission;
+    }
+
+    @Override
+    public @NotNull TreePermission getTreePermission(@NotNull Tree tree, 
@NotNull TreePermission parentPermission) {
+        Tree readOnly = getReadOnlyTree(tree);
+        TreeType parentType;
+        if (parentPermission instanceof AbstractTreePermission) {
+            parentType = ((AbstractTreePermission) parentPermission).getType();
+        } else {
+            parentType = (tree.isRoot()) ? TreeType.DEFAULT : 
typeProvider.getType(tree.getParent());
+        }
+        return getTreePermission(readOnly, typeProvider.getType(readOnly, 
parentType), parentPermission);
+    }
+
+    @Override
+    public boolean isGranted(@NotNull Tree tree, @Nullable PropertyState 
property, long permissions) {
+        Tree readOnly = getReadOnlyTree(tree);
+        TreeType type = typeProvider.getType(readOnly);
+        switch (type) {
+            case HIDDEN:
+                return true;
+            case INTERNAL:
+                return false;
+            case VERSION:
+                if (!isVersionStoreTree(readOnly)) {
+                    Tree versionableTree = getVersionableTree(readOnly);
+                    if (versionableTree == null) {
+                        // unable to determine the location of the versionable 
item -> deny access.
+                        return false;
+                    }
+                    // reset the tree that is target for permission evaluation 
(see below)
+                    readOnly = versionableTree;
+                } // else: versionstore-tree is covered below
+                break;
+            case ACCESS_CONTROL:
+                if (!isGrantedOnEffective(readOnly, permissions)) {
+                    return false;
+                }
+                break;
+            default:
+                // covered below
+                break;
+        }
+
+        return isGranted(readOnly.getPath(), EntryPredicate.create(readOnly, 
property), EntryPredicate.createParent(readOnly, permissions), permissions);
+    }
+
+    @Override
+    public boolean isGranted(@NotNull String oakPath, @NotNull String 
jcrActions) {
+        TreeLocation location = TreeLocation.create(immutableRoot, oakPath);
+
+        boolean isAcContent = 
mgrProvider.getContext().definesLocation(location);
+        long permissions = Permissions.getPermissions(jcrActions, location, 
isAcContent);
+
+        return isGranted(location, permissions);
+    }
+
+    //---------------------------------------< AggregatedPermissionProvider 
>---
+
+    @Override
+    public @NotNull PrivilegeBits supportedPrivileges(@Nullable Tree tree, 
@Nullable PrivilegeBits privilegeBits) {
+        return (privilegeBits != null) ? privilegeBits : new 
PrivilegeBitsProvider(immutableRoot).getBits(PrivilegeConstants.JCR_ALL);
+
+    }
+
+    @Override
+    public long supportedPermissions(@Nullable Tree tree, @Nullable 
PropertyState property, long permissions) {
+        return permissions;
+    }
+
+    @Override
+    public long supportedPermissions(@NotNull TreeLocation location, long 
permissions) {
+        return permissions;
+    }
+
+    @Override
+    public long supportedPermissions(@NotNull TreePermission treePermission, 
@Nullable PropertyState property, long permissions) {
+        return permissions;
+    }
+
+    @Override
+    public boolean isGranted(@NotNull TreeLocation location, long permissions) 
{
+        boolean isGranted = false;
+        PropertyState property = location.getProperty();
+        TreeLocation tl = (property == null) ? location : location.getParent();
+        Tree tree = tl.getTree();
+        String oakPath = location.getPath();
+        if (tree != null) {
+            isGranted = isGranted(tree, property, permissions);
+        } else if (!oakPath.startsWith(VersionConstants.VERSION_STORE_PATH)) {
+            Predicate<PermissionEntry> parentPredicate = 
EntryPredicate.createParent(tl.getPath(), tl.getParent().getTree(), 
permissions);
+            isGranted = isGranted(oakPath, EntryPredicate.create(oakPath), 
parentPredicate, permissions);
+        } else {
+            log.debug("Cannot determine permissions for non-existing location 
{} below the version storage", location);
+        }
+        return isGranted;
+    }
+
+    @Override
+    public @NotNull TreePermission getTreePermission(@NotNull Tree tree, 
@NotNull TreeType type, @NotNull TreePermission parentPermission) {
+        Tree readOnly = getReadOnlyTree(tree);
+        if (readOnly.isRoot()) {
+            return new RegularTreePermission(readOnly, TreeType.DEFAULT);
+        }
+        switch (type) {
+            case HIDDEN:
+                return ALL;
+            case INTERNAL:
+                return EMPTY;
+            case VERSION:
+                if (!isVersionStoreTree(readOnly)) {
+                    if (parentPermission instanceof VersionTreePermission) {
+                        return 
parentPermission.getChildPermission(readOnly.getName(), 
mgrProvider.getTreeProvider().asNodeState(readOnly));
+                    } else {
+                        Tree versionableTree = getVersionableTree(readOnly);
+                        if (versionableTree == null) {
+                            log.warn("Cannot retrieve versionable node for 
{}", readOnly.getPath());
+                            return EMPTY;
+                        } else {
+                            return new VersionTreePermission(readOnly, 
versionableTree);
+                        }
+                    }
+                } else {
+                    // versionstorage -> regular tree permissions
+                    return new RegularTreePermission(readOnly, type);
+                }
+            case ACCESS_CONTROL:
+            default:
+                return new RegularTreePermission(readOnly, type);
+        }
+    }
+
+    
//--------------------------------------------------------------------------
+
+    private Iterator<PermissionEntry> getEntryIterator(@NotNull String path, 
@NotNull Predicate<PermissionEntry> predicate) {
+        return new EntryIterator(path, predicate, entryCache);
+    }
+
+    private boolean isGranted(@NotNull String path, @NotNull 
Predicate<PermissionEntry> predicate,
+                              @NotNull Predicate<PermissionEntry> 
parentPredicate, long permissions) {
+        long allows = Permissions.NO_PERMISSION;
+        PrivilegeBits bits = PrivilegeBits.getInstance();
+        PrivilegeBits parentBits = PrivilegeBits.getInstance();
+
+        Iterator<PermissionEntry> it = getEntryIterator(path, 
Predicates.alwaysTrue());
+        while (it.hasNext()) {
+            PermissionEntry entry = it.next();
+            PrivilegeBits entryBits = entry.getPrivilegeBits();
+            if (parentPredicate.apply(entry)) {
+                parentBits.add(entryBits);
+            }
+            if (predicate.apply(entry)) {
+                bits.add(entryBits);
+            }
+            allows |= PrivilegeBits.calculatePermissions(bits, parentBits, 
true);
+            if ((allows | ~permissions) == -1) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean isGrantedOnEffective(@NotNull Tree tree, long permission) {
+        long toTest = permission & Permissions.MODIFY_ACCESS_CONTROL;
+        if (Permissions.NO_PERMISSION == toTest) {
+            return Boolean.TRUE;
+        }
+
+        String effectivePath = getEffectivePath(tree);
+        if (effectivePath == null) {
+            return Boolean.TRUE;
+        } else if (REPOSITORY_PERMISSION_PATH.equals(effectivePath)) {
+            return getRepositoryPermission().isGranted(toTest);
+        } else {
+            Tree effectiveTree = immutableRoot.getTree(effectivePath);
+            return isGranted(effectiveTree, null, toTest);
+        }
+    }
+
+    @NotNull
+    private PrivilegeBits getGrantedPrivilegeBits(@Nullable Tree tree) {
+        Tree readOnly = (tree == null) ? null : getReadOnlyTree(tree);
+        PrivilegeBits subtract = PrivilegeBits.EMPTY;
+        if (readOnly != null) {
+            TreeType type = typeProvider.getType(readOnly);
+            switch (type) {
+                case HIDDEN:
+                case INTERNAL:
+                    return PrivilegeBits.EMPTY;
+                case VERSION:
+                    if (!isVersionStoreTree(readOnly)) {
+                        Tree versionableTree = getVersionableTree(readOnly);
+                        if (versionableTree == null) {
+                            // unable to determine the location of the 
versionable item -> deny access.
+                            return PrivilegeBits.EMPTY;
+                        } else {
+                            readOnly = versionableTree;
+                        }
+                    }
+                    break;
+                case ACCESS_CONTROL:
+                    subtract = getBitsToSubtract(readOnly);
+                    break;
+                default: // covered below
+                    break;
+
+            }
+        }
+
+        String oakPath;
+        Predicate<PermissionEntry> predicate;
+        if (readOnly == null) {
+            oakPath = REPOSITORY_PERMISSION_PATH;
+            predicate = EntryPredicate.create(null);
+        } else {
+            oakPath = readOnly.getPath();
+            predicate = EntryPredicate.create(readOnly, null);
+        }
+        PrivilegeBits granted = getGrantedPrivilegeBits(oakPath, predicate);
+        return subtract.isEmpty() ? granted : granted.diff(subtract);
+    }
+
+    /**
+     * In case the tree of type access-control represents a principal-based 
entry or a restriction defined below,
+     * modify-access-control is only granted if it is also granted on the 
effective target path.
+     * Calculate if those permissions are granted and if not subtract them 
later from the final result.
+     *
+     * @param tree A read-only tree of type ACCESS_CONTROL.
+     * @return PrivilegeBits to be subtracted from the final result or {@link 
PrivilegeBits#EMPTY}, if the given tree
+     * does not represent a principal-based entry or both read and modify 
access control is granted and nothing needs to
+     * be subtracted.
+     */
+    @NotNull
+    PrivilegeBits getBitsToSubtract(@NotNull Tree tree) {
+        String effectivePath = getEffectivePath(tree);
+        if (effectivePath == null) {
+            return PrivilegeBits.EMPTY;
+        } else {
+            return 
modAcBits.modifiable().diff(getGrantedPrivilegeBits(effectivePath, 
EntryPredicate.create(effectivePath)));
+        }
+    }
+
+    @NotNull
+    private PrivilegeBits getGrantedPrivilegeBits(@NotNull String oakPath, 
@NotNull Predicate<PermissionEntry> predicate) {
+        PrivilegeBits pb = PrivilegeBits.getInstance();
+        Iterator<PermissionEntry> entries = getEntryIterator(oakPath, 
predicate);
+        while (entries.hasNext()) {
+            pb.add(entries.next().getPrivilegeBits());
+        }
+        return pb;
+    }
+
+    @NotNull
+    private Tree getReadOnlyTree(@NotNull Tree tree) {
+        if (tree instanceof ReadOnly) {
+            return tree;
+        } else {
+            return immutableRoot.getTree(tree.getPath());
+        }
+    }
+
+    @Nullable
+    private String getEffectivePath(@NotNull Tree tree) {
+        Tree principalEntry = null;
+        if (Utils.isPrincipalEntry(tree)) {
+            principalEntry = tree;
+        } else if (Utils.isPrincipalEntry(tree.getParent())) {
+            principalEntry = tree.getParent();
+        }
+        return (principalEntry == null) ? null :  
principalEntry.getProperty(REP_EFFECTIVE_PATH).getValue(Type.STRING);
+    }
+
+    @Nullable
+    private Tree getVersionableTree(@NotNull Tree versionTree) {
+        return ReadOnlyVersionManager.getInstance(immutableRoot, 
NamePathMapper.DEFAULT).getVersionable(versionTree, workspaceName);
+    }
+
+    private boolean isVersionStoreTree(@NotNull Tree tree) {
+        return ReadOnlyVersionManager.isVersionStoreTree(tree);
+    }
+
+    @NotNull
+    TreePermission getTreePermission(@NotNull String name, @NotNull NodeState 
nodeState, @NotNull AbstractTreePermission parentTreePermission) {
+        Tree readOnly = 
mgrProvider.getTreeProvider().createReadOnlyTree(parentTreePermission.getTree(),
 name, nodeState);
+        return getTreePermission(readOnly, typeProvider.getType(readOnly, 
parentTreePermission.getType()), parentTreePermission);
+    }
+
+    
//--------------------------------------------------------------------------
+    private final class RepositoryPermissionImpl implements 
RepositoryPermission {
+
+        private long grantedPermissions = -1;
+
+        @Override
+        public boolean isGranted(long repositoryPermissions) {
+            return Permissions.includes(getGranted(), repositoryPermissions);
+        }
+
+        private long getGranted() {
+            if (grantedPermissions == -1) {
+                PrivilegeBits pb = 
getGrantedPrivilegeBits(REPOSITORY_PERMISSION_PATH, 
EntryPredicate.create(null));
+                grantedPermissions = PrivilegeBits.calculatePermissions(pb, 
PrivilegeBits.EMPTY, true);
+            }
+            return grantedPermissions;
+        }
+
+        private void refresh() {
+            grantedPermissions = -1;
+        }
+    }
+
+    private final class RegularTreePermission extends AbstractTreePermission {
+
+        RegularTreePermission(@NotNull Tree tree, @NotNull TreeType type) {
+            super(tree, type);
+        }
+
+        @Override
+        PrincipalBasedPermissionProvider getPermissionProvider() {
+            return PrincipalBasedPermissionProvider.this;
+        }
+    }
+
+    private final class VersionTreePermission extends AbstractTreePermission 
implements VersionConstants {
+
+        private final Tree versionTree;
+
+        VersionTreePermission(@NotNull Tree versionTree, @NotNull Tree 
versionableTree) {
+            super(versionableTree, TreeType.VERSION);
+            this.versionTree = versionTree;
+        }
+
+        @Override
+        PrincipalBasedPermissionProvider getPermissionProvider() {
+            return PrincipalBasedPermissionProvider.this;
+        }
+
+        @Override
+        public @NotNull TreePermission getChildPermission(@NotNull String 
childName, @NotNull NodeState childState) {
+            Tree childVersionTree = 
mgrProvider.getTreeProvider().createReadOnlyTree(versionTree, childName, 
childState);
+            Tree childVersionableTree;
+            String primaryType = NodeStateUtils.getPrimaryTypeName(childState);
+            if (VERSION_NODE_NAMES.contains(childName) || 
NT_VERSION.equals(primaryType)) {
+                // permissions of the original versionable node still apply to 
versioning related structure inside the
+                // version histore tree (labels, references, the frozen node 
itself)
+                childVersionableTree = getTree();
+            } else {
+                // internals of the frozen node -> build new child tree for 
evaluation
+                childVersionableTree = getTree().getChild(childName);
+            }
+            return new VersionTreePermission(childVersionTree, 
childVersionableTree);
+        }
+    }
+}

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

Added: 
jackrabbit/oak/trunk/oak-authorization-principalbased/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/principalbased/impl/PrincipalPolicyImpl.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/PrincipalPolicyImpl.java?rev=1857551&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-authorization-principalbased/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/principalbased/impl/PrincipalPolicyImpl.java
 (added)
+++ 
jackrabbit/oak/trunk/oak-authorization-principalbased/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/principalbased/impl/PrincipalPolicyImpl.java
 Mon Apr 15 07:16:49 2019
@@ -0,0 +1,311 @@
+/*
+ * 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.Objects;
+import com.google.common.base.Strings;
+import com.google.common.collect.Maps;
+import org.apache.jackrabbit.api.security.authorization.PrivilegeManager;
+import org.apache.jackrabbit.oak.api.Tree;
+import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.plugins.tree.TreeUtil;
+import org.apache.jackrabbit.oak.spi.security.authorization.accesscontrol.ACE;
+import 
org.apache.jackrabbit.oak.spi.security.authorization.accesscontrol.AbstractAccessControlList;
+import 
org.apache.jackrabbit.oak.spi.security.authorization.accesscontrol.AccessControlConstants;
+import 
org.apache.jackrabbit.oak.spi.security.authorization.principalbased.PrincipalPolicy;
+import 
org.apache.jackrabbit.oak.spi.security.authorization.restriction.Restriction;
+import 
org.apache.jackrabbit.oak.spi.security.authorization.restriction.RestrictionDefinition;
+import 
org.apache.jackrabbit.oak.spi.security.authorization.restriction.RestrictionProvider;
+import org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeBits;
+import org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeBitsProvider;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jcr.RepositoryException;
+import javax.jcr.Value;
+import javax.jcr.security.AccessControlEntry;
+import javax.jcr.security.AccessControlException;
+import javax.jcr.security.Privilege;
+import java.security.Principal;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import static 
org.apache.jackrabbit.oak.spi.security.authorization.principalbased.impl.Constants.REP_EFFECTIVE_PATH;
+
+class PrincipalPolicyImpl extends AbstractAccessControlList implements 
PrincipalPolicy {
+
+    private static final Logger log = 
LoggerFactory.getLogger(PrincipalPolicyImpl.class);
+
+    private final List<EntryImpl> entries = new ArrayList<>();
+    private final Principal principal;
+
+    private final RestrictionProvider restrictionProvider;
+    private final PrivilegeManager privilegeManager;
+    private final PrivilegeBitsProvider privilegeBitsProvider;
+
+    PrincipalPolicyImpl(@NotNull Principal principal, @NotNull String oakPath, 
@NotNull MgrProvider mgrProvider) {
+        super(oakPath, mgrProvider.getNamePathMapper());
+        this.principal = principal;
+        this.restrictionProvider = mgrProvider.getRestrictionProvider();
+        this.privilegeManager = mgrProvider.getPrivilegeManager();
+        this.privilegeBitsProvider = mgrProvider.getPrivilegeBitsProvider();
+    }
+
+    boolean addEntry(@NotNull Tree entryTree) throws AccessControlException {
+        String oakPath = Strings.emptyToNull(TreeUtil.getString(entryTree, 
REP_EFFECTIVE_PATH));
+        PrivilegeBits bits = 
privilegeBitsProvider.getBits(entryTree.getProperty(Constants.REP_PRIVILEGES).getValue(Type.NAMES));
+        Set<Restriction> restrictions = 
restrictionProvider.readRestrictions(oakPath, entryTree);
+        return addEntry(new EntryImpl(oakPath, bits, restrictions));
+    }
+
+    //------------------------------------------< AbstractAccessControlList 
>---
+
+    @Override
+    @NotNull
+    public List<EntryImpl> getEntries() {
+        return entries;
+    }
+
+    @Override
+    @NotNull
+    public RestrictionProvider getRestrictionProvider() {
+        return restrictionProvider;
+    }
+
+    //----------------------------------------------------< PrincipalPolicy 
>---
+
+    @Override
+    @NotNull
+    public Principal getPrincipal() {
+        return principal;
+    }
+
+    @Override
+    public boolean addEntry(@Nullable String effectivePath, @NotNull 
Privilege[] privileges) throws RepositoryException {
+        return addEntry(effectivePath, privileges, Collections.emptyMap(), 
Collections.emptyMap());
+    }
+
+    @Override
+    public boolean addEntry(@Nullable String effectivePath, @NotNull 
Privilege[] privileges, @NotNull Map<String, Value> restrictions, @NotNull 
Map<String, Value[]> mvRestrictions) throws RepositoryException {
+        String oakPath = (effectivePath == null) ? null : 
getNamePathMapper().getOakPath(effectivePath);
+        Set<Restriction> rs = validateRestrictions(oakPath, restrictions, 
mvRestrictions);
+        PrivilegeBits privilegeBits = validatePrivileges(privileges);
+
+        return addEntry(new EntryImpl(oakPath, privilegeBits, rs));
+    }
+
+
+    //----------------------------------------< JackrabbitAccessControlList 
>---
+    @Override
+    public boolean addEntry(@NotNull Principal principal, @NotNull Privilege[] 
privileges, boolean isAllow, @Nullable Map<String, Value> restrictions, 
@Nullable Map<String, Value[]> mvRestrictions) throws RepositoryException {
+        if (!this.principal.equals(principal)) {
+            throw new AccessControlException("Principal must be the one 
associated with the principal based policy.");
+        }
+        if (!isAllow) {
+            throw new AccessControlException("Principal based access control 
does not support DENY access control entries.");
+        }
+
+        String jcrNodePathName = 
getNamePathMapper().getJcrName(AccessControlConstants.REP_NODE_PATH);
+        String path = extractPathFromRestrictions(restrictions, 
jcrNodePathName);
+        Map<String, Value> filteredRestrictions = 
Maps.filterEntries(restrictions, entry -> 
!jcrNodePathName.equals(entry.getKey()));
+
+        return addEntry(path, privileges, filteredRestrictions, 
(mvRestrictions == null) ? Collections.emptyMap() : mvRestrictions);
+    }
+
+    @Override
+    public void orderBefore(@NotNull AccessControlEntry srcEntry, @Nullable 
AccessControlEntry destEntry) throws RepositoryException {
+        EntryImpl src = validateEntry(srcEntry);
+        EntryImpl dest = (destEntry == null) ? null : validateEntry(destEntry);
+
+        if (src.equals(dest)) {
+            log.debug("'srcEntry' equals 'destEntry' -> no reordering.");
+            return;
+        }
+
+        int index = -1;
+        if (dest != null) {
+            index = entries.indexOf(dest);
+            if (index < 0) {
+                throw new AccessControlException("Destination entry not 
contained in this AccessControlList.");
+            }
+        }
+
+        if (entries.remove(src)) {
+            if (index != -1) {
+                entries.add(index, src);
+            } else {
+                entries.add(src);
+            }
+        } else {
+            throw new AccessControlException("Source entry not contained in 
this AccessControlList");
+        }
+    }
+
+    //--------------------------------------------------< AccessControlList 
>---
+
+    @Override
+    public void removeAccessControlEntry(AccessControlEntry ace) throws 
RepositoryException {
+        validateEntry(ace);
+
+        if (!entries.remove(ace)) {
+            throw new AccessControlException("AccessControlEntry " +ace+ " not 
contained in AccessControlList");
+        }
+    }
+
+    
//--------------------------------------------------------------------------
+
+    @NotNull
+    private String getOakName(@NotNull String jcrName) throws 
RepositoryException {
+        return getNamePathMapper().getOakName(jcrName);
+    }
+
+    @NotNull
+    private Set<Restriction> validateRestrictions(@Nullable String 
effectiveOakPath, @NotNull Map<String, Value> restrictions, @NotNull  
Map<String, Value[]> mvRestrictions) throws RepositoryException {
+        for (RestrictionDefinition def : 
getRestrictionProvider().getSupportedRestrictions(getOakPath())) {
+            String jcrName = getNamePathMapper().getJcrName(def.getName());
+            if (def.isMandatory()) {
+                boolean containsMandatory = (def.getRequiredType().isArray()) ?
+                        mvRestrictions.containsKey(jcrName) :
+                        restrictions.containsKey(jcrName);
+                if (!containsMandatory) {
+                    throw new AccessControlException("Mandatory restriction " 
+ jcrName + " is missing.");
+                }
+            }
+        }
+        return computeRestrictions(effectiveOakPath, restrictions, 
mvRestrictions);
+    }
+
+    @NotNull
+    private Set<Restriction> computeRestrictions(@Nullable String 
effectiveOakPath, @NotNull Map<String, Value> restrictions, @NotNull 
Map<String, Value[]> mvRestrictions) throws RepositoryException {
+        Set<Restriction> rs;
+        if (restrictions.isEmpty() && mvRestrictions.isEmpty()) {
+            rs = Collections.emptySet();
+        } else {
+            RestrictionProvider rp = getRestrictionProvider();
+            rs = new HashSet<>();
+            for (Map.Entry<String, Value> entry : restrictions.entrySet()) {
+                rs.add(rp.createRestriction(effectiveOakPath, 
getOakName(entry.getKey()), entry.getValue()));
+            }
+            for (Map.Entry<String, Value[]> entry : mvRestrictions.entrySet()) 
{
+                rs.add(rp.createRestriction(effectiveOakPath, 
getOakName(entry.getKey()), entry.getValue()));
+            }
+        }
+        return rs;
+    }
+
+    @Nullable
+    private String extractPathFromRestrictions(@Nullable Map<String, Value> 
restrictions, @NotNull String jcrName) throws RepositoryException {
+        if (restrictions == null || !restrictions.containsKey(jcrName)) {
+            throw new AccessControlException("Entries in principal based 
access control need to have a path specified. Add rep:nodePath restriction or 
use PrincipalPolicy.addEntry(String, Privilege[], Map, Map) instead.");
+        }
+
+        // retrieve path from restrictions and filter that restriction entry 
for further processing
+        return Strings.emptyToNull(restrictions.get(jcrName).getString());
+    }
+
+    @NotNull
+    private PrivilegeBits validatePrivileges(@NotNull Privilege[] privileges) 
throws RepositoryException {
+        if (privileges.length == 0) {
+            throw new AccessControlException("Privileges may not be an empty 
array");
+        }
+        for (Privilege p : privileges) {
+            Privilege pv = privilegeManager.getPrivilege(p.getName());
+            if (pv.isAbstract()) {
+                throw new AccessControlException("Privilege " + p + " is 
abstract.");
+            }
+        }
+        return privilegeBitsProvider.getBits(privileges, getNamePathMapper());
+    }
+
+    @NotNull
+    private static EntryImpl validateEntry(@Nullable AccessControlEntry entry) 
throws AccessControlException {
+        if (entry instanceof EntryImpl) {
+            return (EntryImpl) entry;
+        } else {
+            throw new AccessControlException("Invalid AccessControlEntry " + 
entry);
+        }
+    }
+
+    private boolean addEntry(@NotNull EntryImpl entry) {
+        if (entries.contains(entry)) {
+            return false;
+        } else {
+            return entries.add(entry);
+        }
+    }
+
+    //--------------------------------------------------------------< Entry 
>---
+
+    final class EntryImpl extends ACE implements Entry {
+
+        private final String oakPath;
+
+        private int hashCode;
+
+        private EntryImpl(@Nullable String oakPath, @NotNull PrivilegeBits 
privilegeBits, @NotNull  Set<Restriction> restrictions) throws 
AccessControlException {
+            super(principal, privilegeBits, true, restrictions, 
getNamePathMapper());
+            this.oakPath = oakPath;
+        }
+
+        @Nullable
+        String getOakPath() {
+            return oakPath;
+        }
+
+        @Override
+        @Nullable
+        public String getEffectivePath() {
+            return (oakPath == null) ? null : 
getNamePathMapper().getJcrPath(oakPath);
+        }
+
+        @Override
+        public Privilege[] getPrivileges() {
+            Set<String> names =  
privilegeBitsProvider.getPrivilegeNames(getPrivilegeBits());
+            return Utils.privilegesFromOakNames(names, privilegeManager, 
getNamePathMapper());
+        }
+
+        @Override
+        public int hashCode() {
+            if (hashCode == 0) {
+                hashCode = Objects.hashCode(oakPath, principal.getName(), 
getPrivilegeBits(), Boolean.TRUE, getRestrictions());
+            }
+            return hashCode;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (obj == this) {
+                return true;
+            }
+            if (obj instanceof EntryImpl) {
+                EntryImpl other = (EntryImpl) obj;
+                return equivalentPath(other.oakPath) && super.equals(obj);
+            }
+            return false;
+        }
+
+        private boolean equivalentPath(@Nullable String otherPath) {
+            return (oakPath == null) ? otherPath == null : 
oakPath.equals(otherPath);
+        }
+    }
+}

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

Added: 
jackrabbit/oak/trunk/oak-authorization-principalbased/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/principalbased/impl/PrincipalPolicyImporter.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/PrincipalPolicyImporter.java?rev=1857551&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-authorization-principalbased/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/principalbased/impl/PrincipalPolicyImporter.java
 (added)
+++ 
jackrabbit/oak/trunk/oak-authorization-principalbased/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/principalbased/impl/PrincipalPolicyImporter.java
 Mon Apr 15 07:16:49 2019
@@ -0,0 +1,299 @@
+/*
+ * 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.api.security.authorization.PrivilegeManager;
+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.NamePathMapper;
+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.Permissions;
+import 
org.apache.jackrabbit.oak.spi.security.authorization.principalbased.Filter;
+import 
org.apache.jackrabbit.oak.spi.security.authorization.principalbased.FilterProvider;
+import org.apache.jackrabbit.oak.spi.security.principal.PrincipalImpl;
+import org.apache.jackrabbit.oak.spi.xml.ImportBehavior;
+import org.apache.jackrabbit.oak.spi.xml.NodeInfo;
+import org.apache.jackrabbit.oak.spi.xml.PropInfo;
+import org.apache.jackrabbit.oak.spi.xml.ProtectedItemImporter;
+import org.apache.jackrabbit.oak.spi.xml.ProtectedNodeImporter;
+import org.apache.jackrabbit.oak.spi.xml.ProtectedPropertyImporter;
+import org.apache.jackrabbit.oak.spi.xml.ReferenceChangeTracker;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.Value;
+import javax.jcr.nodetype.ConstraintViolationException;
+import javax.jcr.nodetype.PropertyDefinition;
+import javax.jcr.security.AccessControlManager;
+import javax.jcr.security.Privilege;
+import java.security.Principal;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static com.google.common.base.Preconditions.checkState;
+import static 
org.apache.jackrabbit.oak.spi.security.authorization.accesscontrol.AccessControlConstants.REP_NODE_PATH;
+
+/**
+ * Implementation of the {@link ProtectedNodeImporter} and {@link 
ProtectedPropertyImporter}for principal policies.
+ */
+class PrincipalPolicyImporter implements ProtectedNodeImporter, 
ProtectedPropertyImporter, Constants {
+
+    private static final Logger log = 
LoggerFactory.getLogger(PrincipalPolicyImporter.class);
+
+    private Session session;
+    private final MgrProvider mgrProvider;
+    private final FilterProvider filterProvider;
+    private Filter filter;
+
+    private AuthorizationConfiguration authorizationConfiguration;
+
+    private int importBehavior;
+
+    private boolean initialized;
+
+    private PrincipalPolicyImpl policy;
+    private Entry entry;
+
+    PrincipalPolicyImporter(@NotNull FilterProvider filterProvider, @NotNull 
MgrProvider mgrProvider) {
+        this.filterProvider = filterProvider;
+        this.mgrProvider = mgrProvider;
+    }
+
+    //----------------------------------------------< ProtectedItemImporter 
>---
+    @Override
+    public boolean init(@NotNull Session session, @NotNull Root root, @NotNull 
NamePathMapper namePathMapper, boolean isWorkspaceImport, int uuidBehavior, 
@NotNull ReferenceChangeTracker referenceTracker, @NotNull SecurityProvider 
securityProvider) {
+        if (initialized) {
+            throw new IllegalStateException("Already initialized");
+        }
+        this.session = session;
+        mgrProvider.reset(root, namePathMapper);
+        filter = filterProvider.getFilter(mgrProvider.getSecurityProvider(), 
root, namePathMapper);
+
+        authorizationConfiguration = 
securityProvider.getConfiguration(AuthorizationConfiguration.class);
+        importBehavior = 
ImportBehavior.valueFromString(authorizationConfiguration.getParameters().getConfigValue(ProtectedItemImporter.PARAM_IMPORT_BEHAVIOR,
 ImportBehavior.NAME_ABORT));
+
+        initialized = true;
+        return initialized;
+    }
+
+    @Override
+    public void processReferences() {
+        // nothing to do
+    }
+
+    //------------------------------------------< ProtectedPropertyImporter 
>---
+    @Override
+    public boolean handlePropInfo(@NotNull Tree parent, @NotNull PropInfo 
protectedPropInfo, @NotNull PropertyDefinition def) throws RepositoryException {
+        checkState(initialized);
+
+        if (!Utils.isPrincipalPolicyTree(parent) || 
!isValidPrincipalProperty(protectedPropInfo, def)) {
+            return false;
+        }
+        String accessControlledPath = 
PathUtils.getParentPath(parent.getPath());
+        if (!filterProvider.handlesPath(accessControlledPath)) {
+            log.debug("Unable to import principal policy. Access controlled 
path '{}' outside of path supported by FilterProvider.", accessControlledPath);
+            return false;
+        }
+
+        String principalName = protectedPropInfo.getTextValue().getString();
+        Principal principal = filter.getValidPrincipal(accessControlledPath);
+        if (principal == null) {
+            log.debug("Unable to lookup principal by path = {}. Creating by 
name {}.", accessControlledPath, principalName);
+            principal = new PrincipalImpl(principalName);
+        } else if (!principalName.equals(principal.getName())) {
+            log.error("Principal name mismatch expected '{}' but was '{}'.", 
principalName, principal.getName());
+            throw new ConstraintViolationException("Principal name mismatch.");
+        }
+        if (!Utils.canHandle(principal, filter, importBehavior)) {
+            log.debug("Cannot handle principal {} with name = {}", principal, 
principalName);
+            return false;
+        }
+        // make sure editing session has ability to read access control
+        session.checkPermission(accessControlledPath, 
Permissions.getString(Permissions.READ_ACCESS_CONTROL));
+        policy = new PrincipalPolicyImpl(principal, accessControlledPath, 
mgrProvider);
+        return true;
+    }
+
+    @Override
+    public void propertiesCompleted(@NotNull Tree protectedParent) throws 
RepositoryException {
+        checkState(initialized);
+
+        // make sure also an empty policy (with entries) is being written (see 
also #end(Tree) below)
+        if (policy != null) {
+            if (isValidProtectedParent(protectedParent, policy)) {
+                getAccessControlManager().setPolicy(policy.getPath(), policy);
+            } else {
+                log.warn("Protected parent {} does not match path of 
PrincipalPolicy {}.", protectedParent.getPath(), policy.getOakPath());
+                getAccessControlManager().removePolicy(policy.getPath(), 
policy);
+            }
+        }
+    }
+
+    //----------------------------------------------< ProtectedNodeImporter 
>---
+    @Override
+    public boolean start(@NotNull Tree protectedParent) throws 
RepositoryException {
+        checkState(initialized);
+
+        // the policy node was added during the regular import (it's parent 
must not be protected)
+        // and the principal-name property must have been processed by the 
ProtectedPropertyImporter
+        return policy != null && isValidProtectedParent(protectedParent, 
policy);
+    }
+
+    @Override
+    public void end(@NotNull Tree protectedParent) throws RepositoryException {
+        checkState(policy != null);
+
+        if (isValidProtectedParent(protectedParent, policy)) {
+            getAccessControlManager().setPolicy(policy.getPath(), policy);
+        } else {
+            log.warn("Protected parent {} does not match path of 
PrincipalPolicy {}.", protectedParent.getPath(), policy.getOakPath());
+            getAccessControlManager().removePolicy(policy.getPath(), policy);
+        }
+        policy = null;
+    }
+
+    @Override
+    public void startChildInfo(@NotNull NodeInfo childInfo, @NotNull 
List<PropInfo> propInfos) throws RepositoryException {
+        checkState(policy != null);
+        String ntName = getOakName(childInfo.getPrimaryTypeName());
+        if (NT_REP_PRINCIPAL_ENTRY.equals(ntName)) {
+            if (entry != null) {
+                throw new ConstraintViolationException("Invalid child node 
sequence: Entries must not be nested.");
+            }
+            entry = new Entry(propInfos);
+        } else if (NT_REP_RESTRICTIONS.equals(ntName)) {
+            if (entry == null) {
+                throw new ConstraintViolationException("Invalid child node 
sequence: Restriction must be associated with an Entry");
+            }
+            entry.addRestrictions(propInfos);
+        } else {
+            throw new ConstraintViolationException("Invalid child node 
'"+childInfo.getName()+"' with type " + ntName);
+        }
+    }
+
+    @Override
+    public void endChildInfo() throws RepositoryException {
+        checkState(policy != null);
+        if (entry != null) {
+            entry.applyTo(policy);
+            // reset the child entry
+            entry = null;
+        }
+    }
+
+    //------------------------------------------------------------< private 
>---
+
+    private boolean isValidPrincipalProperty(@NotNull PropInfo propertyInfo, 
@NotNull PropertyDefinition def) {
+        return REP_PRINCIPAL_NAME.equals(getOakName(propertyInfo.getName())) &&
+                !def.isMultiple() &&
+                
NT_REP_PRINCIPAL_POLICY.equals(getOakName(def.getDeclaringNodeType().getName()));
+    }
+
+    private static boolean isValidProtectedParent(@NotNull Tree 
protectedParent, @NotNull PrincipalPolicyImpl policy) {
+        String accessControlledPath = 
PathUtils.getParentPath(protectedParent.getPath());
+        return accessControlledPath.equals(policy.getOakPath());
+    }
+
+    @Nullable
+    private String getOakName(@Nullable String name) {
+        return (name == null) ? null : 
getNamePathMapper().getOakNameOrNull(name);
+    }
+
+    private AccessControlManager getAccessControlManager() {
+        return 
authorizationConfiguration.getAccessControlManager(mgrProvider.getRoot(), 
mgrProvider.getNamePathMapper());
+    }
+
+    private NamePathMapper getNamePathMapper() {
+        return mgrProvider.getNamePathMapper();
+    }
+
+    
//--------------------------------------------------------------------------
+    private final class Entry {
+
+        private String effectivePath;
+
+        private final Iterable<Privilege> privileges;
+        private final Map<String, Value> restrictions = new HashMap<>();
+        private final Map<String, Value[]> mvRestrictions = new HashMap<>();
+
+        private Entry(@NotNull List<PropInfo> propInfos) throws 
RepositoryException {
+            Iterable<Privilege> privs = null;
+            for (PropInfo prop : propInfos) {
+                String oakName = getOakName(prop.getName());
+                if (REP_EFFECTIVE_PATH.equals(oakName) && PropertyType.PATH == 
prop.getType()) {
+                    effectivePath = prop.getTextValue().getString();
+                } else if (REP_PRIVILEGES.equals(oakName) && PropertyType.NAME 
== prop.getType()) {
+                    privs = 
getPrivileges(Iterables.transform(prop.getTextValues(), textValue -> 
textValue.getString()));
+                } else {
+                    throw new ConstraintViolationException("Unsupported 
property '"+oakName+"' with type "+prop.getType()+" within policy entry of type 
rep:PrincipalEntry");
+                }
+            }
+            if (privs == null) {
+                throw new ConstraintViolationException("Entries for 
PrincipalPolicy must specify the privileges to be granted.");
+            }
+            privileges = privs;
+        }
+
+        private List<Privilege> getPrivileges(@NotNull Iterable<String> 
jcrPrivNames) throws RepositoryException {
+            List<Privilege> privs = new ArrayList<>();
+            PrivilegeManager privilegeManager = 
mgrProvider.getPrivilegeManager();
+            for (String privName : jcrPrivNames) {
+                privs.add(privilegeManager.getPrivilege(privName));
+            }
+            return privs;
+        }
+
+        private void addRestrictions(@NotNull List<PropInfo> propInfos) throws 
RepositoryException {
+            checkState(restrictions.isEmpty() && mvRestrictions.isEmpty(), 
"Multiple restriction nodes.");
+            for (PropInfo prop : propInfos) {
+                String restrictionName = prop.getName();
+                if (REP_NODE_PATH.equals(getOakName(restrictionName))) {
+                    checkState(effectivePath == null, "Attempt to overwrite 
rep:effectivePath property with rep:nodePath restriction.");
+                    log.debug("Extracting rep:effectivePath from rep:nodePath 
restriction.");
+                    effectivePath = prop.getTextValue().getString();
+                } else {
+                    int targetType = 
policy.getRestrictionType(restrictionName);
+                    List<Value> values = prop.getValues(targetType);
+                    if (policy.isMultiValueRestriction(restrictionName)) {
+                        mvRestrictions.put(restrictionName, values.toArray(new 
Value[0]));
+                    } else {
+                        restrictions.put(restrictionName, values.get(0));
+                    }
+                }
+            }
+        }
+
+        private void applyTo(@NotNull PrincipalPolicyImpl policy) throws 
RepositoryException {
+            if (effectivePath == null) {
+                log.error("Missing rep:effectivePath for entry {} of policy at 
{}", this, policy.getOakPath());
+                throw new ConstraintViolationException("Entries for 
PrincipalPolicy must specify an effective path.");
+            }
+            policy.addEntry(Strings.emptyToNull(effectivePath), 
Iterables.toArray(privileges, Privilege.class), restrictions, mvRestrictions);
+        }
+    }
+}

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


Reply via email to