blob: ce3ff8b2c07be7edd912c1edbd9f209cb1d6323f [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.security.Principal;
import java.util.List;
import java.util.Set;
import javax.jcr.Session;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import org.apache.jackrabbit.JcrConstants;
import org.apache.jackrabbit.api.JackrabbitSession;
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.commons.PathUtils;
import org.apache.jackrabbit.oak.plugins.tree.TreeLocation;
import org.apache.jackrabbit.oak.spi.security.authorization.AuthorizationConfiguration;
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.principal.EveryonePrincipal;
import org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeBits;
import org.apache.jackrabbit.oak.spi.state.NodeState;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
/**
* Test the {@link org.apache.jackrabbit.oak.security.authorization.composite.CompositePermissionProvider}
* where permissions are granted but not all permissions/privileges have been
* covered by the call. This might be the case when combining different
* providers that don't cover the full set of permissions/privileges.
*
* For simplicity the test only lists a single provider which only supports a
* limited set of permissions|privileges.
*
* The expected result is:
* - testing for the supported privileges|permissions must reveal that it is granted
* - any other combination of permissions|privileges must be denied.
*/
public class CompositeProviderCoverageTest extends AbstractCompositeProviderTest {
private CompositePermissionProvider cpp;
private CompositePermissionProvider cppO;
@Override
public void before() throws Exception {
super.before();
cpp = createPermissionProvider();
cppO = createPermissionProviderOR();
}
@Override
AggregatedPermissionProvider getTestPermissionProvider() {
return new LimitCoverageProvider(root);
}
@Override
List<AggregatedPermissionProvider> getAggregatedProviders(@NotNull String workspaceName, @NotNull AuthorizationConfiguration config, @NotNull Set<Principal> principals) {
return ImmutableList.of(getTestPermissionProvider());
}
@Override
@Test
public void testGetTreePermissionInstance() throws Exception {
PermissionProvider pp = createPermissionProvider(EveryonePrincipal.getInstance());
TreePermission parentPermission = TreePermission.EMPTY;
for (String path : TP_PATHS) {
TreePermission tp = pp.getTreePermission(readOnlyRoot.getTree(path), parentPermission);
assertTrue(tp instanceof LimitedTreePermission);
parentPermission = tp;
}
}
@Override
@Test
public void testGetTreePermissionInstanceOR() throws Exception {
PermissionProvider pp = createPermissionProviderOR(EveryonePrincipal.getInstance());
TreePermission parentPermission = TreePermission.EMPTY;
for (String path : TP_PATHS) {
TreePermission tp = pp.getTreePermission(readOnlyRoot.getTree(path), parentPermission);
assertTrue(tp instanceof LimitedTreePermission);
parentPermission = tp;
}
}
@Override
@Test
public void testTreePermissionGetChild() throws Exception {
List<String> childNames = ImmutableList.of("test", "a", "b", "c", "nonexisting");
Tree rootTree = readOnlyRoot.getTree(ROOT_PATH);
NodeState ns = getTreeProvider().asNodeState(rootTree);
TreePermission tp = createPermissionProvider().getTreePermission(rootTree, TreePermission.EMPTY);
for (String cName : childNames) {
ns = ns.getChildNode(cName);
tp = tp.getChildPermission(cName, ns);
assertTrue(tp instanceof LimitedTreePermission);
}
}
@Override
@Test
public void testTreePermissionGetChildOR() throws Exception {
List<String> childNames = ImmutableList.of("test", "a", "b", "c", "nonexisting");
Tree rootTree = readOnlyRoot.getTree(ROOT_PATH);
NodeState ns = getTreeProvider().asNodeState(rootTree);
TreePermission tp = createPermissionProviderOR().getTreePermission(rootTree, TreePermission.EMPTY);
for (String cName : childNames) {
ns = ns.getChildNode(cName);
tp = tp.getChildPermission(cName, ns);
assertTrue(tp instanceof LimitedTreePermission);
}
}
@Test
public void testGetPrivileges() throws Exception {
for (String p : NODE_PATHS) {
assertEquals(ImmutableSet.of(REP_READ_NODES), cpp.getPrivileges(readOnlyRoot.getTree(p)));
assertEquals(ImmutableSet.of(REP_READ_NODES), cppO.getPrivileges(readOnlyRoot.getTree(p)));
}
}
@Test
public void testGetPrivilegesOnRepo() throws Exception {
assertEquals(ImmutableSet.of(JCR_NAMESPACE_MANAGEMENT), cpp.getPrivileges(null));
assertEquals(ImmutableSet.of(JCR_NAMESPACE_MANAGEMENT), cppO.getPrivileges(null));
}
@Test
public void testHasPrivileges() throws Exception {
for (String p : NODE_PATHS) {
Tree tree = readOnlyRoot.getTree(p);
assertTrue(cpp.hasPrivileges(tree, REP_READ_NODES));
assertFalse(cpp.hasPrivileges(tree, JCR_READ));
assertFalse(cpp.hasPrivileges(tree, JCR_WRITE));
assertFalse(cpp.hasPrivileges(tree, JCR_ALL));
assertTrue(cppO.hasPrivileges(tree, REP_READ_NODES));
assertFalse(cppO.hasPrivileges(tree, JCR_READ));
assertFalse(cppO.hasPrivileges(tree, JCR_WRITE));
assertFalse(cppO.hasPrivileges(tree, JCR_ALL));
}
}
@Test
public void testHasPrivilegesOnRepo() throws Exception {
assertTrue(cpp.hasPrivileges(null, JCR_NAMESPACE_MANAGEMENT));
assertFalse(cpp.hasPrivileges(null, JCR_NODE_TYPE_DEFINITION_MANAGEMENT));
assertFalse(cpp.hasPrivileges(null, JCR_ALL));
assertTrue(cppO.hasPrivileges(null, JCR_NAMESPACE_MANAGEMENT));
assertFalse(cppO.hasPrivileges(null, JCR_NODE_TYPE_DEFINITION_MANAGEMENT));
assertFalse(cppO.hasPrivileges(null, JCR_ALL));
}
@Test
public void testIsGranted() throws Exception {
for (String p : NODE_PATHS) {
Tree tree = readOnlyRoot.getTree(p);
assertTrue(cpp.isGranted(tree, null, Permissions.READ_NODE));
assertFalse(cpp.isGranted(tree, null, Permissions.LOCK_MANAGEMENT));
assertFalse(cpp.isGranted(tree, null, Permissions.ALL));
assertFalse(cpp.isGranted(tree, null, Permissions.READ_NODE | Permissions.LOCK_MANAGEMENT));
assertTrue(cppO.isGranted(tree, null, Permissions.READ_NODE));
assertFalse(cppO.isGranted(tree, null, Permissions.LOCK_MANAGEMENT));
assertFalse(cppO.isGranted(tree, null, Permissions.ALL));
assertFalse(cppO.isGranted(tree, null, Permissions.READ_NODE | Permissions.LOCK_MANAGEMENT));
}
}
@Test
public void testIsGrantedProperty() throws Exception {
for (String p : NODE_PATHS) {
Tree tree = readOnlyRoot.getTree(p);
assertTrue(cpp.isGranted(tree, PROPERTY_STATE, Permissions.READ_NODE));
assertFalse(cpp.isGranted(tree, PROPERTY_STATE, Permissions.READ_PROPERTY));
assertFalse(cpp.isGranted(tree, PROPERTY_STATE, Permissions.LOCK_MANAGEMENT));
assertFalse(cpp.isGranted(tree, PROPERTY_STATE, Permissions.ALL));
assertFalse(cpp.isGranted(tree, PROPERTY_STATE, Permissions.READ_NODE | Permissions.LOCK_MANAGEMENT));
assertTrue(cppO.isGranted(tree, PROPERTY_STATE, Permissions.READ_NODE));
assertFalse(cppO.isGranted(tree, PROPERTY_STATE, Permissions.READ_PROPERTY));
assertFalse(cppO.isGranted(tree, PROPERTY_STATE, Permissions.LOCK_MANAGEMENT));
assertFalse(cppO.isGranted(tree, PROPERTY_STATE, Permissions.ALL));
assertFalse(cppO.isGranted(tree, PROPERTY_STATE, Permissions.READ_NODE | Permissions.LOCK_MANAGEMENT));
}
}
@Test
public void testIsGrantedAction() throws Exception {
for (String nodePath : NODE_PATHS) {
String propPath = PathUtils.concat(nodePath, JcrConstants.JCR_PRIMARYTYPE);
assertTrue(cpp.isGranted(nodePath, Session.ACTION_READ));
assertFalse(cpp.isGranted(propPath, Session.ACTION_READ));
assertFalse(cpp.isGranted(nodePath, Session.ACTION_REMOVE));
assertFalse(cpp.isGranted(propPath, JackrabbitSession.ACTION_MODIFY_PROPERTY));
assertFalse(cpp.isGranted(nodePath, getActionString(JackrabbitSession.ACTION_MODIFY_ACCESS_CONTROL, JackrabbitSession.ACTION_READ_ACCESS_CONTROL)));
assertTrue(cppO.isGranted(nodePath, Session.ACTION_READ));
assertFalse(cppO.isGranted(propPath, Session.ACTION_READ));
assertFalse(cppO.isGranted(nodePath, Session.ACTION_REMOVE));
assertFalse(cppO.isGranted(propPath, JackrabbitSession.ACTION_MODIFY_PROPERTY));
assertFalse(cppO.isGranted(nodePath, getActionString(JackrabbitSession.ACTION_MODIFY_ACCESS_CONTROL, JackrabbitSession.ACTION_READ_ACCESS_CONTROL)));
String nonExisting = PathUtils.concat(nodePath, "nonExisting");
assertFalse(cpp.isGranted(nonExisting, Session.ACTION_READ));
assertFalse(cpp.isGranted(nonExisting, JackrabbitSession.ACTION_ADD_PROPERTY));
assertFalse(cpp.isGranted(nonExisting, Session.ACTION_ADD_NODE));
assertFalse(cppO.isGranted(nonExisting, Session.ACTION_READ));
assertFalse(cppO.isGranted(nonExisting, JackrabbitSession.ACTION_ADD_PROPERTY));
assertFalse(cppO.isGranted(nonExisting, Session.ACTION_ADD_NODE));
}
}
@Test
public void testRepositoryPermissionsIsGranted() throws Exception {
RepositoryPermission rp = cpp.getRepositoryPermission();
assertTrue(rp.isGranted(Permissions.NAMESPACE_MANAGEMENT));
assertFalse(rp.isGranted(Permissions.NODE_TYPE_DEFINITION_MANAGEMENT));
assertFalse(rp.isGranted(Permissions.ALL));
RepositoryPermission rpO = cppO.getRepositoryPermission();
assertTrue(rpO.isGranted(Permissions.NAMESPACE_MANAGEMENT));
assertFalse(rpO.isGranted(Permissions.NODE_TYPE_DEFINITION_MANAGEMENT));
assertFalse(rpO.isGranted(Permissions.ALL));
}
@Test
public void testTreePermissionIsGranted() throws Exception {
TreePermission parentPermission = TreePermission.EMPTY;
for (String path : TP_PATHS) {
TreePermission tp = cpp.getTreePermission(readOnlyRoot.getTree(path), parentPermission);
assertTrue(tp.isGranted(Permissions.READ_NODE));
assertFalse(tp.isGranted(Permissions.REMOVE_NODE));
assertFalse(tp.isGranted(Permissions.READ));
assertFalse(tp.isGranted(Permissions.ALL));
parentPermission = tp;
}
}
@Test
public void testTreePermissionIsGrantedOR() throws Exception {
TreePermission parentPermission = TreePermission.EMPTY;
for (String path : TP_PATHS) {
TreePermission tp = cppO.getTreePermission(readOnlyRoot.getTree(path), parentPermission);
assertTrue(tp.isGranted(Permissions.READ_NODE));
assertFalse(tp.isGranted(Permissions.REMOVE_NODE));
assertFalse(tp.isGranted(Permissions.READ));
assertFalse(tp.isGranted(Permissions.ALL));
parentPermission = tp;
}
}
@Test
public void testTreePermissionIsGrantedProperty() throws Exception {
TreePermission parentPermission = TreePermission.EMPTY;
for (String path : TP_PATHS) {
TreePermission tp = cpp.getTreePermission(readOnlyRoot.getTree(path), parentPermission);
assertFalse(tp.isGranted(Permissions.READ_PROPERTY, PROPERTY_STATE));
assertFalse(tp.isGranted(Permissions.REMOVE_PROPERTY, PROPERTY_STATE));
assertFalse(tp.isGranted(Permissions.READ, PROPERTY_STATE));
assertFalse(tp.isGranted(Permissions.ALL, PROPERTY_STATE));
parentPermission = tp;
}
}
@Test
public void testTreePermissionIsGrantedPropertyOR() throws Exception {
TreePermission parentPermission = TreePermission.EMPTY;
for (String path : TP_PATHS) {
TreePermission tp = cppO.getTreePermission(readOnlyRoot.getTree(path), parentPermission);
assertFalse(tp.isGranted(Permissions.READ_PROPERTY, PROPERTY_STATE));
assertFalse(tp.isGranted(Permissions.REMOVE_PROPERTY, PROPERTY_STATE));
assertFalse(tp.isGranted(Permissions.READ, PROPERTY_STATE));
assertFalse(tp.isGranted(Permissions.ALL, PROPERTY_STATE));
parentPermission = tp;
}
}
@Test
public void testTreePermissionCanRead() throws Exception {
TreePermission parentPermission = TreePermission.EMPTY;
for (String path : TP_PATHS) {
Tree t = readOnlyRoot.getTree(path);
TreePermission tp = cpp.getTreePermission(t, parentPermission);
assertTrue(tp.canRead());
parentPermission = tp;
}
}
@Test
public void testTreePermissionCanReadOR() throws Exception {
TreePermission parentPermission = TreePermission.EMPTY;
for (String path : TP_PATHS) {
Tree t = readOnlyRoot.getTree(path);
TreePermission tp = cppO.getTreePermission(t, parentPermission);
assertTrue(tp.canRead());
parentPermission = tp;
}
}
@Test
public void testTreePermissionCanReadProperty() throws Exception {
TreePermission parentPermission = TreePermission.EMPTY;
for (String path : TP_PATHS) {
Tree t = readOnlyRoot.getTree(path);
TreePermission tp = cpp.getTreePermission(t, parentPermission);
assertFalse(tp.canRead(PROPERTY_STATE));
parentPermission = tp;
}
}
@Test
public void testTreePermissionCanReadPropertyOR() throws Exception {
TreePermission parentPermission = TreePermission.EMPTY;
for (String path : TP_PATHS) {
Tree t = readOnlyRoot.getTree(path);
TreePermission tp = cppO.getTreePermission(t, parentPermission);
assertFalse(tp.canRead(PROPERTY_STATE));
parentPermission = tp;
}
}
private final class LimitCoverageProvider extends AbstractAggrProvider {
LimitCoverageProvider(Root root) {
super(root);
}
@NotNull
@Override
public PrivilegeBits supportedPrivileges(@Nullable Tree tree, @Nullable PrivilegeBits privilegeBits) {
PrivilegeBits supported = PrivilegeBits.getInstance(
PrivilegeBits.BUILT_IN.get(JCR_NAMESPACE_MANAGEMENT),
PrivilegeBits.BUILT_IN.get(REP_READ_NODES));
if (privilegeBits != null) {
return PrivilegeBits.getInstance(privilegeBits).retain(supported);
} else {
return supported;
}
}
@Override
public long supportedPermissions(@Nullable Tree tree, @Nullable PropertyState property, long permissions) {
if (tree == null) {
return permissions & Permissions.NAMESPACE_MANAGEMENT;
} else {
return permissions & Permissions.READ_NODE;
}
}
@Override
public long supportedPermissions(@NotNull TreeLocation location, long permissions) {
return permissions & Permissions.READ_NODE;
}
@Override
public long supportedPermissions(@NotNull TreePermission treePermission, @Nullable PropertyState property, long permissions) {
return permissions & Permissions.READ_NODE;
}
@Override
public boolean isGranted(@NotNull TreeLocation location, long permissions) {
return permissions == Permissions.READ_NODE;
}
@NotNull
@Override
public Set<String> getPrivileges(@Nullable Tree tree) {
return (tree == null) ? ImmutableSet.of(JCR_NAMESPACE_MANAGEMENT) : ImmutableSet.of(REP_READ_NODES);
}
@Override
public boolean hasPrivileges(@Nullable Tree tree, @NotNull String... privilegeNames) {
return true;
}
@NotNull
@Override
public RepositoryPermission getRepositoryPermission() {
return new RepositoryPermission() {
@Override
public boolean isGranted(long repositoryPermissions) {
return Permissions.NAMESPACE_MANAGEMENT == repositoryPermissions;
}
};
}
@NotNull
@Override
public TreePermission getTreePermission(@NotNull Tree tree, @NotNull TreePermission parentPermission) {
return new LimitedTreePermission();
}
@Override
public boolean isGranted(@NotNull Tree tree, @Nullable PropertyState property, long permissions) {
return permissions == Permissions.READ_NODE;
}
@Override
public boolean isGranted(@NotNull String oakPath, @NotNull String jcrActions) {
return true;
}
}
private static final class LimitedTreePermission implements TreePermission {
@NotNull
@Override
public TreePermission getChildPermission(@NotNull String childName, @NotNull NodeState childState) {
return this;
}
@Override
public boolean canRead() {
return true;
}
@Override
public boolean canRead(@NotNull PropertyState property) {
return false;
}
@Override
public boolean canReadAll() {
return false;
}
@Override
public boolean canReadProperties() {
return false;
}
@Override
public boolean isGranted(long permissions) {
return Permissions.READ_NODE == permissions;
}
@Override
public boolean isGranted(long permissions, @NotNull PropertyState property) {
return false;
}
}
}