blob: 4aa65ef3cd3500fe596315647882bf868a13cf3a [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.sentry.provider.cache;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang.StringUtils;
import org.apache.sentry.core.common.Authorizable;
import org.apache.sentry.core.common.utils.KeyValue;
import org.apache.sentry.core.common.utils.SentryConstants;
import org.apache.sentry.core.model.db.DBModelAuthorizable.AuthorizableType;
import org.apache.sentry.policy.common.Privilege;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This class is used to store value of the <key, value> used in TreePrivilegeCache
*/
public class TreePrivilegeNode {
Set<Privilege> ownPrivileges;
Set<Privilege> childWildcardPrivileges;
/** the key of childPrivileges is the resource value of the next level hierarchy, i.e., child resource value
* For example, a privilege "server=server1->db=db1->table=table2->column=col3->action=select"
* will be put into the following data structure
* TreePrivilegeCache.cachedPrivilegeMap[server1]
* -> TreePrivilegeNode.childPrivileges[db1] (partIndex = 0, for node of resource value: server1)
* -> TreePrivilegeNode.childPrivileges[table2] (partIndex = 1, for node of resource value: db1)
* -> TreePrivilegeNode.childPrivileges[col3] (partIndex = 2, for node of resource value: table2)
* -> TreePrivilegeNode.ownPrivileges (partIndex = 3, for node of resource value: col3)
*
* A privilege "server=server1->db=db1->table=table2->column=*->action=select"
* will be put into the following data structure
* TreePrivilegeCache.cachedPrivilegeMap[server1]
* -> TreePrivilegeNode.childPrivileges[db1] (partIndex = 0, for node of resource value: server1)
* -> TreePrivilegeNode.childPrivileges[table2] (partIndex = 1, for node of resource value: db1)
* -> TreePrivilegeNode.childWildcardPrivileges (partIndex = 2, for node of resource value: table2)
*
*/
Map<String, TreePrivilegeNode> childPrivileges;
private static final Logger LOGGER = LoggerFactory
.getLogger(TreePrivilegeNode.class);
public TreePrivilegeNode () {
}
public void addPrivilege(final Privilege inPrivilege, int partIndex) {
if (isOwnPrivilege(inPrivilege, partIndex)) {
if (ownPrivileges == null) {
ownPrivileges = new HashSet<>();
}
ownPrivileges.add(inPrivilege);
return;
}
// find the child resource value, which is used as key in childPrivileges
String childResourceValue = getResourceValue(partIndex + 1, inPrivilege);
if (StringUtils.isEmpty(childResourceValue)) {
LOGGER.warn("Child resource value at index [{}] of privilege {} is null", partIndex, inPrivilege.toString());
return;
}
if (isResourceValueWildcard(childResourceValue)) {
if (childWildcardPrivileges == null) {
childWildcardPrivileges = new HashSet<>();
}
childWildcardPrivileges.add(inPrivilege);
return;
}
if (childPrivileges == null) {
childPrivileges = new HashMap<>();
}
TreePrivilegeNode childNode = childPrivileges.get(childResourceValue);
if (childNode == null) {
childNode = new TreePrivilegeNode();
childPrivileges.put(childResourceValue, childNode);
}
childNode.addPrivilege(inPrivilege, partIndex + 1);
}
/**
* Return the set of privileges that could match the authorizable, including own, child wild card, and
* matched child privileges.
* @param authorizationhierarchy list of authorizable in the order of server, db, table, column.
* @param partIndex the current index of the list of authorizable
* @return
*/
public Set<Privilege> listPrivilegeObjects(int partIndex, Authorizable... authorizationhierarchy) {
if (authorizationhierarchy.length < partIndex + 1) {
return null;
}
Set<Privilege> targetSet = new HashSet<>();
if (ownPrivileges != null) {
targetSet.addAll(ownPrivileges);
}
if ((childWildcardPrivileges != null) && (authorizationhierarchy.length > partIndex + 1)) {
// only add when the child authorizable is included
targetSet.addAll(childWildcardPrivileges);
}
Set<Privilege> childPrivileges = listChildPrivilegeObjects(partIndex, authorizationhierarchy);
if (childPrivileges != null) {
targetSet.addAll(childPrivileges);
}
return targetSet;
}
// Check if there is child to process
// true: yes; false: reach to the end of the hierarchy, and there is no more child to process
private static boolean hasChild(int partIndex, int totalLevel) {
if (totalLevel <= partIndex + 1) {
return false;
}
return true;
}
// Check if there is own data to process
// true: yes; false: reach to the end of the hierarchy, and there is no more data to process
private static boolean hasOwn(int partIndex, int totalLevel) {
if (totalLevel < partIndex + 1) {
return false;
}
return true;
}
/**
* Return the set of privileges that could match the authorizable, only including matched child privileges.
* @param partIndex
* @param authorizationhierarchy
* @return
*/
private Set<Privilege> listChildPrivilegeObjects(int partIndex, Authorizable... authorizationhierarchy) {
if (!hasChild(partIndex, authorizationhierarchy.length)) {
return null;
}
String childKey = getResourceValue(partIndex + 1, authorizationhierarchy);
if (StringUtils.isEmpty(childKey)) {
return null;
}
if (isResourceValueWildcard(childKey)) {
// the authorizable for child is wildcard, so return the privileges of all children.
return listAllChildPrivilegeObjects(partIndex, authorizationhierarchy);
}
// the authorizable for child is for a specific child, return its own privileges.
if (childPrivileges == null) {
return null;
}
TreePrivilegeNode childNode = childPrivileges.get(childKey);
if (childNode == null) {
return null;
}
return childNode.listPrivilegeObjects(partIndex + 1, authorizationhierarchy);
}
private Set<Privilege> listAllChildPrivilegeObjects(int partIndex, Authorizable... authorizationhierarchy) {
if (childPrivileges == null) {
return null;
}
Set<Privilege> targetPrivileges = new HashSet<>();
for (TreePrivilegeNode childNode : childPrivileges.values()) {
Set<Privilege> childSet = childNode.listPrivilegeObjects(partIndex + 1, authorizationhierarchy);
if ((childSet == null) || (childSet.size() == 0)) {
continue;
}
targetPrivileges.addAll(childSet);
}
return targetPrivileges;
}
private boolean isOwnPrivilege(Privilege inPrivilege, int partIndex) {
List<KeyValue> parts = inPrivilege.getParts();
if (!hasChild(partIndex, parts.size())) {
return true;
}
// check child resource type
String partType = parts.get(partIndex + 1).getKey();
if (SentryConstants.PRIVILEGE_NAME.equalsIgnoreCase(partType)) {
// the next part is action, not authorizable
return true;
}
if (AuthorizableType.URI.toString().equalsIgnoreCase(partType)) {
// the next part is uri
return true;
}
return false;
}
public static boolean isResourceValueWildcard(String resourceValue) {
if (StringUtils.isEmpty(resourceValue)) {
return false;
}
if (SentryConstants.RESOURCE_WILDCARD_VALUE.equalsIgnoreCase(resourceValue)) {
return true;
}
if (SentryConstants.RESOURCE_WILDCARD_VALUE_SOME.equalsIgnoreCase(resourceValue)) {
return true;
}
if (SentryConstants.RESOURCE_WILDCARD_VALUE_ALL.equalsIgnoreCase(resourceValue)) {
return true;
}
return false;
}
public static String getResourceValue(int partIndex, Authorizable[] authorizables) {
if ((authorizables == null) || (authorizables.length == 0) ) {
return null;
}
if (!hasOwn(partIndex, authorizables.length)) {
return null;
}
Authorizable ownPart = authorizables[partIndex];
if (ownPart == null) {
return null;
}
return ownPart.getName().toLowerCase();
}
public static String getResourceValue(int partIndex, Privilege inPrivilege) {
List<KeyValue> parts = inPrivilege.getParts();
if (parts == null) {
return null;
}
if (!hasOwn(partIndex, parts.size())) {
return null;
}
KeyValue ownPart = parts.get(partIndex);
if (ownPart == null) {
return null;
}
return ownPart.getValue().toLowerCase();
}
}