Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.apache.jackrabbit.oak.security.authorization.permission.PermissionStoreEditor Maven / Gradle / Ivy
/*
* 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.permission;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.google.common.base.Strings;
import com.google.common.collect.Maps;
import org.apache.jackrabbit.oak.api.PropertyState;
import org.apache.jackrabbit.oak.api.Type;
import org.apache.jackrabbit.oak.plugins.nodetype.TypePredicate;
import org.apache.jackrabbit.oak.plugins.tree.TreeProvider;
import org.apache.jackrabbit.oak.security.authorization.accesscontrol.ValidationEntry;
import org.apache.jackrabbit.oak.spi.security.authorization.accesscontrol.AccessControlConstants;
import org.apache.jackrabbit.oak.spi.security.authorization.permission.PermissionConstants;
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.privilege.JcrAllUtil;
import org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeBits;
import org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeBitsProvider;
import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
import org.apache.jackrabbit.oak.spi.state.NodeState;
import org.apache.jackrabbit.util.Text;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static com.google.common.collect.Iterables.addAll;
import static com.google.common.collect.Sets.newLinkedHashSet;
import static org.apache.jackrabbit.JcrConstants.JCR_PRIMARYTYPE;
import static org.apache.jackrabbit.oak.plugins.tree.TreeConstants.OAK_CHILD_ORDER;
final class PermissionStoreEditor implements AccessControlConstants, PermissionConstants {
private static final Logger log = LoggerFactory.getLogger(PermissionStoreEditor.class);
private final String accessControlledPath;
private final String nodeName;
private final Map> entries = Maps.newHashMap();
private final NodeBuilder permissionRoot;
private final PrivilegeBitsProvider bitsProvider;
PermissionStoreEditor(@NotNull String aclPath, @NotNull String name,
@NotNull NodeState node, @NotNull NodeBuilder permissionRoot,
@NotNull TypePredicate isACE, @NotNull TypePredicate isGrantACE,
@NotNull PrivilegeBitsProvider bitsProvider,
@NotNull RestrictionProvider restrictionProvider,
@NotNull TreeProvider treeProvider) {
this.permissionRoot = permissionRoot;
this.bitsProvider = bitsProvider;
if (name.equals(REP_REPO_POLICY)) {
accessControlledPath = "";
} else {
accessControlledPath = aclPath.isEmpty() ? "/" : aclPath;
}
nodeName = PermissionUtil.getEntryName(accessControlledPath);
Set orderedChildNames = newLinkedHashSet(node.getNames(OAK_CHILD_ORDER));
long n = orderedChildNames.size();
if (node.getChildNodeCount(n + 1) > n) {
addAll(orderedChildNames, node.getChildNodeNames());
}
int index = 0;
for (String childName : orderedChildNames) {
NodeState ace = node.getChildNode(childName);
if (isACE.test(ace)) {
boolean isAllow = isGrantACE.test(ace);
PrivilegeBits privilegeBits = bitsProvider.getBits(ace.getNames(REP_PRIVILEGES));
Set restrictions = restrictionProvider.readRestrictions(Strings.emptyToNull(accessControlledPath), treeProvider.createReadOnlyTree(ace));
String principalName = Text.escapeIllegalJcrChars(ace.getString(REP_PRINCIPAL_NAME));
AcEntry entry = new AcEntry(principalName, index, isAllow, privilegeBits, restrictions);
List list = entries.computeIfAbsent(principalName, k -> new ArrayList<>());
list.add(entry);
index++;
}
}
}
@NotNull
String getPath() {
return accessControlledPath;
}
boolean isEmpty() {
return entries.isEmpty();
}
void removePermissionEntries(@NotNull PermissionStoreEditor otherEditor) {
entries.keySet().removeAll(otherEditor.entries.keySet());
}
void removePermissionEntries() {
for (String principalName : entries.keySet()) {
if (permissionRoot.hasChildNode(principalName)) {
NodeBuilder principalRoot = permissionRoot.getChildNode(principalName);
boolean removed = false;
// find the ACL node that for this path and principal
NodeBuilder parent = principalRoot.getChildNode(nodeName);
if (!parent.exists()) {
continue;
}
// check if the node is the correct one
if (PermissionUtil.checkACLPath(parent, accessControlledPath)) {
// remove and reconnect child nodes
NodeBuilder newParent = null;
for (String childName : parent.getChildNodeNames()) {
if (childName.charAt(0) != 'c') {
continue;
}
NodeBuilder child = parent.getChildNode(childName);
if (newParent == null) {
newParent = child;
} else {
newParent.setChildNode(childName, child.getNodeState());
}
}
if (newParent != null) {
// replace the 'parent', which got removed
principalRoot.setChildNode(nodeName, newParent.getNodeState());
removed = true;
} else {
removed = parent.remove();
}
} else {
// check if any of the child nodes match
for (String childName : parent.getChildNodeNames()) {
if (childName.charAt(0) != 'c') {
continue;
}
NodeBuilder child = parent.getChildNode(childName);
if (PermissionUtil.checkACLPath(child, accessControlledPath)) {
removed = child.remove();
}
}
}
if (removed) {
updateNumEntries(principalName, principalRoot, -1);
}
} else {
log.error("Unable to remove permission entry {}: Principal root missing.", this);
}
}
}
void updatePermissionEntries() {
for (Map.Entry> entry: entries.entrySet()) {
String principalName = entry.getKey();
NodeBuilder principalRoot = permissionRoot.child(principalName);
if (!principalRoot.hasProperty(JCR_PRIMARYTYPE)) {
principalRoot.setProperty(JCR_PRIMARYTYPE, NT_REP_PERMISSION_STORE, Type.NAME);
}
NodeBuilder parent = principalRoot.child(nodeName);
if (!parent.hasProperty(JCR_PRIMARYTYPE)) {
parent.setProperty(JCR_PRIMARYTYPE, NT_REP_PERMISSION_STORE, Type.NAME);
}
// check if current parent already has the correct path
if (parent.hasProperty(REP_ACCESS_CONTROLLED_PATH)) {
if (!PermissionUtil.checkACLPath(parent, accessControlledPath)) {
// hash collision, find a new child
NodeBuilder child = null;
int idx = 0;
for (String childName : parent.getChildNodeNames()) {
if (childName.charAt(0) != 'c') {
continue;
}
child = parent.getChildNode(childName);
if (PermissionUtil.checkACLPath(child, accessControlledPath)) {
break;
}
child = null;
idx++;
}
while (child == null) {
String name = 'c' + String.valueOf(idx++);
child = parent.getChildNode(name);
if (child.exists()) {
child = null;
} else {
child = parent.child(name);
child.setProperty(JCR_PRIMARYTYPE, NT_REP_PERMISSION_STORE, Type.NAME);
}
}
parent = child;
parent.setProperty(REP_ACCESS_CONTROLLED_PATH, accessControlledPath);
}
} else {
// new parent
parent.setProperty(REP_ACCESS_CONTROLLED_PATH, accessControlledPath);
}
updateEntries(parent, entry.getValue());
if (parent.isNew()) {
updateNumEntries(principalName, principalRoot, +1);
}
}
}
private void updateEntries(@NotNull NodeBuilder parent, @NotNull List list) {
// remove old entries
for (String childName : parent.getChildNodeNames()) {
if (childName.charAt(0) != 'c') {
parent.getChildNode(childName).remove();
}
}
for (AcEntry ace: list) {
ace.writeToPermissionStore(parent);
}
}
private static void updateNumEntries(@NotNull String principalName, @NotNull NodeBuilder principalRoot, int cnt) {
PropertyState ps = principalRoot.getProperty(REP_NUM_PERMISSIONS);
if (ps == null && !principalRoot.isNew()) {
// existing principal root that doesn't have the rep:numEntries set
return;
}
long numEntries = ((ps == null) ? 0 : ps.getValue(Type.LONG)) + cnt;
if (numEntries < 0) {
// numEntries unexpectedly turned negative
log.error("NumEntries counter for principal '{}' turned negative -> removing 'rep:numPermissions' property.", principalName);
principalRoot.removeProperty(REP_NUM_PERMISSIONS);
} else {
principalRoot.setProperty(REP_NUM_PERMISSIONS, numEntries, Type.LONG);
}
}
private class AcEntry extends ValidationEntry {
AcEntry(@NotNull String principalName, int index,
boolean isAllow, @NotNull PrivilegeBits privilegeBits,
@NotNull Set restrictions) {
super(principalName, privilegeBits, isAllow, restrictions, index);
}
private void writeToPermissionStore(@NotNull NodeBuilder parent) {
NodeBuilder n = parent.child(String.valueOf(index))
.setProperty(JCR_PRIMARYTYPE, NT_REP_PERMISSIONS, Type.NAME)
.setProperty(REP_IS_ALLOW, isAllow)
.setProperty(getPrivilegeBitsProperty());
for (Restriction restriction : restrictions) {
n.setProperty(restriction.getProperty());
}
}
@NotNull
private PropertyState getPrivilegeBitsProperty() {
return JcrAllUtil.asPropertyState(REP_PRIVILEGE_BITS, privilegeBits, bitsProvider);
}
}
}