mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-09-27 09:55:36 +00:00
Integrate DefaultAuthorizer with PolicyEvaluator (#2017)
This commit is contained in:
parent
ed797dc335
commit
321a0e811b
@ -27,7 +27,9 @@ import io.dropwizard.setup.Bootstrap;
|
|||||||
import io.dropwizard.setup.Environment;
|
import io.dropwizard.setup.Environment;
|
||||||
import io.federecio.dropwizard.swagger.SwaggerBundle;
|
import io.federecio.dropwizard.swagger.SwaggerBundle;
|
||||||
import io.federecio.dropwizard.swagger.SwaggerBundleConfiguration;
|
import io.federecio.dropwizard.swagger.SwaggerBundleConfiguration;
|
||||||
|
import java.io.IOException;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.time.temporal.ChronoUnit;
|
||||||
import javax.ws.rs.container.ContainerRequestFilter;
|
import javax.ws.rs.container.ContainerRequestFilter;
|
||||||
import javax.ws.rs.container.ContainerResponseFilter;
|
import javax.ws.rs.container.ContainerResponseFilter;
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
@ -37,6 +39,8 @@ import org.eclipse.jetty.servlet.ErrorPageErrorHandler;
|
|||||||
import org.glassfish.jersey.media.multipart.MultiPartFeature;
|
import org.glassfish.jersey.media.multipart.MultiPartFeature;
|
||||||
import org.glassfish.jersey.server.ServerProperties;
|
import org.glassfish.jersey.server.ServerProperties;
|
||||||
import org.jdbi.v3.core.Jdbi;
|
import org.jdbi.v3.core.Jdbi;
|
||||||
|
import org.jdbi.v3.core.statement.SqlLogger;
|
||||||
|
import org.jdbi.v3.core.statement.StatementContext;
|
||||||
import org.openmetadata.catalog.events.EventFilter;
|
import org.openmetadata.catalog.events.EventFilter;
|
||||||
import org.openmetadata.catalog.events.EventPubSub;
|
import org.openmetadata.catalog.events.EventPubSub;
|
||||||
import org.openmetadata.catalog.exception.CatalogGenericExceptionMapper;
|
import org.openmetadata.catalog.exception.CatalogGenericExceptionMapper;
|
||||||
@ -62,19 +66,25 @@ public class CatalogApplication extends Application<CatalogApplicationConfig> {
|
|||||||
@Override
|
@Override
|
||||||
public void run(CatalogApplicationConfig catalogConfig, Environment environment)
|
public void run(CatalogApplicationConfig catalogConfig, Environment environment)
|
||||||
throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException,
|
throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException,
|
||||||
InvocationTargetException {
|
InvocationTargetException, IOException {
|
||||||
|
|
||||||
final JdbiFactory factory = new JdbiFactory();
|
final JdbiFactory factory = new JdbiFactory();
|
||||||
final Jdbi jdbi = factory.build(environment, catalogConfig.getDataSourceFactory(), "mysql3");
|
final Jdbi jdbi = factory.build(environment, catalogConfig.getDataSourceFactory(), "mysql3");
|
||||||
|
|
||||||
// SqlLogger sqlLogger = new SqlLogger() {
|
SqlLogger sqlLogger =
|
||||||
// @Override
|
new SqlLogger() {
|
||||||
// public void logAfterExecution(StatementContext context) {
|
@Override
|
||||||
// LOG.info("sql {}, parameters {}, timeTaken {} ms", context.getRenderedSql(),
|
public void logAfterExecution(StatementContext context) {
|
||||||
// context.getBinding().toString(), context.getElapsedTime(ChronoUnit.MILLIS));
|
LOG.debug(
|
||||||
// }
|
"sql {}, parameters {}, timeTaken {} ms",
|
||||||
// };
|
context.getRenderedSql(),
|
||||||
// jdbi.setSqlLogger(sqlLogger);
|
context.getBinding(),
|
||||||
|
context.getElapsedTime(ChronoUnit.MILLIS));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if (LOG.isDebugEnabled()) {
|
||||||
|
jdbi.setSqlLogger(sqlLogger);
|
||||||
|
}
|
||||||
|
|
||||||
// Register Authorizer
|
// Register Authorizer
|
||||||
registerAuthorizer(catalogConfig, environment, jdbi);
|
registerAuthorizer(catalogConfig, environment, jdbi);
|
||||||
@ -128,7 +138,7 @@ public class CatalogApplication extends Application<CatalogApplicationConfig> {
|
|||||||
|
|
||||||
private void registerAuthorizer(CatalogApplicationConfig catalogConfig, Environment environment, Jdbi jdbi)
|
private void registerAuthorizer(CatalogApplicationConfig catalogConfig, Environment environment, Jdbi jdbi)
|
||||||
throws NoSuchMethodException, ClassNotFoundException, IllegalAccessException, InvocationTargetException,
|
throws NoSuchMethodException, ClassNotFoundException, IllegalAccessException, InvocationTargetException,
|
||||||
InstantiationException {
|
InstantiationException, IOException {
|
||||||
AuthorizerConfiguration authorizerConf = catalogConfig.getAuthorizerConfiguration();
|
AuthorizerConfiguration authorizerConf = catalogConfig.getAuthorizerConfiguration();
|
||||||
AuthenticationConfiguration authenticationConfiguration = catalogConfig.getAuthenticationConfiguration();
|
AuthenticationConfiguration authenticationConfiguration = catalogConfig.getAuthenticationConfiguration();
|
||||||
if (authorizerConf != null) {
|
if (authorizerConf != null) {
|
||||||
|
@ -15,6 +15,7 @@ package org.openmetadata.catalog;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
|
import java.text.ParseException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@ -24,13 +25,17 @@ import java.util.Map;
|
|||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import javax.ws.rs.core.UriInfo;
|
import javax.ws.rs.core.UriInfo;
|
||||||
|
import lombok.NonNull;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.openmetadata.catalog.exception.CatalogExceptionMessage;
|
import org.openmetadata.catalog.exception.CatalogExceptionMessage;
|
||||||
import org.openmetadata.catalog.exception.EntityNotFoundException;
|
import org.openmetadata.catalog.exception.EntityNotFoundException;
|
||||||
import org.openmetadata.catalog.jdbi3.EntityDAO;
|
import org.openmetadata.catalog.jdbi3.EntityDAO;
|
||||||
import org.openmetadata.catalog.jdbi3.EntityRepository;
|
import org.openmetadata.catalog.jdbi3.EntityRepository;
|
||||||
import org.openmetadata.catalog.type.EntityReference;
|
import org.openmetadata.catalog.type.EntityReference;
|
||||||
import org.openmetadata.catalog.util.EntityInterface;
|
import org.openmetadata.catalog.util.EntityInterface;
|
||||||
|
import org.openmetadata.catalog.util.EntityUtil;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
public final class Entity {
|
public final class Entity {
|
||||||
private static final Map<String, EntityDAO<?>> DAO_MAP = new HashMap<>();
|
private static final Map<String, EntityDAO<?>> DAO_MAP = new HashMap<>();
|
||||||
private static final Map<String, EntityRepository<?>> ENTITY_REPOSITORY_MAP = new HashMap<>();
|
private static final Map<String, EntityRepository<?>> ENTITY_REPOSITORY_MAP = new HashMap<>();
|
||||||
@ -86,7 +91,7 @@ public final class Entity {
|
|||||||
DAO_MAP.put(entity, dao);
|
DAO_MAP.put(entity, dao);
|
||||||
ENTITY_REPOSITORY_MAP.put(entity, entityRepository);
|
ENTITY_REPOSITORY_MAP.put(entity, entityRepository);
|
||||||
CANONICAL_ENTITY_NAME_MAP.put(entity.toLowerCase(Locale.ROOT), entity);
|
CANONICAL_ENTITY_NAME_MAP.put(entity.toLowerCase(Locale.ROOT), entity);
|
||||||
System.out.println("Registering entity " + entity);
|
log.info("Registering entity {}", entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static EntityReference getEntityReference(String entity, UUID id) throws IOException {
|
public static EntityReference getEntityReference(String entity, UUID id) throws IOException {
|
||||||
@ -138,12 +143,45 @@ public final class Entity {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
String entityName = getEntityNameFromObject(entity);
|
String entityName = getEntityNameFromObject(entity);
|
||||||
|
EntityRepository<T> entityRepository = getEntityRepository(entityName);
|
||||||
|
return entityRepository.getEntityInterface(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the entity using id from given entity reference and fields
|
||||||
|
*
|
||||||
|
* @return entity object eg: {@link org.openmetadata.catalog.entity.data.Table}, {@link
|
||||||
|
* org.openmetadata.catalog.entity.data.Topic}, etc
|
||||||
|
*/
|
||||||
|
public static <T> T getEntity(EntityReference entityReference, EntityUtil.Fields fields)
|
||||||
|
throws IOException, ParseException {
|
||||||
|
if (entityReference == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
EntityRepository<?> entityRepository = Entity.getEntityRepository(entityReference.getType());
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
T entity = (T) entityRepository.get(null, entityReference.getId().toString(), fields);
|
||||||
|
if (entity == null) {
|
||||||
|
throw EntityNotFoundException.byMessage(
|
||||||
|
CatalogExceptionMessage.entityNotFound(entityReference.getType(), entityReference.getId()));
|
||||||
|
}
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the corresponding entity repository for a given entity name.
|
||||||
|
*
|
||||||
|
* @param entityName type of entity, eg: {@link Entity#TABLE}, {@link Entity#TOPIC}, etc
|
||||||
|
* @return entity repository corresponding to the entity name
|
||||||
|
*/
|
||||||
|
public static <T> EntityRepository<T> getEntityRepository(@NonNull String entityName) {
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
EntityRepository<T> entityRepository = (EntityRepository<T>) ENTITY_REPOSITORY_MAP.get(entityName);
|
EntityRepository<T> entityRepository = (EntityRepository<T>) ENTITY_REPOSITORY_MAP.get(entityName);
|
||||||
if (entityRepository == null) {
|
if (entityRepository == null) {
|
||||||
throw EntityNotFoundException.byMessage(CatalogExceptionMessage.entityTypeNotFound(entityName));
|
throw EntityNotFoundException.byMessage(CatalogExceptionMessage.entityTypeNotFound(entityName));
|
||||||
}
|
}
|
||||||
return entityRepository.getEntityInterface(entity);
|
return entityRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void deleteEntity(String entity, UUID entityId, boolean recursive) throws IOException {
|
public static void deleteEntity(String entity, UUID entityId, boolean recursive) throws IOException {
|
||||||
|
@ -15,6 +15,7 @@ package org.openmetadata.catalog.jdbi3;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
@ -24,6 +25,7 @@ import org.openmetadata.catalog.Entity;
|
|||||||
import org.openmetadata.catalog.entity.data.Location;
|
import org.openmetadata.catalog.entity.data.Location;
|
||||||
import org.openmetadata.catalog.entity.policies.Policy;
|
import org.openmetadata.catalog.entity.policies.Policy;
|
||||||
import org.openmetadata.catalog.entity.policies.accessControl.Rule;
|
import org.openmetadata.catalog.entity.policies.accessControl.Rule;
|
||||||
|
import org.openmetadata.catalog.exception.CatalogExceptionMessage;
|
||||||
import org.openmetadata.catalog.resources.policies.PolicyResource;
|
import org.openmetadata.catalog.resources.policies.PolicyResource;
|
||||||
import org.openmetadata.catalog.type.ChangeDescription;
|
import org.openmetadata.catalog.type.ChangeDescription;
|
||||||
import org.openmetadata.catalog.type.EntityReference;
|
import org.openmetadata.catalog.type.EntityReference;
|
||||||
@ -184,6 +186,33 @@ public class PolicyRepository extends EntityRepository<Policy> {
|
|||||||
// No validation errors, if execution reaches here.
|
// No validation errors, if execution reaches here.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<Policy> getAccessControlPolicies() throws IOException {
|
||||||
|
EntityUtil.Fields fields = new EntityUtil.Fields(List.of("policyType", "rules"));
|
||||||
|
List<String> jsons = daoCollection.policyDAO().listAfter(null, Integer.MAX_VALUE, "");
|
||||||
|
List<Policy> policies = new ArrayList<>(jsons.size());
|
||||||
|
for (String json : jsons) {
|
||||||
|
Policy policy = setFields(JsonUtils.readValue(json, Policy.class), fields);
|
||||||
|
if (policy.getPolicyType() != PolicyType.AccessControl) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
policies.add(policy);
|
||||||
|
}
|
||||||
|
return policies;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Rule> getAccessControlPolicyRules() throws IOException {
|
||||||
|
List<Policy> policies = getAccessControlPolicies();
|
||||||
|
List<Rule> rules = new ArrayList<>();
|
||||||
|
for (Policy policy : policies) {
|
||||||
|
List<Object> ruleObjects = policy.getRules();
|
||||||
|
for (Object ruleObject : ruleObjects) {
|
||||||
|
Rule rule = JsonUtils.readValue(JsonUtils.getJsonStructure(ruleObject).toString(), Rule.class);
|
||||||
|
rules.add(rule);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rules;
|
||||||
|
}
|
||||||
|
|
||||||
private void setLocation(Policy policy, EntityReference location) {
|
private void setLocation(Policy policy, EntityReference location) {
|
||||||
if (location == null || location.getId() == null) {
|
if (location == null || location.getId() == null) {
|
||||||
return;
|
return;
|
||||||
@ -334,6 +363,10 @@ public class PolicyRepository extends EntityRepository<Policy> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void entitySpecificUpdate() throws IOException {
|
public void entitySpecificUpdate() throws IOException {
|
||||||
|
// Disallow changing policyType.
|
||||||
|
if (original.getEntity().getPolicyType() != updated.getEntity().getPolicyType()) {
|
||||||
|
throw new IllegalArgumentException(CatalogExceptionMessage.readOnlyAttribute(Entity.POLICY, "policyType"));
|
||||||
|
}
|
||||||
recordChange("policyUrl", original.getEntity().getPolicyUrl(), updated.getEntity().getPolicyUrl());
|
recordChange("policyUrl", original.getEntity().getPolicyUrl(), updated.getEntity().getPolicyUrl());
|
||||||
recordChange("enabled", original.getEntity().getEnabled(), updated.getEntity().getEnabled());
|
recordChange("enabled", original.getEntity().getEnabled(), updated.getEntity().getEnabled());
|
||||||
recordChange("rules", original.getEntity().getRules(), updated.getEntity().getRules());
|
recordChange("rules", original.getEntity().getRules(), updated.getEntity().getRules());
|
||||||
|
@ -13,13 +13,15 @@
|
|||||||
|
|
||||||
package org.openmetadata.catalog.security;
|
package org.openmetadata.catalog.security;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import org.jdbi.v3.core.Jdbi;
|
import org.jdbi.v3.core.Jdbi;
|
||||||
import org.openmetadata.catalog.type.EntityReference;
|
import org.openmetadata.catalog.type.EntityReference;
|
||||||
|
import org.openmetadata.catalog.type.MetadataOperation;
|
||||||
|
|
||||||
public interface Authorizer {
|
public interface Authorizer {
|
||||||
|
|
||||||
/** Initialize the authorizer */
|
/** Initialize the authorizer */
|
||||||
void init(AuthorizerConfiguration config, Jdbi jdbi);
|
void init(AuthorizerConfiguration config, Jdbi jdbi) throws IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if the authenticated user has given permission on the target entity identified by the given resourceType and
|
* Check if the authenticated user has given permission on the target entity identified by the given resourceType and
|
||||||
@ -27,6 +29,12 @@ public interface Authorizer {
|
|||||||
*/
|
*/
|
||||||
boolean hasPermissions(AuthenticationContext ctx, EntityReference entityReference);
|
boolean hasPermissions(AuthenticationContext ctx, EntityReference entityReference);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the authenticated user (subject) has permission to perform the {@link MetadataOperation} on the target
|
||||||
|
* entity (object).
|
||||||
|
*/
|
||||||
|
boolean hasPermissions(AuthenticationContext ctx, EntityReference entityReference, MetadataOperation operation);
|
||||||
|
|
||||||
boolean isAdmin(AuthenticationContext ctx);
|
boolean isAdmin(AuthenticationContext ctx);
|
||||||
|
|
||||||
boolean isBot(AuthenticationContext ctx);
|
boolean isBot(AuthenticationContext ctx);
|
||||||
|
@ -19,6 +19,7 @@ import java.io.IOException;
|
|||||||
import java.text.ParseException;
|
import java.text.ParseException;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import org.apache.commons.lang3.exception.ExceptionUtils;
|
import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||||
@ -28,8 +29,11 @@ import org.openmetadata.catalog.entity.teams.User;
|
|||||||
import org.openmetadata.catalog.exception.DuplicateEntityException;
|
import org.openmetadata.catalog.exception.DuplicateEntityException;
|
||||||
import org.openmetadata.catalog.exception.EntityNotFoundException;
|
import org.openmetadata.catalog.exception.EntityNotFoundException;
|
||||||
import org.openmetadata.catalog.jdbi3.CollectionDAO;
|
import org.openmetadata.catalog.jdbi3.CollectionDAO;
|
||||||
|
import org.openmetadata.catalog.jdbi3.PolicyRepository;
|
||||||
import org.openmetadata.catalog.jdbi3.UserRepository;
|
import org.openmetadata.catalog.jdbi3.UserRepository;
|
||||||
|
import org.openmetadata.catalog.security.policyevaluator.PolicyEvaluator;
|
||||||
import org.openmetadata.catalog.type.EntityReference;
|
import org.openmetadata.catalog.type.EntityReference;
|
||||||
|
import org.openmetadata.catalog.type.MetadataOperation;
|
||||||
import org.openmetadata.catalog.util.EntityUtil;
|
import org.openmetadata.catalog.util.EntityUtil;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@ -42,19 +46,22 @@ public class DefaultAuthorizer implements Authorizer {
|
|||||||
|
|
||||||
private String principalDomain;
|
private String principalDomain;
|
||||||
private UserRepository userRepository;
|
private UserRepository userRepository;
|
||||||
private static final String fieldsParam = "teams";
|
private PolicyEvaluator policyEvaluator;
|
||||||
|
private static final String fieldsParam = "roles,teams";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void init(AuthorizerConfiguration config, Jdbi dbi) {
|
public void init(AuthorizerConfiguration config, Jdbi dbi) throws IOException {
|
||||||
LOG.debug("Initializing DefaultAuthorizer with config {}", config);
|
LOG.debug("Initializing DefaultAuthorizer with config {}", config);
|
||||||
this.adminUsers = new HashSet<>(config.getAdminPrincipals());
|
this.adminUsers = new HashSet<>(config.getAdminPrincipals());
|
||||||
this.botUsers = new HashSet<>(config.getBotPrincipals());
|
this.botUsers = new HashSet<>(config.getBotPrincipals());
|
||||||
this.principalDomain = config.getPrincipalDomain();
|
this.principalDomain = config.getPrincipalDomain();
|
||||||
LOG.debug("Admin users: {}", adminUsers);
|
LOG.debug("Admin users: {}", adminUsers);
|
||||||
CollectionDAO repo = dbi.onDemand(CollectionDAO.class);
|
CollectionDAO collectionDAO = dbi.onDemand(CollectionDAO.class);
|
||||||
this.userRepository = new UserRepository(repo);
|
this.userRepository = new UserRepository(collectionDAO);
|
||||||
mayBeAddAdminUsers();
|
mayBeAddAdminUsers();
|
||||||
mayBeAddBotUsers();
|
mayBeAddBotUsers();
|
||||||
|
// Load all rules from access control policies at once during init.
|
||||||
|
this.policyEvaluator = new PolicyEvaluator(new PolicyRepository(collectionDAO).getAccessControlPolicyRules());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void mayBeAddAdminUsers() {
|
private void mayBeAddAdminUsers() {
|
||||||
@ -104,10 +111,8 @@ public class DefaultAuthorizer implements Authorizer {
|
|||||||
if (owner == null) {
|
if (owner == null) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
String userName = SecurityUtil.getUserName(ctx);
|
|
||||||
EntityUtil.Fields fields = new EntityUtil.Fields(FIELD_LIST, fieldsParam);
|
|
||||||
try {
|
try {
|
||||||
User user = userRepository.getByName(null, userName, fields);
|
User user = getUserFromAuthenticationContext(ctx);
|
||||||
if (owner.getType().equals(Entity.TEAM)) {
|
if (owner.getType().equals(Entity.TEAM)) {
|
||||||
for (EntityReference team : user.getTeams()) {
|
for (EntityReference team : user.getTeams()) {
|
||||||
if (team.getName().equals(owner.getName())) {
|
if (team.getName().equals(owner.getName())) {
|
||||||
@ -123,6 +128,20 @@ public class DefaultAuthorizer implements Authorizer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasPermissions(
|
||||||
|
AuthenticationContext ctx, EntityReference entityReference, MetadataOperation operation) {
|
||||||
|
validateAuthenticationContext(ctx);
|
||||||
|
try {
|
||||||
|
return policyEvaluator.hasPermission(
|
||||||
|
getUserFromAuthenticationContext(ctx),
|
||||||
|
Entity.getEntity(entityReference, new EntityUtil.Fields(List.of("tags"))),
|
||||||
|
operation);
|
||||||
|
} catch (IOException | EntityNotFoundException | ParseException ex) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isAdmin(AuthenticationContext ctx) {
|
public boolean isAdmin(AuthenticationContext ctx) {
|
||||||
validateAuthenticationContext(ctx);
|
validateAuthenticationContext(ctx);
|
||||||
@ -161,6 +180,12 @@ public class DefaultAuthorizer implements Authorizer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private User getUserFromAuthenticationContext(AuthenticationContext ctx) throws IOException, ParseException {
|
||||||
|
String userName = SecurityUtil.getUserName(ctx);
|
||||||
|
EntityUtil.Fields fields = new EntityUtil.Fields(FIELD_LIST, fieldsParam);
|
||||||
|
return userRepository.getByName(null, userName, fields);
|
||||||
|
}
|
||||||
|
|
||||||
private void addAdmin(String name) {
|
private void addAdmin(String name) {
|
||||||
User user =
|
User user =
|
||||||
new User()
|
new User()
|
||||||
|
@ -15,6 +15,7 @@ package org.openmetadata.catalog.security;
|
|||||||
|
|
||||||
import org.jdbi.v3.core.Jdbi;
|
import org.jdbi.v3.core.Jdbi;
|
||||||
import org.openmetadata.catalog.type.EntityReference;
|
import org.openmetadata.catalog.type.EntityReference;
|
||||||
|
import org.openmetadata.catalog.type.MetadataOperation;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@ -31,6 +32,12 @@ public class NoopAuthorizer implements Authorizer {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasPermissions(
|
||||||
|
AuthenticationContext ctx, EntityReference entityReference, MetadataOperation operation) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isAdmin(AuthenticationContext ctx) {
|
public boolean isAdmin(AuthenticationContext ctx) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -20,6 +20,7 @@ import javax.ws.rs.client.Invocation;
|
|||||||
import javax.ws.rs.client.WebTarget;
|
import javax.ws.rs.client.WebTarget;
|
||||||
import javax.ws.rs.core.SecurityContext;
|
import javax.ws.rs.core.SecurityContext;
|
||||||
import org.openmetadata.catalog.type.EntityReference;
|
import org.openmetadata.catalog.type.EntityReference;
|
||||||
|
import org.openmetadata.catalog.type.MetadataOperation;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@ -55,6 +56,20 @@ public final class SecurityUtil {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void checkAdminRoleOrPermissions(
|
||||||
|
Authorizer authorizer,
|
||||||
|
SecurityContext securityContext,
|
||||||
|
EntityReference entityReference,
|
||||||
|
MetadataOperation metadataOperation) {
|
||||||
|
Principal principal = securityContext.getUserPrincipal();
|
||||||
|
AuthenticationContext authenticationCtx = SecurityUtil.getAuthenticationContext(principal);
|
||||||
|
if (!authorizer.isAdmin(authenticationCtx)
|
||||||
|
&& !authorizer.isBot(authenticationCtx)
|
||||||
|
&& !authorizer.hasPermissions(authenticationCtx, entityReference, metadataOperation)) {
|
||||||
|
throw new AuthorizationException("Principal: " + principal + " does not have permissions");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static String getUserName(String principalName) {
|
public static String getUserName(String principalName) {
|
||||||
return principalName == null ? null : principalName.split("[/@]")[0];
|
return principalName == null ? null : principalName.split("[/@]")[0];
|
||||||
}
|
}
|
||||||
|
@ -1,25 +1,27 @@
|
|||||||
package org.openmetadata.catalog.security.policyevaluator;
|
package org.openmetadata.catalog.security.policyevaluator;
|
||||||
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import lombok.Builder;
|
import lombok.Builder;
|
||||||
|
import lombok.NonNull;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.jeasy.rules.api.Facts;
|
import org.jeasy.rules.api.Facts;
|
||||||
|
import org.openmetadata.catalog.Entity;
|
||||||
import org.openmetadata.catalog.entity.teams.User;
|
import org.openmetadata.catalog.entity.teams.User;
|
||||||
|
import org.openmetadata.catalog.exception.EntityNotFoundException;
|
||||||
import org.openmetadata.catalog.type.EntityReference;
|
import org.openmetadata.catalog.type.EntityReference;
|
||||||
import org.openmetadata.catalog.type.MetadataOperation;
|
import org.openmetadata.catalog.type.MetadataOperation;
|
||||||
import org.openmetadata.catalog.type.TagLabel;
|
import org.openmetadata.catalog.type.TagLabel;
|
||||||
|
import org.openmetadata.catalog.util.EntityInterface;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Builder(setterPrefix = "with")
|
@Builder(setterPrefix = "with")
|
||||||
class AttributeBasedFacts {
|
class AttributeBasedFacts {
|
||||||
|
|
||||||
private User user;
|
@NonNull private User user;
|
||||||
private Object entity;
|
@NonNull private Object entity;
|
||||||
private MetadataOperation operation;
|
@NonNull private MetadataOperation operation;
|
||||||
|
|
||||||
// Do not allow anything external or the builder itself change the value of facts.
|
// Do not allow anything external or the builder itself change the value of facts.
|
||||||
// Individual Fact(s) within facts may be changed by the RulesEngine.
|
// Individual Fact(s) within facts may be changed by the RulesEngine.
|
||||||
@ -29,10 +31,7 @@ class AttributeBasedFacts {
|
|||||||
* Creates {@link Facts} with the operation, user (subject) and entity (object) attributes so that it is recognizable
|
* Creates {@link Facts} with the operation, user (subject) and entity (object) attributes so that it is recognizable
|
||||||
* by {@link org.jeasy.rules.api.RulesEngine}
|
* by {@link org.jeasy.rules.api.RulesEngine}
|
||||||
*/
|
*/
|
||||||
public Facts getFacts() throws RuntimeException {
|
public Facts getFacts() {
|
||||||
if (!validate()) {
|
|
||||||
throw new RuntimeException("Validation failed while building facts");
|
|
||||||
}
|
|
||||||
facts.put(CommonFields.USER_ROLES, getUserRoles(user));
|
facts.put(CommonFields.USER_ROLES, getUserRoles(user));
|
||||||
facts.put(CommonFields.ENTITY_TAGS, getEntityTags(entity));
|
facts.put(CommonFields.ENTITY_TAGS, getEntityTags(entity));
|
||||||
facts.put(CommonFields.ENTITY_TYPE, getEntityType(entity));
|
facts.put(CommonFields.ENTITY_TYPE, getEntityType(entity));
|
||||||
@ -46,35 +45,29 @@ class AttributeBasedFacts {
|
|||||||
return facts.get(CommonFields.ALLOW);
|
return facts.get(CommonFields.ALLOW);
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<String> getUserRoles(User user) {
|
private List<String> getUserRoles(@NonNull User user) {
|
||||||
// TODO: Fix this to fetch user's roles when roles is added as entity reference list from user schema.
|
return user.getRoles().stream().map(EntityReference::getName).collect(Collectors.toList());
|
||||||
return user.getTeams().stream().map(EntityReference::getName).collect(Collectors.toList());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<String> getEntityTags(Object entity) {
|
private List<String> getEntityTags(@NonNull Object entity) {
|
||||||
// TODO: Fix the entityType fetch such that it is fetched from Repository or using a Util.
|
List<TagLabel> entityTags = null;
|
||||||
// This is done here now to facilitate prototyping and unit testing.
|
|
||||||
List<TagLabel> entityTags = Collections.emptyList();
|
|
||||||
try {
|
try {
|
||||||
entityTags = (List<TagLabel>) entity.getClass().getMethod("getTags").invoke(entity);
|
EntityInterface<?> entityInterface = Entity.getEntityInterface(entity);
|
||||||
} catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) {
|
entityTags = entityInterface.getTags();
|
||||||
log.warn("Could not obtain tags for entity with class {}", entity.getClass());
|
} catch (EntityNotFoundException e) {
|
||||||
|
log.warn("could not obtain tags for the given entity {} - exception: {}", entity, e.toString());
|
||||||
|
}
|
||||||
|
if (entityTags == null) {
|
||||||
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
return entityTags.stream().map(TagLabel::getTagFQN).collect(Collectors.toList());
|
return entityTags.stream().map(TagLabel::getTagFQN).collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String getEntityType(Object object) {
|
private static String getEntityType(@NonNull Object entity) {
|
||||||
// TODO: Fix the entityType fetch such that it is fetched from Repository or using a Util.
|
String entityType = Entity.getEntityNameFromObject(entity);
|
||||||
// This is done here now to facilitate prototyping and unit testing.
|
if (entityType == null) {
|
||||||
return object.getClass().getSimpleName().toLowerCase(Locale.ROOT);
|
log.warn("could not find entity type for the given entity {}", entity);
|
||||||
}
|
}
|
||||||
|
return entityType;
|
||||||
private boolean validate() {
|
|
||||||
log.debug(
|
|
||||||
"Validating attribute based facts - user: {}, entity: {}, operation: {}",
|
|
||||||
this.user,
|
|
||||||
this.entity,
|
|
||||||
this.operation);
|
|
||||||
return this.user != null && this.entity != null && this.operation != null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,7 @@ import java.util.function.BiPredicate;
|
|||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
import javax.ws.rs.WebApplicationException;
|
import javax.ws.rs.WebApplicationException;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.joda.time.Period;
|
import org.joda.time.Period;
|
||||||
import org.joda.time.format.ISOPeriodFormat;
|
import org.joda.time.format.ISOPeriodFormat;
|
||||||
import org.openmetadata.catalog.Entity;
|
import org.openmetadata.catalog.Entity;
|
||||||
@ -391,6 +392,7 @@ public final class EntityUtil {
|
|||||||
return followers;
|
return followers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@RequiredArgsConstructor
|
||||||
public static class Fields {
|
public static class Fields {
|
||||||
public static final Fields EMPTY_FIELDS = new Fields(null, null);
|
public static final Fields EMPTY_FIELDS = new Fields(null, null);
|
||||||
private final List<String> fieldList;
|
private final List<String> fieldList;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user