mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-12-20 12:09:05 +00:00
This commit is contained in:
parent
19a3f1f47b
commit
8a008022c8
@ -124,6 +124,7 @@ public final class Entity {
|
||||
//
|
||||
public static final String ADMIN_USER_NAME = "admin";
|
||||
public static final String ORGANIZATION_NAME = "Organization";
|
||||
public static final String ORGANIZATION_POLICY_NAME = "OrganizationPolicy";
|
||||
public static final String INGESTION_BOT_NAME = "ingestion-bot";
|
||||
public static final String INGESTION_BOT_ROLE = "IngestionBotRole";
|
||||
public static final String PROFILER_BOT_NAME = "profiler-bot";
|
||||
|
||||
@ -12,7 +12,9 @@ public class ResourceRegistry {
|
||||
|
||||
private ResourceRegistry() {}
|
||||
|
||||
public static void add(List<ResourceDescriptor> resourceDescriptors) {
|
||||
public static void initialize(List<ResourceDescriptor> resourceDescriptors) {
|
||||
RESOURCE_DESCRIPTORS.clear();
|
||||
;
|
||||
RESOURCE_DESCRIPTORS.addAll(resourceDescriptors);
|
||||
RESOURCE_DESCRIPTORS.sort(Comparator.comparing(ResourceDescriptor::getName));
|
||||
}
|
||||
|
||||
@ -142,10 +142,10 @@ public abstract class EntityRepository<T extends EntityInterface> {
|
||||
protected final boolean supportsFollower;
|
||||
|
||||
/** Fields that can be updated during PATCH operation */
|
||||
private final Fields patchFields;
|
||||
@Getter private final Fields patchFields;
|
||||
|
||||
/** Fields that can be updated during PUT operation */
|
||||
protected final Fields putFields;
|
||||
@Getter protected final Fields putFields;
|
||||
|
||||
EntityRepository(
|
||||
String collectionPath,
|
||||
|
||||
@ -280,6 +280,16 @@ public final class CollectionRegistry {
|
||||
} catch (Exception ex) {
|
||||
LOG.warn("Encountered exception ", ex);
|
||||
}
|
||||
|
||||
// Call upgrade method, if it exists
|
||||
try {
|
||||
Method upgradeMethod = resource.getClass().getMethod("upgrade");
|
||||
upgradeMethod.invoke(resource);
|
||||
} catch (NoSuchMethodException ignored) {
|
||||
// Method does not exist and initialize is not called
|
||||
} catch (Exception ex) {
|
||||
LOG.warn("Encountered exception ", ex);
|
||||
}
|
||||
return resource;
|
||||
}
|
||||
|
||||
|
||||
@ -20,6 +20,7 @@ import org.openmetadata.schema.type.EntityReference;
|
||||
import org.openmetadata.schema.type.Include;
|
||||
import org.openmetadata.schema.type.MetadataOperation;
|
||||
import org.openmetadata.service.Entity;
|
||||
import org.openmetadata.service.OpenMetadataApplicationConfig;
|
||||
import org.openmetadata.service.jdbi3.EntityRepository;
|
||||
import org.openmetadata.service.jdbi3.ListFilter;
|
||||
import org.openmetadata.service.security.Authorizer;
|
||||
@ -50,6 +51,19 @@ public abstract class EntityResource<T extends EntityInterface, K extends Entity
|
||||
this.authorizer = authorizer;
|
||||
}
|
||||
|
||||
/** Method used for initializing a resource, such as creating default policies, roles, etc. */
|
||||
public void initialize(OpenMetadataApplicationConfig config) throws IOException {
|
||||
// Nothing to do in the default implementation
|
||||
}
|
||||
|
||||
/**
|
||||
* Method used for upgrading a resource such as adding new fields to entities, etc. that can't be done in bootstrap
|
||||
* migrate
|
||||
*/
|
||||
protected void upgrade() throws IOException {
|
||||
// Nothing to do in the default implementation
|
||||
}
|
||||
|
||||
public final Fields getFields(String fields) {
|
||||
if (fields != null && fields.equals("*")) {
|
||||
return new Fields(allowedFields, String.join(",", allowedFields));
|
||||
|
||||
@ -91,7 +91,7 @@ public class WebAnalyticEventResource extends EntityResource<WebAnalyticEvent, W
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused") // Method used for reflection of webAnalyticEventTypes
|
||||
@Override
|
||||
public void initialize(OpenMetadataApplicationConfig config) throws IOException {
|
||||
// Find the existing webAnalyticEventTypes and add them from json files
|
||||
List<WebAnalyticEvent> webAnalyticEvents =
|
||||
|
||||
@ -85,7 +85,7 @@ public class BotResource extends EntityResource<Bot, BotRepository> {
|
||||
super(Bot.class, new BotRepository(dao), authorizer);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused") // Method is used by reflection
|
||||
@Override
|
||||
public void initialize(OpenMetadataApplicationConfig config) throws IOException {
|
||||
// Load system bots
|
||||
List<Bot> bots = dao.getEntitiesFromSeedData();
|
||||
|
||||
@ -74,7 +74,7 @@ public class TestDefinitionResource extends EntityResource<TestDefinition, TestD
|
||||
super(TestDefinition.class, new TestDefinitionRepository(dao), authorizer);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused") // Method used for reflection
|
||||
@Override
|
||||
public void initialize(OpenMetadataApplicationConfig config) throws IOException {
|
||||
// Find tag definitions and load tag categories from the json file, if necessary
|
||||
List<TestDefinition> testDefinitions = dao.getEntitiesFromSeedData(".*json/data/tests/.*\\.json$");
|
||||
|
||||
@ -89,7 +89,7 @@ public class WebhookResource extends EntityResource<Webhook, WebhookRepository>
|
||||
webhookDAO = dao.webhookDAO();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused") // Method used for reflection
|
||||
@Override
|
||||
public void initialize(OpenMetadataApplicationConfig config) {
|
||||
try {
|
||||
List<String> listAllWebhooks = webhookDAO.listAllWebhooks(webhookDAO.getTableName());
|
||||
|
||||
@ -55,6 +55,7 @@ import org.openmetadata.schema.type.EntityHistory;
|
||||
import org.openmetadata.schema.type.EntityReference;
|
||||
import org.openmetadata.schema.type.Function;
|
||||
import org.openmetadata.schema.type.Include;
|
||||
import org.openmetadata.schema.type.MetadataOperation;
|
||||
import org.openmetadata.schema.type.ResourceDescriptor;
|
||||
import org.openmetadata.service.Entity;
|
||||
import org.openmetadata.service.FunctionList;
|
||||
@ -95,11 +96,26 @@ public class PolicyResource extends EntityResource<Policy, PolicyRepository> {
|
||||
super(Policy.class, new PolicyRepository(dao), authorizer);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused") // Method is used by reflection
|
||||
@Override
|
||||
public void initialize(OpenMetadataApplicationConfig config) throws IOException {
|
||||
// Load any existing rules from database, before loading seed data.
|
||||
dao.initSeedDataFromResources();
|
||||
ResourceRegistry.add(listOrEmpty(PolicyResource.getResourceDescriptors()));
|
||||
ResourceRegistry.initialize(listOrEmpty(PolicyResource.getResourceDescriptors()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void upgrade() throws IOException {
|
||||
// OrganizationPolicy rule change
|
||||
Policy originalOrgPolicy = dao.getByName(null, Entity.ORGANIZATION_POLICY_NAME, dao.getPatchFields());
|
||||
Policy updatedOrgPolicy = JsonUtils.readValue(JsonUtils.pojoToJson(originalOrgPolicy), Policy.class);
|
||||
|
||||
// Rules are in alphabetical order - change second rule "OrganizationPolicy-Owner-Rule"
|
||||
// from ALL operation to remove CREATE operation and allow all the other operations for the owner
|
||||
updatedOrgPolicy
|
||||
.getRules()
|
||||
.get(1)
|
||||
.withOperations(List.of(MetadataOperation.EDIT_ALL, MetadataOperation.VIEW_ALL, MetadataOperation.DELETE));
|
||||
dao.patch(null, originalOrgPolicy.getId(), "admin", JsonUtils.getJsonPatch(originalOrgPolicy, updatedOrgPolicy));
|
||||
}
|
||||
|
||||
public static class PolicyList extends ResultList<Policy> {
|
||||
|
||||
@ -99,6 +99,7 @@ public class IngestionPipelineResource extends EntityResource<IngestionPipeline,
|
||||
super(IngestionPipeline.class, new IngestionPipelineRepository(dao), authorizer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(OpenMetadataApplicationConfig config) {
|
||||
this.openMetadataApplicationConfig = config;
|
||||
this.pipelineServiceClient = new AirflowRESTClient(openMetadataApplicationConfig.getAirflowConfiguration());
|
||||
|
||||
@ -89,7 +89,7 @@ public class RoleResource extends EntityResource<Role, RoleRepository> {
|
||||
super(Role.class, new RoleRepository(collectionDAO), authorizer);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused") // Method used for reflection
|
||||
@Override
|
||||
public void initialize(OpenMetadataApplicationConfig config) throws IOException {
|
||||
List<Role> roles = dao.getEntitiesFromSeedData();
|
||||
for (Role role : roles) {
|
||||
|
||||
@ -90,7 +90,7 @@ public class TeamResource extends EntityResource<Team, TeamRepository> {
|
||||
super(Team.class, new TeamRepository(dao), authorizer);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused") // Method used for reflection
|
||||
@Override
|
||||
public void initialize(OpenMetadataApplicationConfig config) throws IOException {
|
||||
dao.initOrganization();
|
||||
}
|
||||
|
||||
@ -150,6 +150,7 @@ public class UserResource extends EntityResource<User, UserRepository> {
|
||||
authHandler = authenticatorHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(OpenMetadataApplicationConfig config) throws IOException {
|
||||
this.authenticationConfiguration = config.getAuthenticationConfiguration();
|
||||
SmtpSettings smtpSettings = config.getSmtpSettings();
|
||||
|
||||
@ -91,7 +91,7 @@ public class TypeResource extends EntityResource<Type, TypeRepository> {
|
||||
super(Type.class, new TypeRepository(dao), authorizer);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused") // Method used for reflection
|
||||
@Override
|
||||
public void initialize(OpenMetadataApplicationConfig config) throws IOException {
|
||||
// Load types defined in OpenMetadata schemas
|
||||
long now = System.currentTimeMillis();
|
||||
|
||||
@ -219,7 +219,7 @@ public class CompiledRule extends Rule {
|
||||
return Boolean.TRUE.equals(expression.getValue(evaluationContext, Boolean.class));
|
||||
}
|
||||
|
||||
static boolean overrideAccess(Access newAccess, Access currentAccess) {
|
||||
public static boolean overrideAccess(Access newAccess, Access currentAccess) {
|
||||
// Lower the ordinal number of access overrides higher ordinal number
|
||||
return currentAccess.ordinal() > newAccess.ordinal();
|
||||
}
|
||||
|
||||
@ -39,8 +39,8 @@ public class OperationContext {
|
||||
return operations;
|
||||
}
|
||||
|
||||
public boolean isCreateOperation() {
|
||||
return operations.contains(MetadataOperation.CREATE);
|
||||
public static List<MetadataOperation> getAllOperations(MetadataOperation... exclude) {
|
||||
return getOperations("", exclude);
|
||||
}
|
||||
|
||||
public static boolean isEditOperation(MetadataOperation operation) {
|
||||
@ -50,4 +50,22 @@ public class OperationContext {
|
||||
public static boolean isViewOperation(MetadataOperation operation) {
|
||||
return operation.value().startsWith("View");
|
||||
}
|
||||
|
||||
public static List<MetadataOperation> getViewOperations(MetadataOperation... exclude) {
|
||||
return getOperations("View", exclude);
|
||||
}
|
||||
|
||||
private static List<MetadataOperation> getOperations(String startsWith, MetadataOperation... exclude) {
|
||||
List<MetadataOperation> list = new ArrayList<>();
|
||||
List<MetadataOperation> excludeList = new ArrayList<>(List.of(exclude));
|
||||
if (!excludeList.isEmpty()) {
|
||||
excludeList.add(MetadataOperation.ALL); // If any operation is excluded then 'All' operation is excluded
|
||||
}
|
||||
for (MetadataOperation operation : MetadataOperation.values()) {
|
||||
if (!excludeList.contains(operation) && operation.value().startsWith(startsWith)) {
|
||||
list.add(operation);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
}
|
||||
|
||||
@ -274,6 +274,12 @@ public final class JsonUtils {
|
||||
return Json.createDiff(source.asJsonObject(), dest.asJsonObject());
|
||||
}
|
||||
|
||||
public static JsonPatch getJsonPatch(Object v1, Object v2) throws JsonProcessingException {
|
||||
JsonValue source = readJson(JsonUtils.pojoToJson(v1));
|
||||
JsonValue dest = readJson(JsonUtils.pojoToJson(v2));
|
||||
return Json.createDiff(source.asJsonObject(), dest.asJsonObject());
|
||||
}
|
||||
|
||||
public static JsonValue readJson(String s) {
|
||||
try (JsonReader reader = Json.createReader(new StringReader(s))) {
|
||||
return reader.readValue();
|
||||
|
||||
@ -367,18 +367,6 @@
|
||||
"EditCustomFields"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name" : "type",
|
||||
"operations" : [
|
||||
"Create",
|
||||
"Delete",
|
||||
"ViewAll",
|
||||
"EditAll",
|
||||
"EditDescription",
|
||||
"EditDisplayName",
|
||||
"EditCustomFields"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name" : "user",
|
||||
"operations" : [
|
||||
@ -441,6 +429,17 @@
|
||||
"ViewAll"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name" : "type",
|
||||
"operations" : [
|
||||
"Create",
|
||||
"Delete",
|
||||
"ViewAll",
|
||||
"EditAll",
|
||||
"EditDescription",
|
||||
"EditDisplayName"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name" : "webAnalyticEvent",
|
||||
"operations" : [
|
||||
@ -451,27 +450,5 @@
|
||||
"EditDescription",
|
||||
"EditDisplayName"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name" : "type",
|
||||
"operations" : [
|
||||
"Create",
|
||||
"Delete",
|
||||
"ViewAll",
|
||||
"EditAll",
|
||||
"EditDescription",
|
||||
"EditDisplayName"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name" : "type",
|
||||
"operations" : [
|
||||
"Create",
|
||||
"Delete",
|
||||
"ViewAll",
|
||||
"EditAll",
|
||||
"EditDescription",
|
||||
"EditDisplayName"
|
||||
]
|
||||
}
|
||||
]
|
||||
@ -10,7 +10,7 @@
|
||||
"name": "OrganizationPolicy-Owner-Rule",
|
||||
"description" : "Allow all the operations on an entity for the owner.",
|
||||
"resources" : ["all"],
|
||||
"operations": ["All"],
|
||||
"operations": ["EditAll", "ViewAll", "Delete"],
|
||||
"effect": "allow",
|
||||
"condition": "isOwner()"
|
||||
},
|
||||
|
||||
@ -15,34 +15,29 @@ package org.openmetadata.service.resources.permissions;
|
||||
|
||||
import static javax.ws.rs.core.Response.Status.FORBIDDEN;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.openmetadata.schema.type.MetadataOperation.ALL;
|
||||
import static org.openmetadata.schema.type.MetadataOperation.CREATE;
|
||||
import static org.openmetadata.schema.type.MetadataOperation.EDIT_DESCRIPTION;
|
||||
import static org.openmetadata.schema.type.MetadataOperation.EDIT_DISPLAY_NAME;
|
||||
import static org.openmetadata.schema.type.MetadataOperation.EDIT_LINEAGE;
|
||||
import static org.openmetadata.schema.type.MetadataOperation.EDIT_OWNER;
|
||||
import static org.openmetadata.schema.type.MetadataOperation.EDIT_TAGS;
|
||||
import static org.openmetadata.schema.type.MetadataOperation.VIEW_ALL;
|
||||
import static org.openmetadata.schema.type.MetadataOperation.VIEW_BASIC;
|
||||
import static org.openmetadata.schema.type.MetadataOperation.VIEW_DATA_PROFILE;
|
||||
import static org.openmetadata.schema.type.MetadataOperation.VIEW_QUERIES;
|
||||
import static org.openmetadata.schema.type.MetadataOperation.VIEW_SAMPLE_DATA;
|
||||
import static org.openmetadata.schema.type.MetadataOperation.VIEW_TESTS;
|
||||
import static org.openmetadata.schema.type.MetadataOperation.VIEW_USAGE;
|
||||
import static org.openmetadata.schema.type.Permission.Access.ALLOW;
|
||||
import static org.openmetadata.schema.type.Permission.Access.CONDITIONAL_ALLOW;
|
||||
import static org.openmetadata.schema.type.Permission.Access.NOT_ALLOW;
|
||||
import static org.openmetadata.service.security.policyevaluator.OperationContext.getAllOperations;
|
||||
import static org.openmetadata.service.security.policyevaluator.OperationContext.getViewOperations;
|
||||
import static org.openmetadata.service.util.TestUtils.ADMIN_AUTH_HEADERS;
|
||||
import static org.openmetadata.service.util.TestUtils.assertResponse;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import javax.ws.rs.client.WebTarget;
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.http.client.HttpResponseException;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
@ -69,6 +64,7 @@ import org.openmetadata.service.resources.permissions.PermissionsResource.Resour
|
||||
import org.openmetadata.service.resources.policies.PolicyResource;
|
||||
import org.openmetadata.service.resources.policies.PolicyResourceTest;
|
||||
import org.openmetadata.service.security.SecurityUtil;
|
||||
import org.openmetadata.service.security.policyevaluator.CompiledRule;
|
||||
import org.openmetadata.service.security.policyevaluator.PolicyEvaluator;
|
||||
import org.openmetadata.service.util.TestUtils;
|
||||
|
||||
@ -76,7 +72,10 @@ import org.openmetadata.service.util.TestUtils;
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
class PermissionsResourceTest extends OpenMetadataApplicationTest {
|
||||
private static final String ORG_POLICY_NAME = "OrganizationPolicy";
|
||||
private static List<Rule> ORG_RULES;
|
||||
private static Rule ORG_IS_OWNER_RULE;
|
||||
private static Rule ORG_NO_OWNER_RULE;
|
||||
private static final List<MetadataOperation> ORG_IS_OWNER_RULE_OPERATIONS = getAllOperations(CREATE);
|
||||
private static final List<MetadataOperation> ORG_NO_OWNER_RULE_OPERATIONS = List.of(EDIT_OWNER);
|
||||
|
||||
private static final String DATA_STEWARD_ROLE_NAME = "DataSteward";
|
||||
private static Policy DATA_STEWARD_POLICY;
|
||||
@ -87,23 +86,16 @@ class PermissionsResourceTest extends OpenMetadataApplicationTest {
|
||||
private static Policy DATA_CONSUMER_POLICY;
|
||||
private static final String DATA_CONSUMER_POLICY_NAME = "DataConsumerPolicy";
|
||||
private static List<Rule> DATA_CONSUMER_RULES;
|
||||
private static List<MetadataOperation> DATA_CONSUMER_ALLOWED =
|
||||
List.of(
|
||||
VIEW_ALL,
|
||||
VIEW_BASIC,
|
||||
VIEW_USAGE,
|
||||
VIEW_DATA_PROFILE,
|
||||
VIEW_SAMPLE_DATA,
|
||||
VIEW_BASIC,
|
||||
VIEW_TESTS,
|
||||
VIEW_QUERIES,
|
||||
EDIT_DESCRIPTION,
|
||||
EDIT_TAGS);
|
||||
|
||||
private static List<MetadataOperation> DATA_STEWARD_ALLOWED = new ArrayList<>(DATA_CONSUMER_ALLOWED);
|
||||
private static final List<MetadataOperation> DATA_CONSUMER_ALLOWED = getViewOperations();
|
||||
|
||||
static {
|
||||
// Additional permissions over DATA_CONSUMER
|
||||
DATA_CONSUMER_ALLOWED.addAll(List.of(EDIT_DESCRIPTION, EDIT_TAGS));
|
||||
}
|
||||
|
||||
private static final List<MetadataOperation> DATA_STEWARD_ALLOWED = new ArrayList<>(DATA_CONSUMER_ALLOWED);
|
||||
|
||||
static {
|
||||
// DATA_CONSUMER + additional operations
|
||||
DATA_STEWARD_ALLOWED.addAll(List.of(EDIT_OWNER, EDIT_DISPLAY_NAME, EDIT_LINEAGE));
|
||||
}
|
||||
|
||||
@ -119,7 +111,10 @@ class PermissionsResourceTest extends OpenMetadataApplicationTest {
|
||||
|
||||
Policy orgPolicy =
|
||||
policyResourceTest.getEntityByName(ORG_POLICY_NAME, null, PolicyResource.FIELDS, ADMIN_AUTH_HEADERS);
|
||||
ORG_RULES = orgPolicy.getRules();
|
||||
List<Rule> orgRules = orgPolicy.getRules();
|
||||
// Rules are alphabetically ordered
|
||||
ORG_NO_OWNER_RULE = orgRules.get(0);
|
||||
ORG_IS_OWNER_RULE = orgRules.get(1);
|
||||
|
||||
DATA_STEWARD_POLICY =
|
||||
policyResourceTest.getEntityByName(DATA_STEWARD_POLICY_NAME, null, PolicyResource.FIELDS, ADMIN_AUTH_HEADERS);
|
||||
@ -154,64 +149,79 @@ class PermissionsResourceTest extends OpenMetadataApplicationTest {
|
||||
|
||||
@Test
|
||||
void get_dataConsumer_permissions() throws HttpResponseException {
|
||||
// Ensure data consumer has permissions based on his role and the inherited roles
|
||||
List<MetadataOperation> conditional = List.of(ALL); // All operations are conditionally allowed
|
||||
|
||||
//
|
||||
// Validate permissions for all resources as logged-in user
|
||||
//
|
||||
Map<String, String> authHeaders = SecurityUtil.authHeaders(DATA_CONSUMER_USER_NAME + "@open-metadata.org");
|
||||
List<ResourcePermission> actualPermissions = getPermissions(authHeaders);
|
||||
assertDataConsumerPermissions(actualPermissions, conditional);
|
||||
ResourcePermissionsBuilder permissionsBuilder = new ResourcePermissionsBuilder();
|
||||
permissionsBuilder.setPermission(
|
||||
DATA_CONSUMER_ALLOWED, ALLOW, DATA_CONSUMER_ROLE_NAME, DATA_CONSUMER_POLICY_NAME, DATA_CONSUMER_RULES.get(0));
|
||||
// Set permissions based on alphabetical order of roles
|
||||
permissionsBuilder.setPermission(
|
||||
ORG_NO_OWNER_RULE_OPERATIONS, CONDITIONAL_ALLOW, null, ORG_POLICY_NAME, ORG_NO_OWNER_RULE);
|
||||
permissionsBuilder.setPermission(
|
||||
ORG_IS_OWNER_RULE_OPERATIONS, CONDITIONAL_ALLOW, null, ORG_POLICY_NAME, ORG_IS_OWNER_RULE);
|
||||
assertResourcePermissions(permissionsBuilder.getResourcePermissions(), actualPermissions);
|
||||
|
||||
// Validate permissions for all resources for data consumer as admin
|
||||
actualPermissions = getPermissions(DATA_CONSUMER_USER_NAME, ADMIN_AUTH_HEADERS);
|
||||
assertDataConsumerPermissions(actualPermissions, conditional);
|
||||
assertResourcePermissions(permissionsBuilder.getResourcePermissions(), actualPermissions);
|
||||
|
||||
// Validate permission as logged-in user for each resource one at a time
|
||||
ResourcePermission actualPermission;
|
||||
for (ResourceDescriptor rd : ResourceRegistry.listResourceDescriptors()) {
|
||||
ResourcePermission actualPermission = getPermission(rd.getName(), null, authHeaders);
|
||||
assertDataConsumerPermission(actualPermission, conditional);
|
||||
actualPermission = getPermission(rd.getName(), null, authHeaders);
|
||||
assertResourcePermission(permissionsBuilder.getPermission(rd.getName()), actualPermission);
|
||||
}
|
||||
|
||||
// Validate permission of data consumer as admin user for each resource one at a time
|
||||
for (ResourceDescriptor rd : ResourceRegistry.listResourceDescriptors()) {
|
||||
ResourcePermission actualPermission = getPermission(rd.getName(), DATA_CONSUMER_USER_NAME, authHeaders);
|
||||
assertDataConsumerPermission(actualPermission, conditional);
|
||||
actualPermission = getPermission(rd.getName(), DATA_CONSUMER_USER_NAME, authHeaders);
|
||||
assertResourcePermission(permissionsBuilder.getPermission(rd.getName()), actualPermission);
|
||||
}
|
||||
|
||||
//
|
||||
// Test getting permissions for an entity as an owner
|
||||
//
|
||||
// Create an entity with data consumer as owner
|
||||
TableResourceTest tableResourceTest = new TableResourceTest();
|
||||
CreateTable createTable =
|
||||
tableResourceTest.createRequest("permissionTest").withOwner(DATA_CONSUMER_USER.getEntityReference());
|
||||
Table table1 = tableResourceTest.createEntity(createTable, ADMIN_AUTH_HEADERS);
|
||||
|
||||
// Data consumer must have all operations allowed based on Organization policy as an owner
|
||||
ResourcePermission actualPermission = getPermission(Entity.TABLE, table1.getId(), null, authHeaders);
|
||||
assertAllOperationsAllowed(actualPermission);
|
||||
|
||||
// get permissions by resource entity name
|
||||
actualPermission = getPermissionByName(Entity.TABLE, table1.getFullyQualifiedName(), null, authHeaders);
|
||||
assertAllOperationsAllowed(actualPermission);
|
||||
|
||||
// Admin getting permissions for a specific resource on for Data consumer
|
||||
actualPermission = getPermission(Entity.TABLE, table1.getId(), DATA_CONSUMER_USER_NAME, ADMIN_AUTH_HEADERS);
|
||||
assertAllOperationsAllowed(actualPermission);
|
||||
|
||||
// Create another table with a different owner and make sure data consumer does not have permission as non owner
|
||||
createTable = tableResourceTest.createRequest("permissionTest1").withOwner(DATA_STEWARD_USER.getEntityReference());
|
||||
Table table2 = tableResourceTest.createEntity(createTable, ADMIN_AUTH_HEADERS);
|
||||
|
||||
//
|
||||
// Test getting permissions for an entity user does not own
|
||||
//
|
||||
permissionsBuilder = new ResourcePermissionsBuilder();
|
||||
permissionsBuilder.setPermission(
|
||||
DATA_CONSUMER_ALLOWED, ALLOW, DATA_CONSUMER_ROLE_NAME, DATA_CONSUMER_POLICY_NAME, DATA_CONSUMER_RULES.get(0));
|
||||
// Organization policy of no owner or isOwner does not apply. Hence, not added to expected permissions
|
||||
|
||||
// Create a table with a different owner and make sure data consumer does not have permission as non owner
|
||||
TableResourceTest tableResourceTest = new TableResourceTest();
|
||||
CreateTable createTable =
|
||||
tableResourceTest.createRequest("permissionTest1").withOwner(DATA_STEWARD_USER.getEntityReference());
|
||||
Table table2 = tableResourceTest.createEntity(createTable, ADMIN_AUTH_HEADERS);
|
||||
|
||||
// Data consumer has non-owner permissions
|
||||
actualPermission = getPermission(Entity.TABLE, table2.getId(), null, authHeaders);
|
||||
|
||||
// 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.
|
||||
assertDataConsumerPermission(actualPermission, Collections.emptyList());
|
||||
assertResourcePermission(permissionsBuilder.getPermission(Entity.TABLE), actualPermission);
|
||||
|
||||
//
|
||||
// Test getting permissions for an entity as an owner - where ORG_POLICY isOwner becomes effective
|
||||
//
|
||||
|
||||
// Create an entity with data consumer as owner
|
||||
createTable = tableResourceTest.createRequest("permissionTest").withOwner(DATA_CONSUMER_USER.getEntityReference());
|
||||
Table table1 = tableResourceTest.createEntity(createTable, ADMIN_AUTH_HEADERS);
|
||||
|
||||
// Data consumer must have all operations except create allowed based on Organization policy as an owner
|
||||
permissionsBuilder.setPermission(ORG_IS_OWNER_RULE_OPERATIONS, ALLOW, null, ORG_POLICY_NAME, ORG_IS_OWNER_RULE);
|
||||
actualPermission = getPermission(Entity.TABLE, table1.getId(), null, authHeaders);
|
||||
assertResourcePermission(permissionsBuilder.getPermission(Entity.TABLE), actualPermission);
|
||||
|
||||
// get permissions by resource entity name
|
||||
actualPermission = getPermissionByName(Entity.TABLE, table1.getFullyQualifiedName(), null, authHeaders);
|
||||
assertResourcePermission(permissionsBuilder.getPermission(Entity.TABLE), actualPermission);
|
||||
|
||||
// Admin getting permissions for a specific resource on for Data consumer
|
||||
actualPermission = getPermission(Entity.TABLE, table1.getId(), DATA_CONSUMER_USER_NAME, ADMIN_AUTH_HEADERS);
|
||||
assertResourcePermission(permissionsBuilder.getPermission(Entity.TABLE), actualPermission);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -219,9 +229,16 @@ class PermissionsResourceTest extends OpenMetadataApplicationTest {
|
||||
Map<String, String> authHeaders = SecurityUtil.authHeaders(DATA_STEWARD_USER_NAME + "@open-metadata.org");
|
||||
List<ResourcePermission> actualPermissions = getPermissions(authHeaders);
|
||||
|
||||
for (ResourcePermission actualPermission : actualPermissions) { // For all resources
|
||||
assertDataStewardPermission(actualPermission);
|
||||
}
|
||||
ResourcePermissionsBuilder permissionsBuilder = new ResourcePermissionsBuilder();
|
||||
permissionsBuilder.setPermission(
|
||||
DATA_STEWARD_ALLOWED, ALLOW, DATA_STEWARD_ROLE_NAME, DATA_STEWARD_POLICY_NAME, DATA_STEWARD_RULES.get(0));
|
||||
// Set permissions based on alphabetical order of roles
|
||||
permissionsBuilder.setPermission(
|
||||
ORG_NO_OWNER_RULE_OPERATIONS, CONDITIONAL_ALLOW, null, ORG_POLICY_NAME, ORG_NO_OWNER_RULE);
|
||||
permissionsBuilder.setPermission(
|
||||
ORG_IS_OWNER_RULE_OPERATIONS, CONDITIONAL_ALLOW, null, ORG_POLICY_NAME, ORG_IS_OWNER_RULE);
|
||||
|
||||
assertResourcePermissions(permissionsBuilder.getResourcePermissions(), actualPermissions);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -229,98 +246,41 @@ class PermissionsResourceTest extends OpenMetadataApplicationTest {
|
||||
// Get permissions for DATA_CONSUMER policy and assert it is correct
|
||||
List<UUID> policies = new ArrayList<>(List.of(DATA_CONSUMER_POLICY.getId()));
|
||||
List<ResourcePermission> actual = getPermissionsForPolicies(policies, ADMIN_AUTH_HEADERS);
|
||||
for (ResourcePermission actualPermission : actual) { // For every resource
|
||||
for (Permission permission : actualPermission.getPermissions()) {
|
||||
if (DATA_CONSUMER_ALLOWED.contains(permission.getOperation())) {
|
||||
assertPermissionAllowed(permission, null, DATA_CONSUMER_POLICY_NAME, DATA_CONSUMER_RULES);
|
||||
} else {
|
||||
assertPermissionNotAllowed(permission);
|
||||
}
|
||||
}
|
||||
}
|
||||
ResourcePermissionsBuilder permissionsBuilder = new ResourcePermissionsBuilder();
|
||||
permissionsBuilder.setPermission(
|
||||
DATA_CONSUMER_ALLOWED, ALLOW, null, DATA_CONSUMER_POLICY_NAME, DATA_CONSUMER_RULES.get(0));
|
||||
|
||||
assertResourcePermissions(permissionsBuilder.getResourcePermissions(), actual);
|
||||
|
||||
// Get permissions for DATA_CONSUMER and DATA_STEWARD policies and assert it is correct
|
||||
policies.add(DATA_STEWARD_POLICY.getId());
|
||||
actual = getPermissionsForPolicies(policies, ADMIN_AUTH_HEADERS);
|
||||
for (ResourcePermission actualPermission : actual) { // For every resource
|
||||
for (Permission permission : actualPermission.getPermissions()) {
|
||||
if (DATA_CONSUMER_ALLOWED.contains(permission.getOperation())) {
|
||||
assertPermissionAllowed(permission, null, DATA_CONSUMER_POLICY_NAME, DATA_CONSUMER_RULES);
|
||||
} else if (DATA_STEWARD_ALLOWED.contains(permission.getOperation())) {
|
||||
assertPermissionAllowed(permission, null, DATA_STEWARD_POLICY_NAME, DATA_STEWARD_RULES);
|
||||
} else {
|
||||
assertPermissionNotAllowed(permission);
|
||||
}
|
||||
}
|
||||
permissionsBuilder.setPermission(
|
||||
DATA_STEWARD_ALLOWED, ALLOW, null, DATA_STEWARD_POLICY_NAME, DATA_STEWARD_RULES.get(0));
|
||||
assertResourcePermissions(permissionsBuilder.getResourcePermissions(), actual);
|
||||
}
|
||||
|
||||
private void assertResourcePermissions(List<ResourcePermission> expected, List<ResourcePermission> actual) {
|
||||
assertEquals(expected.size(), actual.size());
|
||||
Comparator<ResourcePermission> resourcePermissionComparator = Comparator.comparing(ResourcePermission::getResource);
|
||||
expected.sort(resourcePermissionComparator);
|
||||
actual.sort(resourcePermissionComparator);
|
||||
for (int i = 0; i < expected.size(); i++) {
|
||||
assertResourcePermission(expected.get(i), actual.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
private void assertDataStewardPermission(ResourcePermission actualPermission) {
|
||||
// Only allowed operations in DataConsumerRole. All other operations are conditionalAllow by default
|
||||
for (Permission permission : actualPermission.getPermissions()) {
|
||||
if (DATA_STEWARD_ALLOWED.contains(permission.getOperation())) {
|
||||
assertPermissionAllowed(permission, DATA_STEWARD_ROLE_NAME, DATA_STEWARD_POLICY_NAME, DATA_STEWARD_RULES);
|
||||
} else {
|
||||
assertPermissionConditional(permission, null, ORG_POLICY_NAME, ORG_RULES);
|
||||
}
|
||||
private void assertResourcePermission(ResourcePermission expected, ResourcePermission actual) {
|
||||
assertEquals(expected.getPermissions().size(), actual.getPermissions().size());
|
||||
Comparator<Permission> operationComparator = Comparator.comparing(Permission::getOperation);
|
||||
expected.getPermissions().sort(operationComparator);
|
||||
actual.getPermissions().sort(operationComparator);
|
||||
for (int i = 0; i < expected.getPermissions().size(); i++) {
|
||||
// Using for loop to compare instead of equals to help with debugging the difference between the lists
|
||||
assertEquals(expected.getPermissions().get(i), actual.getPermissions().get(i));
|
||||
}
|
||||
}
|
||||
|
||||
private void assertAllOperationsAllowed(ResourcePermission actualPermission) {
|
||||
assertEquals(Entity.TABLE, actualPermission.getResource());
|
||||
for (Permission permission : actualPermission.getPermissions()) {
|
||||
assertEquals(ALLOW, permission.getAccess());
|
||||
assertTrue(List.of(ORG_POLICY_NAME, DATA_CONSUMER_POLICY_NAME).contains(permission.getPolicy()));
|
||||
}
|
||||
}
|
||||
|
||||
private void assertDataConsumerPermissions(
|
||||
List<ResourcePermission> actualPermissions, List<MetadataOperation> conditional) {
|
||||
// Only allowed operations in DataConsumerRole. All other operations are notAllow by default
|
||||
for (ResourcePermission actualPermission : actualPermissions) { // For every resource
|
||||
assertDataConsumerPermission(actualPermission, conditional); // assert permission
|
||||
}
|
||||
}
|
||||
|
||||
private void assertDataConsumerPermission(ResourcePermission actualPermission, List<MetadataOperation> conditional) {
|
||||
// Only allowed operations in DataConsumerRole. All other operations are conditional allow or not allow
|
||||
for (Permission permission : actualPermission.getPermissions()) {
|
||||
if (DATA_CONSUMER_ALLOWED.contains(permission.getOperation())) {
|
||||
assertPermissionAllowed(permission, DATA_CONSUMER_ROLE_NAME, DATA_CONSUMER_POLICY_NAME, DATA_CONSUMER_RULES);
|
||||
} else if (conditional.contains(permission.getOperation()) || conditional.contains(ALL)) {
|
||||
assertPermissionConditional(permission, null, ORG_POLICY_NAME, ORG_RULES);
|
||||
} else {
|
||||
assertPermissionNotAllowed(permission);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void assertPermissionAllowed(
|
||||
Permission permission, String expectedRole, String expectedPolicy, List<Rule> expectedRules) {
|
||||
assertPermission(permission, ALLOW, expectedRole, expectedPolicy, expectedRules);
|
||||
}
|
||||
|
||||
private void assertPermissionConditional(
|
||||
Permission permission, String expectedRole, String expectedPolicy, List<Rule> expectedRules) {
|
||||
assertPermission(permission, CONDITIONAL_ALLOW, expectedRole, expectedPolicy, expectedRules);
|
||||
}
|
||||
|
||||
private void assertPermissionNotAllowed(Permission permission) {
|
||||
assertPermission(permission, NOT_ALLOW, null, null, null);
|
||||
}
|
||||
|
||||
private void assertPermission(
|
||||
Permission permission,
|
||||
Access expectedAccess,
|
||||
String expectedRole,
|
||||
String expectedPolicy,
|
||||
List<Rule> expectedRules) {
|
||||
assertEquals(expectedAccess, permission.getAccess(), permission.toString());
|
||||
assertEquals(expectedRole, permission.getRole(), permission.toString());
|
||||
assertEquals(expectedPolicy, permission.getPolicy(), permission.toString());
|
||||
assertTrue(expectedRules == null || expectedRules.contains(permission.getRule()));
|
||||
}
|
||||
|
||||
public List<ResourcePermission> getPermissions(Map<String, String> authHeaders) throws HttpResponseException {
|
||||
WebTarget target = getResource("permissions");
|
||||
return TestUtils.get(target, ResourcePermissionList.class, authHeaders).getData();
|
||||
@ -362,4 +322,37 @@ class PermissionsResourceTest extends OpenMetadataApplicationTest {
|
||||
}
|
||||
return TestUtils.get(target, ResourcePermissionList.class, authHeaders).getData();
|
||||
}
|
||||
|
||||
/** Build permissions based on role, policies, etc. for testing purposes */
|
||||
public static class ResourcePermissionsBuilder {
|
||||
@Getter
|
||||
private final List<ResourcePermission> resourcePermissions = PolicyEvaluator.getResourcePermissions(NOT_ALLOW);
|
||||
|
||||
public void setPermission(
|
||||
List<MetadataOperation> operations, Access access, String role, String policy, Rule rule) {
|
||||
resourcePermissions.forEach(rp -> setPermission(rp, operations, access, role, policy, rule));
|
||||
}
|
||||
|
||||
public ResourcePermission getPermission(String resource) {
|
||||
return resourcePermissions.stream()
|
||||
.filter(resourcePermission -> resourcePermission.getResource().equals(resource))
|
||||
.findAny()
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
private void setPermission(
|
||||
ResourcePermission resourcePermission,
|
||||
List<MetadataOperation> operations,
|
||||
Access access,
|
||||
String role,
|
||||
String policy,
|
||||
Rule rule) {
|
||||
for (Permission permission : resourcePermission.getPermissions()) {
|
||||
if (operations.contains(permission.getOperation())
|
||||
&& CompiledRule.overrideAccess(access, permission.getAccess())) {
|
||||
permission.withAccess(access).withRole(role).withPolicy(policy).withRule(rule);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user