Fixes #10924 - Trim the redundant permissions in the permission list (#10928)

* Fixes #10924 - Trim the redundant permissions in the permission list

* Fixes #10924 - Trim the redundant permissions in the permission list
This commit is contained in:
Deepa Rao 2023-04-05 19:01:27 -07:00 committed by GitHub
parent 922a8079b1
commit 38d4afb6cb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 248 additions and 19 deletions

View File

@ -128,7 +128,7 @@ public class PolicyEvaluator {
rule.evaluatePermission(resourcePermissionMap, policyContext); rule.evaluatePermission(resourcePermissionMap, policyContext);
} }
} }
return new ArrayList<>(resourcePermissionMap.values()); return PolicyEvaluator.trimResourcePermissions(new ArrayList<>(resourcePermissionMap.values()));
} }
/** Returns a list of operations that a user can perform on all the resources. */ /** Returns a list of operations that a user can perform on all the resources. */
@ -142,7 +142,7 @@ public class PolicyEvaluator {
rule.evaluatePermission(resourcePermissionMap, policyContext); rule.evaluatePermission(resourcePermissionMap, policyContext);
} }
} }
return new ArrayList<>(resourcePermissionMap.values()); return PolicyEvaluator.trimResourcePermissions(new ArrayList<>(resourcePermissionMap.values()));
} }
/** Returns a list of operations that a user can perform on the given resource/entity type */ /** Returns a list of operations that a user can perform on the given resource/entity type */
@ -159,7 +159,7 @@ public class PolicyEvaluator {
rule.evaluatePermission(resourceType, resourcePermission, policyContext); rule.evaluatePermission(resourceType, resourcePermission, policyContext);
} }
} }
return resourcePermission; return PolicyEvaluator.trimResourcePermission(resourcePermission);
} }
public static ResourcePermission getPermission( public static ResourcePermission getPermission(
@ -176,7 +176,7 @@ public class PolicyEvaluator {
rule.evaluatePermission(subjectContext, resourceContext, resourcePermission, policyContext); rule.evaluatePermission(subjectContext, resourceContext, resourcePermission, policyContext);
} }
} }
return resourcePermission; return PolicyEvaluator.trimResourcePermission(resourcePermission);
} }
/** Get list of resources with all their permissions set to given Access */ /** Get list of resources with all their permissions set to given Access */
@ -191,7 +191,7 @@ public class PolicyEvaluator {
new ResourcePermission().withResource(rd.getName()).withPermissions(permissions); new ResourcePermission().withResource(rd.getName()).withPermissions(permissions);
resourcePermissions.add(resourcePermission); resourcePermissions.add(resourcePermission);
} }
return resourcePermissions; return PolicyEvaluator.trimResourcePermissions(resourcePermissions);
} }
/** Get list of resources with all their permissions set to given Access */ /** Get list of resources with all their permissions set to given Access */
@ -201,7 +201,8 @@ public class PolicyEvaluator {
for (MetadataOperation operation : rd.getOperations()) { for (MetadataOperation operation : rd.getOperations()) {
permissions.add(new Permission().withOperation(operation).withAccess(access)); permissions.add(new Permission().withOperation(operation).withAccess(access));
} }
return new ResourcePermission().withResource(rd.getName()).withPermissions(permissions); return PolicyEvaluator.trimResourcePermission(
new ResourcePermission().withResource(rd.getName()).withPermissions(permissions));
} }
/** /**
@ -215,4 +216,45 @@ public class PolicyEvaluator {
resourcePermissions.forEach(rp -> resourcePermissionMap.put(rp.getResource(), rp)); resourcePermissions.forEach(rp -> resourcePermissionMap.put(rp.getResource(), rp));
return resourcePermissionMap; return resourcePermissionMap;
} }
/** Removes the redundant permissions from the list. */
public static List<Permission> trimPermissions(List<Permission> permissions) {
boolean viewAllPermission = false;
boolean editAllPermission = false;
for (Permission p : permissions) {
if ((p.getOperation().equals(MetadataOperation.VIEW_ALL)
&& (p.getAccess().equals(Access.ALLOW) || p.getAccess().equals(Access.DENY)))) {
viewAllPermission = true;
}
if (p.getOperation().equals(MetadataOperation.EDIT_ALL)
&& (p.getAccess().equals(Access.ALLOW) || p.getAccess().equals(Access.DENY))) {
editAllPermission = true;
}
}
Iterator<Permission> permissionIterator = permissions.listIterator();
while (permissionIterator.hasNext()) {
Permission permission = permissionIterator.next();
if (viewAllPermission
&& permission.getOperation() != MetadataOperation.VIEW_ALL
&& permission.getOperation().value().startsWith("View")) {
permissionIterator.remove();
} else if (editAllPermission
&& permission.getOperation() != MetadataOperation.EDIT_ALL
&& permission.getOperation().value().startsWith("Edit")) {
permissionIterator.remove();
}
}
return permissions;
}
public static ResourcePermission trimResourcePermission(ResourcePermission resourcePermission) {
return resourcePermission.withPermissions(trimPermissions(resourcePermission.getPermissions()));
}
public static List<ResourcePermission> trimResourcePermissions(List<ResourcePermission> resourcePermissions) {
for (ResourcePermission resourcePermission : resourcePermissions) {
trimResourcePermission(resourcePermission);
}
return resourcePermissions;
}
} }

View File

@ -150,7 +150,8 @@ class PermissionsResourceTest extends OpenMetadataApplicationTest {
void get_admin_permissions_for_role() throws HttpResponseException { void get_admin_permissions_for_role() throws HttpResponseException {
// Ensure an admin has all the permissions // Ensure an admin has all the permissions
List<ResourcePermission> actualPermissions = getPermissions(ADMIN_AUTH_HEADERS); List<ResourcePermission> actualPermissions = getPermissions(ADMIN_AUTH_HEADERS);
assertEquals(PolicyEvaluator.getResourcePermissions(ALLOW), actualPermissions); assertEquals(
PolicyEvaluator.trimResourcePermissions(PolicyEvaluator.getResourcePermissions(ALLOW)), actualPermissions);
} }
@Test @Test
@ -168,23 +169,27 @@ class PermissionsResourceTest extends OpenMetadataApplicationTest {
ORG_NO_OWNER_RULE_OPERATIONS, CONDITIONAL_ALLOW, null, ORGANIZATION_POLICY_NAME, ORG_NO_OWNER_RULE); ORG_NO_OWNER_RULE_OPERATIONS, CONDITIONAL_ALLOW, null, ORGANIZATION_POLICY_NAME, ORG_NO_OWNER_RULE);
permissionsBuilder.setPermission( permissionsBuilder.setPermission(
ORG_IS_OWNER_RULE_OPERATIONS, CONDITIONAL_ALLOW, null, ORGANIZATION_POLICY_NAME, ORG_IS_OWNER_RULE); ORG_IS_OWNER_RULE_OPERATIONS, CONDITIONAL_ALLOW, null, ORGANIZATION_POLICY_NAME, ORG_IS_OWNER_RULE);
assertResourcePermissions(permissionsBuilder.getResourcePermissions(), actualPermissions); assertResourcePermissions(
PolicyEvaluator.trimResourcePermissions(permissionsBuilder.getResourcePermissions()), actualPermissions);
// Validate permissions for all resources for data consumer as admin // Validate permissions for all resources for data consumer as admin
actualPermissions = getPermissions(DATA_CONSUMER_USER_NAME, ADMIN_AUTH_HEADERS); actualPermissions = getPermissions(DATA_CONSUMER_USER_NAME, ADMIN_AUTH_HEADERS);
assertResourcePermissions(permissionsBuilder.getResourcePermissions(), actualPermissions); assertResourcePermissions(
PolicyEvaluator.trimResourcePermissions(permissionsBuilder.getResourcePermissions()), actualPermissions);
// Validate permission as logged-in user for each resource one at a time // Validate permission as logged-in user for each resource one at a time
ResourcePermission actualPermission; ResourcePermission actualPermission;
for (ResourceDescriptor rd : ResourceRegistry.listResourceDescriptors()) { for (ResourceDescriptor rd : ResourceRegistry.listResourceDescriptors()) {
actualPermission = getPermission(rd.getName(), null, authHeaders); actualPermission = getPermission(rd.getName(), null, authHeaders);
assertResourcePermission(permissionsBuilder.getPermission(rd.getName()), actualPermission); assertResourcePermission(
PolicyEvaluator.trimResourcePermission(permissionsBuilder.getPermission(rd.getName())), actualPermission);
} }
// Validate permission of data consumer as admin user for each resource one at a time // Validate permission of data consumer as admin user for each resource one at a time
for (ResourceDescriptor rd : ResourceRegistry.listResourceDescriptors()) { for (ResourceDescriptor rd : ResourceRegistry.listResourceDescriptors()) {
actualPermission = getPermission(rd.getName(), DATA_CONSUMER_USER_NAME, authHeaders); actualPermission = getPermission(rd.getName(), DATA_CONSUMER_USER_NAME, authHeaders);
assertResourcePermission(permissionsBuilder.getPermission(rd.getName()), actualPermission); assertResourcePermission(
PolicyEvaluator.trimResourcePermission(permissionsBuilder.getPermission(rd.getName())), actualPermission);
} }
} }
@ -202,7 +207,8 @@ class PermissionsResourceTest extends OpenMetadataApplicationTest {
permissionsBuilder.setPermission( permissionsBuilder.setPermission(
ORG_IS_OWNER_RULE_OPERATIONS, CONDITIONAL_ALLOW, null, ORGANIZATION_POLICY_NAME, ORG_IS_OWNER_RULE); ORG_IS_OWNER_RULE_OPERATIONS, CONDITIONAL_ALLOW, null, ORGANIZATION_POLICY_NAME, ORG_IS_OWNER_RULE);
assertResourcePermissions(permissionsBuilder.getResourcePermissions(), actualPermissions); assertResourcePermissions(
PolicyEvaluator.trimResourcePermissions(permissionsBuilder.getResourcePermissions()), actualPermissions);
} }
@Test @Test
@ -214,14 +220,16 @@ class PermissionsResourceTest extends OpenMetadataApplicationTest {
permissionsBuilder.setPermission( permissionsBuilder.setPermission(
DATA_CONSUMER_ALLOWED, ALLOW, null, DATA_CONSUMER_POLICY_NAME, DATA_CONSUMER_RULES.get(0)); DATA_CONSUMER_ALLOWED, ALLOW, null, DATA_CONSUMER_POLICY_NAME, DATA_CONSUMER_RULES.get(0));
assertResourcePermissions(permissionsBuilder.getResourcePermissions(), actual); assertResourcePermissions(
PolicyEvaluator.trimResourcePermissions(permissionsBuilder.getResourcePermissions()), actual);
// Get permissions for DATA_CONSUMER and DATA_STEWARD policies together and assert it is correct // Get permissions for DATA_CONSUMER and DATA_STEWARD policies together and assert it is correct
policies.add(DATA_STEWARD_POLICY.getId()); policies.add(DATA_STEWARD_POLICY.getId());
actual = getPermissionsForPolicies(policies, ADMIN_AUTH_HEADERS); actual = getPermissionsForPolicies(policies, ADMIN_AUTH_HEADERS);
permissionsBuilder.setPermission( permissionsBuilder.setPermission(
DATA_STEWARD_ALLOWED, ALLOW, null, DATA_STEWARD_POLICY_NAME, DATA_STEWARD_RULES.get(0)); DATA_STEWARD_ALLOWED, ALLOW, null, DATA_STEWARD_POLICY_NAME, DATA_STEWARD_RULES.get(0));
assertResourcePermissions(permissionsBuilder.getResourcePermissions(), actual); assertResourcePermissions(
PolicyEvaluator.trimResourcePermissions(permissionsBuilder.getResourcePermissions()), actual);
} }
@Test @Test
@ -247,7 +255,8 @@ class PermissionsResourceTest extends OpenMetadataApplicationTest {
// Note that conditional list is empty. All the required context to resolve is met when requesting permission of // Note that conditional list is empty. All the required context to resolve is met when requesting permission of
// a specific resource (both subject and resource context). Only Deny, Allow, NotAllow permissions are expected. // a specific resource (both subject and resource context). Only Deny, Allow, NotAllow permissions are expected.
assertResourcePermission(permissionsBuilder.getPermission(Entity.TABLE), actualPermission); assertResourcePermission(
PolicyEvaluator.trimResourcePermission(permissionsBuilder.getPermission(Entity.TABLE)), actualPermission);
} }
@Test @Test
@ -274,15 +283,18 @@ class PermissionsResourceTest extends OpenMetadataApplicationTest {
permissionsBuilder.setPermission( permissionsBuilder.setPermission(
ORG_IS_OWNER_RULE_OPERATIONS, ALLOW, null, ORGANIZATION_POLICY_NAME, ORG_IS_OWNER_RULE); ORG_IS_OWNER_RULE_OPERATIONS, ALLOW, null, ORGANIZATION_POLICY_NAME, ORG_IS_OWNER_RULE);
ResourcePermission actualPermission = getPermission(Entity.TABLE, table.getId(), null, authHeaders); ResourcePermission actualPermission = getPermission(Entity.TABLE, table.getId(), null, authHeaders);
assertResourcePermission(permissionsBuilder.getPermission(Entity.TABLE), actualPermission); assertResourcePermission(
PolicyEvaluator.trimResourcePermission(permissionsBuilder.getPermission(Entity.TABLE)), actualPermission);
// get permissions by resource entity name // get permissions by resource entity name
actualPermission = getPermissionByName(Entity.TABLE, table.getFullyQualifiedName(), null, authHeaders); actualPermission = getPermissionByName(Entity.TABLE, table.getFullyQualifiedName(), null, authHeaders);
assertResourcePermission(permissionsBuilder.getPermission(Entity.TABLE), actualPermission); assertResourcePermission(
PolicyEvaluator.trimResourcePermission(permissionsBuilder.getPermission(Entity.TABLE)), actualPermission);
// Admin getting permissions for a specific resource on for Data consumer // Admin getting permissions for a specific resource on for Data consumer
actualPermission = getPermission(Entity.TABLE, table.getId(), DATA_CONSUMER_USER_NAME, ADMIN_AUTH_HEADERS); actualPermission = getPermission(Entity.TABLE, table.getId(), DATA_CONSUMER_USER_NAME, ADMIN_AUTH_HEADERS);
assertResourcePermission(permissionsBuilder.getPermission(Entity.TABLE), actualPermission); assertResourcePermission(
PolicyEvaluator.trimResourcePermission(permissionsBuilder.getPermission(Entity.TABLE)), actualPermission);
PolicyResourceTest policyResourceTest = new PolicyResourceTest(); PolicyResourceTest policyResourceTest = new PolicyResourceTest();
Policy orgPolicy = policyResourceTest.getEntityByName(ORGANIZATION_POLICY_NAME, "", ADMIN_AUTH_HEADERS); Policy orgPolicy = policyResourceTest.getEntityByName(ORGANIZATION_POLICY_NAME, "", ADMIN_AUTH_HEADERS);
@ -306,7 +318,8 @@ class PermissionsResourceTest extends OpenMetadataApplicationTest {
permissionsBuilder.setPermission( permissionsBuilder.setPermission(
allowedOperations, ALLOW, null, ORGANIZATION_POLICY_NAME, orgPolicy.getRules().get(1)); allowedOperations, ALLOW, null, ORGANIZATION_POLICY_NAME, orgPolicy.getRules().get(1));
actualPermission = getPermissionByName(Entity.TABLE, table.getFullyQualifiedName(), null, authHeaders); actualPermission = getPermissionByName(Entity.TABLE, table.getFullyQualifiedName(), null, authHeaders);
assertResourcePermission(permissionsBuilder.getPermission(Entity.TABLE), actualPermission); assertResourcePermission(
PolicyEvaluator.trimResourcePermission(permissionsBuilder.getPermission(Entity.TABLE)), actualPermission);
// Finally, try to patch the field that can't be edited and expect permission denied // Finally, try to patch the field that can't be edited and expect permission denied
String field = ResourceRegistry.getField(editOperation); String field = ResourceRegistry.getField(editOperation);

View File

@ -2,8 +2,13 @@ package org.openmetadata.service.security.policyevaluator;
import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.Assertions.*;
import java.util.*;
import java.util.stream.Collectors;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.openmetadata.schema.type.MetadataOperation;
import org.openmetadata.schema.type.Permission;
import org.openmetadata.schema.type.Permission.Access; import org.openmetadata.schema.type.Permission.Access;
import org.openmetadata.schema.type.ResourcePermission;
class PolicyEvaluatorTest { class PolicyEvaluatorTest {
@Test @Test
@ -55,4 +60,173 @@ class PolicyEvaluatorTest {
// newAccess (ConditionalAllow|NotAllow) and currentAccess ConditionalDeny takes precedence // newAccess (ConditionalAllow|NotAllow) and currentAccess ConditionalDeny takes precedence
assertFalse(CompiledRule.overrideAccess(Access.NOT_ALLOW, Access.NOT_ALLOW)); assertFalse(CompiledRule.overrideAccess(Access.NOT_ALLOW, Access.NOT_ALLOW));
} }
@Test
void trimResourcePermissions() {
MetadataOperation[] op1 = {
MetadataOperation.ALL,
MetadataOperation.VIEW_ALL,
MetadataOperation.VIEW_BASIC,
MetadataOperation.VIEW_QUERIES,
MetadataOperation.EDIT_ALL,
MetadataOperation.EDIT_LINEAGE,
MetadataOperation.EDIT_CUSTOM_FIELDS
};
ResourcePermission rp1 = getResourcePermission("r1", Access.DENY, op1);
List<MetadataOperation> expectedOp1 =
new ArrayList(List.of(MetadataOperation.ALL, MetadataOperation.VIEW_ALL, MetadataOperation.EDIT_ALL));
MetadataOperation[] op2 = {
MetadataOperation.ALL,
MetadataOperation.VIEW_BASIC,
MetadataOperation.VIEW_USAGE,
MetadataOperation.EDIT_ALL,
MetadataOperation.EDIT_LINEAGE,
MetadataOperation.EDIT_CUSTOM_FIELDS,
MetadataOperation.EDIT_DISPLAY_NAME
};
ResourcePermission rp2 = getResourcePermission("r2", Access.ALLOW, op2);
List<MetadataOperation> expectedOp2 =
new ArrayList(
List.of(
MetadataOperation.ALL,
MetadataOperation.VIEW_BASIC,
MetadataOperation.VIEW_USAGE,
MetadataOperation.EDIT_ALL));
List<ResourcePermission> rpList = List.of(rp1, rp2);
PolicyEvaluator.trimResourcePermissions(rpList);
assertEqualsPermissions(expectedOp1, rpList.get(0).getPermissions());
assertEqualsPermissions(expectedOp2, rpList.get(1).getPermissions());
}
@Test
void trimResourcePermission() {
MetadataOperation[] operations = {
MetadataOperation.ALL,
MetadataOperation.VIEW_ALL,
MetadataOperation.VIEW_BASIC,
MetadataOperation.VIEW_QUERIES,
MetadataOperation.EDIT_ALL,
MetadataOperation.EDIT_LINEAGE,
MetadataOperation.EDIT_CUSTOM_FIELDS
};
ResourcePermission rp = getResourcePermission("testResource", Access.ALLOW, operations);
ResourcePermission trimmedRp = PolicyEvaluator.trimResourcePermission(rp);
List<MetadataOperation> expectedOperations =
new ArrayList(List.of(MetadataOperation.ALL, MetadataOperation.VIEW_ALL, MetadataOperation.EDIT_ALL));
assertEqualsPermissions(expectedOperations, trimmedRp.getPermissions());
}
@Test
void trimPermissions_withAllowAccess_trimmed() {
List<Permission> permissions = getPermissions(OperationContext.getAllOperations(), Access.ALLOW);
List<MetadataOperation> expectedOperations =
Arrays.asList(
MetadataOperation.ALL,
MetadataOperation.DELETE,
MetadataOperation.CREATE,
MetadataOperation.VIEW_ALL,
MetadataOperation.EDIT_ALL);
List<Permission> actual = PolicyEvaluator.trimPermissions(permissions);
assertEqualsPermissions(expectedOperations, actual);
}
@Test
void trimPermissions_withDenyAccess_trimmed() {
List<Permission> permissions = getPermissions(OperationContext.getAllOperations(), Access.DENY);
List<MetadataOperation> expectedOperations =
Arrays.asList(
MetadataOperation.ALL,
MetadataOperation.DELETE,
MetadataOperation.CREATE,
MetadataOperation.VIEW_ALL,
MetadataOperation.EDIT_ALL);
List<Permission> actual = PolicyEvaluator.trimPermissions(permissions);
assertEqualsPermissions(expectedOperations, actual);
}
@Test
void trimPermissions_withNotAllowAccessToViewAll_viewOpsNotTrimmed() {
List<Permission> permissions = getPermissions(OperationContext.getAllOperations(), Access.ALLOW);
List<MetadataOperation> expectedOperations =
Arrays.stream(MetadataOperation.values())
.filter(operation -> (!operation.value().startsWith("Edit")))
.collect(Collectors.toList());
expectedOperations.add(MetadataOperation.EDIT_ALL);
updateAccess(permissions, MetadataOperation.VIEW_ALL, Access.NOT_ALLOW);
List<Permission> actual = PolicyEvaluator.trimPermissions(permissions);
assertEqualsPermissions(expectedOperations, actual);
}
@Test
void trimPermissions_withConditionalAllowAccessToEditAll_editOpsNotTrimmed() {
List<Permission> permissions = getPermissions(OperationContext.getAllOperations(), Access.ALLOW);
List<MetadataOperation> expectedOperations =
Arrays.stream(MetadataOperation.values())
.filter(operation -> (!operation.value().startsWith("View")))
.collect(Collectors.toList());
expectedOperations.add(MetadataOperation.VIEW_ALL);
updateAccess(permissions, MetadataOperation.EDIT_ALL, Access.CONDITIONAL_ALLOW);
List<Permission> actual = PolicyEvaluator.trimPermissions(permissions);
assertEqualsPermissions(expectedOperations, actual);
}
@Test
void trimPermissions_withConditionalAccess_notTrimmed() {
List<Permission> permissions = getPermissions(OperationContext.getAllOperations(), Access.ALLOW);
List<MetadataOperation> expectedOperations = OperationContext.getAllOperations();
updateAccess(permissions, MetadataOperation.VIEW_ALL, Access.CONDITIONAL_ALLOW);
updateAccess(permissions, MetadataOperation.EDIT_ALL, Access.CONDITIONAL_DENY);
List<Permission> actual = PolicyEvaluator.trimPermissions(permissions);
assertEqualsPermissions(expectedOperations, actual);
}
public static void assertEqualsPermissions(List<MetadataOperation> expectedOperations, List<Permission> actual) {
assertEquals(expectedOperations.size(), actual.size());
Comparator<Permission> comparator = Comparator.comparing(Permission::getOperation);
actual.sort(comparator);
Collections.sort(expectedOperations);
for (int i = 0; i < expectedOperations.size(); i++) {
assertEquals(expectedOperations.get(i).value(), actual.get(i).getOperation().value());
}
}
public static List<Permission> getPermissions(List<MetadataOperation> operations, Access access) {
ArrayList<Permission> permissions = new ArrayList<>();
operations.stream().forEach(operation -> permissions.add(getPermission(operation, access)));
return permissions;
}
public static List<Permission> updateAccess(
List<Permission> permissions, final MetadataOperation operation, Access access) {
permissions.stream()
.forEach(
permission -> {
if (permission.getOperation().equals(operation)) permission.setAccess(access);
});
return permissions;
}
public static ResourcePermission getResourcePermission(
String resourceName, Access access, MetadataOperation... operations) {
ResourcePermission rp = new ResourcePermission();
List<Permission> permissions = new ArrayList<>();
rp.setResource(resourceName);
for (int i = 0; i < operations.length; i++) {
permissions.add(new Permission().withAccess(access).withOperation(operations[i]));
}
rp.setPermissions(permissions);
return rp;
}
public static Permission getPermission(MetadataOperation operation, Access access) {
Permission permission = new Permission().withOperation(operation);
permission.setAccess(access);
return permission;
}
} }