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