diff --git a/repository/src/main/java/org/apache/atlas/authorizer/AuthorizerUtils.java b/repository/src/main/java/org/apache/atlas/authorizer/AuthorizerUtils.java index 1880252a60..111690499d 100644 --- a/repository/src/main/java/org/apache/atlas/authorizer/AuthorizerUtils.java +++ b/repository/src/main/java/org/apache/atlas/authorizer/AuthorizerUtils.java @@ -106,7 +106,11 @@ public static void verifyRelationshipAccess(AtlasPrivilege action, String relati if (!useAbacAuthorizer) { AtlasAuthorizationUtils.verifyAccess(new AtlasRelationshipAccessRequest(typeRegistry, action, relationShipType, endOneEntity, endTwoEntity)); } else { - if (action == AtlasPrivilege.RELATIONSHIP_ADD) { + NewAuthorizerUtils.verifyRelationshipAccessInMem(action, + relationShipType, + endOneEntity, + endTwoEntity); + /*if (action == AtlasPrivilege.RELATIONSHIP_ADD) { NewAuthorizerUtils.verifyRelationshipCreateAccess(AtlasPrivilege.RELATIONSHIP_ADD, relationShipType, endOneEntity, @@ -116,7 +120,7 @@ public static void verifyRelationshipAccess(AtlasPrivilege action, String relati relationShipType, endOneEntity, endTwoEntity); - } + }*/ } } } diff --git a/repository/src/main/java/org/apache/atlas/authorizer/NewAuthorizerUtils.java b/repository/src/main/java/org/apache/atlas/authorizer/NewAuthorizerUtils.java index 4602129b78..9f31d6eb3a 100644 --- a/repository/src/main/java/org/apache/atlas/authorizer/NewAuthorizerUtils.java +++ b/repository/src/main/java/org/apache/atlas/authorizer/NewAuthorizerUtils.java @@ -186,7 +186,7 @@ public static void verifyRelationshipAccess(AtlasPrivilege action, String relati } } - public static void verifyRelationshipCreateAccess(AtlasPrivilege action, String relationshipType, AtlasEntityHeader endOneEntity, AtlasEntityHeader endTwoEntity) throws AtlasBaseException { + public static void verifyRelationshipAccessInMem(AtlasPrivilege action, String relationshipType, AtlasEntityHeader endOneEntity, AtlasEntityHeader endTwoEntity) throws AtlasBaseException { AtlasPerfMetrics.MetricRecorder recorder = RequestContext.get().startMetricRecord("verifyAccess"); String userName = AuthorizerCommon.getCurrentUserName(); diff --git a/repository/src/main/java/org/apache/atlas/authorizer/authorizers/AuthorizerCommon.java b/repository/src/main/java/org/apache/atlas/authorizer/authorizers/AuthorizerCommon.java index 648ba1d196..1fe9932ffe 100644 --- a/repository/src/main/java/org/apache/atlas/authorizer/authorizers/AuthorizerCommon.java +++ b/repository/src/main/java/org/apache/atlas/authorizer/authorizers/AuthorizerCommon.java @@ -2,6 +2,7 @@ import org.apache.atlas.exception.AtlasBaseException; import org.apache.atlas.model.instance.AtlasEntity; +import org.apache.atlas.model.instance.AtlasEntityHeader; import org.apache.atlas.repository.graphdb.AtlasGraph; import org.apache.atlas.repository.graphdb.AtlasVertex; import org.apache.atlas.repository.store.graph.v2.EntityGraphRetriever; @@ -19,6 +20,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; @Component public class AuthorizerCommon { @@ -104,4 +106,42 @@ public static AtlasEntity toAtlasEntityHeaderWithClassifications(AtlasVertex ver //return new AtlasEntity(entityRetriever.toAtlasEntityHeaderWithClassifications(vertex)); return new AtlasEntity(entityRetriever.toAtlasEntity(vertex)); } + + public static boolean isResourceMatch(List policyValues, String actualValue) { + return isResourceMatch(policyValues, actualValue, false); + } + + public static boolean isResourceMatch(List policyValues, String actualValue, boolean replaceUser) { + if (!policyValues.contains("*")) { + if (replaceUser) { + return policyValues.stream().anyMatch(x -> actualValue.matches(x + .replace("{USER}", AuthorizerCommon.getCurrentUserName()) + .replace("*", ".*"))); + } else { + return policyValues.stream().anyMatch(x -> actualValue.matches(x.replace("*", ".*"))); + } + } + return true; + } + + public static boolean isResourceMatch(List policyValues, Set entityValues) { + if (!policyValues.contains("*")) { + return entityValues.stream().anyMatch(assetType -> policyValues.stream().anyMatch(policyAssetType -> assetType.matches(policyAssetType.replace("*", ".*")))); + } + return true; + } + + public static boolean isTagResourceMatch(List policyValues, AtlasEntityHeader entityHeader) { + if (!policyValues.contains(("*"))) { + if (entityHeader.getClassifications() == null || entityHeader.getClassifications().isEmpty()) { + //since entity does not have tags at all, it should not pass this evaluation + return false; + } + + List assetTags = entityHeader.getClassifications().stream().map(x -> x.getTypeName()).collect(Collectors.toList()); + + return assetTags.stream().anyMatch(assetTag -> policyValues.stream().anyMatch(policyAssetType -> assetTag.matches(policyAssetType.replace("*", ".*")))); + } + return true; + } } diff --git a/repository/src/main/java/org/apache/atlas/authorizer/authorizers/EntityAuthorizer.java b/repository/src/main/java/org/apache/atlas/authorizer/authorizers/EntityAuthorizer.java index dab85f7884..87474f37c5 100644 --- a/repository/src/main/java/org/apache/atlas/authorizer/authorizers/EntityAuthorizer.java +++ b/repository/src/main/java/org/apache/atlas/authorizer/authorizers/EntityAuthorizer.java @@ -10,6 +10,7 @@ import org.apache.atlas.exception.AtlasBaseException; import org.apache.atlas.model.instance.AtlasClassification; import org.apache.atlas.model.instance.AtlasEntity; +import org.apache.atlas.model.instance.AtlasEntityHeader; import org.apache.atlas.plugin.model.RangerPolicy; import org.apache.atlas.repository.graphdb.AtlasVertex; import org.apache.atlas.repository.graphdb.janus.AtlasElasticsearchQuery; @@ -35,6 +36,8 @@ import static org.apache.atlas.authorizer.NewAuthorizerUtils.POLICY_TYPE_ALLOW; import static org.apache.atlas.authorizer.NewAuthorizerUtils.POLICY_TYPE_DENY; import static org.apache.atlas.authorizer.authorizers.AuthorizerCommon.getMap; +import static org.apache.atlas.authorizer.authorizers.AuthorizerCommon.isResourceMatch; +import static org.apache.atlas.authorizer.authorizers.AuthorizerCommon.isTagResourceMatch; import static org.apache.atlas.repository.Constants.QUALIFIED_NAME; import static org.apache.atlas.repository.graphdb.janus.AtlasElasticsearchDatabase.getLowLevelClient; @@ -54,7 +57,7 @@ public static AccessResult isAccessAllowedInMemory(AtlasEntity entity, String ac return isAccessAllowedInMemory(entity, action, POLICY_TYPE_ALLOW); } - public static AccessResult isAccessAllowedInMemory(AtlasEntity entity, String action, String policyType) { + private static AccessResult isAccessAllowedInMemory(AtlasEntity entity, String action, String policyType) { AtlasPerfMetrics.MetricRecorder recorder = RequestContext.get().startMetricRecord("isAccessAllowedInMemory."+policyType); AccessResult result; @@ -74,7 +77,7 @@ public static AccessResult isAccessAllowedInMemory(AtlasEntity entity, String ac return result; } - public static AccessResult evaluateRangerPoliciesInMemory(List resourcePolicies, AtlasEntity entity) { + private static AccessResult evaluateRangerPoliciesInMemory(List resourcePolicies, AtlasEntity entity) { AtlasPerfMetrics.MetricRecorder recorder = RequestContext.get().startMetricRecord("validateResourcesForCreateEntityInMemory"); AccessResult result = new AccessResult(); @@ -116,31 +119,28 @@ public static boolean evaluateRangerPolicyInMemory(RangerPolicy rangerPolicy, At List values = resources.get(resource).getValues(); if ("entity-type".equals(resource)) { - boolean match = entityTypes.stream().anyMatch(assetType -> values.stream().anyMatch(policyAssetType -> assetType.matches(policyAssetType.replace("*", ".*")))); + if (!isResourceMatch(values, entityTypes)) { + resourcesMatched = false; + break; + } + + /*boolean match = entityTypes.stream().anyMatch(assetType -> values.stream().anyMatch(policyAssetType -> assetType.matches(policyAssetType.replace("*", ".*")))); if (!match) { resourcesMatched = false; break; - } + }*/ } if ("entity".equals(resource)) { - if (!values.contains(("*"))) { - String assetQualifiedName = (String) entity.getAttribute(QUALIFIED_NAME); - Optional match = values.stream().filter(x -> assetQualifiedName.matches(x - .replace("{USER}", AuthorizerCommon.getCurrentUserName()) - .replace("*", ".*"))) - .findFirst(); + String assetQualifiedName = (String) entity.getAttribute(QUALIFIED_NAME); - if (!match.isPresent()) { - resourcesMatched = false; - break; - } + if (!isResourceMatch(values, assetQualifiedName, true)) { + resourcesMatched = false; + break; } - } - if ("entity-business-metadata".equals(resource)) { - if (!values.contains(("*"))) { + /*if (!values.contains(("*"))) { String assetQualifiedName = (String) entity.getAttribute(QUALIFIED_NAME); Optional match = values.stream().filter(x -> assetQualifiedName.matches(x .replace("{USER}", AuthorizerCommon.getCurrentUserName()) @@ -151,12 +151,17 @@ public static boolean evaluateRangerPolicyInMemory(RangerPolicy rangerPolicy, At resourcesMatched = false; break; } - } + }*/ } //for tag based policy if ("tag".equals(resource)) { - if (!values.contains(("*"))) { + if (!isTagResourceMatch(values, new AtlasEntityHeader(entity))) { + resourcesMatched = false; + break; + } + + /*if (!values.contains(("*"))) { if (entity.getClassifications() == null || entity.getClassifications().isEmpty()) { //since entity does not have tags at all, it should not pass this evaluation resourcesMatched = false; @@ -173,7 +178,7 @@ public static boolean evaluateRangerPolicyInMemory(RangerPolicy rangerPolicy, At break; } } - } + }*/ } } @@ -186,7 +191,7 @@ public static boolean evaluateRangerPolicyInMemory(RangerPolicy rangerPolicy, At return false; } - public static AccessResult evaluateABACPoliciesInMemory(List abacPolicies, AtlasEntity entity) { + private static AccessResult evaluateABACPoliciesInMemory(List abacPolicies, AtlasEntity entity) { AccessResult result = new AccessResult(); AtlasVertex vertex = AtlasGraphUtilsV2.findByGuid(entity.getGuid()); @@ -432,7 +437,7 @@ private static AccessResult runESQueryAndEvaluateAccess(Map dsl) return result; } - public static Map getElasticsearchDSL(String persona, String purpose, + private static Map getElasticsearchDSL(String persona, String purpose, boolean requestMatchedPolicyId, List actions) { AtlasPerfMetrics.MetricRecorder recorder = RequestContext.get().startMetricRecord("EntityAuthorizer.getElasticsearchDSL"); Map dsl = ListAuthorizer.getElasticsearchDSLForPolicyType(persona, purpose, actions, requestMatchedPolicyId, null); diff --git a/repository/src/main/java/org/apache/atlas/authorizer/authorizers/ListAuthorizer.java b/repository/src/main/java/org/apache/atlas/authorizer/authorizers/ListAuthorizer.java index 227a09aa52..64f845a76b 100644 --- a/repository/src/main/java/org/apache/atlas/authorizer/authorizers/ListAuthorizer.java +++ b/repository/src/main/java/org/apache/atlas/authorizer/authorizers/ListAuthorizer.java @@ -103,7 +103,7 @@ public static Map getElasticsearchDSLForPolicyType(String person return getMap("bool", boolClause); } - public static List> getDSLForResourcePolicies(List policies) { + private static List> getDSLForResourcePolicies(List policies) { // To reduce the number of clauses List combinedEntities = new ArrayList<>(); diff --git a/repository/src/main/java/org/apache/atlas/authorizer/authorizers/RelationshipAuthorizer.java b/repository/src/main/java/org/apache/atlas/authorizer/authorizers/RelationshipAuthorizer.java index eb67386740..f85e6e8bf4 100644 --- a/repository/src/main/java/org/apache/atlas/authorizer/authorizers/RelationshipAuthorizer.java +++ b/repository/src/main/java/org/apache/atlas/authorizer/authorizers/RelationshipAuthorizer.java @@ -38,6 +38,8 @@ import static org.apache.atlas.authorizer.NewAuthorizerUtils.POLICY_TYPE_ALLOW; import static org.apache.atlas.authorizer.NewAuthorizerUtils.POLICY_TYPE_DENY; import static org.apache.atlas.authorizer.authorizers.AuthorizerCommon.getMap; +import static org.apache.atlas.authorizer.authorizers.AuthorizerCommon.isResourceMatch; +import static org.apache.atlas.authorizer.authorizers.AuthorizerCommon.isTagResourceMatch; import static org.apache.atlas.authorizer.authorizers.EntityAuthorizer.validateFilterCriteriaWithEntity; import static org.apache.atlas.authorizer.authorizers.ListAuthorizer.getDSLForResources; import static org.apache.atlas.authorizer.authorizers.ListAuthorizer.getPolicySuffix; @@ -65,7 +67,7 @@ public static AccessResult isAccessAllowedInMemory(String action, String relatio return checkRelationshipAccessAllowedInMemory(action, relationshipType, endOneEntity, endTwoEntity, POLICY_TYPE_ALLOW); } - public static AccessResult checkRelationshipAccessAllowedInMemory(String action, String relationshipType, AtlasEntityHeader endOneEntity, + private static AccessResult checkRelationshipAccessAllowedInMemory(String action, String relationshipType, AtlasEntityHeader endOneEntity, AtlasEntityHeader endTwoEntity, String policyType) throws AtlasBaseException { //Relationship add, update, remove access check in memory AtlasPerfMetrics.MetricRecorder recorder = RequestContext.get().startMetricRecord("checkRelationshipAccessAllowedInMemory."+policyType); @@ -120,7 +122,7 @@ public static AccessResult checkRelationshipAccessAllowedInMemory(String action, } } - public static AccessResult evaluateRangerPoliciesInMemory(List rangerPolicies, String relationshipType, + private static AccessResult evaluateRangerPoliciesInMemory(List rangerPolicies, String relationshipType, AtlasEntityHeader endOneEntity, AtlasEntityHeader endTwoEntity) { AtlasPerfMetrics.MetricRecorder recorder = RequestContext.get().startMetricRecord("ListAuthorizer.evaluateRangerPoliciesInMemory"); AccessResult result = new AccessResult(); @@ -181,29 +183,43 @@ public static boolean evaluateRangerPolicyInMemory(RangerPolicy rangerPolicy, St } if ("end-one-entity-type".equals(resource)) { - if (!values.contains(("*"))) { + if (!isResourceMatch(values, endOneEntityTypes)) { + resourcesMatched = false; + break; + } + /*if (!values.contains(("*"))) { boolean match = endOneEntityTypes.stream().anyMatch(assetType -> values.stream().anyMatch(policyAssetType -> assetType.matches(policyAssetType.replace("*", ".*")))); if (!match) { resourcesMatched = false; break; } - } + }*/ } if ("end-two-entity-type".equals(resource)) { - if (!values.contains(("*"))) { + if (!isResourceMatch(values, endTwoEntityTypes)) { + resourcesMatched = false; + break; + } + /*if (!values.contains(("*"))) { boolean match = endTwoEntityTypes.stream().anyMatch(assetType -> values.stream().anyMatch(policyAssetType -> assetType.matches(policyAssetType.replace("*", ".*")))); if (!match) { resourcesMatched = false; break; } - } + }*/ } if ("end-one-entity".equals(resource)) { - if (!values.contains(("*"))) { + String assetQualifiedName = (String) endOneEntity.getAttribute(QUALIFIED_NAME); + + if (!isResourceMatch(values, assetQualifiedName, true)) { + resourcesMatched = false; + break; + } + /*if (!values.contains(("*"))) { String assetQualifiedName = (String) endOneEntity.getAttribute(QUALIFIED_NAME); Optional match = values.stream().filter(x -> assetQualifiedName.matches(x .replace("{USER}", AuthorizerCommon.getCurrentUserName()) @@ -214,11 +230,17 @@ public static boolean evaluateRangerPolicyInMemory(RangerPolicy rangerPolicy, St resourcesMatched = false; break; } - } + }*/ } if ("end-two-entity".equals(resource)) { - if (!values.contains(("*"))) { + String assetQualifiedName = (String) endTwoEntity.getAttribute(QUALIFIED_NAME); + + if (!isResourceMatch(values, assetQualifiedName, true)) { + resourcesMatched = false; + break; + } + /*if (!values.contains(("*"))) { String assetQualifiedName = (String) endTwoEntity.getAttribute(QUALIFIED_NAME); Optional match = values.stream().filter(x -> assetQualifiedName.matches(x .replace("{USER}", AuthorizerCommon.getCurrentUserName()) @@ -229,12 +251,16 @@ public static boolean evaluateRangerPolicyInMemory(RangerPolicy rangerPolicy, St resourcesMatched = false; break; } - } + }*/ } - //for tag based policy if ("end-one-entity-classification".equals(resource)) { - if (!values.contains(("*"))) { + if (!isTagResourceMatch(values, endOneEntity)) { + resourcesMatched = false; + break; + } + + /*if (!values.contains(("*"))) { if (endOneEntity.getClassifications() == null || endOneEntity.getClassifications().isEmpty()) { //since entity does not have tags at all, it should not pass this evaluation resourcesMatched = false; @@ -249,11 +275,16 @@ public static boolean evaluateRangerPolicyInMemory(RangerPolicy rangerPolicy, St resourcesMatched = false; break; } - } + }*/ } if ("end-two-entity-classification".equals(resource)) { - if (!values.contains(("*"))) { + if (!isTagResourceMatch(values, endTwoEntity)) { + resourcesMatched = false; + break; + } + + /*if (!values.contains(("*"))) { if (endTwoEntity.getClassifications() == null || endTwoEntity.getClassifications().isEmpty()) { //since entity does not have tags at all, it should not pass this evaluation resourcesMatched = false; @@ -268,7 +299,7 @@ public static boolean evaluateRangerPolicyInMemory(RangerPolicy rangerPolicy, St resourcesMatched = false; break; } - } + }*/ } } @@ -286,12 +317,12 @@ public static AccessResult isRelationshipAccessAllowed(String action, AtlasEntit AccessResult result = new AccessResult(); //Relationship update, remove access check with ES query - if (endOneEntity == null || endTwoEntity == null || endOneEntity.getGuid() == null || endTwoEntity.getGuid() == null ) { + if (endOneEntity == null || endTwoEntity == null) { return result; } try { - Map dsl = getElasticsearchDSLForRelationshipActions(Arrays.asList(action), endOneEntity.getGuid(), endTwoEntity.getGuid()); + Map dsl = getElasticsearchDSLForRelationshipActions(Arrays.asList(action), endOneEntity, endTwoEntity); ObjectMapper mapper = new ObjectMapper(); String dslString = mapper.writeValueAsString(dsl); RestClient restClient = getLowLevelClient(); @@ -356,7 +387,8 @@ public static AccessResult isRelationshipAccessAllowed(String action, AtlasEntit return result; } - public static Map getElasticsearchDSLForRelationshipActions(List actions, String endOneGuid, String endTwoGuid) throws JsonProcessingException { + private static Map getElasticsearchDSLForRelationshipActions(List actions, AtlasEntityHeader endOneEntity, + AtlasEntityHeader endTwoEntity) throws JsonProcessingException { AtlasPerfMetrics.MetricRecorder recorder = RequestContext.get().startMetricRecord("RelationshipAuthorizer.getElasticsearchDSLForRelationshipActions"); List> policiesClauses = new ArrayList<>(); @@ -400,8 +432,25 @@ public static Map getElasticsearchDSLForRelationshipActions(List Map entitiesBoolClause = new HashMap<>(); List> entityClauses = new ArrayList<>(); - entityClauses.add(getMap("term", getMap("__guid", endOneGuid))); - entityClauses.add(getMap("term", getMap("__guid", endTwoGuid))); + + if (endOneEntity.getGuid() != null && endTwoEntity.getGuid() != null) { + entityClauses.add(getMap("term", getMap("__guid", endOneEntity.getGuid()))); + entityClauses.add(getMap("term", getMap("__guid", endTwoEntity.getGuid()))); + } else { + //In case of evaluator API, qualifiedName can be * leading to not finding entity in store hence null GUIDs + //Use typeName & qualifiedName to form entity clauses + List> entityOneClauses = new ArrayList<>(); + entityOneClauses.add(getMap("wildcard", getMap("__typeName.keyword", endOneEntity.getTypeName()))); + entityOneClauses.add(getMap("wildcard", getMap("qualifiedName", endOneEntity.getAttribute(QUALIFIED_NAME)))); + + List> entityTwoClauses = new ArrayList<>(); + entityTwoClauses.add(getMap("wildcard", getMap("__typeName.keyword", endTwoEntity.getTypeName()))); + entityTwoClauses.add(getMap("wildcard", getMap("qualifiedName", endTwoEntity.getAttribute(QUALIFIED_NAME)))); + + entityClauses.add(getMap("bool", getMap("must", entityOneClauses))); + entityClauses.add(getMap("bool", getMap("must", entityTwoClauses))); + } + entitiesBoolClause.put("should", entityClauses); entitiesBoolClause.put("minimum_should_match", 1); clauses.add(getMap("bool", entitiesBoolClause));