mirror of
				https://github.com/open-metadata/OpenMetadata.git
				synced 2025-10-31 10:39:30 +00:00 
			
		
		
		
	
							parent
							
								
									55fde2d775
								
							
						
					
					
						commit
						b8e0ae489a
					
				| @ -24,7 +24,6 @@ import lombok.extern.slf4j.Slf4j; | |||||||
| import org.openmetadata.schema.entity.events.EventFilterRule; | import org.openmetadata.schema.entity.events.EventFilterRule; | ||||||
| import org.openmetadata.schema.entity.events.EventSubscription; | import org.openmetadata.schema.entity.events.EventSubscription; | ||||||
| import org.openmetadata.schema.entity.events.SubscriptionStatus; | import org.openmetadata.schema.entity.events.SubscriptionStatus; | ||||||
| import org.openmetadata.schema.type.EntityReference; |  | ||||||
| import org.openmetadata.service.Entity; | import org.openmetadata.service.Entity; | ||||||
| import org.openmetadata.service.events.EventPubSub; | import org.openmetadata.service.events.EventPubSub; | ||||||
| import org.openmetadata.service.events.subscription.AlertUtil; | import org.openmetadata.service.events.subscription.AlertUtil; | ||||||
| @ -75,18 +74,11 @@ public class EventSubscriptionRepository extends EntityRepository<EventSubscript | |||||||
| 
 | 
 | ||||||
|   @Override |   @Override | ||||||
|   public void storeEntity(EventSubscription entity, boolean update) throws IOException { |   public void storeEntity(EventSubscription entity, boolean update) throws IOException { | ||||||
|     EntityReference owner = entity.getOwner(); |  | ||||||
|     // Don't store owner, database, href and tags as JSON. Build it on the fly based on relationships |  | ||||||
|     entity.withOwner(null).withHref(null); |  | ||||||
|     store(entity, update); |     store(entity, update); | ||||||
| 
 |  | ||||||
|     // Restore the relationships |  | ||||||
|     entity.withOwner(owner); |  | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   @Override |   @Override | ||||||
|   public void storeRelationships(EventSubscription entity) { |   public void storeRelationships(EventSubscription entity) { | ||||||
|     // store owner |  | ||||||
|     storeOwner(entity, entity.getOwner()); |     storeOwner(entity, entity.getOwner()); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -13,7 +13,6 @@ | |||||||
| 
 | 
 | ||||||
| package org.openmetadata.service.jdbi3; | package org.openmetadata.service.jdbi3; | ||||||
| 
 | 
 | ||||||
| import static org.openmetadata.common.utils.CommonUtil.listOf; |  | ||||||
| import static org.openmetadata.common.utils.CommonUtil.listOrEmpty; | import static org.openmetadata.common.utils.CommonUtil.listOrEmpty; | ||||||
| import static org.openmetadata.common.utils.CommonUtil.nullOrEmpty; | import static org.openmetadata.common.utils.CommonUtil.nullOrEmpty; | ||||||
| import static org.openmetadata.csv.CsvUtil.addEntityReferences; | import static org.openmetadata.csv.CsvUtil.addEntityReferences; | ||||||
| @ -601,7 +600,7 @@ public class TeamRepository extends EntityRepository<Team> { | |||||||
|           continue; // Parent is being created by CSV import |           continue; // Parent is being created by CSV import | ||||||
|         } |         } | ||||||
|         // Else the parent should already exist |         // Else the parent should already exist | ||||||
|         if (!SubjectCache.getInstance().isInTeam(team.getName(), listOf(parentRef))) { |         if (!SubjectCache.getInstance().isInTeam(team.getName(), parentRef)) { | ||||||
|           importFailure(printer, invalidTeam(4, team.getName(), importedTeam.getName(), parentRef.getName()), record); |           importFailure(printer, invalidTeam(4, team.getName(), importedTeam.getName(), parentRef.getName()), record); | ||||||
|           processRecord = false; |           processRecord = false; | ||||||
|         } |         } | ||||||
|  | |||||||
| @ -13,7 +13,6 @@ | |||||||
| 
 | 
 | ||||||
| package org.openmetadata.service.jdbi3; | package org.openmetadata.service.jdbi3; | ||||||
| 
 | 
 | ||||||
| import static org.openmetadata.common.utils.CommonUtil.listOf; |  | ||||||
| import static org.openmetadata.common.utils.CommonUtil.listOrEmpty; | import static org.openmetadata.common.utils.CommonUtil.listOrEmpty; | ||||||
| import static org.openmetadata.common.utils.CommonUtil.nullOrEmpty; | import static org.openmetadata.common.utils.CommonUtil.nullOrEmpty; | ||||||
| import static org.openmetadata.csv.CsvUtil.addEntityReferences; | import static org.openmetadata.csv.CsvUtil.addEntityReferences; | ||||||
| @ -401,7 +400,7 @@ public class UserRepository extends EntityRepository<User> { | |||||||
|           continue; // Team is same as the team to which CSV is being imported, then it is in the same hierarchy |           continue; // Team is same as the team to which CSV is being imported, then it is in the same hierarchy | ||||||
|         } |         } | ||||||
|         // Else the parent should already exist |         // Else the parent should already exist | ||||||
|         if (!SubjectCache.getInstance().isInTeam(team.getName(), listOf(teamRef))) { |         if (!SubjectCache.getInstance().isInTeam(team.getName(), teamRef)) { | ||||||
|           importFailure(printer, invalidTeam(6, team.getName(), user, teamRef.getName()), record); |           importFailure(printer, invalidTeam(6, team.getName(), user, teamRef.getName()), record); | ||||||
|           processRecord = false; |           processRecord = false; | ||||||
|         } |         } | ||||||
|  | |||||||
| @ -61,12 +61,6 @@ public final class CollectionRegistry { | |||||||
|   /** Map of class name to list of functions exposed for writing conditions */ |   /** Map of class name to list of functions exposed for writing conditions */ | ||||||
|   private final Map<Class<?>, List<org.openmetadata.schema.type.Function>> functionMap = new ConcurrentHashMap<>(); |   private final Map<Class<?>, List<org.openmetadata.schema.type.Function>> functionMap = new ConcurrentHashMap<>(); | ||||||
| 
 | 
 | ||||||
|   /** |  | ||||||
|    * Some functions are used for capturing resource based rules where policies are applied based on resource being |  | ||||||
|    * accessed and team hierarchy the resource belongs to instead of the subject. |  | ||||||
|    */ |  | ||||||
|   @Getter private final List<String> resourceBasedFunctions = new ArrayList<>(); |  | ||||||
| 
 |  | ||||||
|   /** Resources used only for testing */ |   /** Resources used only for testing */ | ||||||
|   @VisibleForTesting private final List<Object> testResources = new ArrayList<>(); |   @VisibleForTesting private final List<Object> testResources = new ArrayList<>(); | ||||||
| 
 | 
 | ||||||
| @ -137,10 +131,6 @@ public final class CollectionRegistry { | |||||||
|               .withParameterInputType(annotation.paramInputType()); |               .withParameterInputType(annotation.paramInputType()); | ||||||
|       functionList.add(function); |       functionList.add(function); | ||||||
|       functionList.sort(Comparator.comparing(org.openmetadata.schema.type.Function::getName)); |       functionList.sort(Comparator.comparing(org.openmetadata.schema.type.Function::getName)); | ||||||
| 
 |  | ||||||
|       if (annotation.resourceBased()) { |  | ||||||
|         resourceBasedFunctions.add(annotation.name()); |  | ||||||
|       } |  | ||||||
|       LOG.info("Initialized for {} function {}\n", method.getDeclaringClass().getSimpleName(), function); |       LOG.info("Initialized for {} function {}\n", method.getDeclaringClass().getSimpleName(), function); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  | |||||||
| @ -7,7 +7,6 @@ import com.fasterxml.jackson.annotation.JsonIgnore; | |||||||
| import java.util.Iterator; | import java.util.Iterator; | ||||||
| import java.util.List; | import java.util.List; | ||||||
| import java.util.Map; | import java.util.Map; | ||||||
| import lombok.Getter; |  | ||||||
| import lombok.extern.slf4j.Slf4j; | import lombok.extern.slf4j.Slf4j; | ||||||
| import org.openmetadata.schema.entity.policies.accessControl.Rule; | import org.openmetadata.schema.entity.policies.accessControl.Rule; | ||||||
| import org.openmetadata.schema.type.MetadataOperation; | import org.openmetadata.schema.type.MetadataOperation; | ||||||
| @ -15,7 +14,6 @@ import org.openmetadata.schema.type.Permission; | |||||||
| import org.openmetadata.schema.type.Permission.Access; | import org.openmetadata.schema.type.Permission.Access; | ||||||
| import org.openmetadata.schema.type.ResourcePermission; | import org.openmetadata.schema.type.ResourcePermission; | ||||||
| import org.openmetadata.service.exception.CatalogExceptionMessage; | import org.openmetadata.service.exception.CatalogExceptionMessage; | ||||||
| import org.openmetadata.service.resources.CollectionRegistry; |  | ||||||
| import org.openmetadata.service.security.AuthorizationException; | import org.openmetadata.service.security.AuthorizationException; | ||||||
| import org.openmetadata.service.security.policyevaluator.SubjectContext.PolicyContext; | import org.openmetadata.service.security.policyevaluator.SubjectContext.PolicyContext; | ||||||
| import org.springframework.expression.Expression; | import org.springframework.expression.Expression; | ||||||
| @ -27,7 +25,6 @@ import org.springframework.expression.spel.support.StandardEvaluationContext; | |||||||
| public class CompiledRule extends Rule { | public class CompiledRule extends Rule { | ||||||
|   private static final SpelExpressionParser EXPRESSION_PARSER = new SpelExpressionParser(); |   private static final SpelExpressionParser EXPRESSION_PARSER = new SpelExpressionParser(); | ||||||
|   @JsonIgnore private Expression expression; |   @JsonIgnore private Expression expression; | ||||||
|   @JsonIgnore @Getter private boolean resourceBased = false; |  | ||||||
| 
 | 
 | ||||||
|   public CompiledRule(Rule rule) { |   public CompiledRule(Rule rule) { | ||||||
|     super(); |     super(); | ||||||
| @ -72,13 +69,6 @@ public class CompiledRule extends Rule { | |||||||
|     } |     } | ||||||
|     if (expression == null) { |     if (expression == null) { | ||||||
|       expression = parseExpression(getCondition()); |       expression = parseExpression(getCondition()); | ||||||
|       List<String> resourceBasedFunctions = CollectionRegistry.getInstance().getResourceBasedFunctions(); |  | ||||||
|       for (String function : resourceBasedFunctions) { |  | ||||||
|         if (getCondition().contains(function)) { |  | ||||||
|           resourceBased = true; |  | ||||||
|           break; |  | ||||||
|         } |  | ||||||
|       } |  | ||||||
|     } |     } | ||||||
|     return expression; |     return expression; | ||||||
|   } |   } | ||||||
|  | |||||||
| @ -50,7 +50,7 @@ public class PolicyCache { | |||||||
|   public static void initialize() { |   public static void initialize() { | ||||||
|     if (!INITIALIZED) { |     if (!INITIALIZED) { | ||||||
|       POLICY_CACHE = |       POLICY_CACHE = | ||||||
|           CacheBuilder.newBuilder().maximumSize(100).expireAfterWrite(3, TimeUnit.MINUTES).build(new PolicyLoader()); |           CacheBuilder.newBuilder().maximumSize(1000).expireAfterWrite(3, TimeUnit.MINUTES).build(new PolicyLoader()); | ||||||
|       POLICY_REPOSITORY = (PolicyRepository) Entity.getEntityRepository(Entity.POLICY); |       POLICY_REPOSITORY = (PolicyRepository) Entity.getEntityRepository(Entity.POLICY); | ||||||
|       FIELDS = POLICY_REPOSITORY.getFields("rules"); |       FIELDS = POLICY_REPOSITORY.getFields("rules"); | ||||||
|       INITIALIZED = true; |       INITIALIZED = true; | ||||||
|  | |||||||
| @ -63,18 +63,12 @@ public class PolicyEvaluator { | |||||||
|       @NonNull ResourceContextInterface resourceContext, |       @NonNull ResourceContextInterface resourceContext, | ||||||
|       @NonNull OperationContext operationContext) |       @NonNull OperationContext operationContext) | ||||||
|       throws IOException { |       throws IOException { | ||||||
|     // First run through all the DENY policies based on the user |     // First run through all the DENY policies | ||||||
|     evaluateDenySubjectPolicies(subjectContext, resourceContext, operationContext); |     evaluateDenySubjectPolicies(subjectContext, resourceContext, operationContext); | ||||||
| 
 | 
 | ||||||
|     // Next run through all the DENY policies based on the resource |  | ||||||
|     evaluateDenyResourcePolicies(subjectContext, resourceContext, operationContext); |  | ||||||
| 
 |  | ||||||
|     // Next run through all the ALLOW policies based on the user |     // Next run through all the ALLOW policies based on the user | ||||||
|     evaluateAllowSubjectPolicies(subjectContext, resourceContext, operationContext); |     evaluateAllowSubjectPolicies(subjectContext, resourceContext, operationContext); | ||||||
| 
 | 
 | ||||||
|     // Next run through all the ALLOW policies based on the resource |  | ||||||
|     evaluateAllowResourcePolicies(subjectContext, resourceContext, operationContext); |  | ||||||
| 
 |  | ||||||
|     if (!operationContext.getOperations().isEmpty()) { // Some operations have not been allowed |     if (!operationContext.getOperations().isEmpty()) { // Some operations have not been allowed | ||||||
|       throw new AuthorizationException( |       throw new AuthorizationException( | ||||||
|           CatalogExceptionMessage.permissionNotAllowed( |           CatalogExceptionMessage.permissionNotAllowed( | ||||||
| @ -83,33 +77,17 @@ public class PolicyEvaluator { | |||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   private static void evaluateDenySubjectPolicies( |   private static void evaluateDenySubjectPolicies( | ||||||
|       SubjectContext subjectContext, ResourceContextInterface resourceContext, OperationContext operationContext) { |       SubjectContext subjectContext, ResourceContextInterface resourceContext, OperationContext operationContext) | ||||||
|     evaluatePolicies(subjectContext.getPolicies(), subjectContext, resourceContext, operationContext, true, false); |       throws IOException { | ||||||
|  |     Iterator<PolicyContext> policyIterator = subjectContext.getPolicies(resourceContext.getOwner()); | ||||||
|  |     evaluatePolicies(policyIterator, subjectContext, resourceContext, operationContext, true); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   private static void evaluateAllowSubjectPolicies( |   private static void evaluateAllowSubjectPolicies( | ||||||
|       SubjectContext subjectContext, ResourceContextInterface resourceContext, OperationContext operationContext) { |  | ||||||
|     evaluatePolicies(subjectContext.getPolicies(), subjectContext, resourceContext, operationContext, false, false); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   private static void evaluateDenyResourcePolicies( |  | ||||||
|       SubjectContext subjectContext, ResourceContextInterface resourceContext, OperationContext operationContext) |       SubjectContext subjectContext, ResourceContextInterface resourceContext, OperationContext operationContext) | ||||||
|       throws IOException { |       throws IOException { | ||||||
|     if (resourceContext == null || resourceContext.getOwner() == null) { |     Iterator<PolicyContext> policyIterator = subjectContext.getPolicies(resourceContext.getOwner()); | ||||||
|       return; // No owner for a resource. No need to walk the hierarchy of user and teams that are resource owners |     evaluatePolicies(policyIterator, subjectContext, resourceContext, operationContext, false); | ||||||
|     } |  | ||||||
|     Iterator<PolicyContext> resourcePolicies = subjectContext.getResourcePolicies(resourceContext.getOwner()); |  | ||||||
|     evaluatePolicies(resourcePolicies, subjectContext, resourceContext, operationContext, true, true); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   private static void evaluateAllowResourcePolicies( |  | ||||||
|       SubjectContext subjectContext, ResourceContextInterface resourceContext, OperationContext operationContext) |  | ||||||
|       throws IOException { |  | ||||||
|     if (resourceContext == null || resourceContext.getOwner() == null) { |  | ||||||
|       return; // No owner for a resource. No need to walk the hierarchy of user and teams that are resource owners |  | ||||||
|     } |  | ||||||
|     Iterator<PolicyContext> resourcePolicies = subjectContext.getResourcePolicies(resourceContext.getOwner()); |  | ||||||
|     evaluatePolicies(resourcePolicies, subjectContext, resourceContext, operationContext, false, true); |  | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   private static void evaluatePolicies( |   private static void evaluatePolicies( | ||||||
| @ -117,16 +95,12 @@ public class PolicyEvaluator { | |||||||
|       SubjectContext subjectContext, |       SubjectContext subjectContext, | ||||||
|       ResourceContextInterface resourceContext, |       ResourceContextInterface resourceContext, | ||||||
|       OperationContext operationContext, |       OperationContext operationContext, | ||||||
|       boolean evaluateDeny, |       boolean evaluateDeny) { | ||||||
|       boolean evaluateResourcePolicies) { |  | ||||||
|     // When an operation is allowed by a rule, it is removed from operation context |     // When an operation is allowed by a rule, it is removed from operation context | ||||||
|     // When list of operations is empty in the operation context, all operations have been allowed |     // When list of operations is empty in the operation context, all operations have been allowed | ||||||
|     while (policies.hasNext() && !operationContext.getOperations().isEmpty()) { |     while (policies.hasNext() && !operationContext.getOperations().isEmpty()) { | ||||||
|       PolicyContext context = policies.next(); |       PolicyContext context = policies.next(); | ||||||
|       for (CompiledRule rule : context.getRules()) { |       for (CompiledRule rule : context.getRules()) { | ||||||
|         if (evaluateResourcePolicies && !rule.isResourceBased()) { |  | ||||||
|           continue; // Only evaluate resource based rules |  | ||||||
|         } |  | ||||||
|         LOG.debug( |         LOG.debug( | ||||||
|             "evaluating policy for {} {}:{}:{}", |             "evaluating policy for {} {}:{}:{}", | ||||||
|             evaluateDeny ? "deny" : "allow", |             evaluateDeny ? "deny" : "allow", | ||||||
| @ -146,7 +120,7 @@ public class PolicyEvaluator { | |||||||
|   public static List<ResourcePermission> listPermission(@NonNull SubjectContext subjectContext) { |   public static List<ResourcePermission> listPermission(@NonNull SubjectContext subjectContext) { | ||||||
|     Map<String, ResourcePermission> resourcePermissionMap = initResourcePermissions(); |     Map<String, ResourcePermission> resourcePermissionMap = initResourcePermissions(); | ||||||
| 
 | 
 | ||||||
|     Iterator<PolicyContext> policies = subjectContext.getPolicies(); |     Iterator<PolicyContext> policies = subjectContext.getPolicies(null); | ||||||
|     while (policies.hasNext()) { |     while (policies.hasNext()) { | ||||||
|       PolicyContext policyContext = policies.next(); |       PolicyContext policyContext = policies.next(); | ||||||
|       for (CompiledRule rule : policyContext.getRules()) { |       for (CompiledRule rule : policyContext.getRules()) { | ||||||
| @ -177,7 +151,7 @@ public class PolicyEvaluator { | |||||||
|     ResourcePermission resourcePermission = getResourcePermission(resourceType, Access.NOT_ALLOW); |     ResourcePermission resourcePermission = getResourcePermission(resourceType, Access.NOT_ALLOW); | ||||||
| 
 | 
 | ||||||
|     // Iterate through policies and set the permissions to DENY, ALLOW, CONDITIONAL_DENY, or CONDITIONAL_ALLOW |     // Iterate through policies and set the permissions to DENY, ALLOW, CONDITIONAL_DENY, or CONDITIONAL_ALLOW | ||||||
|     Iterator<PolicyContext> policies = subjectContext.getPolicies(); |     Iterator<PolicyContext> policies = subjectContext.getPolicies(null); | ||||||
|     while (policies.hasNext()) { |     while (policies.hasNext()) { | ||||||
|       PolicyContext policyContext = policies.next(); |       PolicyContext policyContext = policies.next(); | ||||||
|       for (CompiledRule rule : policyContext.getRules()) { |       for (CompiledRule rule : policyContext.getRules()) { | ||||||
| @ -194,7 +168,7 @@ public class PolicyEvaluator { | |||||||
|     ResourcePermission resourcePermission = getResourcePermission(resourceContext.getResource(), Access.NOT_ALLOW); |     ResourcePermission resourcePermission = getResourcePermission(resourceContext.getResource(), Access.NOT_ALLOW); | ||||||
| 
 | 
 | ||||||
|     // Iterate through policies and set the permissions to DENY, ALLOW, CONDITIONAL_DENY, or CONDITIONAL_ALLOW |     // Iterate through policies and set the permissions to DENY, ALLOW, CONDITIONAL_DENY, or CONDITIONAL_ALLOW | ||||||
|     Iterator<PolicyContext> policies = subjectContext.getPolicies(); |     Iterator<PolicyContext> policies = subjectContext.getPolicies(resourceContext.getOwner()); | ||||||
|     while (policies.hasNext()) { |     while (policies.hasNext()) { | ||||||
|       PolicyContext policyContext = policies.next(); |       PolicyContext policyContext = policies.next(); | ||||||
|       for (CompiledRule rule : policyContext.getRules()) { |       for (CompiledRule rule : policyContext.getRules()) { | ||||||
| @ -202,27 +176,6 @@ public class PolicyEvaluator { | |||||||
|         rule.evaluatePermission(subjectContext, resourceContext, resourcePermission, policyContext); |         rule.evaluatePermission(subjectContext, resourceContext, resourcePermission, policyContext); | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
|     // Iterate through policies and set the permissions to DENY, ALLOW, CONDITIONAL_DENY, or CONDITIONAL_ALLOW |  | ||||||
|     if (resourceContext == null || resourceContext.getOwner() == null) { |  | ||||||
|       return resourcePermission; // No owner - No need to walk the hierarchy of user and teams that are resource owners |  | ||||||
|     } |  | ||||||
|     Iterator<PolicyContext> resourcePolicies = subjectContext.getResourcePolicies(resourceContext.getOwner()); |  | ||||||
|     while (resourcePolicies.hasNext()) { |  | ||||||
|       PolicyContext policyContext = resourcePolicies.next(); |  | ||||||
|       for (CompiledRule rule : policyContext.getRules()) { |  | ||||||
|         rule.getExpression(); |  | ||||||
|         if (rule.isResourceBased() == false) { |  | ||||||
|           continue; |  | ||||||
|         } |  | ||||||
|         LOG.debug( |  | ||||||
|             "evaluating resource policies {}:{}:{}\n", |  | ||||||
|             policyContext.getRoleName(), |  | ||||||
|             policyContext.getPolicyName(), |  | ||||||
|             rule.getName()); |  | ||||||
|         rule.evaluatePermission(subjectContext, resourceContext, resourcePermission, policyContext); |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|     return resourcePermission; |     return resourcePermission; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -39,8 +39,7 @@ public class RuleEvaluator { | |||||||
|       name = "isOwner", |       name = "isOwner", | ||||||
|       input = "none", |       input = "none", | ||||||
|       description = "Returns true if the logged in user is the owner of the entity being accessed", |       description = "Returns true if the logged in user is the owner of the entity being accessed", | ||||||
|       examples = {"isOwner()", "!isOwner", "noOwner() || isOwner()"}, |       examples = {"isOwner()", "!isOwner", "noOwner() || isOwner()"}) | ||||||
|       resourceBased = true) |  | ||||||
|   public boolean isOwner() throws IOException { |   public boolean isOwner() throws IOException { | ||||||
|     return subjectContext != null && subjectContext.isOwner(resourceContext.getOwner()); |     return subjectContext != null && subjectContext.isOwner(resourceContext.getOwner()); | ||||||
|   } |   } | ||||||
| @ -49,8 +48,7 @@ public class RuleEvaluator { | |||||||
|       name = "matchAllTags", |       name = "matchAllTags", | ||||||
|       input = "List of comma separated tag or glossary fully qualified names", |       input = "List of comma separated tag or glossary fully qualified names", | ||||||
|       description = "Returns true if the entity being accessed has all the tags given as input", |       description = "Returns true if the entity being accessed has all the tags given as input", | ||||||
|       examples = {"matchAllTags('PersonalData.Personal', 'Tier.Tier1', 'Business Glossary.Clothing')"}, |       examples = {"matchAllTags('PersonalData.Personal', 'Tier.Tier1', 'Business Glossary.Clothing')"}) | ||||||
|       resourceBased = true) |  | ||||||
|   public boolean matchAllTags(String... tagFQNs) throws IOException { |   public boolean matchAllTags(String... tagFQNs) throws IOException { | ||||||
|     if (resourceContext == null) { |     if (resourceContext == null) { | ||||||
|       return false; |       return false; | ||||||
| @ -70,8 +68,7 @@ public class RuleEvaluator { | |||||||
|       name = "matchAnyTag", |       name = "matchAnyTag", | ||||||
|       input = "List of comma separated tag or glossary fully qualified names", |       input = "List of comma separated tag or glossary fully qualified names", | ||||||
|       description = "Returns true if the entity being accessed has at least one of the tags given as input", |       description = "Returns true if the entity being accessed has at least one of the tags given as input", | ||||||
|       examples = {"matchAnyTag('PersonalData.Personal', 'Tier.Tier1', 'Business Glossary.Clothing')"}, |       examples = {"matchAnyTag('PersonalData.Personal', 'Tier.Tier1', 'Business Glossary.Clothing')"}) | ||||||
|       resourceBased = true) |  | ||||||
|   public boolean matchAnyTag(String... tagFQNs) throws IOException { |   public boolean matchAnyTag(String... tagFQNs) throws IOException { | ||||||
|     if (resourceContext == null) { |     if (resourceContext == null) { | ||||||
|       return false; |       return false; | ||||||
| @ -93,16 +90,48 @@ public class RuleEvaluator { | |||||||
|       description = |       description = | ||||||
|           "Returns true if the user and the resource belongs to the team hierarchy where this policy is" |           "Returns true if the user and the resource belongs to the team hierarchy where this policy is" | ||||||
|               + "attached. This allows restricting permissions to a resource to the members of the team hierarchy.", |               + "attached. This allows restricting permissions to a resource to the members of the team hierarchy.", | ||||||
|       examples = {"matchTeam()"}, |       examples = {"matchTeam()"}) | ||||||
|       resourceBased = true) |  | ||||||
|   public boolean matchTeam() throws IOException { |   public boolean matchTeam() throws IOException { | ||||||
|     if (resourceContext == null || resourceContext.getOwner() == null) { |     if (resourceContext == null || resourceContext.getOwner() == null) { | ||||||
|       return true; // No ownership information |       return false; // No ownership information | ||||||
|     } |     } | ||||||
|     if (policyContext == null || !policyContext.getEntityType().equals(Entity.TEAM)) { |     if (policyContext == null || !policyContext.getEntityType().equals(Entity.TEAM)) { | ||||||
|       return true; // Policy must be attached to a team for this function to work |       return false; // Policy must be attached to a team for this function to work | ||||||
|     } |     } | ||||||
|     return subjectContext.isTeamAsset(policyContext.getEntityName(), resourceContext.getOwner()) |     return subjectContext.isTeamAsset(policyContext.getEntityName(), resourceContext.getOwner()) | ||||||
|         && subjectContext.isUserUnderTeam(policyContext.getEntityName()); |         && subjectContext.isUserUnderTeam(policyContext.getEntityName()); | ||||||
|   } |   } | ||||||
|  | 
 | ||||||
|  |   @Function( | ||||||
|  |       name = "inAnyTeam", | ||||||
|  |       input = "List of comma separated team names", | ||||||
|  |       description = "Returns true if the user belongs under the hierarchy of any of the teams in the given team list.", | ||||||
|  |       examples = {"inAnyTeam('marketing')"}) | ||||||
|  |   public boolean inAnyTeam(String... teams) { | ||||||
|  |     for (String team : teams) { | ||||||
|  |       if (subjectContext.isUserUnderTeam(team)) { | ||||||
|  |         LOG.debug("inAnyTeam - User {} is under the team {}", subjectContext.getUser().getName(), team); | ||||||
|  |         return true; | ||||||
|  |       } | ||||||
|  |       LOG.debug("inAnyTeam - User {} is not under the team {}", subjectContext.getUser().getName(), team); | ||||||
|  |     } | ||||||
|  |     return false; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   @Function( | ||||||
|  |       name = "hasAnyRole", | ||||||
|  |       input = "List of comma separated roles", | ||||||
|  |       description = | ||||||
|  |           "Returns true if the user (either direct or inherited from the parent teams) has one or more roles " | ||||||
|  |               + "from the list.", | ||||||
|  |       examples = {"hasAnyRole('DataSteward', 'DataEngineer')"}) | ||||||
|  |   public boolean hasAnyRole(String... roles) { | ||||||
|  |     for (String role : roles) { | ||||||
|  |       if (subjectContext.hasAnyRole(role)) { | ||||||
|  |         LOG.debug("hasAnyRole - User {} has the role {}", subjectContext.getUser().getName(), role); | ||||||
|  |         return true; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     return false; | ||||||
|  |   } | ||||||
| } | } | ||||||
|  | |||||||
| @ -96,6 +96,14 @@ public class SubjectCache { | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   public User getUser(String userName) throws EntityNotFoundException { | ||||||
|  |     try { | ||||||
|  |       return USER_CACHE.get(userName).getUser(); | ||||||
|  |     } catch (ExecutionException | UncheckedExecutionException ex) { | ||||||
|  |       return null; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   public Team getTeam(UUID teamId) throws EntityNotFoundException { |   public Team getTeam(UUID teamId) throws EntityNotFoundException { | ||||||
|     try { |     try { | ||||||
|       return TEAM_CACHE.get(teamId); |       return TEAM_CACHE.get(teamId); | ||||||
| @ -105,19 +113,41 @@ public class SubjectCache { | |||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   /** Return true if given list of teams is part of the hierarchy of parentTeam */ |   /** Return true if given list of teams is part of the hierarchy of parentTeam */ | ||||||
|   public boolean isInTeam(String parentTeam, List<EntityReference> teams) { |   public boolean isInTeam(String parentTeam, EntityReference team) { | ||||||
|     Stack<EntityReference> stack = new Stack<>(); |     Stack<EntityReference> stack = new Stack<>(); | ||||||
|     listOrEmpty(teams).forEach(stack::push); |     stack.push(team); // Start with team and see if the parent matches | ||||||
|     while (!stack.empty()) { |     while (!stack.empty()) { | ||||||
|       Team parent = getTeam(stack.pop().getId()); |       Team parent = getTeam(stack.pop().getId()); | ||||||
|       if (parent.getName().equals(parentTeam)) { |       if (parent.getName().equals(parentTeam)) { | ||||||
|         return true; |         return true; | ||||||
|       } |       } | ||||||
|       listOrEmpty(parent.getParents()).forEach(stack::push); |       listOrEmpty(parent.getParents()).forEach(stack::push); // Continue to go up the chain of parents | ||||||
|     } |     } | ||||||
|     return false; |     return false; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   /** Return true if the given user has any roles the list of roles */ | ||||||
|  |   public boolean hasRole(User user, String role) { | ||||||
|  |     Stack<EntityReference> stack = new Stack<>(); | ||||||
|  |     // If user has one of the roles directly assigned then return true | ||||||
|  |     if (hasRole(user.getRoles(), role)) { | ||||||
|  |       return true; | ||||||
|  |     } | ||||||
|  |     listOrEmpty(user.getTeams()).forEach(stack::push); // Continue to go up the chain of parents | ||||||
|  |     while (!stack.empty()) { | ||||||
|  |       Team parent = getTeam(stack.pop().getId()); | ||||||
|  |       if (hasRole(parent.getDefaultRoles(), role)) { | ||||||
|  |         return true; | ||||||
|  |       } | ||||||
|  |       listOrEmpty(parent.getParents()).forEach(stack::push); // Continue to go up the chain of parents | ||||||
|  |     } | ||||||
|  |     return false; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   private static boolean hasRole(List<EntityReference> userRoles, String expectedRole) { | ||||||
|  |     return listOrEmpty(userRoles).stream().anyMatch(userRole -> userRole.getName().equals(expectedRole)); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   public static void cleanUp() { |   public static void cleanUp() { | ||||||
|     LOG.info("Subject cache is cleaned up"); |     LOG.info("Subject cache is cleaned up"); | ||||||
|     USER_CACHE.invalidateAll(); |     USER_CACHE.invalidateAll(); | ||||||
|  | |||||||
| @ -16,7 +16,6 @@ package org.openmetadata.service.security.policyevaluator; | |||||||
| import static org.openmetadata.common.utils.CommonUtil.listOrEmpty; | import static org.openmetadata.common.utils.CommonUtil.listOrEmpty; | ||||||
| 
 | 
 | ||||||
| import java.util.ArrayList; | import java.util.ArrayList; | ||||||
| import java.util.Collections; |  | ||||||
| import java.util.Iterator; | import java.util.Iterator; | ||||||
| import java.util.List; | import java.util.List; | ||||||
| import java.util.NoSuchElementException; | import java.util.NoSuchElementException; | ||||||
| @ -64,7 +63,12 @@ public class SubjectContext { | |||||||
| 
 | 
 | ||||||
|   /** Returns true if the user of this SubjectContext is under the team hierarchy of parentTeam */ |   /** Returns true if the user of this SubjectContext is under the team hierarchy of parentTeam */ | ||||||
|   public boolean isUserUnderTeam(String parentTeam) { |   public boolean isUserUnderTeam(String parentTeam) { | ||||||
|     return isInTeam(parentTeam, user.getTeams()); |     for (EntityReference userTeam : user.getTeams()) { | ||||||
|  |       if (isInTeam(parentTeam, userTeam)) { | ||||||
|  |         return true; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     return false; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   /** Returns true if the given resource owner is under the team hierarchy of parentTeam */ |   /** Returns true if the given resource owner is under the team hierarchy of parentTeam */ | ||||||
| @ -74,38 +78,30 @@ public class SubjectContext { | |||||||
|       return subjectContext.isUserUnderTeam(parentTeam); |       return subjectContext.isUserUnderTeam(parentTeam); | ||||||
|     } else if (owner.getType().equals(Entity.TEAM)) { |     } else if (owner.getType().equals(Entity.TEAM)) { | ||||||
|       Team team = SubjectCache.getInstance().getTeam(owner.getId()); |       Team team = SubjectCache.getInstance().getTeam(owner.getId()); | ||||||
|       return isInTeam(parentTeam, List.of(team.getEntityReference())); |       return isInTeam(parentTeam, team.getEntityReference()); | ||||||
|     } |     } | ||||||
|     return false; |     return false; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   /** Return true if given list of teams is part of the hierarchy of parentTeam */ |   /** Return true if the team is part of the hierarchy of parentTeam */ | ||||||
|   private boolean isInTeam(String parentTeam, List<EntityReference> teams) { |   private boolean isInTeam(String parentTeam, EntityReference team) { | ||||||
|     return SubjectCache.getInstance().isInTeam(parentTeam, teams); |     return SubjectCache.getInstance().isInTeam(parentTeam, team); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   // Iterate over all the policies of the team hierarchy the user belongs to |   // Iterate over all the policies of the team hierarchy the user belongs to | ||||||
|   public Iterator<PolicyContext> getPolicies() { |   public Iterator<PolicyContext> getPolicies(EntityReference resourceOwner) { | ||||||
|     return new UserPolicyIterator(user, new ArrayList<>()); |     return new UserPolicyIterator(user, resourceOwner, new ArrayList<>()); | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   // Iterate over all the policies of the team hierarchy the resource belongs to |  | ||||||
|   public Iterator<PolicyContext> getResourcePolicies(EntityReference owner) { |  | ||||||
|     if (owner.getType().equals(Entity.USER)) { |  | ||||||
|       SubjectContext subjectContext = SubjectCache.getInstance().getSubjectContext(owner.getName()); |  | ||||||
|       return subjectContext.getPolicies(); |  | ||||||
|     } else if (owner.getType().equals(Entity.TEAM)) { |  | ||||||
|       Team team = SubjectCache.getInstance().getTeam(owner.getId()); |  | ||||||
|       List<UUID> teamsVisited = new ArrayList<>(); |  | ||||||
|       return new TeamPolicyIterator(team.getId(), teamsVisited); |  | ||||||
|     } |  | ||||||
|     return Collections.emptyIterator(); |  | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   public List<EntityReference> getTeams() { |   public List<EntityReference> getTeams() { | ||||||
|     return user.getTeams(); |     return user.getTeams(); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   /** Returns true if the user has any of the roles (either direct or inherited roles) */ | ||||||
|  |   public boolean hasAnyRole(String roles) { | ||||||
|  |     return SubjectCache.getInstance().hasRole(getUser(), roles); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   @Getter |   @Getter | ||||||
|   static class PolicyContext { |   static class PolicyContext { | ||||||
|     private final String entityType; |     private final String entityType; | ||||||
| @ -123,7 +119,7 @@ public class SubjectContext { | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   /** PolicyIterator goes over policies in a set of policies one by one. */ |   /** PolicyIterator goes over policies from a set of policies one by one. */ | ||||||
|   static class PolicyIterator implements Iterator<PolicyContext> { |   static class PolicyIterator implements Iterator<PolicyContext> { | ||||||
| 
 | 
 | ||||||
|     // When executing roles from a policy, entity type User or Team to which the Role is attached to. |     // When executing roles from a policy, entity type User or Team to which the Role is attached to. | ||||||
| @ -223,7 +219,7 @@ public class SubjectContext { | |||||||
|     private final List<Iterator<PolicyContext>> iterators = new ArrayList<>(); |     private final List<Iterator<PolicyContext>> iterators = new ArrayList<>(); | ||||||
| 
 | 
 | ||||||
|     /** Policy iterator for a user */ |     /** Policy iterator for a user */ | ||||||
|     UserPolicyIterator(User user, List<UUID> teamsVisited) { |     UserPolicyIterator(User user, EntityReference resourceOwner, List<UUID> teamsVisited) { | ||||||
|       this.user = user; |       this.user = user; | ||||||
| 
 | 
 | ||||||
|       // Iterate over policies in user role |       // Iterate over policies in user role | ||||||
| @ -231,13 +227,19 @@ public class SubjectContext { | |||||||
|         iterators.add(new RolePolicyIterator(Entity.USER, user.getName(), user.getRoles())); |         iterators.add(new RolePolicyIterator(Entity.USER, user.getName(), user.getRoles())); | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       if (!Boolean.TRUE.equals(user.getIsBot())) { |       // Next, iterate over policies of teams to which the user belongs to | ||||||
|         // Finally, iterate over policies of teams to which the user belongs to |  | ||||||
|       // Note that ** Bots don't inherit policies or default roles from teams ** |       // Note that ** Bots don't inherit policies or default roles from teams ** | ||||||
|  |       if (!Boolean.TRUE.equals(user.getIsBot())) { | ||||||
|         for (EntityReference team : user.getTeams()) { |         for (EntityReference team : user.getTeams()) { | ||||||
|           iterators.add(new TeamPolicyIterator(team.getId(), teamsVisited)); |           iterators.add(new TeamPolicyIterator(team.getId(), teamsVisited, false)); | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|  | 
 | ||||||
|  |       // Finally, iterate over policies of teams that own the resource | ||||||
|  |       if (resourceOwner != null && resourceOwner.getType().equals(Entity.TEAM)) { | ||||||
|  |         Team team = SubjectCache.getInstance().getTeam(resourceOwner.getId()); | ||||||
|  |         iterators.add(new TeamPolicyIterator(team.getId(), teamsVisited, true)); | ||||||
|  |       } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     @Override |     @Override | ||||||
| @ -248,7 +250,7 @@ public class SubjectContext { | |||||||
|         } |         } | ||||||
|         iteratorIndex++; |         iteratorIndex++; | ||||||
|       } |       } | ||||||
|       LOG.debug("Subject {} policy iteration done" + user.getName()); |       LOG.debug("Subject {} policy iteration done", user.getName()); | ||||||
|       return false; |       return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -270,21 +272,21 @@ public class SubjectContext { | |||||||
|     private final List<Iterator<PolicyContext>> iterators = new ArrayList<>(); |     private final List<Iterator<PolicyContext>> iterators = new ArrayList<>(); | ||||||
| 
 | 
 | ||||||
|     /** Policy iterator for a team */ |     /** Policy iterator for a team */ | ||||||
|     TeamPolicyIterator(UUID teamId, List<UUID> teamsVisited) { |     TeamPolicyIterator(UUID teamId, List<UUID> teamsVisited, boolean skipRoles) { | ||||||
|       Team team = SubjectCache.getInstance().getTeam(teamId); |       Team team = SubjectCache.getInstance().getTeam(teamId); | ||||||
| 
 | 
 | ||||||
|       // If a team is already visited (because user can belong to multiple teams |       // If a team is already visited (because user can belong to multiple teams | ||||||
|       // and a team can belong to multiple teams) then don't visit the roles/policies of that team |       // and a team can belong to multiple teams) then don't visit the roles/policies of that team | ||||||
|       if (!teamsVisited.contains(teamId)) { |       if (!teamsVisited.contains(teamId)) { | ||||||
|         teamsVisited.add(teamId); |         teamsVisited.add(teamId); | ||||||
|         if (team.getDefaultRoles() != null) { |         if (!skipRoles && team.getDefaultRoles() != null) { | ||||||
|           iterators.add(new RolePolicyIterator(Entity.TEAM, team.getName(), team.getDefaultRoles())); |           iterators.add(new RolePolicyIterator(Entity.TEAM, team.getName(), team.getDefaultRoles())); | ||||||
|         } |         } | ||||||
|         if (team.getPolicies() != null) { |         if (team.getPolicies() != null) { | ||||||
|           iterators.add(new PolicyIterator(Entity.TEAM, team.getName(), null, team.getPolicies())); |           iterators.add(new PolicyIterator(Entity.TEAM, team.getName(), null, team.getPolicies())); | ||||||
|         } |         } | ||||||
|         for (EntityReference parentTeam : listOrEmpty(team.getParents())) { |         for (EntityReference parentTeam : listOrEmpty(team.getParents())) { | ||||||
|           iterators.add(new TeamPolicyIterator(parentTeam.getId(), teamsVisited)); |           iterators.add(new TeamPolicyIterator(parentTeam.getId(), teamsVisited, skipRoles)); | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -3,39 +3,52 @@ package org.openmetadata.service.security.policyevaluator; | |||||||
| import static org.junit.jupiter.api.Assertions.assertFalse; | import static org.junit.jupiter.api.Assertions.assertFalse; | ||||||
| import static org.junit.jupiter.api.Assertions.assertTrue; | import static org.junit.jupiter.api.Assertions.assertTrue; | ||||||
| import static org.mockito.ArgumentMatchers.any; | import static org.mockito.ArgumentMatchers.any; | ||||||
|  | import static org.openmetadata.common.utils.CommonUtil.listOf; | ||||||
|  | import static org.openmetadata.common.utils.CommonUtil.listOrEmpty; | ||||||
| import static org.openmetadata.service.security.policyevaluator.CompiledRule.parseExpression; | import static org.openmetadata.service.security.policyevaluator.CompiledRule.parseExpression; | ||||||
| 
 | 
 | ||||||
|  | import java.nio.charset.StandardCharsets; | ||||||
| import java.util.ArrayList; | import java.util.ArrayList; | ||||||
| import java.util.List; | import java.util.List; | ||||||
| import java.util.UUID; | import java.util.UUID; | ||||||
|  | import org.junit.jupiter.api.AfterAll; | ||||||
| import org.junit.jupiter.api.BeforeAll; | import org.junit.jupiter.api.BeforeAll; | ||||||
| import org.junit.jupiter.api.Test; | import org.junit.jupiter.api.Test; | ||||||
| import org.mockito.Mockito; | import org.mockito.Mockito; | ||||||
| import org.mockito.stubbing.Answer; | import org.mockito.stubbing.Answer; | ||||||
| import org.openmetadata.schema.entity.data.Table; | 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.Team; | ||||||
| import org.openmetadata.schema.entity.teams.User; | import org.openmetadata.schema.entity.teams.User; | ||||||
| import org.openmetadata.schema.type.EntityReference; | import org.openmetadata.schema.type.EntityReference; | ||||||
| import org.openmetadata.schema.type.TagLabel; | import org.openmetadata.schema.type.TagLabel; | ||||||
| import org.openmetadata.service.Entity; | import org.openmetadata.service.Entity; | ||||||
|  | import org.openmetadata.service.jdbi3.CollectionDAO.RoleDAO; | ||||||
| import org.openmetadata.service.jdbi3.CollectionDAO.TableDAO; | import org.openmetadata.service.jdbi3.CollectionDAO.TableDAO; | ||||||
| import org.openmetadata.service.jdbi3.CollectionDAO.TeamDAO; | import org.openmetadata.service.jdbi3.CollectionDAO.TeamDAO; | ||||||
| import org.openmetadata.service.jdbi3.CollectionDAO.UserDAO; | import org.openmetadata.service.jdbi3.CollectionDAO.UserDAO; | ||||||
|  | import org.openmetadata.service.jdbi3.RoleRepository; | ||||||
| import org.openmetadata.service.jdbi3.TableRepository; | import org.openmetadata.service.jdbi3.TableRepository; | ||||||
| import org.openmetadata.service.jdbi3.TeamRepository; | import org.openmetadata.service.jdbi3.TeamRepository; | ||||||
| import org.openmetadata.service.jdbi3.UserRepository; | import org.openmetadata.service.jdbi3.UserRepository; | ||||||
|  | import org.openmetadata.service.security.policyevaluator.SubjectContext.PolicyContext; | ||||||
| import org.springframework.expression.EvaluationContext; | import org.springframework.expression.EvaluationContext; | ||||||
| import org.springframework.expression.spel.support.StandardEvaluationContext; | import org.springframework.expression.spel.support.StandardEvaluationContext; | ||||||
| 
 | 
 | ||||||
| class RuleEvaluatorTest { | class RuleEvaluatorTest { | ||||||
|   private static Table table = new Table().withName("table"); |   private static final Table table = new Table().withName("table"); | ||||||
|   private static User user; |   private static User user; | ||||||
|   private static EvaluationContext evaluationContext; |   private static EvaluationContext evaluationContext; | ||||||
|  |   private static SubjectContext subjectContext; | ||||||
|  |   private static ResourceContext resourceContext; | ||||||
| 
 | 
 | ||||||
|   @BeforeAll |   @BeforeAll | ||||||
|   public static void setup() { |   public static void setup() { | ||||||
|     Entity.registerEntity(User.class, Entity.USER, Mockito.mock(UserDAO.class), Mockito.mock(UserRepository.class)); |     Entity.registerEntity(User.class, Entity.USER, Mockito.mock(UserDAO.class), Mockito.mock(UserRepository.class)); | ||||||
|     Entity.registerEntity(Team.class, Entity.TEAM, Mockito.mock(TeamDAO.class), Mockito.mock(TeamRepository.class)); |     Entity.registerEntity(Team.class, Entity.TEAM, Mockito.mock(TeamDAO.class), Mockito.mock(TeamRepository.class)); | ||||||
|  |     Entity.registerEntity(Role.class, Entity.ROLE, Mockito.mock(RoleDAO.class), Mockito.mock(RoleRepository.class)); | ||||||
|  |     SubjectCache.initialize(); | ||||||
|  |     RoleCache.initialize(); | ||||||
| 
 | 
 | ||||||
|     TableRepository tableRepository = Mockito.mock(TableRepository.class); |     TableRepository tableRepository = Mockito.mock(TableRepository.class); | ||||||
|     Mockito.when(tableRepository.getAllTags(any())) |     Mockito.when(tableRepository.getAllTags(any())) | ||||||
| @ -43,18 +56,24 @@ class RuleEvaluatorTest { | |||||||
|     Entity.registerEntity(Table.class, Entity.TABLE, Mockito.mock(TableDAO.class), tableRepository); |     Entity.registerEntity(Table.class, Entity.TABLE, Mockito.mock(TableDAO.class), tableRepository); | ||||||
| 
 | 
 | ||||||
|     user = new User().withId(UUID.randomUUID()).withName("user"); |     user = new User().withId(UUID.randomUUID()).withName("user"); | ||||||
|     ResourceContext resourceContext = |     resourceContext = | ||||||
|         ResourceContext.builder() |         ResourceContext.builder() | ||||||
|             .resource("table") |             .resource("table") | ||||||
|             .entity(table) |             .entity(table) | ||||||
|             .entityRepository(Mockito.mock(TableRepository.class)) |             .entityRepository(Mockito.mock(TableRepository.class)) | ||||||
|             .build(); |             .build(); | ||||||
| 
 | 
 | ||||||
|     SubjectContext subjectContext = new SubjectContext(user); |     subjectContext = new SubjectContext(user); | ||||||
|     RuleEvaluator ruleEvaluator = new RuleEvaluator(null, subjectContext, resourceContext); |     RuleEvaluator ruleEvaluator = new RuleEvaluator(null, subjectContext, resourceContext); | ||||||
|     evaluationContext = new StandardEvaluationContext(ruleEvaluator); |     evaluationContext = new StandardEvaluationContext(ruleEvaluator); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   @AfterAll | ||||||
|  |   public static void cleanup() { | ||||||
|  |     SubjectCache.cleanUp(); | ||||||
|  |     RoleCache.cleanUp(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   @Test |   @Test | ||||||
|   void test_noOwner() { |   void test_noOwner() { | ||||||
|     // Set no owner to the entity and test noOwner method |     // Set no owner to the entity and test noOwner method | ||||||
| @ -133,6 +152,99 @@ class RuleEvaluatorTest { | |||||||
|     assertTrue(evaluateExpression("!matchAnyTag('tag4')")); |     assertTrue(evaluateExpression("!matchAnyTag('tag4')")); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   @Test | ||||||
|  |   void test_matchTeam() { | ||||||
|  |     // Create a team hierarchy | ||||||
|  |     Team team1 = createTeam("team1", null); | ||||||
|  |     Team team11 = createTeam("team11", "team1"); | ||||||
|  |     Team team12 = createTeam("team12", "team1"); | ||||||
|  |     Team team111 = createTeam("team111", "team11"); | ||||||
|  | 
 | ||||||
|  |     // Resource belongs to team111 and the Policy executed is coming from team111 | ||||||
|  |     table.setOwner(team111.getEntityReference()); | ||||||
|  |     updatePolicyContext("team111"); | ||||||
|  |     for (Team team : listOf(team111)) { // For users in team111 hierarchy matchTeam is true | ||||||
|  |       user.setTeams(listOf(team.getEntityReference())); | ||||||
|  |       assertTrue(evaluateExpression("matchTeam()")); | ||||||
|  |     } | ||||||
|  |     for (Team team : listOf(team1, team12, team11)) { // For users not in team111 hierarchy matchTeam is false | ||||||
|  |       user.setTeams(listOf(team.getEntityReference())); | ||||||
|  |       assertFalse(evaluateExpression("matchTeam()"), "Failed for team " + team.getName()); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Resource belongs to team111 and the Policy executed is coming from team11 | ||||||
|  |     updatePolicyContext("team11"); | ||||||
|  |     for (Team team : listOf(team11, team111)) { // For users in team11 hierarchy matchTeam is true | ||||||
|  |       user.setTeams(listOf(team.getEntityReference())); | ||||||
|  |       assertTrue(evaluateExpression("matchTeam()")); | ||||||
|  |     } | ||||||
|  |     for (Team team : listOf(team1, team12)) { // For users not in team11 hierarchy matchTeam is false | ||||||
|  |       user.setTeams(listOf(team.getEntityReference())); | ||||||
|  |       assertFalse(evaluateExpression("matchTeam()"), "Failed for team " + team.getName()); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Resource belongs to team111 and the Policy executed is coming from team1 | ||||||
|  |     updatePolicyContext("team1"); | ||||||
|  |     for (Team team : listOf(team1, team11, team111, team12)) { // For users in team1 hierarchy matchTeam is true | ||||||
|  |       user.setTeams(listOf(team.getEntityReference())); | ||||||
|  |       assertTrue(evaluateExpression("matchTeam()")); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   @Test | ||||||
|  |   void test_inAnyTeam() { | ||||||
|  |     // Create a team hierarchy | ||||||
|  |     Team team1 = createTeam("team1", null); | ||||||
|  |     createTeam("team11", "team1"); | ||||||
|  |     Team team12 = createTeam("team12", "team1"); | ||||||
|  |     Team team111 = createTeam("team111", "team11"); | ||||||
|  | 
 | ||||||
|  |     // User in team111 - that means user is also in parent teams team11 and team1 | ||||||
|  |     user.setTeams(listOf(team111.getEntityReference())); | ||||||
|  |     assertTrue(evaluateExpression("inAnyTeam('team1')")); | ||||||
|  |     assertTrue(evaluateExpression("inAnyTeam('team11')")); | ||||||
|  |     assertTrue(evaluateExpression("inAnyTeam('team111')")); | ||||||
|  |     assertFalse(evaluateExpression("inAnyTeam('team12')")); | ||||||
|  | 
 | ||||||
|  |     // User in team12 - that means user is also in parent team team1 | ||||||
|  |     user.setTeams(listOf(team12.getEntityReference())); | ||||||
|  |     assertTrue(evaluateExpression("inAnyTeam('team1')")); | ||||||
|  |     assertTrue(evaluateExpression("inAnyTeam('team12')")); | ||||||
|  |     assertFalse(evaluateExpression("inAnyTeam('team111', 'team11')")); | ||||||
|  | 
 | ||||||
|  |     // User in team1 with no parents | ||||||
|  |     user.setTeams(listOf(team1.getEntityReference())); | ||||||
|  |     assertTrue(evaluateExpression("inAnyTeam('team1')")); | ||||||
|  |     assertFalse(evaluateExpression("inAnyTeam('team12', 'team11', 'team111')")); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   @Test | ||||||
|  |   void test_hasAnyRole() { | ||||||
|  |     // Create a team hierarchy | ||||||
|  |     Team team1 = createTeamWithRole("team1", null); | ||||||
|  |     Team team11 = createTeamWithRole("team11", "team1"); | ||||||
|  |     Team team111 = createTeamWithRole("team111", "team11"); | ||||||
|  |     user.setRoles(listOf(createRole("user").getEntityReference())); | ||||||
|  | 
 | ||||||
|  |     // User in team111 inherits all roles | ||||||
|  |     user.setTeams(listOf(team111.getEntityReference())); | ||||||
|  |     for (String role : listOf("user", "team111", "team11", "team1")) { | ||||||
|  |       assertTrue(evaluateExpression(String.format("hasAnyRole('%s')", role))); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // User in team11 inherits all roles except team111 | ||||||
|  |     user.setTeams(listOf(team11.getEntityReference())); | ||||||
|  |     for (String role : listOf("user", "team11", "team1")) { | ||||||
|  |       assertTrue(evaluateExpression(String.format("hasAnyRole('%s')", role))); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // User in team1 does not have parent team to inherit from | ||||||
|  |     user.setTeams(listOf(team1.getEntityReference())); | ||||||
|  |     for (String role : listOf("user", "team1")) { | ||||||
|  |       assertTrue(evaluateExpression(String.format("hasAnyRole('%s')", role))); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   private Boolean evaluateExpression(String condition) { |   private Boolean evaluateExpression(String condition) { | ||||||
|     return parseExpression(condition).getValue(evaluationContext, Boolean.class); |     return parseExpression(condition).getValue(evaluationContext, Boolean.class); | ||||||
|   } |   } | ||||||
| @ -144,4 +256,42 @@ class RuleEvaluatorTest { | |||||||
|     } |     } | ||||||
|     return tagLabels; |     return tagLabels; | ||||||
|   } |   } | ||||||
|  | 
 | ||||||
|  |   private Team createTeam(String teamName, String parentName) { | ||||||
|  |     UUID teamId = UUID.nameUUIDFromBytes(teamName.getBytes(StandardCharsets.UTF_8)); | ||||||
|  |     Team team = new Team().withName(teamName).withId(teamId); | ||||||
|  |     if (parentName != null) { | ||||||
|  |       UUID parentId = UUID.nameUUIDFromBytes(parentName.getBytes(StandardCharsets.UTF_8)); | ||||||
|  |       Team parentTeam = SubjectCache.getInstance().getTeam(parentId); | ||||||
|  |       team.setParents(listOf(parentTeam.getEntityReference())); | ||||||
|  |     } | ||||||
|  |     SubjectCache.TEAM_CACHE.put(team.getId(), team); | ||||||
|  |     return team; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   private Team createTeamWithRole(String teamName, String parentName) { | ||||||
|  |     Team team = createTeam(teamName, parentName); | ||||||
|  |     Role role = createRole(teamName); // Create a role with same name as the teamName | ||||||
|  |     team.setDefaultRoles(listOf(role.getEntityReference())); | ||||||
|  |     team.setInheritedRoles(new ArrayList<>()); | ||||||
|  |     for (EntityReference parent : listOrEmpty(team.getParents())) { | ||||||
|  |       Team parentTeam = SubjectCache.getInstance().getTeam(parent.getId()); | ||||||
|  |       team.getInheritedRoles().addAll(listOrEmpty(parentTeam.getDefaultRoles())); | ||||||
|  |       team.getInheritedRoles().addAll(listOrEmpty(parentTeam.getInheritedRoles())); | ||||||
|  |     } | ||||||
|  |     return team; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   private Role createRole(String roleName) { | ||||||
|  |     UUID roleId = UUID.nameUUIDFromBytes(roleName.getBytes(StandardCharsets.UTF_8)); | ||||||
|  |     Role role = new Role().withName(roleName).withId(roleId); | ||||||
|  |     RoleCache.ROLE_CACHE.put(role.getId(), role); | ||||||
|  |     return role; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   private void updatePolicyContext(String team) { | ||||||
|  |     PolicyContext policyContext = new PolicyContext(Entity.TEAM, team, null, null, null); | ||||||
|  |     RuleEvaluator ruleEvaluator = new RuleEvaluator(policyContext, subjectContext, resourceContext); | ||||||
|  |     evaluationContext = new StandardEvaluationContext(ruleEvaluator); | ||||||
|  |   } | ||||||
| } | } | ||||||
|  | |||||||
| @ -15,6 +15,7 @@ package org.openmetadata.service.security.policyevaluator; | |||||||
| import static org.junit.jupiter.api.Assertions.assertEquals; | import static org.junit.jupiter.api.Assertions.assertEquals; | ||||||
| import static org.junit.jupiter.api.Assertions.assertFalse; | import static org.junit.jupiter.api.Assertions.assertFalse; | ||||||
| import static org.junit.jupiter.api.Assertions.assertTrue; | import static org.junit.jupiter.api.Assertions.assertTrue; | ||||||
|  | import static org.openmetadata.common.utils.CommonUtil.listOrEmpty; | ||||||
| 
 | 
 | ||||||
| import java.util.ArrayList; | import java.util.ArrayList; | ||||||
| import java.util.Iterator; | import java.util.Iterator; | ||||||
| @ -45,19 +46,25 @@ import org.openmetadata.service.security.policyevaluator.SubjectContext.PolicyCo | |||||||
| public class SubjectContextTest { | public class SubjectContextTest { | ||||||
|   private static List<Role> team1Roles; |   private static List<Role> team1Roles; | ||||||
|   private static List<Policy> team1Policies; |   private static List<Policy> team1Policies; | ||||||
|   private static Team team1; |  | ||||||
| 
 | 
 | ||||||
|   private static List<Role> team11Roles; |   private static List<Role> team11Roles; | ||||||
|   private static List<Policy> team11Policies; |   private static List<Policy> team11Policies; | ||||||
|   private static Team team11; |  | ||||||
| 
 | 
 | ||||||
|   private static List<Role> team12Roles; |   private static List<Role> team12Roles; | ||||||
|   private static List<Policy> team12Policies; |   private static List<Policy> team12Policies; | ||||||
| 
 | 
 | ||||||
|  |   private static List<Role> team13Roles; | ||||||
|  |   private static List<Policy> team13Policies; | ||||||
|  |   private static Team team13; | ||||||
|  | 
 | ||||||
|   private static List<Role> team111Roles; |   private static List<Role> team111Roles; | ||||||
|   private static List<Policy> team111Policies; |   private static List<Policy> team111Policies; | ||||||
|   private static Team team111; |   private static Team team111; | ||||||
| 
 | 
 | ||||||
|  |   private static List<Role> team131Roles; | ||||||
|  |   private static List<Policy> team131Policies; | ||||||
|  |   private static Team team131; | ||||||
|  | 
 | ||||||
|   private static List<Role> userRoles; |   private static List<Role> userRoles; | ||||||
|   private static User user; |   private static User user; | ||||||
| 
 | 
 | ||||||
| @ -73,32 +80,40 @@ public class SubjectContextTest { | |||||||
|     SubjectCache.initialize(); |     SubjectCache.initialize(); | ||||||
| 
 | 
 | ||||||
|     // Create team hierarchy: |     // Create team hierarchy: | ||||||
|     // team1, has  team11, team12, team13 as children |     //                           team1 | ||||||
|     // team11 has team111 as children |     //                      /      |      \ | ||||||
|     // team111 has user as children |     //                   team11  team12  team13 | ||||||
|     // Each with 3 roles and 3 policies |     //                    /         /      \ | ||||||
|     team1Roles = getRoles("team1", 3); |     //               team111       /       team131 | ||||||
|     team1Policies = getPolicies("team1", 3); |     //                    \      / | ||||||
|     team1 = createTeam("team1", team1Roles, team1Policies, null); |     //                      user | ||||||
|  |     // Each team has 3 roles and 3 policies | ||||||
|  |     team1Roles = getRoles("team1"); | ||||||
|  |     team1Policies = getPolicies("team1"); | ||||||
|  |     Team team1 = createTeam("team1", team1Roles, team1Policies, null); | ||||||
| 
 | 
 | ||||||
|     team11Roles = getRoles("team11", 3); |     team11Roles = getRoles("team11"); | ||||||
|     team11Policies = getPolicies("team11", 3); |     team11Policies = getPolicies("team11"); | ||||||
|     team11 = createTeam("team11", team11Roles, team11Policies, List.of(team1)); |     Team team11 = createTeam("team11", team11Roles, team11Policies, List.of(team1)); | ||||||
| 
 | 
 | ||||||
|     team12Roles = getRoles("team12", 3); |     team12Roles = getRoles("team12"); | ||||||
|     team12Policies = getPolicies("team12", 3); |     team12Policies = getPolicies("team12"); | ||||||
|     Team team12 = createTeam("team12", team12Roles, team12Policies, List.of(team1)); |     Team team12 = createTeam("team12", team12Roles, team12Policies, List.of(team1)); | ||||||
| 
 | 
 | ||||||
|     List<Role> team13Roles = getRoles("team13", 3); |     team13Roles = getRoles("team13"); | ||||||
|     List<Policy> team13Policies = getPolicies("team13", 3); |     team13Policies = getPolicies("team13"); | ||||||
|     createTeam("team13", team13Roles, team13Policies, List.of(team1)); |     team13 = createTeam("team13", team13Roles, team13Policies, List.of(team1)); | ||||||
| 
 | 
 | ||||||
|     team111Roles = getRoles("team111", 3); |     team111Roles = getRoles("team111"); | ||||||
|     team111Policies = getPolicies("team111", 3); |     team111Policies = getPolicies("team111"); | ||||||
|     team111 = createTeam("team111", team111Roles, team111Policies, List.of(team11, team12)); |     team111 = createTeam("team111", team111Roles, team111Policies, List.of(team11, team12)); | ||||||
| 
 | 
 | ||||||
|  |     team131Roles = getRoles("team131"); | ||||||
|  |     team131Policies = getPolicies("team131"); | ||||||
|  |     team131 = createTeam("team131", team131Roles, team131Policies, List.of(team13)); | ||||||
|  | 
 | ||||||
|     // Add user to team111 |     // Add user to team111 | ||||||
|     userRoles = getRoles("user", 3); |     userRoles = getRoles("user"); | ||||||
|     List<EntityReference> userRolesRef = toEntityReferences(userRoles); |     List<EntityReference> userRolesRef = toEntityReferences(userRoles); | ||||||
|     user = new User().withName("user").withRoles(userRolesRef).withTeams(List.of(team111.getEntityReference())); |     user = new User().withName("user").withRoles(userRolesRef).withTeams(List.of(team111.getEntityReference())); | ||||||
|     SubjectCache.USER_CACHE.put("user", new SubjectContext(user)); |     SubjectCache.USER_CACHE.put("user", new SubjectContext(user)); | ||||||
| @ -113,12 +128,36 @@ public class SubjectContextTest { | |||||||
| 
 | 
 | ||||||
|   @Test |   @Test | ||||||
|   void testPolicyIterator() { |   void testPolicyIterator() { | ||||||
|     // |     // Check iteration order of the policies without resourceOwner | ||||||
|     // Check iteration order of the policies |  | ||||||
|     // |  | ||||||
|     SubjectContext subjectContext = SubjectCache.getInstance().getSubjectContext(user.getName()); |     SubjectContext subjectContext = SubjectCache.getInstance().getSubjectContext(user.getName()); | ||||||
|     Iterator<PolicyContext> policyContextIterator = subjectContext.getPolicies(); |     Iterator<PolicyContext> policyContextIterator = subjectContext.getPolicies(null); | ||||||
|     assertUserPolicyIterator(policyContextIterator); |     List<String> expectedUserPolicyOrder = new ArrayList<>(); | ||||||
|  |     expectedUserPolicyOrder.addAll(getPolicyListFromRoles(userRoles)); // First polices associated with user roles | ||||||
|  |     expectedUserPolicyOrder.addAll(getAllTeamPolicies(team111Roles, team111Policies)); // Next parent team111 policies | ||||||
|  |     expectedUserPolicyOrder.addAll( | ||||||
|  |         getAllTeamPolicies(team11Roles, team11Policies)); // Next team111 parent team11 policies | ||||||
|  |     expectedUserPolicyOrder.addAll(getAllTeamPolicies(team1Roles, team1Policies)); // Next team11 parent team1 policies | ||||||
|  |     expectedUserPolicyOrder.addAll( | ||||||
|  |         getAllTeamPolicies(team12Roles, team12Policies)); // Next team111 parent team12 policies | ||||||
|  |     assertPolicyIterator(expectedUserPolicyOrder, policyContextIterator); | ||||||
|  | 
 | ||||||
|  |     // Check iteration order of policies with team13 as the resource owner | ||||||
|  |     subjectContext = SubjectCache.getInstance().getSubjectContext(user.getName()); | ||||||
|  |     policyContextIterator = subjectContext.getPolicies(team13.getEntityReference()); | ||||||
|  |     List<String> expectedUserAndTeam13PolicyOrder = new ArrayList<>(); | ||||||
|  |     expectedUserAndTeam13PolicyOrder.addAll(expectedUserPolicyOrder); | ||||||
|  |     expectedUserAndTeam13PolicyOrder.addAll(getAllTeamPolicies(null, team13Policies)); | ||||||
|  |     assertPolicyIterator(expectedUserAndTeam13PolicyOrder, policyContextIterator); | ||||||
|  | 
 | ||||||
|  |     // Check iteration order of policies with team131 as the resource owner | ||||||
|  |     subjectContext = SubjectCache.getInstance().getSubjectContext(user.getName()); | ||||||
|  |     policyContextIterator = subjectContext.getPolicies(team131.getEntityReference()); | ||||||
|  |     // Roles & policies are inherited from resource owner team131 | ||||||
|  |     List<String> expectedUserAndTeam131PolicyOrder = new ArrayList<>(); | ||||||
|  |     expectedUserAndTeam131PolicyOrder.addAll(expectedUserPolicyOrder); | ||||||
|  |     expectedUserAndTeam131PolicyOrder.addAll(getAllTeamPolicies(null, team131Policies)); | ||||||
|  |     expectedUserAndTeam131PolicyOrder.addAll(getAllTeamPolicies(null, team13Policies)); | ||||||
|  |     assertPolicyIterator(expectedUserAndTeam131PolicyOrder, policyContextIterator); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   @Test |   @Test | ||||||
| @ -160,44 +199,12 @@ public class SubjectContextTest { | |||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   @Test |   @Test | ||||||
|   void testResourcePolicyIterator() { |   private static List<Role> getRoles(String prefix) { | ||||||
|     // A resource with user as owner and make sure all policies from user's hierarchy is in the iterator |  | ||||||
|     EntityReference userOwner = user.getEntityReference(); |  | ||||||
|     SubjectContext subjectContext = SubjectCache.getInstance().getSubjectContext(user.getName()); |  | ||||||
|     Iterator<PolicyContext> actualPolicyIterator = subjectContext.getResourcePolicies(userOwner); |  | ||||||
|     assertUserPolicyIterator(actualPolicyIterator); |  | ||||||
| 
 |  | ||||||
|     // A resource with team1 as owner and make sure all policies from user's hierarchy is in the iterator |  | ||||||
|     EntityReference team1Owner = team1.getEntityReference(); |  | ||||||
|     actualPolicyIterator = subjectContext.getResourcePolicies(team1Owner); |  | ||||||
|     // add policies from team1 |  | ||||||
|     List<String> expectedPolicyOrder = new ArrayList<>(getAllTeamPolicies(team1Roles, team1Policies)); |  | ||||||
|     assertPolicyIterator(expectedPolicyOrder, actualPolicyIterator); |  | ||||||
| 
 |  | ||||||
|     // A resource with team11 as owner and make sure all policies from user's hierarchy is in the iterator |  | ||||||
|     EntityReference team11Owner = team11.getEntityReference(); |  | ||||||
|     actualPolicyIterator = subjectContext.getResourcePolicies(team11Owner); |  | ||||||
|     List<String> list = new ArrayList<>(getAllTeamPolicies(team11Roles, team11Policies)); // add policies from team11 |  | ||||||
|     list.addAll(expectedPolicyOrder); // Add all policies from parent team1 previously setup |  | ||||||
|     expectedPolicyOrder = list; |  | ||||||
|     assertPolicyIterator(expectedPolicyOrder, actualPolicyIterator); |  | ||||||
| 
 |  | ||||||
|     // A resource with team11 as owner and make sure all policies from user's hierarchy is in the iterator |  | ||||||
|     EntityReference team111Owner = team111.getEntityReference(); |  | ||||||
|     actualPolicyIterator = subjectContext.getResourcePolicies(team111Owner); |  | ||||||
|     list = new ArrayList<>(getAllTeamPolicies(team111Roles, team111Policies)); // add policies from team111 |  | ||||||
|     list.addAll(expectedPolicyOrder); // Add all policies form team11 and team1 previously setup |  | ||||||
|     list.addAll(getPolicyListFromRoles(team12Roles)); // add policies from team12 roles |  | ||||||
|     list.addAll(getPolicyList(team12Policies)); // add team12 policies |  | ||||||
|     assertPolicyIterator(list, actualPolicyIterator); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   private static List<Role> getRoles(String prefix, int count) { |  | ||||||
|     // Create roles with 3 policies each and each policy with 3 rules |     // Create roles with 3 policies each and each policy with 3 rules | ||||||
|     List<Role> roles = new ArrayList<>(count); |     List<Role> roles = new ArrayList<>(3); | ||||||
|     for (int i = 1; i <= count; i++) { |     for (int i = 1; i <= 3; i++) { | ||||||
|       String name = prefix + "_role_" + i; |       String name = prefix + "_role_" + i; | ||||||
|       List<EntityReference> policies = toEntityReferences(getPolicies(name, 3)); |       List<EntityReference> policies = toEntityReferences(getPolicies(name)); | ||||||
|       Role role = new Role().withName(name).withId(UUID.randomUUID()).withPolicies(policies); |       Role role = new Role().withName(name).withId(UUID.randomUUID()).withPolicies(policies); | ||||||
|       RoleCache.ROLE_CACHE.put(role.getId(), role); |       RoleCache.ROLE_CACHE.put(role.getId(), role); | ||||||
|       roles.add(role); |       roles.add(role); | ||||||
| @ -205,21 +212,21 @@ public class SubjectContextTest { | |||||||
|     return roles; |     return roles; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   private static List<Policy> getPolicies(String prefix, int count) { |   private static List<Policy> getPolicies(String prefix) { | ||||||
|     List<Policy> policies = new ArrayList<>(count); |     List<Policy> policies = new ArrayList<>(3); | ||||||
|     for (int i = 1; i <= count; i++) { |     for (int i = 1; i <= 3; i++) { | ||||||
|       String name = prefix + "_policy_" + i; |       String name = prefix + "_policy_" + i; | ||||||
|       Policy policy = new Policy().withName(name).withId(UUID.randomUUID()).withRules(getRules(name, 3)); |       Policy policy = new Policy().withName(name).withId(UUID.randomUUID()).withRules(getRules(name)); | ||||||
|       policies.add(policy); |       policies.add(policy); | ||||||
|       PolicyCache.POLICY_CACHE.put(policy.getId(), PolicyCache.getInstance().getRules(policy)); |       PolicyCache.POLICY_CACHE.put(policy.getId(), PolicyCache.getInstance().getRules(policy)); | ||||||
|     } |     } | ||||||
|     return policies; |     return policies; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   private static List<Rule> getRules(String prefix, int count) { |   private static List<Rule> getRules(String prefix) { | ||||||
|     List<Rule> rules = new ArrayList<>(count); |     List<Rule> rules = new ArrayList<>(3); | ||||||
|     for (int i = 1; i <= count; i++) { |     for (int i = 1; i <= 3; i++) { | ||||||
|       rules.add(new Rule().withName(prefix + "rule" + count)); |       rules.add(new Rule().withName(prefix + "rule" + 3)); | ||||||
|     } |     } | ||||||
|     return rules; |     return rules; | ||||||
|   } |   } | ||||||
| @ -234,14 +241,14 @@ public class SubjectContextTest { | |||||||
| 
 | 
 | ||||||
|   private static List<String> getAllTeamPolicies(List<Role> roles, List<Policy> policies) { |   private static List<String> getAllTeamPolicies(List<Role> roles, List<Policy> policies) { | ||||||
|     List<String> list = new ArrayList<>(); |     List<String> list = new ArrayList<>(); | ||||||
|     list.addAll(getPolicyListFromRoles(roles)); |     listOrEmpty(list).addAll(getPolicyListFromRoles(roles)); | ||||||
|     list.addAll(getPolicyList(policies)); |     listOrEmpty(list).addAll(getPolicyList(policies)); | ||||||
|     return list; |     return list; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   private static List<String> getPolicyListFromRoles(List<Role> roles) { |   private static List<String> getPolicyListFromRoles(List<Role> roles) { | ||||||
|     List<String> list = new ArrayList<>(); |     List<String> list = new ArrayList<>(); | ||||||
|     roles.forEach(r -> list.addAll(getPolicyRefList(r.getPolicies()))); |     listOrEmpty(roles).forEach(r -> list.addAll(getPolicyRefList(r.getPolicies()))); | ||||||
|     return list; |     return list; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
| @ -270,19 +277,6 @@ public class SubjectContextTest { | |||||||
|     return team; |     return team; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   void assertUserPolicyIterator(Iterator<PolicyContext> actualPolicyIterator) { |  | ||||||
|     // |  | ||||||
|     // Check iteration order of the policies |  | ||||||
|     // |  | ||||||
|     List<String> expectedPolicyOrder = new ArrayList<>(); |  | ||||||
|     expectedPolicyOrder.addAll(getPolicyListFromRoles(userRoles)); // First polices associated with user roles |  | ||||||
|     expectedPolicyOrder.addAll(getAllTeamPolicies(team111Roles, team111Policies)); // Next parent team111 policies |  | ||||||
|     expectedPolicyOrder.addAll(getAllTeamPolicies(team11Roles, team11Policies)); // Next team111 parent team11 policies |  | ||||||
|     expectedPolicyOrder.addAll(getAllTeamPolicies(team1Roles, team1Policies)); // Next team11 parent team1 policies |  | ||||||
|     expectedPolicyOrder.addAll(getAllTeamPolicies(team12Roles, team12Policies)); // Next team111 parent team12 policies |  | ||||||
|     assertPolicyIterator(expectedPolicyOrder, actualPolicyIterator); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   void assertPolicyIterator(List<String> expectedPolicyOrder, Iterator<PolicyContext> actualPolicyIterator) { |   void assertPolicyIterator(List<String> expectedPolicyOrder, Iterator<PolicyContext> actualPolicyIterator) { | ||||||
|     int count = 0; |     int count = 0; | ||||||
|     while (actualPolicyIterator.hasNext()) { |     while (actualPolicyIterator.hasNext()) { | ||||||
|  | |||||||
| @ -18,9 +18,4 @@ public @interface Function { | |||||||
|   String[] examples(); |   String[] examples(); | ||||||
| 
 | 
 | ||||||
|   ParameterType paramInputType() default ParameterType.NOT_REQUIRED; |   ParameterType paramInputType() default ParameterType.NOT_REQUIRED; | ||||||
|   /** |  | ||||||
|    * Some functions are used for capturing resource based rules where policies are applied based on resource being |  | ||||||
|    * accessed and team hierarchy the resource belongs to instead of the subject. |  | ||||||
|    */ |  | ||||||
|   boolean resourceBased() default false; |  | ||||||
| } | } | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Suresh Srinivas
						Suresh Srinivas