blob: 60eb45fe21e8f9caa0417649566c373e7012c18e [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.policy.common;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import org.apache.sentry.core.common.BitFieldAction;
import org.apache.sentry.core.common.BitFieldActionFactory;
import org.apache.sentry.core.common.ImplyMethodType;
import org.apache.sentry.core.common.Model;
import org.apache.sentry.core.common.exception.SentryUserException;
import org.apache.sentry.core.common.utils.KeyValue;
import org.apache.sentry.core.common.utils.PathUtils;
import org.apache.sentry.core.common.utils.SentryConstants;
import java.util.ArrayList;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
// The class is used to compare the privilege
public class CommonPrivilege implements Privilege {
private ImmutableList<KeyValue> parts;
private boolean grantOption = false;
private static final Logger LOGGER = LoggerFactory.getLogger(CommonPrivilege.class);
public CommonPrivilege(String privilegeStr) {
privilegeStr = Strings.nullToEmpty(privilegeStr).trim();
if (privilegeStr.isEmpty()) {
throw new IllegalArgumentException("Privilege string cannot be null or empty.");
}
LOGGER.debug("Create Privilege instance for privilegeStr={}", privilegeStr);
List<KeyValue> parts = Lists.newArrayList();
for (String authorizable : SentryConstants.AUTHORIZABLE_SPLITTER.trimResults().split(
privilegeStr)) {
if (authorizable.isEmpty()) {
throw new IllegalArgumentException("Privilege '" + privilegeStr + "' has an empty section");
}
parts.add(new KeyValue(authorizable));
}
if (parts.isEmpty()) {
throw new AssertionError("Should never occur: " + privilegeStr);
}
// check if grant option is present
KeyValue lastPart = parts.get(parts.size() - 1);
if (lastPart.getKey().equalsIgnoreCase(SentryConstants.GRANT_OPTION)) {
grantOption = lastPart.getValue().equalsIgnoreCase("true");
parts.remove(parts.size() - 1);
}
this.parts = ImmutableList.copyOf(parts);
}
@Override
public boolean implies(Privilege privilege, Model model) {
// By default only supports comparisons with other IndexerWildcardPermissions
if (!(privilege instanceof CommonPrivilege)) {
return false;
}
CommonPrivilege requiredPrivilege = (CommonPrivilege) privilege;
if ((requiredPrivilege.grantOption == true) && (this.grantOption == false)) {
// the required privilege wp needs grant option, but this privilege does not have grant option
return false;
}
List<KeyValue> otherParts = requiredPrivilege.getParts();
if(parts.equals(otherParts)) {
return true;
}
int index = 0;
for (KeyValue otherPart : otherParts) {
// If this privilege has less parts than the other privilege, everything
// after the number of parts contained
// in this privilege is automatically implied, so return true
if (parts.size() - 1 < index) {
return true;
} else {
KeyValue part = parts.get(index);
String policyKey = part.getKey();
// are the keys even equal
if(!policyKey.equalsIgnoreCase(otherPart.getKey())) {
// Support for action inheritance from parent to child
if (SentryConstants.PRIVILEGE_NAME.equalsIgnoreCase(policyKey)) {
continue;
}
return false;
}
// do the imply for action
if (SentryConstants.PRIVILEGE_NAME.equalsIgnoreCase(policyKey)) {
if (!impliesAction(part.getValue(), otherPart.getValue(), model.getBitFieldActionFactory())) {
return false;
}
} else {
if (!impliesResource(model.getImplyMethodMap().get(policyKey.toLowerCase()),
part.getValue(), otherPart.getValue())) {
return false;
}
}
index++;
}
}
// If this privilege has more parts than the other parts, only imply it if
// all of the other parts are wildcards
for (; index < parts.size(); index++) {
KeyValue part = parts.get(index);
if (!isPrivilegeActionAll(part, model.getBitFieldActionFactory())) {
return false;
}
}
return true;
}
/**
* Check if the action part in a privilege is ALL. Owner privilege is
* treated as ALL for authorization
* @param actionPart it must be the action of a privilege
* @return true if the action is ALL; false otherwise
*/
private boolean isPrivilegeActionAll(KeyValue actionPart,
BitFieldActionFactory bitFieldActionFactory) {
return impliesAction(actionPart.getValue(), SentryConstants.PRIVILEGE_WILDCARD_VALUE,
bitFieldActionFactory);
}
@Override
public List<KeyValue> getAuthorizable() {
List<KeyValue> authorizable = new ArrayList<>();
for (KeyValue part : parts) {
// Authorizeable is the same as privileges but should exclude action
if (!SentryConstants.PRIVILEGE_NAME.equalsIgnoreCase(part.getKey())) {
KeyValue keyValue = new KeyValue(part.getKey().toLowerCase(),
part.getValue().toLowerCase());
authorizable.add(keyValue);
}
}
return authorizable;
}
// The method is used for compare the value of resource by the ImplyMethodType.
// for Hive, databaseName, tableName, columnName will be compared using String.equal(wildcard support)
// url will be compared using PathUtils.impliesURI
private boolean impliesResource(ImplyMethodType implyMethodType, String policyValue, String requestValue) {
// wildcard support, "*", "+", "all"("+" and "all" are for backward compatibility) are represented as wildcard
// if requestValue is wildcard, means privilege request is to match with any value of given resource
if (SentryConstants.RESOURCE_WILDCARD_VALUE.equals(policyValue)
|| SentryConstants.RESOURCE_WILDCARD_VALUE.equals(requestValue)
|| SentryConstants.RESOURCE_WILDCARD_VALUE_ALL.equalsIgnoreCase(policyValue)
|| SentryConstants.RESOURCE_WILDCARD_VALUE_ALL.equalsIgnoreCase(requestValue)
|| SentryConstants.RESOURCE_WILDCARD_VALUE_SOME.equals(requestValue)) {
return true;
}
// compare as the url
if (ImplyMethodType.URL == implyMethodType) {
return PathUtils.impliesURI(policyValue, requestValue);
} else if (ImplyMethodType.STRING_CASE_SENSITIVE == implyMethodType) {
// compare as the string case sensitive
return policyValue.equals(requestValue);
}
// default: compare as the string case insensitive
return policyValue.equalsIgnoreCase(requestValue);
}
// The method is used for compare the action for the privilege model.
// for Hive, the action will be select, insert, etc.
// for Solr, the action will be update, query, etc.
private boolean impliesAction(String policyValue, String requestValue,
BitFieldActionFactory bitFieldActionFactory) {
BitFieldAction currentAction;
BitFieldAction requestAction;
try {
currentAction = bitFieldActionFactory.getActionByName(policyValue);
requestAction = bitFieldActionFactory.getActionByName(requestValue);
} catch (SentryUserException e) {
return false;
}
// the action in privilege is not supported
if (currentAction == null || requestAction == null) {
return false;
}
return currentAction.implies(requestAction);
}
@Override
public String toString() {
return SentryConstants.AUTHORIZABLE_JOINER.join(parts);
}
@Override
public List<KeyValue> getParts() {
return parts;
}
@Override
public boolean equals(Object o) {
if (o instanceof CommonPrivilege) {
CommonPrivilege cp = (CommonPrivilege) o;
return parts.equals(cp.parts);
}
return false;
}
@Override
public int hashCode() {
return parts.hashCode();
}
}