blob: 31f9720f34ff1124a9045f237a7e200fdb867dd5 [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.ranger.plugin.policyengine;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.ranger.authorization.hadoop.config.RangerConfiguration;
import org.apache.ranger.plugin.contextenricher.RangerContextEnricher;
import org.apache.ranger.plugin.model.RangerPolicy;
import org.apache.ranger.plugin.model.RangerServiceDef;
import org.apache.ranger.plugin.policyevaluator.RangerCachedPolicyEvaluator;
import org.apache.ranger.plugin.policyevaluator.RangerOptimizedPolicyEvaluator;
import org.apache.ranger.plugin.policyevaluator.RangerPolicyEvaluator;
import org.apache.ranger.plugin.util.RangerPerfTracer;
import org.apache.ranger.plugin.util.RangerResourceTrie;
import org.apache.ranger.plugin.util.ServicePolicies;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class RangerPolicyRepository {
private static final Log LOG = LogFactory.getLog(RangerPolicyRepository.class);
private static final Log PERF_CONTEXTENRICHER_INIT_LOG = RangerPerfTracer.getPerfLogger("contextenricher.init");
private final String serviceName;
private final RangerServiceDef serviceDef;
private final List<RangerPolicy> policies;
private final long policyVersion;
private List<RangerContextEnricher> contextEnrichers;
private List<RangerPolicyEvaluator> policyEvaluators;
private final Map<String, Boolean> accessAuditCache;
private final boolean disableTrieLookupPrefilter;
private final Map<String, RangerResourceTrie> policyResourceTrie;
private static int RANGER_POLICYENGINE_AUDITRESULT_CACHE_SIZE = 64*1024;
RangerPolicyRepository(ServicePolicies servicePolicies, RangerPolicyEngineOptions options) {
super();
serviceName = servicePolicies.getServiceName();
serviceDef = servicePolicies.getServiceDef();
policies = Collections.unmodifiableList(servicePolicies.getPolicies());
policyVersion = servicePolicies.getPolicyVersion() != null ? servicePolicies.getPolicyVersion().longValue() : -1;
List<RangerContextEnricher> contextEnrichers = new ArrayList<RangerContextEnricher>();
if (!options.disableContextEnrichers && !CollectionUtils.isEmpty(serviceDef.getContextEnrichers())) {
for (RangerServiceDef.RangerContextEnricherDef enricherDef : serviceDef.getContextEnrichers()) {
if (enricherDef == null) {
continue;
}
RangerContextEnricher contextEnricher = buildContextEnricher(enricherDef);
if(contextEnricher != null) {
contextEnrichers.add(contextEnricher);
}
}
}
this.contextEnrichers = Collections.unmodifiableList(contextEnrichers);
List<RangerPolicyEvaluator> policyEvaluators = new ArrayList<RangerPolicyEvaluator>();
for (RangerPolicy policy : servicePolicies.getPolicies()) {
if (skipBuildingPolicyEvaluator(policy, options)) {
continue;
}
RangerPolicyEvaluator evaluator = buildPolicyEvaluator(policy, serviceDef, options);
if (evaluator != null) {
policyEvaluators.add(evaluator);
}
}
Collections.sort(policyEvaluators);
this.policyEvaluators = Collections.unmodifiableList(policyEvaluators);
if(LOG.isDebugEnabled()) {
LOG.debug("policy evaluation order: " + this.policyEvaluators.size() + " policies");
int order = 0;
for(RangerPolicyEvaluator policyEvaluator : this.policyEvaluators) {
RangerPolicy policy = policyEvaluator.getPolicy();
LOG.debug("policy evaluation order: #" + (++order) + " - policy id=" + policy.getId() + "; name=" + policy.getName() + "; evalOrder=" + policyEvaluator.getEvalOrder());
}
}
String propertyName = "ranger.plugin." + serviceName + ".policyengine.auditcachesize";
if(options.cacheAuditResults) {
int auditResultCacheSize = RangerConfiguration.getInstance().getInt(propertyName, RANGER_POLICYENGINE_AUDITRESULT_CACHE_SIZE);
accessAuditCache = Collections.synchronizedMap(new CacheMap<String, Boolean>(auditResultCacheSize));
} else {
accessAuditCache = null;
}
this.disableTrieLookupPrefilter = options.disableTrieLookupPrefilter;
if(this.disableTrieLookupPrefilter) {
policyResourceTrie = null;
} else {
policyResourceTrie = new HashMap<String, RangerResourceTrie>();
}
initResourceTries();
}
public String getServiceName() {
return serviceName;
}
public RangerServiceDef getServiceDef() {
return serviceDef;
}
public List<RangerPolicy> getPolicies() {
return policies;
}
public long getPolicyVersion() {
return policyVersion;
}
public List<RangerContextEnricher> getContextEnrichers() {
return contextEnrichers;
}
public List<RangerPolicyEvaluator> getPolicyEvaluators() {
return policyEvaluators;
}
public List<RangerPolicyEvaluator> getPolicyEvaluators(RangerAccessResource resource) {
String resourceStr = resource == null ? null : resource.getAsString();
return disableTrieLookupPrefilter || StringUtils.isEmpty(resourceStr) ? getPolicyEvaluators() : getPolicyEvaluators(policyResourceTrie, resource);
}
public static boolean isDelegateAdminPolicy(RangerPolicy policy) {
boolean ret = false;
ret = hasDelegateAdminItems(policy.getPolicyItems());
return ret;
}
private static boolean hasDelegateAdminItems(List<RangerPolicy.RangerPolicyItem> items) {
boolean ret = false;
if (CollectionUtils.isNotEmpty(items)) {
for (RangerPolicy.RangerPolicyItem item : items) {
if(item.getDelegateAdmin()) {
ret = true;
break;
}
}
}
return ret;
}
private static boolean skipBuildingPolicyEvaluator(RangerPolicy policy, RangerPolicyEngineOptions options) {
boolean ret = false;
if (!policy.getIsEnabled()) {
ret = true;
} else if (options.evaluateDelegateAdminOnly && !isDelegateAdminPolicy(policy)) {
ret = true;
}
return ret;
}
private RangerContextEnricher buildContextEnricher(RangerServiceDef.RangerContextEnricherDef enricherDef) {
if(LOG.isDebugEnabled()) {
LOG.debug("==> RangerPolicyRepository.buildContextEnricher(" + enricherDef + ")");
}
RangerContextEnricher ret = null;
RangerPerfTracer perf = null;
if(RangerPerfTracer.isPerfTraceEnabled(PERF_CONTEXTENRICHER_INIT_LOG)) {
perf = RangerPerfTracer.getPerfTracer(PERF_CONTEXTENRICHER_INIT_LOG, "RangerContextEnricher.init(name=" + enricherDef.getName() + ")");
}
String name = enricherDef != null ? enricherDef.getName() : null;
String clsName = enricherDef != null ? enricherDef.getEnricher() : null;
if(! StringUtils.isEmpty(clsName)) {
try {
@SuppressWarnings("unchecked")
Class<RangerContextEnricher> enricherClass = (Class<RangerContextEnricher>)Class.forName(clsName);
ret = enricherClass.newInstance();
} catch(Exception excp) {
LOG.error("failed to instantiate context enricher '" + clsName + "' for '" + name + "'", excp);
}
}
if(ret != null) {
ret.setContextEnricherDef(enricherDef);
ret.init();
}
RangerPerfTracer.log(perf);
if(LOG.isDebugEnabled()) {
LOG.debug("<== RangerPolicyRepository.buildContextEnricher(" + enricherDef + "): " + ret);
}
return ret;
}
private RangerPolicyEvaluator buildPolicyEvaluator(RangerPolicy policy, RangerServiceDef serviceDef, RangerPolicyEngineOptions options) {
if(LOG.isDebugEnabled()) {
LOG.debug("==> RangerPolicyRepository.buildPolicyEvaluator(" + policy + "," + serviceDef + ", " + options + ")");
}
scrubPolicy(policy);
RangerPolicyEvaluator ret = null;
if(StringUtils.equalsIgnoreCase(options.evaluatorType, RangerPolicyEvaluator.EVALUATOR_TYPE_CACHED)) {
ret = new RangerCachedPolicyEvaluator();
} else {
ret = new RangerOptimizedPolicyEvaluator();
}
ret.init(policy, serviceDef, options);
if(LOG.isDebugEnabled()) {
LOG.debug("<== RangerPolicyRepository.buildPolicyEvaluator(" + policy + "," + serviceDef + "): " + ret);
}
return ret;
}
private void initResourceTries() {
if (!this.disableTrieLookupPrefilter) {
policyResourceTrie.clear();
if (serviceDef != null && serviceDef.getResources() != null) {
for (RangerServiceDef.RangerResourceDef resourceDef : serviceDef.getResources()) {
policyResourceTrie.put(resourceDef.getName(), new RangerResourceTrie(resourceDef, policyEvaluators));
}
}
}
}
private List<RangerPolicyEvaluator> getPolicyEvaluators(Map<String, RangerResourceTrie> resourceTrie, RangerAccessResource resource) {
List<RangerPolicyEvaluator> ret = null;
Set<String> resourceKeys = resource == null ? null : resource.getKeys();
if(CollectionUtils.isNotEmpty(resourceKeys)) {
boolean isRetModifiable = false;
for(String resourceName : resourceKeys) {
RangerResourceTrie trie = resourceTrie.get(resourceName);
if(trie == null) { // if no trie exists for this resource level, ignore and continue to next level
continue;
}
List<RangerPolicyEvaluator> resourceEvaluators = trie.getEvaluatorsForResource(resource.getValue(resourceName));
if(CollectionUtils.isEmpty(resourceEvaluators)) { // no policies for this resource, bail out
ret = null;
} else if(ret == null) { // initialize ret with policies found for this resource
ret = resourceEvaluators;
} else { // remove policies from ret that are not in resourceEvaluators
if(isRetModifiable) {
ret.retainAll(resourceEvaluators);
} else {
final List<RangerPolicyEvaluator> shorterList;
final List<RangerPolicyEvaluator> longerList;
if (ret.size() < resourceEvaluators.size()) {
shorterList = ret;
longerList = resourceEvaluators;
} else {
shorterList = resourceEvaluators;
longerList = ret;
}
ret = new ArrayList<>(shorterList);
ret.retainAll(longerList);
isRetModifiable = true;
}
}
if(CollectionUtils.isEmpty(ret)) { // if no policy exists, bail out and return empty list
ret = null;
break;
}
}
}
if(ret == null) {
ret = Collections.emptyList();
}
if(LOG.isDebugEnabled()) {
LOG.debug("<== RangerPolicyRepository.getPolicyEvaluators(" + resource.getAsString() + "): evaluatorCount=" + ret.size());
}
return ret;
}
boolean setAuditEnabledFromCache(RangerAccessRequest request, RangerAccessResult result) {
if (LOG.isDebugEnabled()) {
LOG.debug("==> RangerPolicyRepository.setAuditEnabledFromCache()");
}
Boolean value = null;
if (accessAuditCache != null) {
value = accessAuditCache.get(request.getResource().getAsString());
}
if ((value != null)) {
result.setIsAudited(value);
}
if (LOG.isDebugEnabled()) {
LOG.debug("<== RangerPolicyRepository.setAuditEnabledFromCache()");
}
return value != null;
}
void storeAuditEnabledInCache(RangerAccessRequest request, RangerAccessResult ret) {
if (LOG.isDebugEnabled()) {
LOG.debug("==> RangerPolicyRepository.storeAuditEnabledInCache()");
}
if ((ret.getIsAuditedDetermined() == true)) {
String strResource = request.getResource().getAsString();
Boolean value = ret.getIsAudited() ? Boolean.TRUE : Boolean.FALSE;
if (accessAuditCache != null) {
accessAuditCache.put(strResource, value);
}
}
if (LOG.isDebugEnabled()) {
LOG.debug("<== RangerPolicyRepository.storeAuditEnabledInCache()");
}
}
/**
* Remove nulls from policy resource values
* @param policy
*/
boolean scrubPolicy(RangerPolicy policy) {
if (LOG.isDebugEnabled()) {
LOG.debug("==> RangerPolicyRepository.scrubPolicy(" + policy + ")");
}
boolean altered = false;
Long policyId = policy.getId();
Map<String, RangerPolicy.RangerPolicyResource> resourceMap = policy.getResources();
for (Map.Entry<String, RangerPolicy.RangerPolicyResource> entry : resourceMap.entrySet()) {
String resourceName = entry.getKey();
RangerPolicy.RangerPolicyResource resource = entry.getValue();
Iterator<String> iterator = resource.getValues().iterator();
while (iterator.hasNext()) {
String value = iterator.next();
if (value == null) {
LOG.warn("RangerPolicyRepository.scrubPolicyResource: found null resource value for " + resourceName + " in policy " + policyId + "! Removing...");
iterator.remove();
altered = true;
}
}
}
if (LOG.isDebugEnabled()) {
LOG.debug("<== RangerPolicyRepository.scrubPolicy(" + policy + "): " + altered);
}
return altered;
}
@Override
public String toString( ) {
StringBuilder sb = new StringBuilder();
toString(sb);
return sb.toString();
}
public StringBuilder toString(StringBuilder sb) {
sb.append("RangerPolicyRepository={");
sb.append("serviceName={").append(serviceName).append("} ");
sb.append("serviceDef={").append(serviceDef).append("} ");
sb.append("policyEvaluators={");
if (policyEvaluators != null) {
for (RangerPolicyEvaluator policyEvaluator : policyEvaluators) {
if (policyEvaluator != null) {
sb.append(policyEvaluator).append(" ");
}
}
}
if (contextEnrichers != null) {
for (RangerContextEnricher contextEnricher : contextEnrichers) {
if (contextEnricher != null) {
sb.append(contextEnricher).append(" ");
}
}
}
sb.append("} ");
return sb;
}
}