blob: a2a31170c266f84be62db77aa2633b28b7091610 [file] [log] [blame]
/*
* 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.security.authorization.composite;
import java.util.List;
import java.util.Set;
import java.util.function.Function;
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.plugins.tree.RootProvider;
import org.apache.jackrabbit.oak.plugins.tree.TreeLocation;
import org.apache.jackrabbit.oak.plugins.tree.TreeProvider;
import org.apache.jackrabbit.oak.plugins.tree.TreeType;
import org.apache.jackrabbit.oak.plugins.tree.TreeTypeProvider;
import org.apache.jackrabbit.oak.security.authorization.composite.CompositeAuthorizationConfiguration.CompositionType;
import org.apache.jackrabbit.oak.security.authorization.permission.PermissionUtil;
import org.apache.jackrabbit.oak.spi.security.Context;
import org.apache.jackrabbit.oak.spi.security.authorization.permission.AggregatedPermissionProvider;
import org.apache.jackrabbit.oak.spi.security.authorization.permission.PermissionProvider;
import org.apache.jackrabbit.oak.spi.security.authorization.permission.Permissions;
import org.apache.jackrabbit.oak.spi.security.authorization.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.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import static org.apache.jackrabbit.oak.security.authorization.composite.CompositeAuthorizationConfiguration.CompositionType.AND;
/**
* Permission provider implementation that aggregates a list of different
* provider implementations. Note, that the aggregated provider implementations
* *must* implement the
* {@link org.apache.jackrabbit.oak.spi.security.authorization.permission.AggregatedPermissionProvider}
* interface.
*/
class CompositePermissionProvider implements AggregatedPermissionProvider {
private final Root root;
private final AggregatedPermissionProvider[] pps;
private final Context ctx;
private final CompositionType compositionType;
private final RootProvider rootProvider;
private final TreeProvider treeProvider;
private final RepositoryPermission repositoryPermission;
private Root immutableRoot;
private PrivilegeBitsProvider privilegeBitsProvider;
private TreeTypeProvider typeProvider;
CompositePermissionProvider(@NotNull Root root, @NotNull List<AggregatedPermissionProvider> pps,
@NotNull Context acContext, @NotNull CompositionType compositionType,
@NotNull RootProvider rootProvider, @NotNull TreeProvider treeProvider) {
this.root = root;
this.pps = pps.toArray(new AggregatedPermissionProvider[pps.size()]);
this.ctx = acContext;
this.compositionType = compositionType;
this.rootProvider = rootProvider;
this.treeProvider = treeProvider;
repositoryPermission = new CompositeRepositoryPermission(this.pps, this.compositionType);
immutableRoot = rootProvider.createReadOnlyRoot(root);
privilegeBitsProvider = new PrivilegeBitsProvider(immutableRoot);
typeProvider = new TreeTypeProvider(ctx);
}
//-------------------------------------------------< PermissionProvider >---
@Override
public void refresh() {
immutableRoot = rootProvider.createReadOnlyRoot(root);
privilegeBitsProvider = new PrivilegeBitsProvider(immutableRoot);
for (PermissionProvider pp : pps) {
pp.refresh();
}
}
@NotNull
@Override
public Set<String> getPrivileges(@Nullable Tree tree) {
Tree immutableTree = PermissionUtil.getReadOnlyTree(tree, immutableRoot);
PrivilegeBits result = PrivilegeBits.getInstance();
PrivilegeBits denied = PrivilegeBits.getInstance();
for (AggregatedPermissionProvider aggregatedPermissionProvider : pps) {
PrivilegeBits supported = aggregatedPermissionProvider.supportedPrivileges(immutableTree, null).modifiable();
if (doEvaluate(supported)) {
PrivilegeBits granted = privilegeBitsProvider.getBits(aggregatedPermissionProvider.getPrivileges(immutableTree));
// add the granted privileges to the result
if (!granted.isEmpty()) {
result.add(granted);
}
if (compositionType == AND) {
// update the set of denied privs by comparing the granted privs
// with the complete set of supported privileges
denied.add(supported.diff(granted));
}
}
}
// subtract all denied privileges from the result
if (!denied.isEmpty()) {
result.diff(denied);
}
return privilegeBitsProvider.getPrivilegeNames(result);
}
@Override
public boolean hasPrivileges(@Nullable Tree tree, @NotNull String... privilegeNames) {
Tree immutableTree = PermissionUtil.getReadOnlyTree(tree, immutableRoot);
PrivilegeBits privilegeBits = privilegeBitsProvider.getBits(privilegeNames);
if (privilegeBits.isEmpty()) {
return true;
}
boolean hasPrivileges = false;
PrivilegeBits coveredPrivs = PrivilegeBits.getInstance();
for (AggregatedPermissionProvider aggregatedPermissionProvider : pps) {
PrivilegeBits supported = aggregatedPermissionProvider.supportedPrivileges(immutableTree, privilegeBits);
if (doEvaluate(supported)) {
Set<String> supportedNames = privilegeBitsProvider.getPrivilegeNames(supported);
if (compositionType == AND) {
hasPrivileges = aggregatedPermissionProvider.hasPrivileges(immutableTree,
supportedNames.toArray(new String[supportedNames.size()]));
if (!hasPrivileges) {
return false;
}
coveredPrivs.add(supported);
} else {
// evaluate one by one so we can aggregate fragments of
// supported privileges
for (String p : supportedNames) {
if (aggregatedPermissionProvider.hasPrivileges(immutableTree, p)) {
PrivilegeBits granted = privilegeBitsProvider.getBits(p);
coveredPrivs.add(granted);
hasPrivileges = true;
}
}
}
}
}
return hasPrivileges && coveredPrivs.includes(privilegeBits);
}
@NotNull
@Override
public RepositoryPermission getRepositoryPermission() {
return repositoryPermission;
}
@NotNull
@Override
public TreePermission getTreePermission(@NotNull Tree tree, @NotNull TreePermission parentPermission) {
Tree readOnlyTree = PermissionUtil.getReadOnlyTree(tree, immutableRoot);
if (tree.isRoot()) {
return CompositeTreePermission.create(readOnlyTree, treeProvider, typeProvider, pps, compositionType);
} else if (parentPermission instanceof CompositeTreePermission) {
return CompositeTreePermission.create(readOnlyTree, treeProvider, ((CompositeTreePermission) parentPermission));
} else {
return parentPermission.getChildPermission(readOnlyTree.getName(), treeProvider.asNodeState(readOnlyTree));
}
}
@Override
public boolean isGranted(@NotNull Tree parent, @Nullable PropertyState property, long permissions) {
Tree immParent = PermissionUtil.getReadOnlyTree(parent, immutableRoot);
boolean isGranted = false;
long coveredPermissions = Permissions.NO_PERMISSION;
for (AggregatedPermissionProvider aggregatedPermissionProvider : pps) {
long supportedPermissions = aggregatedPermissionProvider.supportedPermissions(immParent, property, permissions);
if (doEvaluate(supportedPermissions)) {
if (compositionType == AND) {
isGranted = aggregatedPermissionProvider.isGranted(immParent, property, supportedPermissions);
if (!isGranted) {
return false;
}
coveredPermissions |= supportedPermissions;
} else {
for (long p : Permissions.aggregates(permissions)) {
if (aggregatedPermissionProvider.isGranted(immParent, property, p)) {
coveredPermissions |= p;
isGranted = true;
}
}
}
}
}
return isGranted && coveredPermissions == permissions;
}
@Override
public boolean isGranted(@NotNull String oakPath, @NotNull String jcrActions) {
TreeLocation location = TreeLocation.create(immutableRoot, oakPath);
boolean isAcContent = ctx.definesLocation(location);
long permissions = Permissions.getPermissions(jcrActions, location, isAcContent);
return isGranted(location, permissions);
}
//------------------------------------------------------------< private >---
private static boolean doEvaluate(long supportedPermissions) {
return supportedPermissions != Permissions.NO_PERMISSION;
}
private static boolean doEvaluate(PrivilegeBits supportedPrivileges) {
return !supportedPrivileges.isEmpty();
}
//-----------------------------------------------< RepositoryPermission >---
/**
* {@code RepositoryPermission} implementation that wraps multiple implementations.
*/
private final static class CompositeRepositoryPermission implements RepositoryPermission {
private final AggregatedPermissionProvider[] pps;
private final CompositionType compositionType;
public CompositeRepositoryPermission(@NotNull AggregatedPermissionProvider[] pps,
@NotNull CompositionType compositionType) {
this.pps = pps;
this.compositionType = compositionType;
}
@Override
public boolean isGranted(long repositoryPermissions) {
boolean isGranted = false;
long coveredPermissions = Permissions.NO_PERMISSION;
for (AggregatedPermissionProvider aggregatedPermissionProvider : pps) {
long supportedPermissions = aggregatedPermissionProvider.supportedPermissions((Tree) null, null, repositoryPermissions);
if (doEvaluate(supportedPermissions)) {
RepositoryPermission rp = aggregatedPermissionProvider.getRepositoryPermission();
if (compositionType == AND) {
isGranted = rp.isGranted(supportedPermissions);
if (!isGranted) {
return false;
}
coveredPermissions |= supportedPermissions;
} else {
for (long p : Permissions.aggregates(repositoryPermissions)) {
if (rp.isGranted(p)) {
coveredPermissions |= p;
isGranted = true;
}
}
}
}
}
return isGranted && coveredPermissions == repositoryPermissions;
}
}
//---------------------------------------< AggregatedPermissionProvider >---
@NotNull
@Override
public PrivilegeBits supportedPrivileges(@Nullable Tree tree, @Nullable PrivilegeBits privilegeBits) {
PrivilegeBits result = PrivilegeBits.getInstance();
for (AggregatedPermissionProvider aggregatedPermissionProvider : pps) {
PrivilegeBits supported = aggregatedPermissionProvider.supportedPrivileges(tree, privilegeBits);
result.add(supported);
}
return result;
}
@Override
public long supportedPermissions(@Nullable Tree tree, @Nullable PropertyState property, long permissions) {
return supportedPermissions((aggregatedPermissionProvider) -> aggregatedPermissionProvider
.supportedPermissions(tree, property, permissions));
}
@Override
public long supportedPermissions(TreeLocation location, long permissions) {
return supportedPermissions((aggregatedPermissionProvider) -> aggregatedPermissionProvider
.supportedPermissions(location, permissions));
}
@Override
public long supportedPermissions(TreePermission treePermission, PropertyState property, long permissions) {
return supportedPermissions((aggregatedPermissionProvider) -> aggregatedPermissionProvider
.supportedPermissions(treePermission, property, permissions));
}
private long supportedPermissions(Function<AggregatedPermissionProvider, Long> supported) {
long coveredPermissions = Permissions.NO_PERMISSION;
for (AggregatedPermissionProvider aggregatedPermissionProvider : pps) {
long supportedPermissions = supported.apply(aggregatedPermissionProvider);
coveredPermissions |= supportedPermissions;
}
return coveredPermissions;
}
@Override
public boolean isGranted(@NotNull TreeLocation location, long permissions) {
PropertyState property = location.getProperty();
Tree tree = (property == null) ? location.getTree() : location.getParent().getTree();
if (tree != null) {
return isGranted(tree, property, permissions);
} else {
boolean isGranted = false;
long coveredPermissions = Permissions.NO_PERMISSION;
for (AggregatedPermissionProvider aggregatedPermissionProvider : pps) {
long supportedPermissions = aggregatedPermissionProvider.supportedPermissions(location, permissions);
if (doEvaluate(supportedPermissions)) {
if (compositionType == AND) {
isGranted = aggregatedPermissionProvider.isGranted(location, supportedPermissions);
if (!isGranted) {
return false;
}
coveredPermissions |= supportedPermissions;
} else {
for (long p : Permissions.aggregates(permissions)) {
if (aggregatedPermissionProvider.isGranted(location, p)) {
coveredPermissions |= p;
isGranted = true;
}
}
}
}
}
return isGranted && coveredPermissions == permissions;
}
}
@NotNull
@Override
public TreePermission getTreePermission(@NotNull Tree tree, @NotNull TreeType type,
@NotNull TreePermission parentPermission) {
Tree immutableTree = PermissionUtil.getReadOnlyTree(tree, immutableRoot);
if (tree.isRoot()) {
return CompositeTreePermission.create(immutableTree, treeProvider, typeProvider, pps, compositionType);
} else if (parentPermission instanceof CompositeTreePermission) {
return CompositeTreePermission.create(immutableTree, treeProvider, ((CompositeTreePermission) parentPermission), type);
} else {
return parentPermission.getChildPermission(immutableTree.getName(), treeProvider.asNodeState(immutableTree));
}
}
}