diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/Entity.java b/openmetadata-service/src/main/java/org/openmetadata/service/Entity.java index f91a9e2e07b..da6afe08d56 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/Entity.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/Entity.java @@ -108,7 +108,6 @@ public final class Entity { public static final String TEST_DEFINITION = "testDefinition"; public static final String TEST_CONNECTION_DEFINITION = "testConnectionDefinition"; public static final String WORKFLOW = "workflow"; - public static final String ALERT_ACTION = "alertAction"; public static final String TEST_SUITE = "testSuite"; public static final String KPI = "kpi"; public static final String TEST_CASE = "testCase"; @@ -290,7 +289,6 @@ public final class Entity { /** Retrieve the corresponding entity repository for a given entity name. */ public static EntityRepository getEntityRepository(@NonNull String entityType) { - @SuppressWarnings("unchecked") EntityRepository entityRepository = ENTITY_REPOSITORY_MAP.get(entityType); if (entityRepository == null) { throw EntityNotFoundException.byMessage(CatalogExceptionMessage.entityTypeNotFound(entityType)); @@ -301,7 +299,6 @@ public final class Entity { /** Retrieve the corresponding entity repository for a given entity name. */ public static EntityRepository getServiceEntityRepository( @NonNull ServiceType serviceType) { - @SuppressWarnings("unchecked") EntityRepository entityRepository = ENTITY_REPOSITORY_MAP.get(SERVICE_TYPE_ENTITY_MAP.get(serviceType)); if (entityRepository == null) { diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/PolicyRepository.java b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/PolicyRepository.java index c83ebd3210f..e0f97ceda8b 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/PolicyRepository.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/PolicyRepository.java @@ -47,6 +47,7 @@ import org.openmetadata.service.exception.CatalogExceptionMessage; import org.openmetadata.service.jdbi3.CollectionDAO.EntityRelationshipRecord; import org.openmetadata.service.resources.policies.PolicyResource; import org.openmetadata.service.security.policyevaluator.CompiledRule; +import org.openmetadata.service.security.policyevaluator.PolicyCache; import org.openmetadata.service.util.EntityUtil; import org.openmetadata.service.util.EntityUtil.Fields; @@ -118,6 +119,9 @@ public class PolicyRepository extends EntityRepository { EntityReference location = policy.getLocation(); policy.withLocation(null); store(policy, update); + if (update) { + PolicyCache.getInstance().invalidatePolicy(policy.getId()); + } policy.withLocation(location); } @@ -142,6 +146,12 @@ public class PolicyRepository extends EntityRepository { } } + @Override + protected void cleanup(Policy policy) throws IOException { + super.cleanup(policy); + PolicyCache.getInstance().invalidatePolicy(policy.getId()); + } + public void validateRules(Policy policy) { // Resolve JSON blobs into Rule object and perform schema based validation List rules = policy.getRules(); diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/RoleRepository.java b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/RoleRepository.java index d65b7557d5c..05d24dadf46 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/RoleRepository.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/RoleRepository.java @@ -32,6 +32,7 @@ import org.openmetadata.service.Entity; import org.openmetadata.service.exception.CatalogExceptionMessage; import org.openmetadata.service.jdbi3.CollectionDAO.EntityRelationshipRecord; import org.openmetadata.service.resources.teams.RoleResource; +import org.openmetadata.service.security.policyevaluator.RoleCache; import org.openmetadata.service.util.EntityUtil; import org.openmetadata.service.util.EntityUtil.Fields; @@ -94,6 +95,9 @@ public class RoleRepository extends EntityRepository { List policies = role.getPolicies(); role.withPolicies(null); store(role, update); + if (update) { + RoleCache.getInstance().invalidateRole(role.getId()); + } role.withPolicies(policies); } @@ -117,6 +121,12 @@ public class RoleRepository extends EntityRepository { } } + @Override + protected void cleanup(Role role) throws IOException { + super.cleanup(role); + RoleCache.getInstance().invalidateRole(role.getId()); + } + /** Handles entity updated from PUT and POST operation. */ public class RoleUpdater extends EntityUpdater { public RoleUpdater(Role original, Role updated, Operation operation) { diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/TeamRepository.java b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/TeamRepository.java index 4f1d3ecd68b..5d24a5418e7 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/TeamRepository.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/TeamRepository.java @@ -132,6 +132,9 @@ public class TeamRepository extends EntityRepository { team.withUsers(null).withDefaultRoles(null).withInheritedRoles(null); store(team, update); + if (update) { + SubjectCache.getInstance().invalidateTeam(team.getId()); + } // Restore the relationships team.withUsers(users) diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/UserRepository.java b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/UserRepository.java index 600d82ccfd3..93e0fa4cb34 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/UserRepository.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/UserRepository.java @@ -120,6 +120,9 @@ public class UserRepository extends EntityRepository { } store(user, update); + if (update) { + SubjectCache.getInstance().invalidateUser(user.getName()); + } // Restore the relationships user.withRoles(roles).withTeams(teams); @@ -142,6 +145,12 @@ public class UserRepository extends EntityRepository { SubjectCache.getInstance().invalidateUser(entity.getName()); } + @Override + protected void cleanup(User user) throws IOException { + super.cleanup(user); + SubjectCache.getInstance().invalidateUser(user.getName()); + } + @Override public User setFields(User user, Fields fields) throws IOException { user.setProfile(fields.contains("profile") ? user.getProfile() : null); diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/EntityResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/EntityResource.java index e3a919e0cd6..d2b17a8603c 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/EntityResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/EntityResource.java @@ -300,7 +300,6 @@ public abstract class EntityResource { })) JsonPatch patch) throws IOException { - Response response = patchInternal(uriInfo, securityContext, id, patch); - Policy policy = (Policy) response.getEntity(); - PolicyCache.getInstance().invalidatePolicy(policy.getId()); - return response; + return patchInternal(uriInfo, securityContext, id, patch); } @PUT @@ -393,9 +390,7 @@ public class PolicyResource extends EntityResource { @Context UriInfo uriInfo, @Context SecurityContext securityContext, @Valid CreatePolicy create) throws IOException { Policy policy = getPolicy(create, securityContext.getUserPrincipal().getName()); - Response response = createOrUpdate(uriInfo, securityContext, policy); - PolicyCache.getInstance().invalidatePolicy(policy.getId()); - return response; + return createOrUpdate(uriInfo, securityContext, policy); } @DELETE @@ -418,9 +413,7 @@ public class PolicyResource extends EntityResource { boolean hardDelete, @Parameter(description = "Id of the policy", schema = @Schema(type = "UUID")) @PathParam("id") UUID id) throws IOException { - Response response = delete(uriInfo, securityContext, id, false, hardDelete); - PolicyCache.getInstance().invalidatePolicy(id); - return response; + return delete(uriInfo, securityContext, id, false, hardDelete); } @DELETE diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/teams/RoleResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/teams/RoleResource.java index c589936c30c..a269e18daed 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/teams/RoleResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/teams/RoleResource.java @@ -323,9 +323,7 @@ public class RoleResource extends EntityResource { @Context UriInfo uriInfo, @Context SecurityContext securityContext, @Valid CreateRole createRole) throws IOException { Role role = getRole(createRole, securityContext.getUserPrincipal().getName()); - Response response = createOrUpdate(uriInfo, securityContext, role); - RoleCache.getInstance().invalidateRole(role.getId()); - return response; + return createOrUpdate(uriInfo, securityContext, role); } @PATCH @@ -351,10 +349,7 @@ public class RoleResource extends EntityResource { })) JsonPatch patch) throws IOException { - Response response = patchInternal(uriInfo, securityContext, id, patch); - Role role = (Role) response.getEntity(); - RoleCache.getInstance().invalidateRole(role.getId()); - return response; + return patchInternal(uriInfo, securityContext, id, patch); } @DELETE @@ -379,9 +374,7 @@ public class RoleResource extends EntityResource { throws IOException { // A role has a strong relationship with a policy. Recursively delete the policy that the role contains, to avoid // leaving a dangling policy without a role. - Response response = delete(uriInfo, securityContext, id, true, hardDelete); - RoleCache.getInstance().invalidateRole(id); - return response; + return delete(uriInfo, securityContext, id, true, hardDelete); } @DELETE diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/security/policyevaluator/ResourceContext.java b/openmetadata-service/src/main/java/org/openmetadata/service/security/policyevaluator/ResourceContext.java index 59766713c1a..96f3ba07bbf 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/security/policyevaluator/ResourceContext.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/security/policyevaluator/ResourceContext.java @@ -27,7 +27,7 @@ public class ResourceContext implements ResourceContextInterface { @NonNull private EntityRepository entityRepository; private UUID id; private String name; - @Getter private EntityInterface entity; // Will be lazily initialized + private EntityInterface entity; // Will be lazily initialized // Builder class added for getting around javadoc errors. This class will be filled in by lombok. public static class ResourceContextBuilder {} @@ -41,7 +41,7 @@ public class ResourceContext implements ResourceContextInterface { @Override public List getTags() throws IOException { resolveEntity(); - return entity == null ? Collections.EMPTY_LIST : Entity.getEntityTags(getResource(), entity); + return entity == null ? Collections.emptyList() : Entity.getEntityTags(getResource(), entity); } @Override diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/security/policyevaluator/SubjectContext.java b/openmetadata-service/src/main/java/org/openmetadata/service/security/policyevaluator/SubjectContext.java index 377c0c6b325..ad7a8158b2f 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/security/policyevaluator/SubjectContext.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/security/policyevaluator/SubjectContext.java @@ -223,7 +223,7 @@ public class SubjectContext { this.user = user; // Iterate over policies in user role - if (user.getRoles() != null) { + if (!listOrEmpty(user.getRoles()).isEmpty()) { iterators.add(new RolePolicyIterator(Entity.USER, user.getName(), user.getRoles())); } diff --git a/openmetadata-service/src/test/java/org/openmetadata/service/resources/EntityResourceTest.java b/openmetadata-service/src/test/java/org/openmetadata/service/resources/EntityResourceTest.java index 60e9cfd67cc..bb69a9ca918 100644 --- a/openmetadata-service/src/test/java/org/openmetadata/service/resources/EntityResourceTest.java +++ b/openmetadata-service/src/test/java/org/openmetadata/service/resources/EntityResourceTest.java @@ -30,7 +30,6 @@ import static org.openmetadata.common.utils.CommonUtil.listOrEmpty; import static org.openmetadata.csv.EntityCsvTest.assertSummary; import static org.openmetadata.schema.type.MetadataOperation.EDIT_ALL; import static org.openmetadata.schema.type.MetadataOperation.EDIT_TESTS; -import static org.openmetadata.schema.type.MetadataOperation.VIEW_ALL; import static org.openmetadata.service.Entity.ADMIN_USER_NAME; import static org.openmetadata.service.Entity.FIELD_DELETED; import static org.openmetadata.service.Entity.FIELD_EXTENSION; @@ -40,7 +39,6 @@ import static org.openmetadata.service.Entity.FIELD_TAGS; import static org.openmetadata.service.exception.CatalogExceptionMessage.ENTITY_ALREADY_EXISTS; import static org.openmetadata.service.exception.CatalogExceptionMessage.entityIsNotEmpty; import static org.openmetadata.service.exception.CatalogExceptionMessage.entityNotFound; -import static org.openmetadata.service.exception.CatalogExceptionMessage.permissionDenied; import static org.openmetadata.service.exception.CatalogExceptionMessage.permissionNotAllowed; import static org.openmetadata.service.exception.CatalogExceptionMessage.readOnlyAttribute; import static org.openmetadata.service.security.SecurityUtil.authHeaders; @@ -1616,61 +1614,6 @@ public abstract class EntityResourceTest getEntity(entity.getId(), authHeaders(userInTeam11.getName())), - FORBIDDEN, - permissionDenied( - userInTeam11.getName(), - disallowedOperation, - null, - TEAM_ONLY_POLICY.getName(), - TEAM_ONLY_POLICY_RULES.get(0).getName())); - assertResponse( - () -> getEntity(entity.getId(), authHeaders(userInTeam1.getName())), - FORBIDDEN, - permissionDenied( - userInTeam1.getName(), - disallowedOperation, - null, - TEAM_ONLY_POLICY.getName(), - TEAM_ONLY_POLICY_RULES.get(0).getName())); - } - @Test @Execution(ExecutionMode.CONCURRENT) void delete_systemEntity() throws IOException { diff --git a/openmetadata-service/src/test/java/org/openmetadata/service/resources/dqtests/TestCaseResourceTest.java b/openmetadata-service/src/test/java/org/openmetadata/service/resources/dqtests/TestCaseResourceTest.java index fc6ef09d70d..e140f84dd4d 100644 --- a/openmetadata-service/src/test/java/org/openmetadata/service/resources/dqtests/TestCaseResourceTest.java +++ b/openmetadata-service/src/test/java/org/openmetadata/service/resources/dqtests/TestCaseResourceTest.java @@ -8,7 +8,6 @@ import static javax.ws.rs.core.Response.Status.OK; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.openmetadata.schema.type.MetadataOperation.EDIT_TESTS; -import static org.openmetadata.schema.type.MetadataOperation.VIEW_TESTS; import static org.openmetadata.service.Entity.ADMIN_USER_NAME; import static org.openmetadata.service.exception.CatalogExceptionMessage.permissionNotAllowed; import static org.openmetadata.service.security.SecurityUtil.authHeaders; @@ -26,7 +25,6 @@ import java.io.IOException; import java.text.ParseException; import java.util.*; import javax.ws.rs.client.WebTarget; -import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.apache.http.client.HttpResponseException; import org.junit.jupiter.api.MethodOrderer; @@ -130,26 +128,6 @@ public class TestCaseResourceTest extends EntityResourceTest { + public static TableResourceTest tableTest = new TableResourceTest(); + public static TeamResourceTest teamTest = new TeamResourceTest(); public PolicyResourceTest() { super( @@ -381,12 +394,11 @@ public class PolicyResourceTest extends EntityResourceTest policies.add(createEntity(create, ADMIN_AUTH_HEADERS)); } - TeamResourceTest teamResourceTest = new TeamResourceTest(); List teams = new ArrayList<>(); for (int i = 0; i < 3; i++) { // Team X has Policy X - CreateTeam createTeam = teamResourceTest.createRequest(test, i).withPolicies(List.of(policies.get(i).getId())); - teams.add(teamResourceTest.createEntity(createTeam, ADMIN_AUTH_HEADERS)); + CreateTeam createTeam = teamTest.createRequest(test, i).withPolicies(List.of(policies.get(i).getId())); + teams.add(teamTest.createEntity(createTeam, ADMIN_AUTH_HEADERS)); } // Create a role with all the policies @@ -410,6 +422,132 @@ public class PolicyResourceTest extends EntityResourceTest assertEquals(expectedFunctions, actualFunctions); } + @Test + void test_roles_policies_scenarios() throws HttpResponseException, JsonProcessingException { + // + // Create a team hierarchy: + // - Organization has Team1 with user1 and Team2 with user2 + // - Team1 has Team11 with user11 and table11, and Team12 with user12 and table12 + // - Team2 has Team21 with user21 and Team22 with user22 + // - Team2 has DATA_STEWARD_ROLE which is inherited by user2, user21, and user22 + // + CreateTeam createTeam = teamTest.createRequest("rolesPoliciesTeam1").withTeamType(DEPARTMENT); + Team team1 = teamTest.createEntity(createTeam, ADMIN_AUTH_HEADERS); + createTeam = + teamTest + .createRequest("rolesPoliciesTeam2") + .withTeamType(DEPARTMENT) + .withDefaultRoles(listOf(DATA_STEWARD_ROLE.getId())); + Team team2 = teamTest.createEntity(createTeam, ADMIN_AUTH_HEADERS); + createTeam = teamTest.createRequest("rolesPoliciesTeam11").withParents(listOf(team1.getId())); + Team team11 = teamTest.createEntity(createTeam, ADMIN_AUTH_HEADERS); + createTeam = teamTest.createRequest("rolesPoliciesTeam12").withParents(listOf(team1.getId())); + Team team12 = teamTest.createEntity(createTeam, ADMIN_AUTH_HEADERS); + createTeam = teamTest.createRequest("rolesPoliciesTeam21").withParents(listOf(team2.getId())); + Team team21 = teamTest.createEntity(createTeam, ADMIN_AUTH_HEADERS); + createTeam = teamTest.createRequest("rolesPoliciesTeam22").withParents(listOf(team2.getId())); + Team team22 = teamTest.createEntity(createTeam, ADMIN_AUTH_HEADERS); + + // Create users - Team2 has default role DataSteward + UserResourceTest userTest = new UserResourceTest(); + CreateUser createUser = userTest.createRequest("rolesAndPoliciesUser1").withTeams(listOf(team1.getId())); + User user1 = userTest.createEntity(createUser, ADMIN_AUTH_HEADERS); + createUser = userTest.createRequest("rolesAndPoliciesUser2").withTeams(listOf(team2.getId())); + User user2 = userTest.createEntity(createUser, ADMIN_AUTH_HEADERS); + createUser = userTest.createRequest("rolesAndPoliciesUser11").withTeams(listOf(team11.getId())); + User user11 = userTest.createEntity(createUser, ADMIN_AUTH_HEADERS); + createUser = userTest.createRequest("rolesAndPoliciesUser12").withTeams(listOf(team12.getId())); + User user12 = userTest.createEntity(createUser, ADMIN_AUTH_HEADERS); + createUser = userTest.createRequest("rolesAndPoliciesUser21").withTeams(listOf(team21.getId())); + User user21 = userTest.createEntity(createUser, ADMIN_AUTH_HEADERS); + createUser = userTest.createRequest("rolesAndPoliciesUser22").withTeams(listOf(team22.getId())); + User user22 = userTest.createEntity(createUser, ADMIN_AUTH_HEADERS); + + // Create resources - table11 has PII sensitive tags + CreateTable createTable = + tableTest + .createRequest("rolesAndPoliciesTable11") + .withOwner(team11.getEntityReference()) + .withTags(listOf(PII_SENSITIVE_TAG_LABEL)); + Table table11 = tableTest.createEntity(createTable, ADMIN_AUTH_HEADERS); + + // table12 does not have PII + createTable = tableTest.createRequest("rolesAndPoliciesTable12").withOwner(team12.getEntityReference()); + createTable.getColumns().forEach(c -> c.withTags(null)); // Clear all the tag labels + Table table12 = tableTest.createEntity(createTable, ADMIN_AUTH_HEADERS); + + // Create policies + Policy denyAllPIIAccess = createPolicy("disallowAllPIIAccess", "matchAnyTag('PII.Sensitive')"); + Policy denyPIIAccessExceptTeam11 = + createPolicy("denyPIIAccessExceptTeam11", "matchAnyTag('PII.Sensitive') && !inAnyTeam('rolesPoliciesTeam11')"); + Policy denyPIIAccessExceptTeam1 = + createPolicy("denyPIIAccessExceptTeam1", "matchAnyTag('PII.Sensitive') && !inAnyTeam('rolesPoliciesTeam1')"); + Policy denyPIIAccessExceptRole = + createPolicy("denyPIIAccessExceptRole", "matchAnyTag('PII.Sensitive') && !hasAnyRole('DataSteward')"); + + // Array made of team, policy to attach to the team, allowed users list, denied users list, entity being accessed + Object[][] scenarios = { + // 0 - TEAM_ONLY_POLICY attached to team11. Only user11 in team11 has access + {team11, TEAM_ONLY_POLICY, listOf(user11), listOf(user1, user2, user12, user21, user22), table11}, + // 1 - TEAM_ONLY_POLICY attached to team1. All the users under team1 have access + {team1, TEAM_ONLY_POLICY, listOf(user1, user11, user12), listOf(user2, user21, user22), table11}, + // 2 - denyPIIAccess attached to team1. No users can access table11 that has PII + {team1, denyAllPIIAccess, emptyList(), listOf(user1, user2, user11, user12, user21, user22), table11}, + // 3 - denyPIIAccess attached to team1. All users can access table12 that has no PII + {team1, denyAllPIIAccess, listOf(user1, user11, user12, user2, user21, user22), emptyList(), table12}, + // 4 - denyPIIAccessExceptTeam11 attached to team1. Only Team11 users can access table11 with PII + {team1, denyPIIAccessExceptTeam11, listOf(user11), listOf(user1, user2, user12, user21, user22), table11}, + // 5 - denyPIIAccessExceptTeam11 attached to team1. All users can access table12 that has no PII + {team1, denyPIIAccessExceptTeam11, listOf(user1, user11, user12, user2, user21, user22), emptyList(), table12}, + // 6 - denyPIIAccessExceptTeam11 attached to team11. Only Team11 users can access table11 with PII + {team11, denyPIIAccessExceptTeam11, listOf(user11), listOf(user1, user2, user12, user21, user22), table11}, + // 7 - denyPIIAccessExceptTeam11 attached to team11. All users can access table12 that has no PII + {team11, denyPIIAccessExceptTeam11, listOf(user1, user11, user12, user2, user21, user22), emptyList(), table12}, + // 8 - denyPIIAccessExceptTeam1 attached to team1. Only Team1 users can access table11 with PII + {team1, denyPIIAccessExceptTeam1, listOf(user1, user11, user12), listOf(user2, user21, user22), table11}, + // 9 - denyPIIAccessExceptTeam1 attached to team1. All users can access table12 that has no PII + {team1, denyPIIAccessExceptTeam1, listOf(user1, user11, user12, user2, user21, user22), emptyList(), table12}, + // 10 - denyPIIAccessExceptRole attached to team1. Only user2, user21 and user22 with DataStewardRole can access + {team1, denyPIIAccessExceptRole, listOf(user2, user21, user22), listOf(user1, user11, user12), table11}, + // 11- denyPIIAccessExceptRole attached to team1. All users can access table12 that has no PII + {team1, denyPIIAccessExceptTeam1, listOf(user1, user11, user12, user2, user21, user22), emptyList(), table12}, + }; + for (int i = 0; i < scenarios.length; i++) { + Object[] scenario = scenarios[i]; + testScenario(i, scenario); + } + } + + private Policy createPolicy(String name, String condition) throws HttpResponseException { + Rule rule = + new Rule() + .withName(name) + .withResources(listOf(ALL_RESOURCES)) + .withOperations(listOf(MetadataOperation.ALL)) + .withEffect(DENY) + .withCondition(condition); + CreatePolicy createPolicy = createRequest(name).withRules(listOf(rule)); + return createEntity(createPolicy, ADMIN_AUTH_HEADERS); + } + + @SuppressWarnings("unchecked") + private void testScenario(int index, Object[] scenario) throws HttpResponseException, JsonProcessingException { + Team team = (Team) scenario[0]; + Policy policy = (Policy) scenario[1]; + List allowedUsers = (List) scenario[2]; + List disallowedUsers = (List) scenario[3]; + Table table = (Table) scenario[4]; + addTeamPolicy(team, policy); + LOG.info( + "Testing scenario at {} with team:{} policy:{} table:{}", + index, + team.getName(), + policy.getName(), + table.getName()); + checkAccess(allowedUsers, disallowedUsers, table); + removeTeamPolicy(team); + } + @Override public Policy validateGetWithDifferentFields(Policy policy, boolean byName) throws HttpResponseException { String fields = ""; @@ -465,4 +603,28 @@ public class PolicyResourceTest extends EntityResourceTest String name, List resources, List operations, Effect effect) { return new Rule().withName(name).withResources(resources).withOperations(operations).withEffect(effect); } + + private void addTeamPolicy(Team team, Policy policy) throws JsonProcessingException, HttpResponseException { + String json = JsonUtils.pojoToJson(team); + team.setPolicies(listOf(policy.getEntityReference())); + teamTest.patchEntity(team.getId(), json, team, ADMIN_AUTH_HEADERS); + } + + private void removeTeamPolicy(Team team) throws JsonProcessingException, HttpResponseException { + String json = JsonUtils.pojoToJson(team); + team.setPolicies(null); + teamTest.patchEntity(team.getId(), json, team, ADMIN_AUTH_HEADERS); + } + + private void checkAccess(List allowedUsers, List deniedUsers, Table table) throws HttpResponseException { + for (User allowed : allowedUsers) { + LOG.info("Expecting access allowed for user:{}", allowed.getName()); + assertNotNull(tableTest.getEntity(table.getId(), "", authHeaders(allowed.getName()))); + } + for (User deniedUser : deniedUsers) { + LOG.info("Expecting access denied for user:{}", deniedUser.getName()); + assertResponseContains( + () -> tableTest.getEntity(table.getId(), "", authHeaders(deniedUser.getName())), FORBIDDEN, "denied"); + } + } }