This is an automated email from the ASF dual-hosted git repository. madhan pushed a commit to branch RANGER-3923 in repository https://gitbox.apache.org/repos/asf/ranger.git
The following commit(s) were added to refs/heads/RANGER-3923 by this push: new 3842fd756 RANGER-4269: gds enricher implementation to grant access using dataset/project policies 3842fd756 is described below commit 3842fd75656c3dbf06328a501093ddae0ad3623b Author: Madhan Neethiraj <mad...@apache.org> AuthorDate: Wed Nov 8 14:36:20 2023 -0800 RANGER-4269: gds enricher implementation to grant access using dataset/project policies --- .../plugin/contextenricher/RangerGdsEnricher.java | 126 +------ .../model/RangerPolicyResourceSignature.java | 26 ++ .../model/validation/RangerServiceDefHelper.java | 56 +++ .../plugin/policyengine/RangerAccessResult.java | 36 +- .../policyengine/RangerPolicyEngineImpl.java | 39 +++ .../policyengine/RangerPolicyEngineOptions.java | 6 + .../plugin/policyengine/gds/GdsAccessResult.java | 174 ++++++++++ .../policyengine/gds/GdsDataShareEvaluator.java | 164 +++++++++ .../policyengine/gds/GdsDatasetEvaluator.java | 175 ++++++++++ .../plugin/policyengine/gds/GdsDipEvaluator.java | 69 ++++ .../plugin/policyengine/gds/GdsDshidEvaluator.java | 69 ++++ .../plugin/policyengine/gds/GdsPolicyEngine.java | 298 ++++++++++++++++ .../policyengine/gds/GdsProjectEvaluator.java | 160 +++++++++ .../gds/GdsSharedResourceEvaluator.java | 179 ++++++++++ .../RangerCustomConditionEvaluator.java | 28 ++ .../plugin/util/RangerAccessRequestUtil.java | 29 ++ .../apache/ranger/plugin/util/ServiceGdsInfo.java | 78 +---- .../plugin/policyengine/TestRangerAuthContext.java | 5 +- .../policyengine/gds/TestGdsPolicyEngine.java | 131 +++++++ .../gds/test_gds_policy_engine_hive.json | 381 +++++++++++++++++++++ .../java/org/apache/ranger/biz/GdsDBStore.java | 9 + .../service/RangerGdsSharedResourceService.java | 2 +- 22 files changed, 2059 insertions(+), 181 deletions(-) diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/contextenricher/RangerGdsEnricher.java b/agents-common/src/main/java/org/apache/ranger/plugin/contextenricher/RangerGdsEnricher.java index da51b9d85..8a7936766 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/contextenricher/RangerGdsEnricher.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/contextenricher/RangerGdsEnricher.java @@ -22,17 +22,14 @@ package org.apache.ranger.plugin.contextenricher; import org.apache.commons.lang3.StringUtils; import org.apache.ranger.plugin.model.validation.RangerServiceDefHelper; import org.apache.ranger.plugin.policyengine.RangerAccessRequest; +import org.apache.ranger.plugin.policyengine.gds.GdsAccessResult; +import org.apache.ranger.plugin.policyengine.gds.GdsPolicyEngine; import org.apache.ranger.plugin.service.RangerAuthContext; import org.apache.ranger.plugin.util.DownloadTrigger; import org.apache.ranger.plugin.util.DownloaderTask; import org.apache.ranger.plugin.util.JsonUtilsV2; +import org.apache.ranger.plugin.util.RangerAccessRequestUtil; import org.apache.ranger.plugin.util.ServiceGdsInfo; -import org.apache.ranger.plugin.util.ServiceGdsInfo.DatasetInfo; -import org.apache.ranger.plugin.util.ServiceGdsInfo.DatasetInProjectInfo; -import org.apache.ranger.plugin.util.ServiceGdsInfo.DataShareInfo; -import org.apache.ranger.plugin.util.ServiceGdsInfo.DataShareInDatasetInfo; -import org.apache.ranger.plugin.util.ServiceGdsInfo.ProjectInfo; -import org.apache.ranger.plugin.util.ServiceGdsInfo.SharedResourceInfo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -50,7 +47,7 @@ public class RangerGdsEnricher extends RangerAbstractContextEnricher { private RangerGdsInfoRetriever gdsInfoRetriever; private RangerGdsInfoRefresher gdsInfoRefresher; private RangerServiceDefHelper serviceDefHelper; - private EnhancedGdsInfo gdsInfo = null; + private GdsPolicyEngine gdsPolicyEngine = null; @Override public void init() { @@ -145,24 +142,26 @@ public class RangerGdsEnricher extends RangerAbstractContextEnricher { public void enrich(RangerAccessRequest request, Object dataStore) { LOG.debug("==> RangerGdsEnricher.enrich({}, {})", request, dataStore); - EnhancedGdsInfo gdsInfo = (dataStore instanceof EnhancedGdsInfo) ? (EnhancedGdsInfo) dataStore : this.gdsInfo; + GdsPolicyEngine policyEngine = (dataStore instanceof GdsPolicyEngine) ? (GdsPolicyEngine) dataStore : this.gdsPolicyEngine; - LOG.debug("RangerGdsEnricher.enrich(): using gdsInfo={}", gdsInfo); + LOG.debug("RangerGdsEnricher.enrich(): using policyEngine={}", policyEngine); - // TODO: + GdsAccessResult result = policyEngine != null ? policyEngine.evaluate(request) : null; + + RangerAccessRequestUtil.setGdsResultInContext(request, result); LOG.debug("<== RangerGdsEnricher.enrich({}, {})", request, dataStore); } public void setGdsInfo(ServiceGdsInfo gdsInfo) { - this.gdsInfo = new EnhancedGdsInfo(gdsInfo); + this.gdsPolicyEngine = new GdsPolicyEngine(gdsInfo, serviceDefHelper, getPluginContext()); setGdsInfoInPlugin(); } public RangerServiceDefHelper getServiceDefHelper() { return serviceDefHelper; } - public EnhancedGdsInfo getGdsInfo() { return gdsInfo; } + public GdsPolicyEngine getGdsPolicyEngine() { return gdsPolicyEngine; } private void setGdsInfoInPlugin() { LOG.debug("==> setGdsInfoInPlugin()"); @@ -170,7 +169,7 @@ public class RangerGdsEnricher extends RangerAbstractContextEnricher { RangerAuthContext authContext = getAuthContext(); if (authContext != null) { - authContext.addOrReplaceRequestContextEnricher(this, gdsInfo); + authContext.addOrReplaceRequestContextEnricher(this, gdsPolicyEngine); notifyAuthContextChanged(); } @@ -345,105 +344,4 @@ public class RangerGdsEnricher extends RangerAbstractContextEnricher { LOG.debug("<== RangerGdsInfoRefresher(serviceName={}).saveToCache()", getServiceName()); } } - - public static class EnhancedGdsInfo { - private final RangerServiceDefHelper gdsServiceDefHelper; - private final Map<String, List<DataShareInfo>> zoneShares; - private final Map<Long, List<SharedResourceInfo>> shareResources; - private final Map<Long, DatasetInfo> datasets; - private final Map<Long, ProjectInfo> projects; - private final Map<Long, List<DataShareInDatasetInfo>> shareDatasets; - private final Map<Long, List<DatasetInProjectInfo>> datasetProjects; - - EnhancedGdsInfo(ServiceGdsInfo gdsInfo) { - if (gdsInfo != null) { - this.gdsServiceDefHelper = gdsInfo.getGdsServiceDef() != null ? new RangerServiceDefHelper(gdsInfo.getGdsServiceDef(), false) : null; - this.zoneShares = new HashMap<>(); - this.shareResources = new HashMap<>(); - this.datasets = new HashMap<>(); - this.projects = new HashMap<>(); - this.shareDatasets = new HashMap<>(); - this.datasetProjects = new HashMap<>(); - - if (gdsInfo.getDataShares() != null) { - for (DataShareInfo dataShareInfo : gdsInfo.getDataShares()) { - if (dataShareInfo != null) { - String zoneName = dataShareInfo.getZoneName() == null ? StringUtils.EMPTY : dataShareInfo.getZoneName(); - List<DataShareInfo> shares = zoneShares.computeIfAbsent(zoneName, k -> new ArrayList<>()); - - shares.add(dataShareInfo); - } - } - } - - if (gdsInfo.getResources() != null) { - for (SharedResourceInfo resource : gdsInfo.getResources()) { - if (resource != null) { - List<SharedResourceInfo> resources = shareResources.computeIfAbsent(resource.getDataShareId(), k -> new ArrayList<>()); - - resources.add(resource); - } - } - } - - if (gdsInfo.getDatasets() != null) { - for (DatasetInfo datasetInfo : gdsInfo.getDatasets()) { - if (datasetInfo != null) { - datasets.put(datasetInfo.getId(), datasetInfo); - } - } - } - - if (gdsInfo.getProjects() != null) { - for (ProjectInfo projectInfo : gdsInfo.getProjects()) { - if (projectInfo != null) { - projects.put(projectInfo.getId(), projectInfo); - } - } - } - - if (gdsInfo.getDshids() != null) { - for (DataShareInDatasetInfo dshid : gdsInfo.getDshids()) { - if (dshid != null) { - List<DataShareInDatasetInfo> dshids = shareDatasets.computeIfAbsent(dshid.getDatasetId(), k -> new ArrayList<>()); - - dshids.add(dshid); - } - } - } - - if (gdsInfo.getDips() != null) { - for (DatasetInProjectInfo dip : gdsInfo.getDips()) { - if (dip != null) { - List<DatasetInProjectInfo> dips = datasetProjects.computeIfAbsent(dip.getDatasetId(), k -> new ArrayList<>()); - - dips.add(dip); - } - } - } - } else { - this.gdsServiceDefHelper = null; - this.zoneShares = Collections.emptyMap(); - this.shareResources = Collections.emptyMap(); - this.datasets = Collections.emptyMap(); - this.projects = Collections.emptyMap(); - this.shareDatasets = Collections.emptyMap(); - this.datasetProjects = Collections.emptyMap(); - } - } - - public RangerServiceDefHelper getGdsServiceDefHelper() { return gdsServiceDefHelper; } - - public List<DataShareInfo> getDataSharesForZone(String zoneName) { return zoneShares.get(zoneName); } - - public List<SharedResourceInfo> getResourcesForDataShare(Long dataShareId) { return shareResources.get(dataShareId); } - - public DatasetInfo getDatasetInfo(Long datasetId) { return datasets.get(datasetId); } - - public ProjectInfo getProjectInfo(Long projectId) { return projects.get(projectId); } - - public List<DataShareInDatasetInfo> getDatasetsForDataShare(Long dataShareId) { return shareDatasets.get(dataShareId); } - - public List<DatasetInProjectInfo> getProjectsForDataset(Long datasetId) { return datasetProjects.get(datasetId); } - } } diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerPolicyResourceSignature.java b/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerPolicyResourceSignature.java index bcdc86779..75dc6870b 100755 --- a/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerPolicyResourceSignature.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerPolicyResourceSignature.java @@ -21,6 +21,7 @@ package org.apache.ranger.plugin.model; import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; @@ -30,6 +31,7 @@ import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.MapUtils; import org.apache.ranger.authorization.hadoop.config.RangerAdminConfig; +import org.apache.ranger.plugin.model.RangerGds.RangerSharedResource; import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyResource; import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyItemCondition; import org.apache.commons.lang.StringUtils; @@ -57,6 +59,10 @@ public class RangerPolicyResourceSignature { } } + public RangerPolicyResourceSignature(RangerSharedResource resource) { + this(toSignatureString(resource)); + } + public RangerPolicyResourceSignature(Map<String, RangerPolicyResource> resources) { this(toSignatureString(resources)); } @@ -214,6 +220,26 @@ public class RangerPolicyResourceSignature { return ret; } + public static String toSignatureString(RangerSharedResource resource) { + final Map<String, RangerPolicyResource> policyResource; + + if (StringUtils.isNotBlank(resource.getSubResourceType()) && resource.getSubResource() != null && CollectionUtils.isNotEmpty(resource.getSubResource().getValues())) { + policyResource = new HashMap<>(resource.getResource()); + + policyResource.put(resource.getSubResourceType(), resource.getSubResource()); + } else { + policyResource = resource.getResource(); + } + + String signature = toSignatureString(policyResource); + + if (StringUtils.isNotEmpty(resource.getConditionExpr())) { + signature += resource.getConditionExpr(); + } + + return String.format("{version=%d,ret=%s}", _SignatureVersion, signature); + } + private static Map<String, RangerPolicyResource> toPolicyResources(Map<String, List<String>> resources) { Map<String, RangerPolicyResource> ret = new TreeMap<>(); diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerServiceDefHelper.java b/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerServiceDefHelper.java index d99d342bd..51a3aae6f 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerServiceDefHelper.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerServiceDefHelper.java @@ -172,6 +172,14 @@ public class RangerServiceDefHelper { _delegate.patchServiceDefWithDefaultValues(); } + public RangerResourceDef getResourceDef(String resourceName) { + return _delegate.getResourceDef(resourceName, RangerPolicy.POLICY_TYPE_ACCESS); + } + + public RangerResourceDef getResourceDef(String resourceName, Integer policyType) { + return _delegate.getResourceDef(resourceName, policyType); + } + /** * for a resource definition as follows: * @@ -346,6 +354,32 @@ public class RangerServiceDefHelper { return _delegate.getImpliedAccessGrants(); } + public Set<String> expandImpliedAccessGrants(Set<String> accessTypes) { + final Set<String> ret; + + if (CollectionUtils.isNotEmpty(accessTypes)) { + Map<String, Collection<String>> impliedGrants = getImpliedAccessGrants(); + + if (CollectionUtils.containsAny(impliedGrants.keySet(), accessTypes)) { + ret = new HashSet<>(accessTypes); + + for (String accessType : accessTypes) { + Collection<String> impliedAccessTypes = impliedGrants.get(accessType); + + if (CollectionUtils.isNotEmpty(impliedAccessTypes)) { + ret.addAll(impliedAccessTypes); + } + } + } else { + ret = accessTypes; + } + } else { + ret = Collections.emptySet(); + } + + return ret; + } + /** * Not designed for public access. Package level only for testability. */ @@ -428,6 +462,28 @@ public class RangerServiceDefHelper { } } + public RangerResourceDef getResourceDef(String resourceName, Integer policyType) { + RangerResourceDef ret = null; + + if (policyType == null) { + policyType = RangerPolicy.POLICY_TYPE_ACCESS; + } + + List<RangerResourceDef> resourceDefs = this.getResourceDefs(_serviceDef, policyType); + + if (resourceDefs != null) { + for (RangerResourceDef resourceDef : resourceDefs) { + if (StringUtils.equals(resourceName, resourceDef.getName())) { + ret = resourceDef; + + break; + } + } + } + + return ret; + } + public Set<List<RangerResourceDef>> getResourceHierarchies(Integer policyType) { if(policyType == null) { policyType = RangerPolicy.POLICY_TYPE_ACCESS; diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerAccessResult.java b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerAccessResult.java index 407e3e747..69e8ed9fc 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerAccessResult.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerAccessResult.java @@ -25,14 +25,16 @@ import org.apache.ranger.plugin.model.RangerServiceDef; import org.apache.ranger.plugin.util.ServiceDefUtil; import java.util.HashMap; +import java.util.List; import java.util.Map; public class RangerAccessResult { - public final static String KEY_MASK_TYPE = "maskType"; - public final static String KEY_MASK_CONDITION = "maskCondition"; - public final static String KEY_MASKED_VALUE = "maskedValue"; - - private static String KEY_FILTER_EXPR = "filterExpr"; + public final static String KEY_MASK_TYPE = "maskType"; + public final static String KEY_MASK_CONDITION = "maskCondition"; + public final static String KEY_MASKED_VALUE = "maskedValue"; + private final static String KEY_FILTER_EXPR = "filterExpr"; + private final static String KEY_DATASETS = "datasets"; + private final static String KEY_PROJECTS = "projects"; private final String serviceName; private final RangerServiceDef serviceDef; @@ -326,6 +328,30 @@ public class RangerAccessResult { return StringUtils.isNotEmpty(getFilterExpr()); } + public List<String> getDatasets() { + return additionalInfo == null ? null : (List<String>) additionalInfo.get(KEY_DATASETS); + } + + public void setDatasets(List<String> datasets) { + if (datasets == null) { + removeAdditionalInfo(KEY_DATASETS); + } else { + addAdditionalInfo(KEY_DATASETS, datasets); + } + } + + public List<String> getProjects() { + return additionalInfo == null ? null : (List<String>) additionalInfo.get(KEY_PROJECTS); + } + + public void setProjects(List<String> projects) { + if (projects == null) { + removeAdditionalInfo(KEY_PROJECTS); + } else { + addAdditionalInfo(KEY_PROJECTS, projects); + } + } + @Override public String toString( ) { StringBuilder sb = new StringBuilder(); diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerPolicyEngineImpl.java b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerPolicyEngineImpl.java index ed6ded49e..868122869 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerPolicyEngineImpl.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerPolicyEngineImpl.java @@ -29,6 +29,7 @@ import org.apache.ranger.authorization.utils.StringUtil; import org.apache.ranger.plugin.contextenricher.RangerTagForEval; import org.apache.ranger.plugin.model.RangerPolicy; import org.apache.ranger.plugin.model.RangerServiceDef; +import org.apache.ranger.plugin.policyengine.gds.GdsAccessResult; import org.apache.ranger.plugin.policyevaluator.RangerPolicyEvaluator; import org.apache.ranger.plugin.policyresourcematcher.RangerPolicyResourceMatcher.MatchType; import org.apache.ranger.plugin.service.RangerDefaultRequestProcessor; @@ -648,6 +649,8 @@ public class RangerPolicyEngineImpl implements RangerPolicyEngine { } } + updateFromGdsResult(ret); + if (LOG.isDebugEnabled()) { LOG.debug("<== RangerPolicyEngineImpl.zoneAwareAccessEvaluationWithNoAudit(" + request + ", policyType =" + policyType + "): " + ret); } @@ -1128,6 +1131,42 @@ public class RangerPolicyEngineImpl implements RangerPolicyEngine { return policyEngine.getPluginContext().getConfig().getIsFallbackSupported(); } + private void updateFromGdsResult(RangerAccessResult result) { + if (LOG.isDebugEnabled()) { + LOG.debug("==> updateFromGdsResult(result={})", result); + } + + RangerAccessRequest request = result.getAccessRequest(); + GdsAccessResult gdsResult = request != null ? RangerAccessRequestUtil.getGdsResultFromContext(request.getContext()) : null; + + if (gdsResult != null) { + if (result.getPolicyType() == RangerPolicy.POLICY_TYPE_ACCESS) { + if (!result.getIsAccessDetermined() && gdsResult.getIsAllowed()) { + result.setIsAllowed(true); + result.setIsAccessDetermined(true); + result.setPolicyId(gdsResult.getPolicyId()); + result.setPolicyVersion(gdsResult.getPolicyVersion()); + result.setPolicyPriority(RangerPolicy.POLICY_PRIORITY_NORMAL); + } + } + + if (!result.getIsAuditedDetermined() && gdsResult.getIsAudited()) { + result.setIsAudited(true); + } + + result.setDatasets(gdsResult.getDatasetNames()); + result.setProjects(gdsResult.getProjectNames()); + } else { + if (LOG.isDebugEnabled()) { + LOG.debug("updateFromGdsResult(): no GdsAccessResult found in request context({})", request); + } + } + + if (LOG.isDebugEnabled()) { + LOG.debug("<== updateFromGdsResult(result={})", result); + } + } + private static class ServiceConfig { private final Set<String> auditExcludedUsers; private final Set<String> auditExcludedGroups; diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerPolicyEngineOptions.java b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerPolicyEngineOptions.java index bf9385720..a222eefef 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerPolicyEngineOptions.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerPolicyEngineOptions.java @@ -69,6 +69,12 @@ public class RangerPolicyEngineOptions { this.optimizeTagTrieForSpace = other.optimizeTagTrieForSpace; } + public RangerPolicyEngineOptions(final RangerPolicyEngineOptions other, RangerServiceDefHelper serviceDefHelper) { + this(other); + + this.serviceDefHelper = serviceDefHelper; + } + public void configureForPlugin(Configuration conf, String propertyPrefix) { disableContextEnrichers = conf.getBoolean(propertyPrefix + ".policyengine.option.disable.context.enrichers", false); disableCustomConditions = conf.getBoolean(propertyPrefix + ".policyengine.option.disable.custom.conditions", false); diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/gds/GdsAccessResult.java b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/gds/GdsAccessResult.java new file mode 100644 index 000000000..2d0ec0379 --- /dev/null +++ b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/gds/GdsAccessResult.java @@ -0,0 +1,174 @@ +/* + * 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.gds; + + +import java.util.*; + +public class GdsAccessResult { + private Set<Long> datasets; + private Set<Long> projects; + private List<String> datasetNames; + private List<String> projectNames; + private boolean isAllowed; + private boolean isAudited; + private long policyId = -1; + private Long policyVersion; + + + public GdsAccessResult() { + } + + public void addDataset(Long datasetId) { + if (datasets == null) { + datasets = new HashSet<>(); + } + + datasets.add(datasetId); + } + + public boolean hasDataset(Long datasetId) { + return datasets != null && datasets.contains(datasetId); + } + + public Set<Long> getDatasets() { + return datasets; + } + + public void addDatasetName(String name) { + if (datasetNames == null) { + datasetNames = datasets == null ? new ArrayList<>() : new ArrayList<>(datasets.size()); + } + + datasetNames.add(name); + } + + public List<String> getDatasetNames() { + return datasetNames; + } + + public void addProject(Long projectId) { + if (projects == null) { + projects = new HashSet<>(); + } + + projects.add(projectId); + } + + public boolean hasProject(Long projectId) { + return projects != null && projects.contains(projectId); + } + + public Set<Long> getProjects() { + return projects; + } + + public void addProjectName(String name) { + if (projectNames == null) { + projectNames = projects == null ? new ArrayList<>() : new ArrayList<>(projects.size()); + } + + projectNames.add(name); + } + + public List<String> getProjectNames() { + return projectNames; + } + + public boolean getIsAllowed() { + return isAllowed; + } + + public void setIsAllowed(boolean allowed) { + isAllowed = allowed; + } + + public boolean getIsAudited() { + return isAudited; + } + + public void setIsAudited(boolean audited) { + isAudited = audited; + } + + public long getPolicyId() { + return policyId; + } + + public void setPolicyId(long policyId) { + this.policyId = policyId; + } + + public Long getPolicyVersion() { + return policyVersion; + } + + public void setPolicyVersion(Long policyVersion) { + this.policyVersion = policyVersion; + } + + @Override + public int hashCode() { + return Objects.hash(datasets, projects, datasetNames, projectNames, isAllowed, isAudited, policyId, policyVersion); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } else if ((obj == null) || !obj.getClass().equals(getClass())) { + return false; + } else { + GdsAccessResult other = (GdsAccessResult) obj; + + return Objects.equals(datasets, other.datasets) && + Objects.equals(projects, other.projects) && + Objects.equals(datasetNames, other.datasetNames) && + Objects.equals(projectNames, other.projectNames) && + Objects.equals(isAllowed, other.isAllowed) && + Objects.equals(isAudited, other.isAudited) && + Objects.equals(policyId, other.policyId) && + Objects.equals(policyVersion, other.policyVersion); + } + } + + @Override + public String toString( ) { + StringBuilder sb = new StringBuilder(); + + toString(sb); + + return sb.toString(); + } + + public StringBuilder toString(StringBuilder sb) { + sb.append("RangerGdsAccessResult={"); + sb.append("datasets={").append(datasets).append("}"); + sb.append(", projects={").append(projects).append("}"); + sb.append(", datasetNames={").append(datasetNames).append("}"); + sb.append(", projectNames={").append(projectNames).append("}"); + sb.append(", isAllowed={").append(isAllowed).append("}"); + sb.append(", isAudited={").append(isAudited).append("}"); + sb.append(", policyId={").append(policyId).append("}"); + sb.append(", policyVersion={").append(policyVersion).append("}"); + sb.append("}"); + + return sb; + } +} diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/gds/GdsDataShareEvaluator.java b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/gds/GdsDataShareEvaluator.java new file mode 100644 index 000000000..198907855 --- /dev/null +++ b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/gds/GdsDataShareEvaluator.java @@ -0,0 +1,164 @@ +/* + * 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.gds; + +import org.apache.commons.lang.StringUtils; +import org.apache.ranger.plugin.conditionevaluator.RangerConditionEvaluator; +import org.apache.ranger.plugin.model.RangerServiceDef; +import org.apache.ranger.plugin.model.validation.RangerServiceDefHelper; +import org.apache.ranger.plugin.policyengine.RangerAccessRequest; +import org.apache.ranger.plugin.policyengine.RangerResourceTrie; +import org.apache.ranger.plugin.policyevaluator.RangerCustomConditionEvaluator; +import org.apache.ranger.plugin.policyresourcematcher.RangerResourceEvaluator; +import org.apache.ranger.plugin.util.RangerResourceEvaluatorsRetriever; +import org.apache.ranger.plugin.util.ServiceGdsInfo.DataShareInfo; +import org.apache.ranger.plugin.util.ServiceGdsInfo.SharedResourceInfo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.*; + +public class GdsDataShareEvaluator { + private static final Logger LOG = LoggerFactory.getLogger(GdsDataShareEvaluator.class); + + public static final GdsDataShareEvalOrderComparator EVAL_ORDER_COMPARATOR = new GdsDataShareEvalOrderComparator(); + + private final DataShareInfo dsh; + private final String name; + private final String zoneName; + private final Map<String, RangerResourceTrie<GdsSharedResourceEvaluator>> resourceTries = new HashMap<>(); + private final List<GdsDshidEvaluator> dsidEvaluators = new ArrayList<>(); + private final RangerConditionEvaluator conditionEvaluator; + + public GdsDataShareEvaluator(DataShareInfo dsh, List<SharedResourceInfo> resources, RangerServiceDefHelper serviceDefHelper) { + LOG.debug("==> GdsDataShareEvaluator({}, {})", dsh, resources); + + this.dsh = dsh; + this.name = StringUtils.isBlank(dsh.getName()) ? StringUtils.EMPTY : dsh.getName(); + this.zoneName = StringUtils.isBlank(dsh.getZoneName()) ? StringUtils.EMPTY : dsh.getZoneName(); + this.conditionEvaluator = RangerCustomConditionEvaluator.getInstance().getExpressionEvaluator(dsh.getConditionExpr(), serviceDefHelper.getServiceDef()); + + if (resources != null) { + Set<String> resourceKeys = new HashSet<>(); + List<RangerResourceEvaluator> evaluators = new ArrayList<>(resources.size()); + + for (SharedResourceInfo resource : resources) { + GdsSharedResourceEvaluator evaluator = new GdsSharedResourceEvaluator(resource, dsh.getDefaultAccessTypes(), serviceDefHelper); + + evaluators.add(evaluator); + + resourceKeys.addAll(evaluator.getResourceKeys()); + } + + for (String resourceKey : resourceKeys) { + RangerServiceDef.RangerResourceDef resourceDef = serviceDefHelper.getResourceDef(resourceKey); + RangerResourceTrie resourceTrie = new RangerResourceTrie<>(resourceDef, evaluators); + + resourceTries.put(resourceKey, resourceTrie); + } + } + + LOG.debug("<== GdsDataShareEvaluator({}, {})", dsh, resources); + } + + public Long getId() { + return dsh.getId(); + } + + public String getName() { + return name; + } + + public String getZoneName() { + return zoneName; + } + + public void addDshidEvaluator(GdsDshidEvaluator dhidEvaluator) { + dsidEvaluators.add(dhidEvaluator); + } + + public void evaluate(RangerAccessRequest request, GdsAccessResult result) { + LOG.debug("==> GdsDataShareEvaluator.evaluate({}, {})", request, result); + + Collection<GdsSharedResourceEvaluator> evaluators = RangerResourceEvaluatorsRetriever.getEvaluators(resourceTries, request.getResource().getAsMap(), request.getResourceElementMatchingScopes()); + + if (evaluators == null) { + evaluators = Collections.emptyList(); + } else if (evaluators.size() > 1) { + List<GdsSharedResourceEvaluator> list = new ArrayList<>(evaluators); + + list.sort(GdsSharedResourceEvaluator.EVAL_ORDER_COMPARATOR); + + evaluators = list; + } + + LOG.debug("GdsDataShareEvaluator.evaluate({}): found {} evaluators", request, evaluators.size()); + + if (!evaluators.isEmpty()) { + boolean isAllowed = conditionEvaluator == null || conditionEvaluator.isMatched(request); + + if (isAllowed) { + // find if any of the shared resources allow the request + for (GdsSharedResourceEvaluator evaluator : evaluators) { + isAllowed = evaluator.isAllowed(request); + + if (isAllowed) { + break; + } + } + + if (isAllowed) { // now find dsidEvaluators that allow the request and collect their datasetIds + for (GdsDshidEvaluator dsidEvaluator : dsidEvaluators) { + if (!result.hasDataset(dsidEvaluator.getDatasetId())) { + if (dsidEvaluator.isAllowed(request)) { + result.addDataset(dsidEvaluator.getDatasetId()); + } + } + } + } + } else { + LOG.debug("GdsDataShareEvaluator.evaluate({}): conditions {} didn't match. Skipped", request, dsh.getConditionExpr()); + } + } + + LOG.debug("<== GdsDataShareEvaluator.evaluate({}, {})", request, result); + } + + public static class GdsDataShareEvalOrderComparator implements Comparator<GdsDataShareEvaluator> { + @Override + public int compare(GdsDataShareEvaluator me, GdsDataShareEvaluator other) { + int ret = 0; + + if (me != null && other != null) { + ret = me.getName().compareTo(other.dsh.getName()); + + if (ret == 0) { + ret = me.getId().compareTo(other.getId()); + } + } else if (me != null) { + ret = -1; + } else if (other != null) { + ret = 1; + } + + return ret; + } + } +} diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/gds/GdsDatasetEvaluator.java b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/gds/GdsDatasetEvaluator.java new file mode 100644 index 000000000..1de430def --- /dev/null +++ b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/gds/GdsDatasetEvaluator.java @@ -0,0 +1,175 @@ +/* + * 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.gds; + +import org.apache.commons.lang.StringUtils; +import org.apache.ranger.plugin.model.RangerPolicy; +import org.apache.ranger.plugin.model.RangerServiceDef; +import org.apache.ranger.plugin.policyengine.*; +import org.apache.ranger.plugin.policyevaluator.RangerOptimizedPolicyEvaluator; +import org.apache.ranger.plugin.policyevaluator.RangerPolicyEvaluator; +import org.apache.ranger.plugin.util.ServiceGdsInfo.DatasetInfo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +public class GdsDatasetEvaluator { + private static final Logger LOG = LoggerFactory.getLogger(GdsDatasetEvaluator.class); + + public static final GdsDatasetEvalOrderComparator EVAL_ORDER_COMPARATOR = new GdsDatasetEvalOrderComparator(); + + + private final DatasetInfo dataset; + private final String name; + private final List<GdsDipEvaluator> dipEvaluators = new ArrayList<>(); + private final List<RangerPolicyEvaluator> policyEvaluators; + + + public GdsDatasetEvaluator(DatasetInfo dataset, RangerServiceDef gdsServiceDef, RangerPolicyEngineOptions options) { + LOG.debug("==> GdsDatasetEvaluator()"); + + this.dataset = dataset; + this.name = StringUtils.isBlank(dataset.getName()) ? StringUtils.EMPTY : dataset.getName(); + + if (dataset.getPolicies() != null) { + policyEvaluators = new ArrayList<>(dataset.getPolicies().size()); + + for (RangerPolicy policy : dataset.getPolicies()) { + RangerPolicyEvaluator evaluator = new RangerOptimizedPolicyEvaluator(); + + evaluator.init(policy, gdsServiceDef, options); + + policyEvaluators.add(evaluator); + } + } else { + policyEvaluators = Collections.emptyList(); + } + + LOG.debug("<== GdsDatasetEvaluator()"); + } + + public Long getId() { + return dataset.getId(); + } + + public String getName() { + return name; + } + + public void addDipEvaluator(GdsDipEvaluator dipEvaluator) { + dipEvaluators.add(dipEvaluator); + } + + public void evaluate(RangerAccessRequest request, GdsAccessResult result, RangerServiceDef gdsServiceDef) { + LOG.debug("==> GdsDatasetEvaluator.evaluate({}, {})", request, result); + + result.addDatasetName(getName()); + + if (!policyEvaluators.isEmpty()) { + GdsDatasetAccessRequest datasetRequest = new GdsDatasetAccessRequest(getId(), gdsServiceDef, request); + RangerAccessResult datasetResult = datasetRequest.createAccessResult(); + + for (RangerPolicyEvaluator policyEvaluator : policyEvaluators) { + policyEvaluator.evaluate(datasetRequest, datasetResult); + } + + if (!result.getIsAllowed()) { + if (datasetResult.getIsAllowed()) { + result.setIsAllowed(true); + result.setPolicyId(datasetResult.getPolicyId()); + result.setPolicyVersion(datasetResult.getPolicyVersion()); + } + } + + if (!result.getIsAudited()) { + result.setIsAudited(datasetResult.getIsAudited()); + } + } + + for (GdsDipEvaluator dipEvaluator : dipEvaluators) { + if (!result.hasProject(dipEvaluator.getProjectId())) { + if (dipEvaluator.isAllowed(request)) { + result.addProject(dipEvaluator.getProjectId()); + } + } + } + + LOG.debug("<== GdsDatasetEvaluator.evaluate({}, {})", request, result); + } + + + public static class GdsDatasetAccessRequest extends RangerAccessRequestImpl { + public GdsDatasetAccessRequest(Long datasetId, RangerServiceDef gdsServiceDef, RangerAccessRequest request) { + super.setResource(new RangerDatasetResource(datasetId, gdsServiceDef, request.getResource().getOwnerUser())); + + super.setUser(request.getUser()); + super.setUserGroups(request.getUserGroups()); + super.setUserRoles(request.getUserRoles()); + super.setAction(request.getAction()); + super.setAccessType(request.getAccessType()); + super.setAccessTime(request.getAccessTime()); + super.setRequestData(request.getRequestData()); + super.setContext(request.getContext()); + super.setClientType(request.getClientType()); + super.setClientIPAddress(request.getClientIPAddress()); + super.setRemoteIPAddress(request.getRemoteIPAddress()); + super.setForwardedAddresses(request.getForwardedAddresses()); + super.setSessionId(request.getSessionId()); + super.setResourceMatchingScope(request.getResourceMatchingScope()); + } + + public RangerAccessResult createAccessResult() { + return new RangerAccessResult(RangerPolicy.POLICY_TYPE_ACCESS, GdsPolicyEngine.GDS_SERVICE_NAME, getResource().getServiceDef(), this); + } + } + + public static class RangerDatasetResource extends RangerAccessResourceImpl { + public RangerDatasetResource(Long datasetd, RangerServiceDef gdsServiceDef, String ownerUser) { + super.setValue(GdsPolicyEngine.RESOURCE_NAME_DATASET_ID, datasetd.toString()); + super.setServiceDef(gdsServiceDef); + super.setOwnerUser(ownerUser); + } + } + + public static class GdsDatasetEvalOrderComparator implements Comparator<GdsDatasetEvaluator> { + @Override + public int compare(GdsDatasetEvaluator me, GdsDatasetEvaluator other) { + int ret = 0; + + if (me != null && other != null) { + ret = me.getName().compareTo(other.getName()); + + if (ret == 0) { + ret = me.getId().compareTo(other.getId()); + } + } else if (me != null) { + ret = -1; + } else if (other != null) { + ret = 1; + } + + return ret; + } + } +} diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/gds/GdsDipEvaluator.java b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/gds/GdsDipEvaluator.java new file mode 100644 index 000000000..2198eadf1 --- /dev/null +++ b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/gds/GdsDipEvaluator.java @@ -0,0 +1,69 @@ +/* + * 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.gds; + +import org.apache.ranger.plugin.model.RangerGds; +import org.apache.ranger.plugin.policyengine.RangerAccessRequest; +import org.apache.ranger.plugin.policyevaluator.RangerValidityScheduleEvaluator; +import org.apache.ranger.plugin.util.ServiceGdsInfo.DatasetInProjectInfo; + +public class GdsDipEvaluator { + private final DatasetInProjectInfo dip; + private final RangerValidityScheduleEvaluator scheduleEvaluator; + + public GdsDipEvaluator(DatasetInProjectInfo dip) { + this.dip = dip; + + if (dip.getValiditySchedule() != null) { + scheduleEvaluator = new RangerValidityScheduleEvaluator(dip.getValiditySchedule()); + } else { + scheduleEvaluator = null; + } + } + + public long getDatasetId() { + return dip.getDatasetId(); + } + + public long getProjectId() { + return dip.getProjectId(); + } + + public boolean isAllowed(RangerAccessRequest request) { + boolean ret = isActive(); + + if (ret) { + // TODO: + } + + return ret; + } + + + private boolean isActive() { + boolean ret = dip.getStatus() == RangerGds.GdsShareStatus.ACTIVE; + + if (ret && scheduleEvaluator != null) { + ret = scheduleEvaluator.isApplicable(System.currentTimeMillis()); + } + + return ret; + } +} diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/gds/GdsDshidEvaluator.java b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/gds/GdsDshidEvaluator.java new file mode 100644 index 000000000..97bb06b2e --- /dev/null +++ b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/gds/GdsDshidEvaluator.java @@ -0,0 +1,69 @@ +/* + * 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.gds; + +import org.apache.ranger.plugin.model.RangerGds; +import org.apache.ranger.plugin.policyengine.RangerAccessRequest; +import org.apache.ranger.plugin.policyevaluator.RangerValidityScheduleEvaluator; +import org.apache.ranger.plugin.util.ServiceGdsInfo.DataShareInDatasetInfo; + +public class GdsDshidEvaluator { + private final DataShareInDatasetInfo dshid; + private final RangerValidityScheduleEvaluator scheduleEvaluator; + + public GdsDshidEvaluator(DataShareInDatasetInfo dshid) { + this.dshid = dshid; + + if (dshid.getValiditySchedule() != null) { + scheduleEvaluator = new RangerValidityScheduleEvaluator(dshid.getValiditySchedule()); + } else { + scheduleEvaluator = null; + } + } + + public long getDataShareId() { + return dshid.getDataShareId(); + } + + public long getDatasetId() { + return dshid.getDatasetId(); + } + + public boolean isAllowed(RangerAccessRequest request) { + boolean ret = isActive(); + + if (ret) { + // TODO: + } + + return ret; + } + + + private boolean isActive() { + boolean ret = dshid.getStatus() == RangerGds.GdsShareStatus.ACTIVE; + + if (ret && scheduleEvaluator != null) { + ret = scheduleEvaluator.isApplicable(System.currentTimeMillis()); + } + + return ret; + } +} diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/gds/GdsPolicyEngine.java b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/gds/GdsPolicyEngine.java new file mode 100644 index 000000000..64be44893 --- /dev/null +++ b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/gds/GdsPolicyEngine.java @@ -0,0 +1,298 @@ +/* + * 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.gds; + +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang.StringUtils; +import org.apache.ranger.plugin.model.RangerGds; +import org.apache.ranger.plugin.model.RangerServiceDef; +import org.apache.ranger.plugin.model.RangerServiceDef.RangerAccessTypeDef; +import org.apache.ranger.plugin.model.validation.RangerServiceDefHelper; +import org.apache.ranger.plugin.policyengine.RangerAccessRequest; +import org.apache.ranger.plugin.policyengine.RangerPluginContext; +import org.apache.ranger.plugin.policyengine.RangerPolicyEngineOptions; +import org.apache.ranger.plugin.util.RangerAccessRequestUtil; +import org.apache.ranger.plugin.util.ServiceGdsInfo; +import org.apache.ranger.plugin.util.ServiceGdsInfo.DatasetInfo; +import org.apache.ranger.plugin.util.ServiceGdsInfo.DatasetInProjectInfo; +import org.apache.ranger.plugin.util.ServiceGdsInfo.DataShareInDatasetInfo; +import org.apache.ranger.plugin.util.ServiceGdsInfo.DataShareInfo; +import org.apache.ranger.plugin.util.ServiceGdsInfo.ProjectInfo; +import org.apache.ranger.plugin.util.ServiceGdsInfo.SharedResourceInfo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.*; + + +public class GdsPolicyEngine { + private static final Logger LOG = LoggerFactory.getLogger(GdsPolicyEngine.class); + + public static final String GDS_SERVICE_NAME = "_gds"; + public static final String RESOURCE_NAME_DATASET_ID = "dataset-id"; + public static final String RESOURCE_NAME_PROJECT_ID = "project-id"; + + private final ServiceGdsInfo gdsInfo; + private final Map<String, List<GdsDataShareEvaluator>> zoneDataShares = new HashMap<>(); + private final Map<Long, GdsDatasetEvaluator> datasets = new HashMap<>(); + private final Map<Long, GdsProjectEvaluator> projects = new HashMap<>(); + + public GdsPolicyEngine(ServiceGdsInfo gdsInfo, RangerServiceDefHelper serviceDefHelper, RangerPluginContext pluginContext) { + LOG.debug("==> RangerGdsPolicyEngine()"); + + this.gdsInfo = gdsInfo; + + init(serviceDefHelper, pluginContext); + + LOG.debug("<== RangerGdsPolicyEngine()"); + } + + public GdsAccessResult evaluate(RangerAccessRequest request) { + LOG.debug("==> RangerGdsPolicyEngine.evaluate({})", request);; + + GdsAccessResult ret = null; + List<GdsDataShareEvaluator> dataShares = getDataShareEvaluators(request); + + if (!dataShares.isEmpty()) { + ret = new GdsAccessResult(); + + if (dataShares.size() > 1) { + dataShares.sort(GdsDataShareEvaluator.EVAL_ORDER_COMPARATOR); + } + + for (GdsDataShareEvaluator dshEvaluator : dataShares) { + dshEvaluator.evaluate(request, ret); + } + + evaluateDatasetPolicies(ret.getDatasets(), request, ret); + evaluateProjectPolicies(ret.getProjects(), request, ret); + } + + LOG.debug("<== RangerGdsPolicyEngine.evaluate({}): {}", request, ret); + + return ret; + } + + private void init(RangerServiceDefHelper serviceDefHelper, RangerPluginContext pluginContext) { + LOG.debug("==> RangerGdsPolicyEngine.init()"); + + preprocessGdsServiceDef(gdsInfo.getGdsServiceDef(), serviceDefHelper); + + RangerServiceDef gdsServiceDef = gdsInfo.getGdsServiceDef(); + RangerPolicyEngineOptions options = new RangerPolicyEngineOptions(pluginContext.getConfig().getPolicyEngineOptions(), new RangerServiceDefHelper(gdsServiceDef, false)); + Map<Long, List<SharedResourceInfo>> dshResources = new HashMap<>(); + Map<Long, GdsDataShareEvaluator> dshEvaluators = new HashMap<>(); + + if (gdsInfo.getProjects() != null) { + for (ProjectInfo projectInfo : gdsInfo.getProjects()) { + projects.put(projectInfo.getId(), new GdsProjectEvaluator(projectInfo, gdsServiceDef, options)); + } + } + + if (gdsInfo.getDatasets() != null) { + for (DatasetInfo datasetInfo : gdsInfo.getDatasets()) { + datasets.put(datasetInfo.getId(), new GdsDatasetEvaluator(datasetInfo, gdsServiceDef, options)); + } + } + + // dshResources must be populated before processing dataShares; hence resources should be processed before dataShares + if (gdsInfo.getResources() != null) { + for (SharedResourceInfo resource : gdsInfo.getResources()) { + List<SharedResourceInfo> resources = dshResources.computeIfAbsent(resource.getDataShareId(), k -> new ArrayList<>()); + + resources.add(resource); + } + } + + if (gdsInfo.getDataShares() != null) { + for (DataShareInfo dsh : gdsInfo.getDataShares()) { + GdsDataShareEvaluator dshEvaluator = new GdsDataShareEvaluator(dsh, dshResources.get(dsh.getId()), serviceDefHelper); + List<GdsDataShareEvaluator> zoneEvaluators = zoneDataShares.computeIfAbsent(dshEvaluator.getZoneName(), k -> new ArrayList<>()); + + zoneEvaluators.add(dshEvaluator); + dshEvaluators.put(dsh.getId(), dshEvaluator); + } + } + + if (gdsInfo.getDshids() != null) { + for (DataShareInDatasetInfo dshid : gdsInfo.getDshids()) { + if (dshid.getStatus() != RangerGds.GdsShareStatus.ACTIVE) { + LOG.error("RangerGdsPolicyEngine(): dshid is not active {}. Ignored", dshid); + + continue; + } + + GdsDataShareEvaluator dshEvaluator = dshEvaluators.get(dshid.getDataShareId()); + + if (dshEvaluator == null) { + LOG.error("RangerGdsPolicyEngine(): invalid dataShareId in dshid: {}. Ignored", dshid); + + continue; + } + + GdsDshidEvaluator dshidEvaluator = new GdsDshidEvaluator(dshid); + + dshEvaluator.addDshidEvaluator(dshidEvaluator); + } + } + + if (gdsInfo.getDips() != null) { + for (DatasetInProjectInfo dip : gdsInfo.getDips()) { + if (dip.getStatus() != RangerGds.GdsShareStatus.ACTIVE) { + LOG.error("RangerGdsPolicyEngine(): dip is not active {}. Ignored", dip); + + continue; + } + + GdsDatasetEvaluator datasetEvaluator = datasets.get(dip.getDatasetId()); + + if (datasetEvaluator == null) { + LOG.error("RangerGdsPolicyEngine(): invalid datasetId in dip: {}. Ignored", dip); + + continue; + } + + GdsDipEvaluator dipEvaluator = new GdsDipEvaluator(dip); + + datasetEvaluator.addDipEvaluator(dipEvaluator); + } + } + + LOG.debug("<== RangerGdsPolicyEngine.init()"); + } + + private void preprocessGdsServiceDef(RangerServiceDef gdsServiceDef, RangerServiceDefHelper serviceDefHelper) { + // populate accessTypes in GDS servicedef with implied accessTypes from the service + for (RangerAccessTypeDef gdsAccessTypeDef : gdsServiceDef.getAccessTypes()) { + Collection<String> impliedGrants = serviceDefHelper.getImpliedAccessGrants().get(gdsAccessTypeDef.getName()); + + if (impliedGrants != null) { + gdsAccessTypeDef.getImpliedGrants().addAll(impliedGrants); + } + } + + gdsServiceDef.getAccessTypes().addAll(serviceDefHelper.getServiceDef().getAccessTypes()); + } + + private List<GdsDataShareEvaluator> getDataShareEvaluators(RangerAccessRequest request) { + LOG.debug("==> RangerGdsPolicyEngine.getDataShareEvaluators({})", request); + + List<GdsDataShareEvaluator> ret = null; + + if (!zoneDataShares.isEmpty()) { + Set<String> zoneNames = RangerAccessRequestUtil.getResourceZoneNamesFromContext(request.getContext()); + + if (zoneNames == null || zoneNames.isEmpty()) { + zoneNames = Collections.singleton(StringUtils.EMPTY); // unzoned + } else if (zoneNames.size() > 1 && !request.isAccessTypeAny()) { + LOG.warn("RangerGdsPolicyEngine.getDataShareEvaluators(): resource matches multiple zones and accessType is not ANY - ignored. resource={}, zones={}", request.getResource(), zoneNames); + + zoneNames = Collections.emptySet(); + } + + for (String zoneName : zoneNames) { + List<GdsDataShareEvaluator> zonEvaluators = zoneDataShares.get(zoneName); + + if (zonEvaluators != null && !zonEvaluators.isEmpty()) { + if (ret == null) { + ret = new ArrayList<>(); + } + + ret.addAll(zonEvaluators); + } + } + } + + if (ret == null) { + ret = Collections.emptyList(); + } + + LOG.debug("<== RangerGdsPolicyEngine.getDataShareEvaluators({}): {}", request, ret); + + return ret; + } + + private void evaluateDatasetPolicies(Set<Long> datasetIds, RangerAccessRequest request, GdsAccessResult result) { + if (CollectionUtils.isNotEmpty(datasetIds)) { + List<GdsDatasetEvaluator> evaluators = new ArrayList<>(datasetIds.size()); + + for (Long datasetId : datasetIds) { + GdsDatasetEvaluator evaluator = datasets.get(datasetId); + + if (evaluator == null) { + LOG.error("evaluateDatasetPolicies(): invalid datasetId in result: {}. Ignored", datasetId); + + continue; + } + + evaluators.add(evaluator); + } + + if (evaluators.size() > 1) { + evaluators.sort(GdsDatasetEvaluator.EVAL_ORDER_COMPARATOR); + } + + for (GdsDatasetEvaluator evaluator : evaluators) { + evaluator.evaluate(request, result, gdsInfo.getGdsServiceDef()); + } + } + } + + private void evaluateProjectPolicies(Set<Long> projectIds, RangerAccessRequest request, GdsAccessResult result) { + if (CollectionUtils.isNotEmpty(projectIds)) { + List<GdsProjectEvaluator> evaluators = new ArrayList<>(projectIds.size()); + + for (Long projectId : projectIds) { + GdsProjectEvaluator evaluator = projects.get(projectId); + + if (evaluator == null) { + LOG.error("evaluateProjectPolicies(): invalid projectId in result: {}. Ignored", projectId); + + continue; + } + + evaluators.add(evaluator); + } + + if (evaluators.size() > 1) { + evaluators.sort(GdsProjectEvaluator.EVAL_ORDER_COMPARATOR); + } + + for (GdsProjectEvaluator evaluator : evaluators) { + evaluator.evaluate(request, result, gdsInfo.getGdsServiceDef()); + } + } + } +} + +/* + sharedRes-1--\ + |-- dataShare-1------- dataset-1--\ + sharedRes-2--/ / \ + / \_____ project-1 + sharedRes-3------ dataShare-2---\ / + \___ dataset-2---/ + / + sharedRes-4------ dataShare-3------/ + + sharedRes-5------ dataShare-4-------- dataset-3 --------- project-2 + + sharedRes-6------ dataShare-5-------- dataset-4 + */ \ No newline at end of file diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/gds/GdsProjectEvaluator.java b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/gds/GdsProjectEvaluator.java new file mode 100644 index 000000000..d5ce0e904 --- /dev/null +++ b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/gds/GdsProjectEvaluator.java @@ -0,0 +1,160 @@ +/* + * 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.gds; + +import org.apache.commons.lang.StringUtils; +import org.apache.ranger.plugin.model.RangerPolicy; +import org.apache.ranger.plugin.model.RangerServiceDef; +import org.apache.ranger.plugin.policyengine.*; +import org.apache.ranger.plugin.policyevaluator.RangerOptimizedPolicyEvaluator; +import org.apache.ranger.plugin.policyevaluator.RangerPolicyEvaluator; +import org.apache.ranger.plugin.util.ServiceGdsInfo.ProjectInfo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +public class GdsProjectEvaluator { + private static final Logger LOG = LoggerFactory.getLogger(GdsDatasetEvaluator.class); + + public static final GdsProjectEvalOrderComparator EVAL_ORDER_COMPARATOR = new GdsProjectEvalOrderComparator(); + + private final ProjectInfo project; + private final String name; + private final List<RangerPolicyEvaluator> policyEvaluators; + + public GdsProjectEvaluator(ProjectInfo project, RangerServiceDef gdsServiceDef, RangerPolicyEngineOptions options) { + LOG.debug("==> GdsProjectEvaluator({})", project); + + this.project = project; + this.name = StringUtils.isBlank(project.getName()) ? StringUtils.EMPTY : project.getName(); + + if (project.getPolicies() != null) { + policyEvaluators = new ArrayList<>(project.getPolicies().size()); + + for (RangerPolicy policy : project.getPolicies()) { + RangerPolicyEvaluator evaluator = new RangerOptimizedPolicyEvaluator(); + + evaluator.init(policy, gdsServiceDef, options); + + policyEvaluators.add(evaluator); + } + } else { + policyEvaluators = Collections.emptyList(); + } + + LOG.debug("<== GdsProjectEvaluator({})", project); + } + + public Long getId() { + return project.getId(); + } + + public String getName() { + return name; + } + + public void evaluate(RangerAccessRequest request, GdsAccessResult result, RangerServiceDef gdsServiceDef) { + LOG.debug("==> GdsDatasetEvaluator.evaluate({}, {})", request, result); + + result.addProjectName(getName()); + + if (!policyEvaluators.isEmpty()) { + GdsProjectAccessRequest projectRequest = new GdsProjectAccessRequest(getId(), gdsServiceDef, request); + RangerAccessResult projectResult = projectRequest.createAccessResult(); + + for (RangerPolicyEvaluator policyEvaluator : policyEvaluators) { + policyEvaluator.evaluate(projectRequest, projectResult); + } + + if (!result.getIsAllowed()) { + if (projectResult.getIsAllowed()) { + result.setIsAllowed(true); + result.setPolicyId(projectResult.getPolicyId()); + result.setPolicyVersion(projectResult.getPolicyVersion()); + } + } + + if (!result.getIsAudited()) { + result.setIsAudited(projectResult.getIsAudited()); + } + } + + LOG.debug("<== GdsDatasetEvaluator.evaluate({}, {})", request, result); + } + + + public static class GdsProjectAccessRequest extends RangerAccessRequestImpl { + public GdsProjectAccessRequest(Long projectId, RangerServiceDef gdsServiceDef, RangerAccessRequest request) { + super.setResource(new RangerProjectResource(projectId, gdsServiceDef, request.getResource().getOwnerUser())); + + super.setUser(request.getUser()); + super.setUserGroups(request.getUserGroups()); + super.setUserRoles(request.getUserRoles()); + super.setAction(request.getAction()); + super.setAccessType(request.getAccessType()); + super.setAccessTime(request.getAccessTime()); + super.setRequestData(request.getRequestData()); + super.setContext(request.getContext()); + super.setClientType(request.getClientType()); + super.setClientIPAddress(request.getClientIPAddress()); + super.setRemoteIPAddress(request.getRemoteIPAddress()); + super.setForwardedAddresses(request.getForwardedAddresses()); + super.setSessionId(request.getSessionId()); + super.setResourceMatchingScope(request.getResourceMatchingScope()); + } + + public RangerAccessResult createAccessResult() { + return new RangerAccessResult(RangerPolicy.POLICY_TYPE_ACCESS, GdsPolicyEngine.GDS_SERVICE_NAME, getResource().getServiceDef(), this); + } + } + + public static class RangerProjectResource extends RangerAccessResourceImpl { + public RangerProjectResource(Long projectd, RangerServiceDef gdsServiceDef, String ownerUser) { + super.setValue(GdsPolicyEngine.RESOURCE_NAME_PROJECT_ID, projectd.toString()); + super.setServiceDef(gdsServiceDef); + super.setOwnerUser(ownerUser); + } + } + + public static class GdsProjectEvalOrderComparator implements Comparator<GdsProjectEvaluator> { + @Override + public int compare(GdsProjectEvaluator me, GdsProjectEvaluator other) { + int ret = 0; + + if (me != null && other != null) { + ret = me.getName().compareTo(other.getName()); + + if (ret == 0) { + ret = me.getId().compareTo(other.getId()); + } + } else if (me != null) { + ret = -1; + } else if (other != null) { + ret = 1; + } + + return ret; + } + } +} diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/gds/GdsSharedResourceEvaluator.java b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/gds/GdsSharedResourceEvaluator.java new file mode 100644 index 000000000..33a9e44fa --- /dev/null +++ b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/gds/GdsSharedResourceEvaluator.java @@ -0,0 +1,179 @@ +/* + * 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.gds; + +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.ranger.plugin.conditionevaluator.RangerConditionEvaluator; +import org.apache.ranger.plugin.model.RangerPolicy; +import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyItemDataMaskInfo; +import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyItemRowFilterInfo; +import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyResource; +import org.apache.ranger.plugin.model.RangerServiceDef.RangerResourceDef; +import org.apache.ranger.plugin.model.validation.RangerServiceDefHelper; +import org.apache.ranger.plugin.policyengine.RangerAccessRequest; +import org.apache.ranger.plugin.policyevaluator.RangerCustomConditionEvaluator; +import org.apache.ranger.plugin.policyresourcematcher.RangerDefaultPolicyResourceMatcher; +import org.apache.ranger.plugin.policyresourcematcher.RangerPolicyResourceMatcher; +import org.apache.ranger.plugin.policyresourcematcher.RangerResourceEvaluator; +import org.apache.ranger.plugin.resourcematcher.RangerResourceMatcher; +import org.apache.ranger.plugin.util.ServiceDefUtil; +import org.apache.ranger.plugin.util.ServiceGdsInfo.SharedResourceInfo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.*; + +public class GdsSharedResourceEvaluator implements RangerResourceEvaluator { + private static final Logger LOG = LoggerFactory.getLogger(GdsSharedResourceEvaluator.class); + + public static final GdsSharedResourceEvalOrderComparator EVAL_ORDER_COMPARATOR = new GdsSharedResourceEvalOrderComparator(); + + private final SharedResourceInfo resource; + private final RangerConditionEvaluator conditionEvaluator; + private final Map<String, RangerPolicyResource> policyResource; + private final RangerPolicyResourceMatcher policyResourceMatcher; + private final RangerResourceDef leafResourceDef; + private final Set<String> allowedAccessTypes; + + public GdsSharedResourceEvaluator(SharedResourceInfo resource, Set<String> defaultAccessTypes, RangerServiceDefHelper serviceDefHelper) { + this.resource = resource; + this.conditionEvaluator = RangerCustomConditionEvaluator.getInstance().getExpressionEvaluator(resource.getConditionExpr(), serviceDefHelper.getServiceDef()); + + if (this.resource.getResource() == null) { + this.resource.setResource(Collections.emptyMap()); + } + + if (StringUtils.isNotBlank(resource.getSubResourceType()) && resource.getSubResource() != null && CollectionUtils.isNotEmpty(resource.getSubResource().getValues())) { + this.policyResource = new HashMap<>(resource.getResource()); + + this.policyResource.put(resource.getSubResourceType(), resource.getSubResource()); + } else { + this.policyResource = resource.getResource(); + } + + this.policyResourceMatcher = initPolicyResourceMatcher(policyResource, serviceDefHelper); + this.leafResourceDef = ServiceDefUtil.getLeafResourceDef(serviceDefHelper.getServiceDef(), policyResource); + this.allowedAccessTypes = serviceDefHelper.expandImpliedAccessGrants(resource.getAccessTypes() != null ? resource.getAccessTypes() : defaultAccessTypes); + + LOG.debug("GdsSharedResourceEvaluator: resource={}, conditionEvaluator={}, policyResource={}, leafResourceDef={}, allowedAccessTypes={}", + resource, conditionEvaluator, policyResource, leafResourceDef, allowedAccessTypes); + } + + @Override + public long getId() { + return resource.getId(); + } + + @Override + public RangerPolicyResourceMatcher getPolicyResourceMatcher() { + return policyResourceMatcher; + } + + @Override + public Map<String, RangerPolicyResource> getPolicyResource() { + return policyResource; + } + + @Override + public RangerResourceMatcher getResourceMatcher(String resourceName) { + return policyResourceMatcher.getResourceMatcher(resourceName); + } + + @Override + public boolean isAncestorOf(RangerResourceDef resourceDef) { + return ServiceDefUtil.isAncestorOf(policyResourceMatcher.getServiceDef(), leafResourceDef, resourceDef); + } + + public Collection<String> getResourceKeys() { + return resource != null && resource.getResource() != null ? resource.getResource().keySet() : Collections.emptySet(); + } + + public boolean isAllowed(RangerAccessRequest request) { + LOG.debug("==> GdsSharedResourceEvaluator.evaluate({})", request); + + boolean ret = conditionEvaluator == null || conditionEvaluator.isMatched(request); + + if (ret) { + ret = request.isAccessTypeAny() ? !allowedAccessTypes.isEmpty() : allowedAccessTypes.contains(request.getAccessType()); + + if (ret) { + ret = policyResourceMatcher.isMatch(request.getResource(), request.getResourceElementMatchingScopes(), request.getContext()); + + if (!ret) { + LOG.debug("GdsSharedResourceEvaluator.evaluate({}): not matched for resource {}", request, request.getResource()); + } + } else { + LOG.debug("GdsSharedResourceEvaluator.evaluate({}): not matched for accessType {}", request, request.getAccessType()); + } + } else { + LOG.debug("GdsSharedResourceEvaluator.evaluate({}): not matched for condition {}", request, resource.getConditionExpr()); + } + + LOG.debug("<== GdsSharedResourceEvaluator.evaluate({})", request); + + return ret; + } + + public RangerPolicyItemRowFilterInfo getRowFilter() { + return resource.getRowFilter(); + } + + public RangerPolicyItemDataMaskInfo getDataMask(String subResourceName) { + return resource.getSubResourceMasks() != null ? resource.getSubResourceMasks().get(subResourceName) : null; + } + + private static RangerPolicyResourceMatcher initPolicyResourceMatcher(Map<String, RangerPolicyResource> policyResource, RangerServiceDefHelper serviceDefHelper) { + RangerDefaultPolicyResourceMatcher matcher = new RangerDefaultPolicyResourceMatcher(); + + matcher.setServiceDefHelper(serviceDefHelper); + matcher.setServiceDef(serviceDefHelper.getServiceDef()); + matcher.setPolicyResources(policyResource, RangerPolicy.POLICY_TYPE_ACCESS); + + matcher.init(); + + return matcher; + } + + public static class GdsSharedResourceEvalOrderComparator implements Comparator<GdsSharedResourceEvaluator> { + @Override + public int compare(GdsSharedResourceEvaluator me, GdsSharedResourceEvaluator other) { + int ret = 0; + + if (me != null && other != null) { + ret = StringUtils.compare(me.resource.getName(), other.resource.getName()); + + if (ret == 0) { + ret = Integer.compare(me.resource.getResource().size(), other.resource.getResource().size()); + + if (ret == 0) { + ret = Long.compare(me.getId(), other.getId()); + } + } + } else if (me != null) { + ret = -1; + } else if (other != null) { + ret = 1; + } + + return ret; + } + } +} diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerCustomConditionEvaluator.java b/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerCustomConditionEvaluator.java index 271edf97f..511c459a5 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerCustomConditionEvaluator.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerCustomConditionEvaluator.java @@ -20,6 +20,7 @@ package org.apache.ranger.plugin.policyevaluator; import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang.StringUtils; import org.apache.ranger.plugin.conditionevaluator.RangerConditionEvaluator; import org.apache.ranger.plugin.model.RangerPolicy; import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyItemCondition; @@ -36,6 +37,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; + // // this class should have been named RangerConditionEvaluatorFactory // @@ -45,6 +47,8 @@ public class RangerCustomConditionEvaluator { private static final Logger PERF_POLICYITEM_INIT_LOG = RangerPerfTracer.getPerfLogger("policyitem.init"); private static final Logger PERF_POLICYCONDITION_INIT_LOG = RangerPerfTracer.getPerfLogger("policycondition.init"); + private static final RangerPolicyConditionDef EXPRESSION_CONDITION_DEF = ServiceDefUtil.createImplicitExpressionConditionDef(-1L); + public static RangerCustomConditionEvaluator getInstance() { return RangerCustomConditionEvaluator.SingletonHolder.s_instance; @@ -141,6 +145,30 @@ public class RangerCustomConditionEvaluator { return ret; } + public RangerConditionEvaluator getExpressionEvaluator(String expression, RangerServiceDef serviceDef) { + final RangerConditionEvaluator ret; + + if (StringUtils.isNotBlank(expression)) { + ret = newConditionEvaluator(EXPRESSION_CONDITION_DEF.getEvaluator()); + + if (ret != null) { + ret.setServiceDef(serviceDef); + ret.setConditionDef(EXPRESSION_CONDITION_DEF); + ret.setPolicyItemCondition(new RangerPolicyItemCondition(EXPRESSION_CONDITION_DEF.getName(), Collections.singletonList(expression))); + + RangerPerfTracer perf = RangerPerfTracer.getPerfTracer(PERF_POLICYCONDITION_INIT_LOG, "RangerConditionEvaluator.init(_expression)"); + + ret.init(); + + RangerPerfTracer.log(perf); + } + } else { + ret = null; + } + + return ret; + } + private RangerConditionEvaluator newConditionEvaluator(String className) { if (LOG.isDebugEnabled()) { LOG.debug("==> RangerCustomConditionEvaluator.newConditionEvaluator(" + className + ")"); diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerAccessRequestUtil.java b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerAccessRequestUtil.java index b088ed7ef..92a4fe02e 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerAccessRequestUtil.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerAccessRequestUtil.java @@ -29,6 +29,7 @@ import org.apache.commons.collections.MapUtils; import org.apache.ranger.plugin.contextenricher.RangerTagForEval; import org.apache.ranger.plugin.policyengine.RangerAccessRequest; import org.apache.ranger.plugin.policyengine.RangerAccessResource; +import org.apache.ranger.plugin.policyengine.gds.GdsAccessResult; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -47,6 +48,7 @@ public class RangerAccessRequestUtil { public static final String KEY_CONTEXT_ACCESSTYPES = "ACCESSTYPES"; public static final String KEY_CONTEXT_IS_ANY_ACCESS = "ISANYACCESS"; public static final String KEY_CONTEXT_REQUEST = "_REQUEST"; + public static final String KEY_CONTEXT_GDS_RESULT = "_GDS_RESULT"; public static final String KEY_CONTEXT_IS_REQUEST_PREPROCESSED = "ISREQUESTPREPROCESSED"; public static final String KEY_CONTEXT_RESOURCE_ZONE_NAMES = "RESOURCE_ZONE_NAMES"; @@ -134,6 +136,7 @@ public class RangerAccessRequestUtil { ret.remove(KEY_CONTEXT_RESOURCE); ret.remove(KEY_CONTEXT_RESOURCE_ZONE_NAMES); ret.remove(KEY_CONTEXT_REQUEST); + ret.remove(KEY_CONTEXT_GDS_RESULT); ret.remove(KEY_CONTEXT_ACCESSTYPES); ret.remove(KEY_CONTEXT_IS_ANY_ACCESS); ret.remove(KEY_CONTEXT_IS_REQUEST_PREPROCESSED); @@ -259,6 +262,32 @@ public class RangerAccessRequestUtil { return ret; } + public static void setGdsResultInContext(RangerAccessRequest request, GdsAccessResult result) { + Map<String, Object> context = request.getContext(); + + if (context != null) { + context.put(KEY_CONTEXT_GDS_RESULT, result); + } + } + + public static GdsAccessResult getGdsResultFromContext(Map<String, Object> context) { + GdsAccessResult ret = null; + + if (context != null) { + Object val = context.get(KEY_CONTEXT_GDS_RESULT); + + if (val != null) { + if (val instanceof GdsAccessResult) { + ret = (GdsAccessResult) val; + } else { + LOG.error("getGdsResultFromContext(): expected RangerGdsAccessResult, but found " + val.getClass().getCanonicalName()); + } + } + } + + return ret; + } + public static void setResourceZoneNamesInContext(RangerAccessRequest request, Set<String> zoneNames) { Map<String, Object> context = request.getContext(); diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/util/ServiceGdsInfo.java b/agents-common/src/main/java/org/apache/ranger/plugin/util/ServiceGdsInfo.java index 928807382..40d6fa472 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/util/ServiceGdsInfo.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/util/ServiceGdsInfo.java @@ -279,13 +279,12 @@ public class ServiceGdsInfo implements java.io.Serializable { public static class DataShareInfo implements java.io.Serializable { private static final long serialVersionUID = 1L; - private Long id; - private String name; - private String zoneName; - private String conditionExpr; - private Set<String> defaultAccessTypes; - private List<RangerTagDataMaskInfo> defaultTagMasks; - private List<DataShareInDatasetInfo> datasets; + private Long id; + private String name; + private String zoneName; + private String conditionExpr; + private Set<String> defaultAccessTypes; + private List<RangerTagDataMaskInfo> defaultTagMasks; public DataShareInfo() { } @@ -338,22 +337,6 @@ public class ServiceGdsInfo implements java.io.Serializable { this.defaultTagMasks = defaultTagMasks; } - public List<DataShareInDatasetInfo> getDatasets() { - return datasets; - } - - public void setDatasets(List<DataShareInDatasetInfo> datasets) { - this.datasets = datasets; - } - - public void addDataset(DataShareInDatasetInfo dshInDsInfo) { - if (datasets == null) { - datasets = new ArrayList<>(); - } - - datasets.add(dshInDsInfo); - } - @Override public String toString( ) { return toString(new StringBuilder()).toString(); @@ -383,14 +366,6 @@ public class ServiceGdsInfo implements java.io.Serializable { } sb.append("]"); - sb.append(", datasets=["); - if (datasets != null) { - for (DataShareInDatasetInfo dataset : datasets) { - dataset.toString(sb).append(", "); - } - } - sb.append("]"); - sb.append("}"); return sb; @@ -404,6 +379,7 @@ public class ServiceGdsInfo implements java.io.Serializable { private static final long serialVersionUID = 1L; private Long id; + private String name; private Long dataShareId; private Map<String, RangerPolicyResource> resource; private RangerPolicyResource subResource; @@ -422,6 +398,14 @@ public class ServiceGdsInfo implements java.io.Serializable { this.id = id; } + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + public Long getDataShareId() { return dataShareId; } @@ -502,6 +486,7 @@ public class ServiceGdsInfo implements java.io.Serializable { public StringBuilder toString(StringBuilder sb) { sb.append("SharedResourceInfo={") .append("id=").append(id) + .append(", name=").append(name) .append(", dataShareId=").append(dataShareId) .append(", resource=").append(resource) .append(", subResource=").append(subResource) @@ -552,10 +537,9 @@ public class ServiceGdsInfo implements java.io.Serializable { public static class DatasetInfo implements java.io.Serializable { private static final long serialVersionUID = 1L; - private Long id; - private String name; - private List<RangerPolicy> policies; - private List<DatasetInProjectInfo> projects; + private Long id; + private String name; + private List<RangerPolicy> policies; public DatasetInfo() { } @@ -584,22 +568,6 @@ public class ServiceGdsInfo implements java.io.Serializable { this.policies = policies; } - public List<DatasetInProjectInfo> getProjects() { - return projects; - } - - public void setProjects(List<DatasetInProjectInfo> projects) { - this.projects = projects; - } - - public void addProject(DatasetInProjectInfo project) { - if (projects == null) { - projects = new ArrayList<>(); - } - - projects.add(project); - } - @Override public String toString( ) { return toString(new StringBuilder()).toString(); @@ -618,14 +586,6 @@ public class ServiceGdsInfo implements java.io.Serializable { } sb.append("]"); - sb.append(", projects=["); - if (projects != null) { - for (DatasetInProjectInfo project : projects) { - project.toString(sb).append(", "); - } - } - sb.append("]"); - sb.append("}"); return sb; diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/policyengine/TestRangerAuthContext.java b/agents-common/src/test/java/org/apache/ranger/plugin/policyengine/TestRangerAuthContext.java index a23765520..f45bc43f6 100644 --- a/agents-common/src/test/java/org/apache/ranger/plugin/policyengine/TestRangerAuthContext.java +++ b/agents-common/src/test/java/org/apache/ranger/plugin/policyengine/TestRangerAuthContext.java @@ -31,6 +31,7 @@ import org.apache.commons.collections.MapUtils; import org.apache.ranger.plugin.contextenricher.RangerContextEnricher; import org.apache.ranger.plugin.contextenricher.RangerGdsEnricher; import org.apache.ranger.plugin.contextenricher.RangerTagEnricher; +import org.apache.ranger.plugin.policyengine.gds.GdsPolicyEngine; import org.apache.ranger.plugin.service.RangerAuthContext; import org.apache.ranger.plugin.service.RangerBasePlugin; import org.apache.ranger.plugin.util.ServicePolicies; @@ -107,9 +108,9 @@ public class TestRangerAuthContext { } else if (enricherName.equals("TagEnricher")) { assertTrue("- Invalid contextEnricher", (enricherData instanceof RangerTagEnricher || enricherData instanceof RangerTagEnricher.EnrichedServiceTags)); } else if (enricherName.equals(IMPLICIT_GDS_ENRICHER_NAME) || enricher instanceof RangerGdsEnricher) { - assertTrue("- Invalid contextEnricher", (enricherData instanceof RangerGdsEnricher || enricherData instanceof RangerGdsEnricher.EnhancedGdsInfo)); + assertTrue("- Invalid contextEnricher", (enricherData instanceof RangerGdsEnricher || enricherData instanceof GdsPolicyEngine)); } else { - assertTrue(fileName + "-" + testName + " - Unexpected type of contextEnricher", false); + fail(fileName + "-" + testName + " - Unexpected type of contextEnricher: " + enricher); } } } diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/policyengine/gds/TestGdsPolicyEngine.java b/agents-common/src/test/java/org/apache/ranger/plugin/policyengine/gds/TestGdsPolicyEngine.java new file mode 100644 index 000000000..83bfc99f5 --- /dev/null +++ b/agents-common/src/test/java/org/apache/ranger/plugin/policyengine/gds/TestGdsPolicyEngine.java @@ -0,0 +1,131 @@ +/* + * 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.gds; + +import com.google.gson.*; +import org.apache.ranger.authorization.hadoop.config.RangerPluginConfig; +import org.apache.ranger.plugin.model.RangerServiceDef; +import org.apache.ranger.plugin.model.validation.RangerServiceDefHelper; +import org.apache.ranger.plugin.policyengine.*; +import org.apache.ranger.plugin.util.RangerAccessRequestUtil; +import org.apache.ranger.plugin.util.ServiceDefUtil; +import org.apache.ranger.plugin.util.ServiceGdsInfo; +import org.apache.ranger.plugin.util.ServicePolicies.SecurityZoneInfo; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.lang.reflect.Type; +import java.util.*; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class TestGdsPolicyEngine { + static Gson gsonBuilder; + + @BeforeClass + public static void setUpBeforeClass() throws Exception { + gsonBuilder = new GsonBuilder().setDateFormat("yyyyMMdd-HH:mm:ss.SSSZ") + .setPrettyPrinting() + .registerTypeAdapter(RangerAccessRequest.class, new RangerAccessRequestDeserializer()) + .registerTypeAdapter(RangerAccessResource.class, new RangerResourceDeserializer()) + .create(); + } + + @Test + public void testGdsPolicyEngineHive() throws Exception { + runTestsFromResourceFile("/policyengine/gds/test_gds_policy_engine_hive.json"); + } + + private void runTestsFromResourceFile(String resourceFile) throws Exception { + InputStream inStream = this.getClass().getResourceAsStream(resourceFile); + InputStreamReader reader = new InputStreamReader(inStream); + + runTests(reader, resourceFile); + } + + private void runTests(Reader reader, String testName) { + GdsPolicyEngineTestCase testCase = gsonBuilder.fromJson(reader, GdsPolicyEngineTestCase.class); + + assertTrue("invalid input: " + testName, testCase != null && testCase.gdsInfo != null && testCase.tests != null); + + testCase.serviceDef.setMarkerAccessTypes(ServiceDefUtil.getMarkerAccessTypes(testCase.serviceDef.getAccessTypes())); + + RangerPluginContext pluginContext = new RangerPluginContext(new RangerPluginConfig(testCase.serviceDef.getName(), null, "hive", "cl1", "on-prem", null)); + RangerSecurityZoneMatcher zoneMatcher = new RangerSecurityZoneMatcher(testCase.securityZones, testCase.serviceDef, pluginContext); + GdsPolicyEngine policyEngine = new GdsPolicyEngine(testCase.gdsInfo, new RangerServiceDefHelper(testCase.serviceDef, false), pluginContext); + + for (TestData test : testCase.tests) { + Set<String> zoneNames = zoneMatcher.getZonesForResourceAndChildren(test.request.getResource()); + + RangerAccessRequestUtil.setResourceZoneNamesInContext(test.request, zoneNames); + + GdsAccessResult result = policyEngine.evaluate(test.request); + + assertEquals(test.name, test.result, result); + } + } + + static class GdsPolicyEngineTestCase { + public RangerServiceDef serviceDef; + public Map<String, SecurityZoneInfo> securityZones; + public ServiceGdsInfo gdsInfo; + public List<TestData> tests; + } + + static class TestData { + public String name; + public RangerAccessRequest request; + public GdsAccessResult result; + } + + static class RangerAccessRequestDeserializer implements JsonDeserializer<RangerAccessRequest> { + @Override + public RangerAccessRequest deserialize(JsonElement jsonObj, Type type, + JsonDeserializationContext context) throws JsonParseException { + RangerAccessRequestImpl ret = gsonBuilder.fromJson(jsonObj, RangerAccessRequestImpl.class); + + ret.setAccessType(ret.getAccessType()); // to force computation of isAccessTypeAny and isAccessTypeDelegatedAdmin + if (ret.getAccessTime() == null) { + ret.setAccessTime(new Date()); + } + Map<String, Object> reqContext = ret.getContext(); + Object accessTypes = reqContext.get("ACCESSTYPES"); + if (accessTypes != null) { + Collection<String> accessTypesCollection = (Collection<String>) accessTypes; + Set<String> requestedAccesses = new HashSet<>(accessTypesCollection); + ret.getContext().put("ACCESSTYPES", requestedAccesses); + } + + return ret; + } + } + + static class RangerResourceDeserializer implements JsonDeserializer<RangerAccessResource> { + @Override + public RangerAccessResource deserialize(JsonElement jsonObj, Type type, + JsonDeserializationContext context) throws JsonParseException { + return gsonBuilder.fromJson(jsonObj, RangerAccessResourceImpl.class); + } + } +} diff --git a/agents-common/src/test/resources/policyengine/gds/test_gds_policy_engine_hive.json b/agents-common/src/test/resources/policyengine/gds/test_gds_policy_engine_hive.json new file mode 100644 index 000000000..c3ef3c484 --- /dev/null +++ b/agents-common/src/test/resources/policyengine/gds/test_gds_policy_engine_hive.json @@ -0,0 +1,381 @@ +{ + "serviceDef":{ + "name": "hive", + "id": 3, + "resources": [ + { "name": "database", "level":1, "parent": "", "mandatory": true, "lookupSupported": true, "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", "matcherOptions":{ "wildCard": true, "ignoreCase": true }, "label": "Hive Database", "description": "Hive Database" }, + { "name": "url", "level":1, "parent": "", "mandatory": true, "lookupSupported": false, "matcher": "org.apache.ranger.plugin.resourcematcher.RangerURLResourceMatcher", "matcherOptions":{ "wildCard": true, "ignoreCase": true }, "label": "URL", "description": "URL", "recursiveSupported": true }, + { "name": "hiveservice", "level":1, "parent": "", "mandatory": true, "lookupSupported": false, "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", "matcherOptions":{ "wildCard": true, "ignoreCase": true }, "label": "HiveService", "description": "HiveService" }, + { "name": "table", "level":2, "parent": "database", "mandatory": true, "lookupSupported": true, "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", "matcherOptions":{ "wildCard": true, "ignoreCase": true }, "label": "Hive Table", "description": "Hive Table" }, + { "name": "udf", "level":2, "parent": "database", "mandatory": true, "lookupSupported": true, "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", "matcherOptions":{ "wildCard": true, "ignoreCase": true }, "label": "Hive UDF", "description": "Hive UDF" }, + { "name": "column", "level":3, "parent": "table", "mandatory": true, "lookupSupported": true, "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", "matcherOptions":{ "wildCard": true, "ignoreCase": true }, "label": "Hive Column", "description": "Hive Column" } + ], + "accessTypes":[ + { "name": "select", "label": "Select", "category": "READ" }, + { "name": "update", "label": "Update", "category": "UPDATE" }, + { "name": "create", "label": "Create", "category": "CREATE" }, + { "name": "drop", "label": "Drop", "category": "DELETE" }, + { "name": "alter", "label": "Alter", "category": "CREATE" }, + { "name": "index", "label": "Index", "category": "MANAGE" }, + { "name": "lock", "label": "Lock", "category": "MANAGE" }, + { "name": "read", "label": "Read", "category": "READ" }, + { "name": "write", "label": "Write", "category": "UPDATE" }, + { "name": "repladmin", "label": "ReplAdmin", "category": "MANAGE" }, + { "name": "serviceadmin", "label": "ServiceAdmin", "category": "MANAGE" }, + { "name": "all", "label": "All", + "impliedGrants": [ "select", "update", "create", "drop", "alter", "index", "lock", "read", "write", "repladmin", "serviceadmin" ] + } + ] + }, + "securityZones": { + "sales": { "zoneName": "sales", "resources": [ { "database": [ "sales" ] } ] }, + "finance": { "zoneName": "finance", "resources": [ { "database": [ "finance" ] } ] }, + "shipping": { "zoneName": "shipping", "resources": [ { "database": [ "shipping" ] } ] } + }, + "gdsInfo": { + "serviceName": "dev_hive", + "dataShares": [ + { "id": 1, "name": "hive-sales-2023", "zoneName": "sales", "conditionExpr": "", "defaultAccessTypes": [ "_READ" ], "defaultTagMasks": [ ] }, + { "id": 2, "name": "hive-finance-2023", "zoneName": "finance", "conditionExpr": "", "defaultAccessTypes": [ "_READ" ], "defaultTagMasks": [ ] }, + { "id": 3, "name": "hive-shipping-2023", "zoneName": "shipping", "conditionExpr": "", "defaultAccessTypes": [ "_READ" ], "defaultTagMasks": [ ] }, + { "id": 4, "name": "hive-new-customers-2023", "zoneName": "", "conditionExpr": "", "defaultAccessTypes": [ "_READ" ], "defaultTagMasks": [ ] }, + { "id": 5, "name": "hive-facilities", "zoneName": "", "conditionExpr": "", "defaultAccessTypes": [ "_READ" ], "defaultTagMasks": [ ] } + ], + "dshids": [ + { "dataShareId": 1, "datasetId": 1, "status": "ACTIVE", "validitySchedule": null }, + { "dataShareId": 2, "datasetId": 1, "status": "ACTIVE", "validitySchedule": null }, + { "dataShareId": 2, "datasetId": 2, "status": "ACTIVE", "validitySchedule": null }, + { "dataShareId": 3, "datasetId": 2, "status": "ACTIVE", "validitySchedule": null }, + { "dataShareId": 4, "datasetId": 3, "status": "ACTIVE", "validitySchedule": null }, + { "dataShareId": 5, "datasetId": 4, "status": "ACTIVE", "validitySchedule": null } + ], + "dips": [ + { "datasetId": 1, "projectId": 1, "status": "ACTIVE", "validitySchedule": null }, + { "datasetId": 2, "projectId": 1, "status": "ACTIVE", "validitySchedule": null }, + { "datasetId": 3, "projectId": 2, "status": "ACTIVE", "validitySchedule": null } + ], + "resources": [ + { + "id": 11, "dataShareId": 1, "conditionExpr": "", "accessTypes": [ "select" ], + "resource": { "database": { "values": [ "sales" ] }, "table": { "values": [ "prospects" ] } }, "rowFilter": { "filterExpr": "created_time >= '2023-01-01' and created_time < '2024-01-01'" }, + "subResourceType": "column", "subResource": { "values": [ "*" ] }, "subResourceMasks": { } + }, + { + "id": 12, "dataShareId": 1, "conditionExpr": "", "accessTypes": [ "select" ], + "resource": { "database": { "values": [ "sales" ] }, "table": { "values": [ "orders" ] } }, "rowFilter": { "filterExpr": "created_time >= '2023-01-01' and created_time < '2024-01-01'" }, + "subResourceType": "column", "subResource": { "values": [ "*" ] }, "subResourceMasks": { } + }, + { + "id": 21, "dataShareId": 2, "conditionExpr": "", "accessTypes": [ "select" ], + "resource": { "database": { "values": [ "finance" ] }, "table": { "values": [ "invoices" ] } }, "rowFilter": { "filterExpr": "created_time >= '2023-01-01' and created_time < '2024-01-01'" }, + "subResourceType": "column", "subResource": { "values": [ "*" ] }, "subResourceMasks": { } + }, + { + "id": 22, "dataShareId": 2, "conditionExpr": "", "accessTypes": [ "select" ], + "resource": { "database": { "values": [ "finance" ] }, "table": { "values": [ "payments" ] } }, "rowFilter": { "filterExpr": "created_time >= '2023-01-01' and created_time < '2024-01-01'" }, + "subResourceType": "column", "subResource": { "values": [ "*" ] }, "subResourceMasks": { } + }, + { + "id": 31, "dataShareId": 3, "conditionExpr": "", "accessTypes": [ "select" ], + "resource": { "database": { "values": [ "shipping" ] }, "table": { "values": [ "shipments" ] } }, "rowFilter": { "filterExpr": "created_time >= '2023-01-01' and created_time < '2024-01-01'" }, + "subResourceType": "column", "subResource": { "values": [ "*" ] }, "subResourceMasks": { } + }, + { + "id": 41, "dataShareId": 4, "conditionExpr": "", "accessTypes": [ "select" ], + "resource": { "database": { "values": [ "customers" ] }, "table": { "values": [ "contact_info" ] } }, "rowFilter": { "filterExpr": "created_time >= '2023-01-01' and created_time < '2024-01-01'" }, + "subResourceType": "column", "subResource": { "values": [ "*" ] }, "subResourceMasks": null + }, + { + "id": 51, "dataShareId": 5, "conditionExpr": "", "accessTypes": [ "select" ], + "resource": { "database": { "values": [ "operations" ] }, "table": { "values": [ "facilities" ] } }, "rowFilter": null, + "subResourceType": "column", "subResource": { "values": [ "*" ] }, "subResourceMasks": null + } + ], + "datasets": [ + { + "id": 1, + "name": "dataset-1", + "policies": [ + { "id": 2001, "name": "dataset-3", "isEnabled": true, "isAuditEnabled": true, + "resources": { "dataset-id": { "values": ["1"] } }, + "policyItems":[ + { "accesses":[ { "type": "_READ", "isAllowed": true } ], "users": [ "ds-user" ], "groups": []} + ] + } + ] + }, + { + "id": 2, + "name": "dataset-2", + "policies": [ + { "id": 2002, "name": "dataset-2", "isEnabled": true, "isAuditEnabled": true, + "resources": { "dataset-id": { "values": ["2"] } }, + "policyItems":[ + { "accesses":[ { "type": "_READ", "isAllowed": true } ], "users": [ "ds-user" ], "groups": []} + ] + } + ] + }, + { + "id": 3, + "name": "dataset-3", + "policies": [ + { "id": 2003, "name": "dataset-3", "isEnabled": true, "isAuditEnabled": true, + "resources": { "dataset-id": { "values": ["3"] } }, + "policyItems":[ + { "accesses":[ { "type": "_READ", "isAllowed": true } ], "users": [ "ds-user" ], "groups": []} + ] + } + ] + }, + { + "id": 4, + "name": "dataset-4", + "policies": [ + { "id": 2004, "name": "dataset-4", "isEnabled": true, "isAuditEnabled": true, + "resources": { "dataset-id": { "values": ["4"] } }, + "policyItems":[ + { "accesses":[ { "type": "_READ", "isAllowed": true } ], "users": [ "ds-user" ], "groups": []} + ] + } + ] + } + ], + "projects": [ + { + "id": 1, + "name": "project-1", + "policies": [ + { "id": 3001, "name": "project-1", "isEnabled": true, "isAuditEnabled": true, + "resources": { "project-id": { "values": ["1"] } }, + "policyItems":[ + { "accesses":[ { "type": "_READ", "isAllowed": true } ], "users": [ "proj-user" ], "groups": []} + ] + } + ] + }, + { + "id": 2, + "name": "project-2", + "policies": [ + { "id": 3002, "name": "project-2", "isEnabled": true, "isAuditEnabled": true, + "resources": { "project-id": { "values": ["2"] } }, + "policyItems":[ + { "accesses":[ { "type": "_READ", "isAllowed": true } ], "users": [ "proj-user" ], "groups": []} + ] + } + ] + } + ], + "gdsServiceDef": { + "name": "gds", + "id": 3, + "resources": [ + { "name": "dataset-id", "level":1, "parent": "", "mandatory": true, "lookupSupported": false, "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", "matcherOptions":{ "wildCard": false, "ignoreCase": true }, "label": "Dataset ID", "description": "Dataset ID" }, + { "name": "project-id", "level":1, "parent": "", "mandatory": true, "lookupSupported": false, "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", "matcherOptions":{ "wildCard": false, "ignoreCase": true }, "label": "Project ID", "description": "Project ID" } + ], + "accessTypes":[ + { "itemId": 1, "name": "_CREATE", "label": "_CREATE" }, + { "itemId": 2, "name": "_READ", "label": "_READ" }, + { "itemId": 3, "name": "_UPDATE", "label": "_UPDATE" }, + { "itemId": 4, "name": "_DELETE", "label": "_DELETE" }, + { "itemId": 5, "name": "_MANAGE", "label": "_MANAGE" }, + { "itemId": 6, "name": "_ALL", "label": "_ALL" } + ] + }, + "gdsVersion": 1 + }, + "tests": [ + { + "name": "table: sales.prospects, user: ds-user, access: select", + "request": { + "resource": { "elements": { "database": "sales", "table": "prospects" } }, + "accessType": "select", "user": "ds-user", "userGroups": [] + }, + "result": { "datasets": [ 1 ], "projects": [ 1 ], "datasetNames": [ "dataset-1" ], "projectNames": [ "project-1" ], "isAllowed": true, "isAudited": true, "policyId": 2001 } + }, + { + "name": "table: sales.orders, user: ds-user, access: select", + "request": { + "resource": { "elements": { "database": "sales", "table": "orders" } }, + "accessType": "select", "user": "ds-user", "userGroups": [] + }, + "result": { "datasets": [ 1 ], "projects": [ 1 ], "datasetNames": [ "dataset-1" ], "projectNames": [ "project-1" ], "isAllowed": true, "isAudited": true, "policyId": 2001 } + }, + { + "name": "table: finance.invoices, user: ds-user, access: select", + "request": { + "resource": { "elements": { "database": "finance", "table": "invoices" } }, + "accessType": "select", "user": "ds-user", "userGroups": [] + }, + "result": { "datasets": [ 1, 2 ], "projects": [ 1 ], "datasetNames": [ "dataset-1", "dataset-2" ], "projectNames": [ "project-1" ], "isAllowed": true, "isAudited": true, "policyId": 2001 } + }, + { + "name": "table: finance.payments, user: ds-user, access: select", + "request": { + "resource": { "elements": { "database": "finance", "table": "payments" } }, + "accessType": "select", "user": "ds-user", "userGroups": [] + }, + "result": { "datasets": [ 1, 2 ], "projects": [ 1 ], "datasetNames": [ "dataset-1", "dataset-2" ], "projectNames": [ "project-1" ], "isAllowed": true, "isAudited": true, "policyId": 2001 } + }, + { + "name": "table: shipping.shipments, user: ds-user, access: select", + "request": { + "resource": { "elements": { "database": "shipping", "table": "shipments" } }, + "accessType": "select", "user": "ds-user", "userGroups": [] + }, + "result": { "datasets": [ 2 ], "projects": [ 1 ], "datasetNames": [ "dataset-2" ], "projectNames": [ "project-1" ], "isAllowed": true, "isAudited": true, "policyId": 2002 } + }, + { + "name": "table: customers.contact_info, user: ds-user, access: select", + "request": { + "resource": { "elements": { "database": "customers", "table": "contact_info" } }, + "accessType": "select", "user": "ds-user", "userGroups": [] + }, + "result": { "datasets": [ 3 ], "projects": [ 2 ], "datasetNames": [ "dataset-3" ], "projectNames": [ "project-2" ], "isAllowed": true, "isAudited": true, "policyId": 2003 } + }, + { + "name": "table: operations.facilities, user: ds-user, access: select", + "request": { + "resource": { "elements": { "database": "operations", "table": "facilities" } }, + "accessType": "select", "user": "ds-user", "userGroups": [] + }, + "result": { "datasets": [ 4 ], "projects": null, "datasetNames": [ "dataset-4" ], "projectNames": null, "isAllowed": true, "isAudited": true, "policyId": 2004 } + }, + + + { + "name": "table: sales.prospects, user: proj-user, access: select", + "request": { + "resource": { "elements": { "database": "sales", "table": "prospects" } }, + "accessType": "select", "user": "proj-user", "userGroups": [] + }, + "result": { "datasets": [ 1 ], "projects": [ 1 ], "datasetNames": [ "dataset-1" ], "projectNames": [ "project-1" ], "isAllowed": true, "isAudited": true, "policyId": 3001 } + }, + { + "name": "table: sales.orders, user: proj-user, access: select", + "request": { + "resource": { "elements": { "database": "sales", "table": "orders" } }, + "accessType": "select", "user": "proj-user", "userGroups": [] + }, + "result": { "datasets": [ 1 ], "projects": [ 1 ], "datasetNames": [ "dataset-1" ], "projectNames": [ "project-1" ], "isAllowed": true, "isAudited": true, "policyId": 3001 } + }, + { + "name": "table: finance.invoices, user: proj-user, access: select", + "request": { + "resource": { "elements": { "database": "finance", "table": "invoices" } }, + "accessType": "select", "user": "proj-user", "userGroups": [] + }, + "result": { "datasets": [ 1, 2 ], "projects": [ 1 ], "datasetNames": [ "dataset-1", "dataset-2" ], "projectNames": [ "project-1" ], "isAllowed": true, "isAudited": true, "policyId": 3001 } + }, + { + "name": "table: finance.payments, user: proj-user, access: select", + "request": { + "resource": { "elements": { "database": "finance", "table": "payments" } }, + "accessType": "select", "user": "proj-user", "userGroups": [] + }, + "result": { "datasets": [ 1, 2 ], "projects": [ 1 ], "datasetNames": [ "dataset-1", "dataset-2" ], "projectNames": [ "project-1" ], "isAllowed": true, "isAudited": true, "policyId": 3001 } + }, + { + "name": "table: shipping.shipments, user: proj-user, access: select", + "request": { + "resource": { "elements": { "database": "shipping", "table": "shipments" } }, + "accessType": "select", "user": "proj-user", "userGroups": [] + }, + "result": { "datasets": [ 2 ], "projects": [ 1 ], "datasetNames": [ "dataset-2" ], "projectNames": [ "project-1" ], "isAllowed": true, "isAudited": true, "policyId": 3001 } + }, + { + "name": "table: customers.contact_info, user: proj-user, access: select", + "request": { + "resource": { "elements": { "database": "customers", "table": "contact_info" } }, + "accessType": "select", "user": "proj-user", "userGroups": [] + }, + "result": { "datasets": [ 3 ], "projects": [ 2 ], "datasetNames": [ "dataset-3" ], "projectNames": [ "project-2" ], "isAllowed": true, "isAudited": true, "policyId": 3002 } + }, + { + "name": "table: operations.facilities, user: proj-user, access: select", + "request": { + "resource": { "elements": { "database": "operations", "table": "facilities" } }, + "accessType": "select", "user": "proj-user", "userGroups": [] + }, + "result": { "datasets": [ 4 ], "projects": null, "datasetNames": [ "dataset-4" ], "projectNames": null, "isAllowed": false, "isAudited": true, "policyId": -1 } + }, + + + { + "name": "table: sales.prospects, user: scott, access: select", + "request": { + "resource": { "elements": { "database": "sales", "table": "prospects" } }, + "accessType": "select", "user": "scott", "userGroups": [] + }, + "result": { "datasets": [ 1 ], "projects": [ 1 ], "datasetNames": [ "dataset-1" ], "projectNames": [ "project-1" ], "isAllowed": false, "isAudited": true, "policyId": -1 } + }, + { + "name": "table: sales.orders, user: scott, access: select", + "request": { + "resource": { "elements": { "database": "sales", "table": "orders" } }, + "accessType": "select", "user": "scott", "userGroups": [] + }, + "result": { "datasets": [ 1 ], "projects": [ 1 ], "datasetNames": [ "dataset-1" ], "projectNames": [ "project-1" ], "isAllowed": false, "isAudited": true, "policyId": -1 } + }, + { + "name": "table: finance.invoices, user: scott, access: select", + "request": { + "resource": { "elements": { "database": "finance", "table": "invoices" } }, + "accessType": "select", "user": "scott", "userGroups": [] + }, + "result": { "datasets": [ 1, 2 ], "projects": [ 1 ], "datasetNames": [ "dataset-1", "dataset-2" ], "projectNames": [ "project-1" ], "isAllowed": false, "isAudited": true, "policyId": -1 } + }, + { + "name": "table: finance.payments, user: scott, access: select", + "request": { + "resource": { "elements": { "database": "finance", "table": "payments" } }, + "accessType": "select", "user": "scott", "userGroups": [] + }, + "result": { "datasets": [ 1, 2 ], "projects": [ 1 ], "datasetNames": [ "dataset-1", "dataset-2" ], "projectNames": [ "project-1" ], "isAllowed": false, "isAudited": true, "policyId": -1 } + }, + { + "name": "table: shipping.shipments, user: scott, access: select", + "request": { + "resource": { "elements": { "database": "shipping", "table": "shipments" } }, + "accessType": "select", "user": "scott", "userGroups": [] + }, + "result": { "datasets": [ 2 ], "projects": [ 1 ], "datasetNames": [ "dataset-2" ], "projectNames": [ "project-1" ], "isAllowed": false, "isAudited": true, "policyId": -1 } + }, + { + "name": "table: customers.contact_info, user: scott, access: select", + "request": { + "resource": { "elements": { "database": "customers", "table": "contact_info" } }, + "accessType": "select", "user": "scott", "userGroups": [] + }, + "result": { "datasets": [ 3 ], "projects": [ 2 ], "datasetNames": [ "dataset-3" ], "projectNames": [ "project-2" ], "isAllowed": false, "isAudited": true, "policyId": -1 } + }, + { + "name": "table: operations.facilities, user: scott, access: select", + "request": { + "resource": { "elements": { "database": "operations", "table": "facilities" } }, + "accessType": "select", "user": "scott", "userGroups": [] + }, + "result": { "datasets": [ 4 ], "projects": null, "datasetNames": [ "dataset-4" ], "projectNames": null, "isAllowed": false, "isAudited": true, "policyId": -1 } + }, + + + { + "name": "table: operations.facilities, user: scott, access: select", + "request": { + "resource": { "elements": { "database": "operations", "table": "facilities" } }, + "accessType": "select", "user": "scott", "userGroups": [] + }, + "result": { "datasets": [ 4 ], "projects": null, "datasetNames": [ "dataset-4" ], "projectNames": null, "isAllowed": false, "isAudited": true, "policyId": -1 } + }, + + { + "name": "table: operations.facilities, user: ds-user, access: update", + "request": { + "resource": { "elements": { "database": "operations", "table": "facilities" } }, + "accessType": "update", "user": "ds-user", "userGroups": [] + }, + "result": { "datasets": null, "projects": null, "datasetNames": null, "projectNames": null, "isAllowed": false, "isAudited": false, "policyId": -1 } + } + ] +} diff --git a/security-admin/src/main/java/org/apache/ranger/biz/GdsDBStore.java b/security-admin/src/main/java/org/apache/ranger/biz/GdsDBStore.java index c8dd235e9..f58943617 100755 --- a/security-admin/src/main/java/org/apache/ranger/biz/GdsDBStore.java +++ b/security-admin/src/main/java/org/apache/ranger/biz/GdsDBStore.java @@ -1922,6 +1922,7 @@ public class GdsDBStore extends AbstractGdsStore { SharedResourceInfo resourceInfo = new SharedResourceInfo(); resourceInfo.setId(resource.getId()); + resourceInfo.setName(resource.getName()); resourceInfo.setDataShareId(resource.getDataShareId()); resourceInfo.setResource(resource.getResource()); resourceInfo.setSubResource(resource.getSubResource()); @@ -1938,6 +1939,10 @@ public class GdsDBStore extends AbstractGdsStore { private void populateDataSharesInDataset(ServiceGdsInfo gdsInfo, SearchFilter filter) { for (RangerDataShareInDataset dshInDs : dataShareInDatasetService.searchDataShareInDatasets(filter).getList()) { + if (dshInDs.getStatus() != GdsShareStatus.ACTIVE) { + continue; + } + DataShareInDatasetInfo dshInDsInfo = new DataShareInDatasetInfo(); dshInDsInfo.setDatasetId(dshInDs.getDatasetId()); @@ -1952,6 +1957,10 @@ public class GdsDBStore extends AbstractGdsStore { private void populateDatasetsInProject(ServiceGdsInfo gdsInfo, SearchFilter filter) { for (RangerDatasetInProject dip : datasetInProjectService.searchDatasetInProjects(filter).getList()) { + if (dip.getStatus() != GdsShareStatus.ACTIVE) { + continue; + } + DatasetInProjectInfo dipInfo = new DatasetInProjectInfo(); dipInfo.setDatasetId(dip.getDatasetId()); diff --git a/security-admin/src/main/java/org/apache/ranger/service/RangerGdsSharedResourceService.java b/security-admin/src/main/java/org/apache/ranger/service/RangerGdsSharedResourceService.java index 4685bc4f3..b1b5a841a 100755 --- a/security-admin/src/main/java/org/apache/ranger/service/RangerGdsSharedResourceService.java +++ b/security-admin/src/main/java/org/apache/ranger/service/RangerGdsSharedResourceService.java @@ -216,7 +216,7 @@ public class RangerGdsSharedResourceService extends RangerGdsBaseModelService<XX xObj.setResource(JsonUtils.mapToJson(vObj.getResource())); xObj.setSubResource(JsonUtils.objectToJson(vObj.getSubResource())); xObj.setSubResourceType(vObj.getSubResourceType()); - xObj.setResourceSignature(new RangerPolicyResourceSignature(vObj.getResource()).getSignature()); + xObj.setResourceSignature(new RangerPolicyResourceSignature(vObj).getSignature()); xObj.setConditionExpr(vObj.getConditionExpr()); xObj.setAccessTypes(JsonUtils.objectToJson(vObj.getAccessTypes())); xObj.setRowFilter(JsonUtils.objectToJson(vObj.getRowFilter()));