Fixes #10791 Add tests for various Resource & Policy scenarios (#10792)

This commit is contained in:
Suresh Srinivas 2023-03-28 18:30:53 -07:00 committed by GitHub
parent 4dad80e2fe
commit 263da23220
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 206 additions and 109 deletions

View File

@ -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<? extends EntityInterface> getEntityRepository(@NonNull String entityType) {
@SuppressWarnings("unchecked")
EntityRepository<? extends EntityInterface> 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<? extends EntityInterface> getServiceEntityRepository(
@NonNull ServiceType serviceType) {
@SuppressWarnings("unchecked")
EntityRepository<? extends EntityInterface> entityRepository =
ENTITY_REPOSITORY_MAP.get(SERVICE_TYPE_ENTITY_MAP.get(serviceType));
if (entityRepository == null) {

View File

@ -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<Policy> {
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<Policy> {
}
}
@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<Rule> rules = policy.getRules();

View File

@ -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<Role> {
List<EntityReference> 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<Role> {
}
}
@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) {

View File

@ -132,6 +132,9 @@ public class TeamRepository extends EntityRepository<Team> {
team.withUsers(null).withDefaultRoles(null).withInheritedRoles(null);
store(team, update);
if (update) {
SubjectCache.getInstance().invalidateTeam(team.getId());
}
// Restore the relationships
team.withUsers(users)

View File

@ -120,6 +120,9 @@ public class UserRepository extends EntityRepository<User> {
}
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<User> {
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);

View File

@ -300,7 +300,6 @@ public abstract class EntityResource<T extends EntityInterface, K extends Entity
public static final MetadataOperation[] VIEW_ALL_OPERATIONS = {MetadataOperation.VIEW_ALL};
protected MetadataOperation[] getViewOperations(Fields fields) {
return VIEW_ALL_OPERATIONS;
}

View File

@ -370,10 +370,7 @@ public class PolicyResource extends EntityResource<Policy, PolicyRepository> {
}))
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<Policy, PolicyRepository> {
@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<Policy, PolicyRepository> {
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

View File

@ -323,9 +323,7 @@ public class RoleResource extends EntityResource<Role, RoleRepository> {
@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<Role, RoleRepository> {
}))
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<Role, RoleRepository> {
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

View File

@ -27,7 +27,7 @@ public class ResourceContext implements ResourceContextInterface {
@NonNull private EntityRepository<? extends EntityInterface> 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<TagLabel> getTags() throws IOException {
resolveEntity();
return entity == null ? Collections.EMPTY_LIST : Entity.getEntityTags(getResource(), entity);
return entity == null ? Collections.emptyList() : Entity.getEntityTags(getResource(), entity);
}
@Override

View File

@ -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()));
}

View File

@ -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<T extends EntityInterface, K extends Cr
"Invalid entity invalidEntity in query param entityDeleted");
}
@Test
@Execution(ExecutionMode.CONCURRENT)
protected void testTeamOnlyPolicy(TestInfo test) throws HttpResponseException {
testTeamOnlyPolicy(test, VIEW_ALL);
}
protected void testTeamOnlyPolicy(TestInfo test, MetadataOperation disallowedOperation) throws HttpResponseException {
if (!supportsOwner) {
return;
}
// TEAM2 allows operations on its entities to users only with in that team due to team only policy attached to it
// Create an entity owned by TEAM21
K createRequest = createRequest("teamOnlyPolicyTest", "", "", TEAM21.getEntityReference());
T entity = createEntity(createRequest, ADMIN_AUTH_HEADERS);
UserResourceTest userResourceTest = new UserResourceTest();
User userInTeam21 =
userResourceTest.createEntity(
userResourceTest.createRequest(test, 21).withTeams(List.of(TEAM21.getId())), ADMIN_AUTH_HEADERS);
User userInTeam2 =
userResourceTest.createEntity(
userResourceTest.createRequest(test, 2).withTeams(List.of(TEAM2.getId())), ADMIN_AUTH_HEADERS);
User userInTeam11 =
userResourceTest.createEntity(
userResourceTest.createRequest(test, 11).withTeams(List.of(TEAM11.getId())), ADMIN_AUTH_HEADERS);
User userInTeam1 =
userResourceTest.createEntity(
userResourceTest.createRequest(test, 1).withTeams(List.of(TEAM1.getId())), ADMIN_AUTH_HEADERS);
// users in team21 and team2 have all the operations allowed
T getEntity = getEntity(entity.getId(), authHeaders(userInTeam21.getName()));
assertEquals(getEntity.getId(), entity.getId());
getEntity = getEntity(entity.getId(), authHeaders(userInTeam2.getName()));
assertEquals(getEntity.getId(), entity.getId());
// users in team11 and team12 have all the operations denied due to Team2 team only policy
assertResponse(
() -> 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 {

View File

@ -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<TestCase, CreateTes
patchEntityAndCheckAuthorization(entity, USER2.getName(), EDIT_TESTS, true);
}
@Override
@Test
@SneakyThrows
protected void testTeamOnlyPolicy(TestInfo test) {
// Testcase's are authorized based on the ownership of the entity to which testcases are attached to
// Change the TABLE1 ownership to Team21 and then change it back to USER1
// Set the owner for the entity
TableResourceTest tableResourceTest = new TableResourceTest();
String originalJson = JsonUtils.pojoToJson(TEST_TABLE1);
TEST_TABLE1.setOwner(TEAM21.getEntityReference());
TEST_TABLE1 = tableResourceTest.patchEntity(TEST_TABLE1.getId(), originalJson, TEST_TABLE1, ADMIN_AUTH_HEADERS);
super.testTeamOnlyPolicy(test, VIEW_TESTS);
// Revert the ownership back
originalJson = JsonUtils.pojoToJson(TEST_TABLE1);
TEST_TABLE1.setOwner(USER1_REF);
TEST_TABLE1 = tableResourceTest.patchEntity(TEST_TABLE1.getId(), originalJson, TEST_TABLE1, ADMIN_AUTH_HEADERS);
}
@Test
void post_testWithoutRequiredFields_4xx(TestInfo test) {
// name is required field

View File

@ -13,16 +13,20 @@
package org.openmetadata.service.resources.policies;
import static java.util.Collections.emptyList;
import static javax.ws.rs.core.Response.Status.BAD_REQUEST;
import static javax.ws.rs.core.Response.Status.FORBIDDEN;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.openmetadata.common.utils.CommonUtil.listOf;
import static org.openmetadata.schema.api.teams.CreateTeam.TeamType.DEPARTMENT;
import static org.openmetadata.schema.entity.policies.accessControl.Rule.Effect.ALLOW;
import static org.openmetadata.schema.entity.policies.accessControl.Rule.Effect.DENY;
import static org.openmetadata.schema.type.MetadataOperation.EDIT_ALL;
import static org.openmetadata.schema.type.MetadataOperation.VIEW_ALL;
import static org.openmetadata.service.Entity.ALL_RESOURCES;
import static org.openmetadata.service.Entity.FIELD_DESCRIPTION;
import static org.openmetadata.service.security.SecurityUtil.authHeaders;
import static org.openmetadata.service.util.EntityUtil.fieldAdded;
import static org.openmetadata.service.util.EntityUtil.fieldDeleted;
import static org.openmetadata.service.util.EntityUtil.fieldUpdated;
@ -34,6 +38,7 @@ import static org.openmetadata.service.util.TestUtils.assertListNull;
import static org.openmetadata.service.util.TestUtils.assertResponse;
import static org.openmetadata.service.util.TestUtils.assertResponseContains;
import com.fasterxml.jackson.core.JsonProcessingException;
import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
@ -48,15 +53,19 @@ import org.apache.http.client.HttpResponseException;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInfo;
import org.openmetadata.schema.api.data.CreateLocation;
import org.openmetadata.schema.api.data.CreateTable;
import org.openmetadata.schema.api.policies.CreatePolicy;
import org.openmetadata.schema.api.teams.CreateRole;
import org.openmetadata.schema.api.teams.CreateTeam;
import org.openmetadata.schema.api.teams.CreateUser;
import org.openmetadata.schema.entity.data.Location;
import org.openmetadata.schema.entity.data.Table;
import org.openmetadata.schema.entity.policies.Policy;
import org.openmetadata.schema.entity.policies.accessControl.Rule;
import org.openmetadata.schema.entity.policies.accessControl.Rule.Effect;
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.ChangeDescription;
import org.openmetadata.schema.type.EntityReference;
import org.openmetadata.schema.type.Function;
@ -67,11 +76,13 @@ import org.openmetadata.service.FunctionList;
import org.openmetadata.service.exception.CatalogExceptionMessage;
import org.openmetadata.service.resources.CollectionRegistry;
import org.openmetadata.service.resources.EntityResourceTest;
import org.openmetadata.service.resources.databases.TableResourceTest;
import org.openmetadata.service.resources.locations.LocationResourceTest;
import org.openmetadata.service.resources.policies.PolicyResource.PolicyList;
import org.openmetadata.service.resources.policies.PolicyResource.ResourceDescriptorList;
import org.openmetadata.service.resources.teams.RoleResourceTest;
import org.openmetadata.service.resources.teams.TeamResourceTest;
import org.openmetadata.service.resources.teams.UserResourceTest;
import org.openmetadata.service.security.policyevaluator.RuleEvaluator;
import org.openmetadata.service.util.EntityUtil;
import org.openmetadata.service.util.JsonUtils;
@ -79,6 +90,8 @@ import org.openmetadata.service.util.TestUtils;
@Slf4j
public class PolicyResourceTest extends EntityResourceTest<Policy, CreatePolicy> {
public static TableResourceTest tableTest = new TableResourceTest();
public static TeamResourceTest teamTest = new TeamResourceTest();
public PolicyResourceTest() {
super(
@ -381,12 +394,11 @@ public class PolicyResourceTest extends EntityResourceTest<Policy, CreatePolicy>
policies.add(createEntity(create, ADMIN_AUTH_HEADERS));
}
TeamResourceTest teamResourceTest = new TeamResourceTest();
List<Team> 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<Policy, CreatePolicy>
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<User> allowedUsers = (List<User>) scenario[2];
List<User> disallowedUsers = (List<User>) 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<Policy, CreatePolicy>
String name, List<String> resources, List<MetadataOperation> 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<User> allowedUsers, List<User> 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");
}
}
}