mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-10-29 17:49:14 +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.EventSubscription;
|
||||
import org.openmetadata.schema.entity.events.SubscriptionStatus;
|
||||
import org.openmetadata.schema.type.EntityReference;
|
||||
import org.openmetadata.service.Entity;
|
||||
import org.openmetadata.service.events.EventPubSub;
|
||||
import org.openmetadata.service.events.subscription.AlertUtil;
|
||||
@ -75,18 +74,11 @@ public class EventSubscriptionRepository extends EntityRepository<EventSubscript
|
||||
|
||||
@Override
|
||||
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);
|
||||
|
||||
// Restore the relationships
|
||||
entity.withOwner(owner);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void storeRelationships(EventSubscription entity) {
|
||||
// store owner
|
||||
storeOwner(entity, entity.getOwner());
|
||||
}
|
||||
|
||||
|
||||
@ -13,7 +13,6 @@
|
||||
|
||||
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.nullOrEmpty;
|
||||
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
|
||||
}
|
||||
// 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);
|
||||
processRecord = false;
|
||||
}
|
||||
|
||||
@ -13,7 +13,6 @@
|
||||
|
||||
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.nullOrEmpty;
|
||||
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
|
||||
}
|
||||
// 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);
|
||||
processRecord = false;
|
||||
}
|
||||
|
||||
@ -61,12 +61,6 @@ public final class CollectionRegistry {
|
||||
/** 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<>();
|
||||
|
||||
/**
|
||||
* 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 */
|
||||
@VisibleForTesting private final List<Object> testResources = new ArrayList<>();
|
||||
|
||||
@ -137,10 +131,6 @@ public final class CollectionRegistry {
|
||||
.withParameterInputType(annotation.paramInputType());
|
||||
functionList.add(function);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,7 +7,6 @@ import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.openmetadata.schema.entity.policies.accessControl.Rule;
|
||||
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.ResourcePermission;
|
||||
import org.openmetadata.service.exception.CatalogExceptionMessage;
|
||||
import org.openmetadata.service.resources.CollectionRegistry;
|
||||
import org.openmetadata.service.security.AuthorizationException;
|
||||
import org.openmetadata.service.security.policyevaluator.SubjectContext.PolicyContext;
|
||||
import org.springframework.expression.Expression;
|
||||
@ -27,7 +25,6 @@ import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||
public class CompiledRule extends Rule {
|
||||
private static final SpelExpressionParser EXPRESSION_PARSER = new SpelExpressionParser();
|
||||
@JsonIgnore private Expression expression;
|
||||
@JsonIgnore @Getter private boolean resourceBased = false;
|
||||
|
||||
public CompiledRule(Rule rule) {
|
||||
super();
|
||||
@ -72,13 +69,6 @@ public class CompiledRule extends Rule {
|
||||
}
|
||||
if (expression == null) {
|
||||
expression = parseExpression(getCondition());
|
||||
List<String> resourceBasedFunctions = CollectionRegistry.getInstance().getResourceBasedFunctions();
|
||||
for (String function : resourceBasedFunctions) {
|
||||
if (getCondition().contains(function)) {
|
||||
resourceBased = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return expression;
|
||||
}
|
||||
|
||||
@ -50,7 +50,7 @@ public class PolicyCache {
|
||||
public static void initialize() {
|
||||
if (!INITIALIZED) {
|
||||
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);
|
||||
FIELDS = POLICY_REPOSITORY.getFields("rules");
|
||||
INITIALIZED = true;
|
||||
|
||||
@ -63,18 +63,12 @@ public class PolicyEvaluator {
|
||||
@NonNull ResourceContextInterface resourceContext,
|
||||
@NonNull OperationContext operationContext)
|
||||
throws IOException {
|
||||
// First run through all the DENY policies based on the user
|
||||
// First run through all the DENY policies
|
||||
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
|
||||
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
|
||||
throw new AuthorizationException(
|
||||
CatalogExceptionMessage.permissionNotAllowed(
|
||||
@ -83,33 +77,17 @@ public class PolicyEvaluator {
|
||||
}
|
||||
|
||||
private static void evaluateDenySubjectPolicies(
|
||||
SubjectContext subjectContext, ResourceContextInterface resourceContext, OperationContext operationContext) {
|
||||
evaluatePolicies(subjectContext.getPolicies(), subjectContext, resourceContext, operationContext, true, false);
|
||||
SubjectContext subjectContext, ResourceContextInterface resourceContext, OperationContext operationContext)
|
||||
throws IOException {
|
||||
Iterator<PolicyContext> policyIterator = subjectContext.getPolicies(resourceContext.getOwner());
|
||||
evaluatePolicies(policyIterator, subjectContext, resourceContext, operationContext, true);
|
||||
}
|
||||
|
||||
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)
|
||||
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, 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);
|
||||
Iterator<PolicyContext> policyIterator = subjectContext.getPolicies(resourceContext.getOwner());
|
||||
evaluatePolicies(policyIterator, subjectContext, resourceContext, operationContext, false);
|
||||
}
|
||||
|
||||
private static void evaluatePolicies(
|
||||
@ -117,16 +95,12 @@ public class PolicyEvaluator {
|
||||
SubjectContext subjectContext,
|
||||
ResourceContextInterface resourceContext,
|
||||
OperationContext operationContext,
|
||||
boolean evaluateDeny,
|
||||
boolean evaluateResourcePolicies) {
|
||||
boolean evaluateDeny) {
|
||||
// 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
|
||||
while (policies.hasNext() && !operationContext.getOperations().isEmpty()) {
|
||||
PolicyContext context = policies.next();
|
||||
for (CompiledRule rule : context.getRules()) {
|
||||
if (evaluateResourcePolicies && !rule.isResourceBased()) {
|
||||
continue; // Only evaluate resource based rules
|
||||
}
|
||||
LOG.debug(
|
||||
"evaluating policy for {} {}:{}:{}",
|
||||
evaluateDeny ? "deny" : "allow",
|
||||
@ -146,7 +120,7 @@ public class PolicyEvaluator {
|
||||
public static List<ResourcePermission> listPermission(@NonNull SubjectContext subjectContext) {
|
||||
Map<String, ResourcePermission> resourcePermissionMap = initResourcePermissions();
|
||||
|
||||
Iterator<PolicyContext> policies = subjectContext.getPolicies();
|
||||
Iterator<PolicyContext> policies = subjectContext.getPolicies(null);
|
||||
while (policies.hasNext()) {
|
||||
PolicyContext policyContext = policies.next();
|
||||
for (CompiledRule rule : policyContext.getRules()) {
|
||||
@ -177,7 +151,7 @@ public class PolicyEvaluator {
|
||||
ResourcePermission resourcePermission = getResourcePermission(resourceType, Access.NOT_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()) {
|
||||
PolicyContext policyContext = policies.next();
|
||||
for (CompiledRule rule : policyContext.getRules()) {
|
||||
@ -194,7 +168,7 @@ public class PolicyEvaluator {
|
||||
ResourcePermission resourcePermission = getResourcePermission(resourceContext.getResource(), Access.NOT_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()) {
|
||||
PolicyContext policyContext = policies.next();
|
||||
for (CompiledRule rule : policyContext.getRules()) {
|
||||
@ -202,27 +176,6 @@ public class PolicyEvaluator {
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@ -39,8 +39,7 @@ public class RuleEvaluator {
|
||||
name = "isOwner",
|
||||
input = "none",
|
||||
description = "Returns true if the logged in user is the owner of the entity being accessed",
|
||||
examples = {"isOwner()", "!isOwner", "noOwner() || isOwner()"},
|
||||
resourceBased = true)
|
||||
examples = {"isOwner()", "!isOwner", "noOwner() || isOwner()"})
|
||||
public boolean isOwner() throws IOException {
|
||||
return subjectContext != null && subjectContext.isOwner(resourceContext.getOwner());
|
||||
}
|
||||
@ -49,8 +48,7 @@ public class RuleEvaluator {
|
||||
name = "matchAllTags",
|
||||
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",
|
||||
examples = {"matchAllTags('PersonalData.Personal', 'Tier.Tier1', 'Business Glossary.Clothing')"},
|
||||
resourceBased = true)
|
||||
examples = {"matchAllTags('PersonalData.Personal', 'Tier.Tier1', 'Business Glossary.Clothing')"})
|
||||
public boolean matchAllTags(String... tagFQNs) throws IOException {
|
||||
if (resourceContext == null) {
|
||||
return false;
|
||||
@ -70,8 +68,7 @@ public class RuleEvaluator {
|
||||
name = "matchAnyTag",
|
||||
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",
|
||||
examples = {"matchAnyTag('PersonalData.Personal', 'Tier.Tier1', 'Business Glossary.Clothing')"},
|
||||
resourceBased = true)
|
||||
examples = {"matchAnyTag('PersonalData.Personal', 'Tier.Tier1', 'Business Glossary.Clothing')"})
|
||||
public boolean matchAnyTag(String... tagFQNs) throws IOException {
|
||||
if (resourceContext == null) {
|
||||
return false;
|
||||
@ -93,16 +90,48 @@ public class RuleEvaluator {
|
||||
description =
|
||||
"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.",
|
||||
examples = {"matchTeam()"},
|
||||
resourceBased = true)
|
||||
examples = {"matchTeam()"})
|
||||
public boolean matchTeam() throws IOException {
|
||||
if (resourceContext == null || resourceContext.getOwner() == null) {
|
||||
return true; // No ownership information
|
||||
return false; // No ownership information
|
||||
}
|
||||
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())
|
||||
&& 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 {
|
||||
try {
|
||||
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 */
|
||||
public boolean isInTeam(String parentTeam, List<EntityReference> teams) {
|
||||
public boolean isInTeam(String parentTeam, EntityReference team) {
|
||||
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()) {
|
||||
Team parent = getTeam(stack.pop().getId());
|
||||
if (parent.getName().equals(parentTeam)) {
|
||||
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 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() {
|
||||
LOG.info("Subject cache is cleaned up");
|
||||
USER_CACHE.invalidateAll();
|
||||
|
||||
@ -16,7 +16,6 @@ package org.openmetadata.service.security.policyevaluator;
|
||||
import static org.openmetadata.common.utils.CommonUtil.listOrEmpty;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
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 */
|
||||
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 */
|
||||
@ -74,38 +78,30 @@ public class SubjectContext {
|
||||
return subjectContext.isUserUnderTeam(parentTeam);
|
||||
} else if (owner.getType().equals(Entity.TEAM)) {
|
||||
Team team = SubjectCache.getInstance().getTeam(owner.getId());
|
||||
return isInTeam(parentTeam, List.of(team.getEntityReference()));
|
||||
return isInTeam(parentTeam, team.getEntityReference());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Return true if given list of teams is part of the hierarchy of parentTeam */
|
||||
private boolean isInTeam(String parentTeam, List<EntityReference> teams) {
|
||||
return SubjectCache.getInstance().isInTeam(parentTeam, teams);
|
||||
/** Return true if the team is part of the hierarchy of parentTeam */
|
||||
private boolean isInTeam(String parentTeam, EntityReference team) {
|
||||
return SubjectCache.getInstance().isInTeam(parentTeam, team);
|
||||
}
|
||||
|
||||
// Iterate over all the policies of the team hierarchy the user belongs to
|
||||
public Iterator<PolicyContext> getPolicies() {
|
||||
return new UserPolicyIterator(user, 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 Iterator<PolicyContext> getPolicies(EntityReference resourceOwner) {
|
||||
return new UserPolicyIterator(user, resourceOwner, new ArrayList<>());
|
||||
}
|
||||
|
||||
public List<EntityReference> 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
|
||||
static class PolicyContext {
|
||||
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> {
|
||||
|
||||
// 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<>();
|
||||
|
||||
/** Policy iterator for a user */
|
||||
UserPolicyIterator(User user, List<UUID> teamsVisited) {
|
||||
UserPolicyIterator(User user, EntityReference resourceOwner, List<UUID> teamsVisited) {
|
||||
this.user = user;
|
||||
|
||||
// Iterate over policies in user role
|
||||
@ -231,13 +227,19 @@ public class SubjectContext {
|
||||
iterators.add(new RolePolicyIterator(Entity.USER, user.getName(), user.getRoles()));
|
||||
}
|
||||
|
||||
// Next, iterate over policies of teams to which the user belongs to
|
||||
// Note that ** Bots don't inherit policies or default roles from teams **
|
||||
if (!Boolean.TRUE.equals(user.getIsBot())) {
|
||||
// Finally, iterate over policies of teams to which the user belongs to
|
||||
// Note that ** Bots don't inherit policies or default roles from teams **
|
||||
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
|
||||
@ -248,7 +250,7 @@ public class SubjectContext {
|
||||
}
|
||||
iteratorIndex++;
|
||||
}
|
||||
LOG.debug("Subject {} policy iteration done" + user.getName());
|
||||
LOG.debug("Subject {} policy iteration done", user.getName());
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -270,21 +272,21 @@ public class SubjectContext {
|
||||
private final List<Iterator<PolicyContext>> iterators = new ArrayList<>();
|
||||
|
||||
/** 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);
|
||||
|
||||
// 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
|
||||
if (!teamsVisited.contains(teamId)) {
|
||||
teamsVisited.add(teamId);
|
||||
if (team.getDefaultRoles() != null) {
|
||||
if (!skipRoles && team.getDefaultRoles() != null) {
|
||||
iterators.add(new RolePolicyIterator(Entity.TEAM, team.getName(), team.getDefaultRoles()));
|
||||
}
|
||||
if (team.getPolicies() != null) {
|
||||
iterators.add(new PolicyIterator(Entity.TEAM, team.getName(), null, team.getPolicies()));
|
||||
}
|
||||
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.assertTrue;
|
||||
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 java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.stubbing.Answer;
|
||||
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.EntityReference;
|
||||
import org.openmetadata.schema.type.TagLabel;
|
||||
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.TeamDAO;
|
||||
import org.openmetadata.service.jdbi3.CollectionDAO.UserDAO;
|
||||
import org.openmetadata.service.jdbi3.RoleRepository;
|
||||
import org.openmetadata.service.jdbi3.TableRepository;
|
||||
import org.openmetadata.service.jdbi3.TeamRepository;
|
||||
import org.openmetadata.service.jdbi3.UserRepository;
|
||||
import org.openmetadata.service.security.policyevaluator.SubjectContext.PolicyContext;
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||
|
||||
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 EvaluationContext evaluationContext;
|
||||
private static SubjectContext subjectContext;
|
||||
private static ResourceContext resourceContext;
|
||||
|
||||
@BeforeAll
|
||||
public static void setup() {
|
||||
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(Role.class, Entity.ROLE, Mockito.mock(RoleDAO.class), Mockito.mock(RoleRepository.class));
|
||||
SubjectCache.initialize();
|
||||
RoleCache.initialize();
|
||||
|
||||
TableRepository tableRepository = Mockito.mock(TableRepository.class);
|
||||
Mockito.when(tableRepository.getAllTags(any()))
|
||||
@ -43,18 +56,24 @@ class RuleEvaluatorTest {
|
||||
Entity.registerEntity(Table.class, Entity.TABLE, Mockito.mock(TableDAO.class), tableRepository);
|
||||
|
||||
user = new User().withId(UUID.randomUUID()).withName("user");
|
||||
ResourceContext resourceContext =
|
||||
resourceContext =
|
||||
ResourceContext.builder()
|
||||
.resource("table")
|
||||
.entity(table)
|
||||
.entityRepository(Mockito.mock(TableRepository.class))
|
||||
.build();
|
||||
|
||||
SubjectContext subjectContext = new SubjectContext(user);
|
||||
subjectContext = new SubjectContext(user);
|
||||
RuleEvaluator ruleEvaluator = new RuleEvaluator(null, subjectContext, resourceContext);
|
||||
evaluationContext = new StandardEvaluationContext(ruleEvaluator);
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
public static void cleanup() {
|
||||
SubjectCache.cleanUp();
|
||||
RoleCache.cleanUp();
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_noOwner() {
|
||||
// Set no owner to the entity and test noOwner method
|
||||
@ -133,6 +152,99 @@ class RuleEvaluatorTest {
|
||||
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) {
|
||||
return parseExpression(condition).getValue(evaluationContext, Boolean.class);
|
||||
}
|
||||
@ -144,4 +256,42 @@ class RuleEvaluatorTest {
|
||||
}
|
||||
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.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.openmetadata.common.utils.CommonUtil.listOrEmpty;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
@ -45,19 +46,25 @@ import org.openmetadata.service.security.policyevaluator.SubjectContext.PolicyCo
|
||||
public class SubjectContextTest {
|
||||
private static List<Role> team1Roles;
|
||||
private static List<Policy> team1Policies;
|
||||
private static Team team1;
|
||||
|
||||
private static List<Role> team11Roles;
|
||||
private static List<Policy> team11Policies;
|
||||
private static Team team11;
|
||||
|
||||
private static List<Role> team12Roles;
|
||||
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<Policy> team111Policies;
|
||||
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 User user;
|
||||
|
||||
@ -73,32 +80,40 @@ public class SubjectContextTest {
|
||||
SubjectCache.initialize();
|
||||
|
||||
// Create team hierarchy:
|
||||
// team1, has team11, team12, team13 as children
|
||||
// team11 has team111 as children
|
||||
// team111 has user as children
|
||||
// Each with 3 roles and 3 policies
|
||||
team1Roles = getRoles("team1", 3);
|
||||
team1Policies = getPolicies("team1", 3);
|
||||
team1 = createTeam("team1", team1Roles, team1Policies, null);
|
||||
// team1
|
||||
// / | \
|
||||
// team11 team12 team13
|
||||
// / / \
|
||||
// team111 / team131
|
||||
// \ /
|
||||
// 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);
|
||||
team11Policies = getPolicies("team11", 3);
|
||||
team11 = createTeam("team11", team11Roles, team11Policies, List.of(team1));
|
||||
team11Roles = getRoles("team11");
|
||||
team11Policies = getPolicies("team11");
|
||||
Team team11 = createTeam("team11", team11Roles, team11Policies, List.of(team1));
|
||||
|
||||
team12Roles = getRoles("team12", 3);
|
||||
team12Policies = getPolicies("team12", 3);
|
||||
team12Roles = getRoles("team12");
|
||||
team12Policies = getPolicies("team12");
|
||||
Team team12 = createTeam("team12", team12Roles, team12Policies, List.of(team1));
|
||||
|
||||
List<Role> team13Roles = getRoles("team13", 3);
|
||||
List<Policy> team13Policies = getPolicies("team13", 3);
|
||||
createTeam("team13", team13Roles, team13Policies, List.of(team1));
|
||||
team13Roles = getRoles("team13");
|
||||
team13Policies = getPolicies("team13");
|
||||
team13 = createTeam("team13", team13Roles, team13Policies, List.of(team1));
|
||||
|
||||
team111Roles = getRoles("team111", 3);
|
||||
team111Policies = getPolicies("team111", 3);
|
||||
team111Roles = getRoles("team111");
|
||||
team111Policies = getPolicies("team111");
|
||||
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
|
||||
userRoles = getRoles("user", 3);
|
||||
userRoles = getRoles("user");
|
||||
List<EntityReference> userRolesRef = toEntityReferences(userRoles);
|
||||
user = new User().withName("user").withRoles(userRolesRef).withTeams(List.of(team111.getEntityReference()));
|
||||
SubjectCache.USER_CACHE.put("user", new SubjectContext(user));
|
||||
@ -113,12 +128,36 @@ public class SubjectContextTest {
|
||||
|
||||
@Test
|
||||
void testPolicyIterator() {
|
||||
//
|
||||
// Check iteration order of the policies
|
||||
//
|
||||
// Check iteration order of the policies without resourceOwner
|
||||
SubjectContext subjectContext = SubjectCache.getInstance().getSubjectContext(user.getName());
|
||||
Iterator<PolicyContext> policyContextIterator = subjectContext.getPolicies();
|
||||
assertUserPolicyIterator(policyContextIterator);
|
||||
Iterator<PolicyContext> policyContextIterator = subjectContext.getPolicies(null);
|
||||
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
|
||||
@ -160,44 +199,12 @@ public class SubjectContextTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testResourcePolicyIterator() {
|
||||
// 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) {
|
||||
private static List<Role> getRoles(String prefix) {
|
||||
// Create roles with 3 policies each and each policy with 3 rules
|
||||
List<Role> roles = new ArrayList<>(count);
|
||||
for (int i = 1; i <= count; i++) {
|
||||
List<Role> roles = new ArrayList<>(3);
|
||||
for (int i = 1; i <= 3; 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);
|
||||
RoleCache.ROLE_CACHE.put(role.getId(), role);
|
||||
roles.add(role);
|
||||
@ -205,21 +212,21 @@ public class SubjectContextTest {
|
||||
return roles;
|
||||
}
|
||||
|
||||
private static List<Policy> getPolicies(String prefix, int count) {
|
||||
List<Policy> policies = new ArrayList<>(count);
|
||||
for (int i = 1; i <= count; i++) {
|
||||
private static List<Policy> getPolicies(String prefix) {
|
||||
List<Policy> policies = new ArrayList<>(3);
|
||||
for (int i = 1; i <= 3; 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);
|
||||
PolicyCache.POLICY_CACHE.put(policy.getId(), PolicyCache.getInstance().getRules(policy));
|
||||
}
|
||||
return policies;
|
||||
}
|
||||
|
||||
private static List<Rule> getRules(String prefix, int count) {
|
||||
List<Rule> rules = new ArrayList<>(count);
|
||||
for (int i = 1; i <= count; i++) {
|
||||
rules.add(new Rule().withName(prefix + "rule" + count));
|
||||
private static List<Rule> getRules(String prefix) {
|
||||
List<Rule> rules = new ArrayList<>(3);
|
||||
for (int i = 1; i <= 3; i++) {
|
||||
rules.add(new Rule().withName(prefix + "rule" + 3));
|
||||
}
|
||||
return rules;
|
||||
}
|
||||
@ -234,14 +241,14 @@ public class SubjectContextTest {
|
||||
|
||||
private static List<String> getAllTeamPolicies(List<Role> roles, List<Policy> policies) {
|
||||
List<String> list = new ArrayList<>();
|
||||
list.addAll(getPolicyListFromRoles(roles));
|
||||
list.addAll(getPolicyList(policies));
|
||||
listOrEmpty(list).addAll(getPolicyListFromRoles(roles));
|
||||
listOrEmpty(list).addAll(getPolicyList(policies));
|
||||
return list;
|
||||
}
|
||||
|
||||
private static List<String> getPolicyListFromRoles(List<Role> roles) {
|
||||
List<String> list = new ArrayList<>();
|
||||
roles.forEach(r -> list.addAll(getPolicyRefList(r.getPolicies())));
|
||||
listOrEmpty(roles).forEach(r -> list.addAll(getPolicyRefList(r.getPolicies())));
|
||||
return list;
|
||||
}
|
||||
|
||||
@ -270,19 +277,6 @@ public class SubjectContextTest {
|
||||
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) {
|
||||
int count = 0;
|
||||
while (actualPolicyIterator.hasNext()) {
|
||||
|
||||
@ -18,9 +18,4 @@ public @interface Function {
|
||||
String[] examples();
|
||||
|
||||
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