From 9697ee6271364ee10d1c3fe03049f672d5ae9945 Mon Sep 17 00:00:00 2001 From: IceS2 Date: Wed, 8 Jan 2025 11:16:56 +0100 Subject: [PATCH] Add Match Any Certification to Policies (#19271) --- .../security/RBACConditionEvaluator.java | 20 ++++++++++ .../policyevaluator/RuleEvaluator.java | 39 +++++++++++++++++++ .../policyevaluator/RuleEvaluatorTest.java | 31 +++++++++++++++ 3 files changed, 90 insertions(+) diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/search/security/RBACConditionEvaluator.java b/openmetadata-service/src/main/java/org/openmetadata/service/search/security/RBACConditionEvaluator.java index f2f66a008e9..8f8cc8a1816 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/search/security/RBACConditionEvaluator.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/search/security/RBACConditionEvaluator.java @@ -200,6 +200,10 @@ public class RBACConditionEvaluator { String methodName = methodRef.getName(); switch (methodName) { + case "matchAnyCertification" -> { + List certificationLabels = extractMethodArguments(methodRef); + matchAnyCertification(certificationLabels, collector); + } case "matchAnyTag" -> { List tags = extractMethodArguments(methodRef); matchAnyTag(tags, collector); @@ -259,6 +263,22 @@ public class RBACConditionEvaluator { } } + public void matchAnyCertification( + List certificationLabels, ConditionCollector collector) { + List certificationQueries = new ArrayList<>(); + for (String certificationLabel : certificationLabels) { + certificationQueries.add( + queryBuilderFactory.termQuery("certification.tagLabel.tagFQN", certificationLabel)); + } + OMQueryBuilder certificationQueriesCombined; + if (certificationQueries.size() == 1) { + certificationQueriesCombined = certificationQueries.get(0); + } else { + certificationQueriesCombined = queryBuilderFactory.boolQuery().should(certificationQueries); + } + collector.addMust(certificationQueriesCombined); + } + public void isOwner(User user, ConditionCollector collector) { List ownerQueries = new ArrayList<>(); ownerQueries.add(queryBuilderFactory.termQuery("owners.id", user.getId().toString())); diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/security/policyevaluator/RuleEvaluator.java b/openmetadata-service/src/main/java/org/openmetadata/service/security/policyevaluator/RuleEvaluator.java index a1e6dcb627b..4c5bff12f9c 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/security/policyevaluator/RuleEvaluator.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/security/policyevaluator/RuleEvaluator.java @@ -5,8 +5,10 @@ import static org.openmetadata.schema.type.Include.NON_DELETED; import java.util.Arrays; import java.util.List; +import java.util.Optional; import lombok.extern.slf4j.Slf4j; import org.openmetadata.schema.Function; +import org.openmetadata.schema.type.AssetCertification; import org.openmetadata.schema.type.TagLabel; import org.openmetadata.service.Entity; import org.openmetadata.service.security.policyevaluator.SubjectContext.PolicyContext; @@ -158,6 +160,43 @@ public class RuleEvaluator { return false; } + @Function( + name = "matchAnyCertification", + input = "List of comma separated Certification fully qualified names", + description = + "Returns true if the entity being accessed has any of the Certification given as input", + examples = {"matchAnyCertification('Certification.Silver', 'Certification.Gold')"}) + @SuppressWarnings("unused") // Used in SpelExpressions + public boolean matchAnyCertification(String... tagFQNs) { + if (expressionValidation) { + for (String tagFqn : tagFQNs) { + Entity.getEntityReferenceByName(Entity.TAG, tagFqn, NON_DELETED); // Validate tag exists + } + return false; + } + if (resourceContext == null) { + return false; + } + + Optional oCertification = + Optional.ofNullable(resourceContext.getEntity().getCertification()); + + if (oCertification.isEmpty()) { + LOG.debug( + "matchAnyCertification {} resourceCertification is null.", Arrays.toString(tagFQNs)); + return false; + } else { + AssetCertification certification = oCertification.get(); + + LOG.debug( + "matchAnyCertification {} resourceCertification {}", + Arrays.toString(tagFQNs), + certification.getTagLabel().getTagFQN()); + return Arrays.stream(tagFQNs) + .anyMatch(tagFQN -> tagFQN.equals(certification.getTagLabel().getTagFQN())); + } + } + @Function( name = "matchTeam", input = "None", diff --git a/openmetadata-service/src/test/java/org/openmetadata/service/security/policyevaluator/RuleEvaluatorTest.java b/openmetadata-service/src/test/java/org/openmetadata/service/security/policyevaluator/RuleEvaluatorTest.java index 40c9397316d..7f930c93fa9 100644 --- a/openmetadata-service/src/test/java/org/openmetadata/service/security/policyevaluator/RuleEvaluatorTest.java +++ b/openmetadata-service/src/test/java/org/openmetadata/service/security/policyevaluator/RuleEvaluatorTest.java @@ -25,6 +25,7 @@ import org.openmetadata.schema.entity.data.Table; import org.openmetadata.schema.entity.teams.Role; import org.openmetadata.schema.entity.teams.Team; import org.openmetadata.schema.entity.teams.User; +import org.openmetadata.schema.type.AssetCertification; import org.openmetadata.schema.type.EntityReference; import org.openmetadata.schema.type.Include; import org.openmetadata.schema.type.TagLabel; @@ -187,6 +188,36 @@ class RuleEvaluatorTest { assertTrue(evaluateExpression("!matchAnyTag('tag4')")); } + @Test + void test_matchAnyCertification() { + // Certification is not Present + assertFalse(evaluateExpression("!matchAnyCertification('Certification.Gold')")); + assertFalse( + evaluateExpression("!matchAnyCertification('Certification.Gold', 'Certification.Silver')")); + assertFalse(evaluateExpression("matchAnyCertification('Certification.Bronze')")); + + table.withCertification( + new AssetCertification().withTagLabel(new TagLabel().withTagFQN("Certification.Gold"))); + + // Certification is present + assertTrue( + evaluateExpression("matchAnyCertification('Certification.Gold', 'Certification.Silver')")); + assertFalse( + evaluateExpression("!matchAnyCertification('Certification.Gold', 'Certification.Silver')")); + assertTrue(evaluateExpression("matchAnyCertification('Certification.Gold')")); + assertFalse(evaluateExpression("!matchAnyCertification('Certification.Gold')")); + + // Certification is different + assertFalse( + evaluateExpression( + "matchAnyCertification('Certification.Bronze', 'Certification.Silver')")); + assertTrue( + evaluateExpression( + "!matchAnyCertification('Certification.Bronze', 'Certification.Silver')")); + assertFalse(evaluateExpression("matchAnyCertification('Certification.Bronze')")); + assertTrue(evaluateExpression("!matchAnyCertification('Certification.Bronze')")); + } + @Test void test_matchTeam() { // Create a team hierarchy