mirror of
https://github.com/datahub-project/datahub.git
synced 2025-10-18 20:34:14 +00:00
feat(GraphQL): Support for Deleting Domains, Tags via GraphQL API (#5272)
This commit is contained in:
parent
be0bc0f0f0
commit
fdee7a787b
@ -84,6 +84,7 @@ import com.linkedin.datahub.graphql.resolvers.container.ParentContainersResolver
|
||||
import com.linkedin.datahub.graphql.resolvers.dataset.DatasetHealthResolver;
|
||||
import com.linkedin.datahub.graphql.resolvers.deprecation.UpdateDeprecationResolver;
|
||||
import com.linkedin.datahub.graphql.resolvers.domain.CreateDomainResolver;
|
||||
import com.linkedin.datahub.graphql.resolvers.domain.DeleteDomainResolver;
|
||||
import com.linkedin.datahub.graphql.resolvers.domain.DomainEntitiesResolver;
|
||||
import com.linkedin.datahub.graphql.resolvers.domain.ListDomainsResolver;
|
||||
import com.linkedin.datahub.graphql.resolvers.domain.SetDomainResolver;
|
||||
@ -153,6 +154,8 @@ import com.linkedin.datahub.graphql.resolvers.search.AutoCompleteResolver;
|
||||
import com.linkedin.datahub.graphql.resolvers.search.SearchAcrossEntitiesResolver;
|
||||
import com.linkedin.datahub.graphql.resolvers.search.SearchAcrossLineageResolver;
|
||||
import com.linkedin.datahub.graphql.resolvers.search.SearchResolver;
|
||||
import com.linkedin.datahub.graphql.resolvers.tag.CreateTagResolver;
|
||||
import com.linkedin.datahub.graphql.resolvers.tag.DeleteTagResolver;
|
||||
import com.linkedin.datahub.graphql.resolvers.tag.SetTagColorResolver;
|
||||
import com.linkedin.datahub.graphql.resolvers.test.CreateTestResolver;
|
||||
import com.linkedin.datahub.graphql.resolvers.test.DeleteTestResolver;
|
||||
@ -672,8 +675,10 @@ public class GmsGraphQLEngine {
|
||||
private void configureMutationResolvers(final RuntimeWiring.Builder builder) {
|
||||
builder.type("Mutation", typeWiring -> typeWiring
|
||||
.dataFetcher("updateDataset", new MutableTypeResolver<>(datasetType))
|
||||
.dataFetcher("createTag", new CreateTagResolver(entityService))
|
||||
.dataFetcher("updateTag", new MutableTypeResolver<>(tagType))
|
||||
.dataFetcher("setTagColor", new SetTagColorResolver(entityClient, entityService))
|
||||
.dataFetcher("deleteTag", new DeleteTagResolver(entityClient))
|
||||
.dataFetcher("updateChart", new MutableTypeResolver<>(chartType))
|
||||
.dataFetcher("updateDashboard", new MutableTypeResolver<>(dashboardType))
|
||||
.dataFetcher("updateNotebook", new MutableTypeResolver<>(notebookType))
|
||||
@ -702,7 +707,8 @@ public class GmsGraphQLEngine {
|
||||
.dataFetcher("removeUser", new RemoveUserResolver(this.entityClient))
|
||||
.dataFetcher("removeGroup", new RemoveGroupResolver(this.entityClient))
|
||||
.dataFetcher("updateUserStatus", new UpdateUserStatusResolver(this.entityClient))
|
||||
.dataFetcher("createDomain", new CreateDomainResolver(this.entityClient))
|
||||
.dataFetcher("createDomain", new CreateDomainResolver(this.entityService))
|
||||
.dataFetcher("deleteDomain", new DeleteDomainResolver(entityClient))
|
||||
.dataFetcher("setDomain", new SetDomainResolver(this.entityClient, this.entityService))
|
||||
.dataFetcher("updateDeprecation", new UpdateDeprecationResolver(this.entityClient, this.entityService))
|
||||
.dataFetcher("unsetDomain", new UnsetDomainResolver(this.entityClient, this.entityService))
|
||||
|
@ -5,14 +5,24 @@ import com.datahub.authorization.AuthorizationResult;
|
||||
import com.datahub.authorization.Authorizer;
|
||||
import com.datahub.authorization.ResourceSpec;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.linkedin.common.AuditStamp;
|
||||
import com.linkedin.common.urn.Urn;
|
||||
import com.linkedin.common.urn.UrnUtils;
|
||||
import com.linkedin.datahub.graphql.QueryContext;
|
||||
import com.linkedin.metadata.authorization.PoliciesConfig;
|
||||
import java.time.Clock;
|
||||
import java.util.Optional;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
|
||||
public class AuthorizationUtils {
|
||||
|
||||
private static final Clock CLOCK = Clock.systemUTC();
|
||||
|
||||
public static AuditStamp createAuditStamp(@Nonnull QueryContext context) {
|
||||
return new AuditStamp().setTime(CLOCK.millis()).setActor(UrnUtils.getUrn(context.getActorUrn()));
|
||||
}
|
||||
|
||||
public static boolean canManageUsersAndGroups(@Nonnull QueryContext context) {
|
||||
return isAuthorized(context, Optional.empty(), PoliciesConfig.MANAGE_USERS_AND_GROUPS_PRIVILEGE);
|
||||
}
|
||||
@ -25,6 +35,24 @@ public class AuthorizationUtils {
|
||||
return isAuthorized(context, Optional.empty(), PoliciesConfig.MANAGE_ACCESS_TOKENS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the current used is able to create Domains. This is true if the user has the 'Manage Domains' or 'Create Domains' platform privilege.
|
||||
*/
|
||||
public static boolean canCreateDomains(@Nonnull QueryContext context) {
|
||||
final DisjunctivePrivilegeGroup orPrivilegeGroups = new DisjunctivePrivilegeGroup(
|
||||
ImmutableList.of(
|
||||
new ConjunctivePrivilegeGroup(ImmutableList.of(
|
||||
PoliciesConfig.CREATE_DOMAINS_PRIVILEGE.getType())),
|
||||
new ConjunctivePrivilegeGroup(ImmutableList.of(
|
||||
PoliciesConfig.MANAGE_DOMAINS_PRIVILEGE.getType()))
|
||||
));
|
||||
|
||||
return AuthorizationUtils.isAuthorized(
|
||||
context.getAuthorizer(),
|
||||
context.getActorUrn(),
|
||||
orPrivilegeGroups);
|
||||
}
|
||||
|
||||
public static boolean canManageDomains(@Nonnull QueryContext context) {
|
||||
return isAuthorized(context, Optional.empty(), PoliciesConfig.MANAGE_DOMAINS_PRIVILEGE);
|
||||
}
|
||||
@ -33,6 +61,32 @@ public class AuthorizationUtils {
|
||||
return isAuthorized(context, Optional.empty(), PoliciesConfig.MANAGE_GLOSSARIES_PRIVILEGE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the current used is able to create Tags. This is true if the user has the 'Manage Tags' or 'Create Tags' platform privilege.
|
||||
*/
|
||||
public static boolean canCreateTags(@Nonnull QueryContext context) {
|
||||
final DisjunctivePrivilegeGroup orPrivilegeGroups = new DisjunctivePrivilegeGroup(
|
||||
ImmutableList.of(
|
||||
new ConjunctivePrivilegeGroup(ImmutableList.of(
|
||||
PoliciesConfig.CREATE_TAGS_PRIVILEGE.getType())),
|
||||
new ConjunctivePrivilegeGroup(ImmutableList.of(
|
||||
PoliciesConfig.MANAGE_TAGS_PRIVILEGE.getType()))
|
||||
));
|
||||
|
||||
return AuthorizationUtils.isAuthorized(
|
||||
context.getAuthorizer(),
|
||||
context.getActorUrn(),
|
||||
orPrivilegeGroups);
|
||||
}
|
||||
|
||||
public static boolean canManageTags(@Nonnull QueryContext context) {
|
||||
return isAuthorized(context, Optional.empty(), PoliciesConfig.MANAGE_TAGS_PRIVILEGE);
|
||||
}
|
||||
|
||||
public static boolean canDeleteEntity(@Nonnull Urn entityUrn, @Nonnull QueryContext context) {
|
||||
return isAuthorized(context, Optional.of(new ResourceSpec(entityUrn.getEntityType(), entityUrn.toString())), PoliciesConfig.DELETE_ENTITY_PRIVILEGE);
|
||||
}
|
||||
|
||||
public static boolean canManageUserCredentials(@Nonnull QueryContext context) {
|
||||
return isAuthorized(context, Optional.empty(), PoliciesConfig.MANAGE_USER_CREDENTIALS_PRIVILEGE);
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import com.datahub.authorization.AuthorizationResult;
|
||||
import com.datahub.authorization.Authorizer;
|
||||
import com.linkedin.common.urn.Urn;
|
||||
import com.linkedin.datahub.graphql.QueryContext;
|
||||
import com.linkedin.datahub.graphql.authorization.AuthorizationUtils;
|
||||
import com.linkedin.datahub.graphql.generated.AuthenticatedUser;
|
||||
import com.linkedin.datahub.graphql.generated.CorpUser;
|
||||
import com.linkedin.datahub.graphql.generated.PlatformPrivileges;
|
||||
@ -65,6 +66,9 @@ public class MeResolver implements DataFetcher<CompletableFuture<AuthenticatedUs
|
||||
platformPrivileges.setManageTests(canManageTests(context));
|
||||
platformPrivileges.setManageGlossaries(canManageGlossaries(context));
|
||||
platformPrivileges.setManageUserCredentials(canManageUserCredentials(context));
|
||||
platformPrivileges.setCreateDomains(AuthorizationUtils.canCreateDomains(context));
|
||||
platformPrivileges.setCreateTags(AuthorizationUtils.canCreateTags(context));
|
||||
platformPrivileges.setManageTags(AuthorizationUtils.canManageTags(context));
|
||||
|
||||
// Construct and return authenticated user object.
|
||||
final AuthenticatedUser authUser = new AuthenticatedUser();
|
||||
|
@ -1,19 +1,16 @@
|
||||
package com.linkedin.datahub.graphql.resolvers.domain;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.linkedin.data.template.SetMode;
|
||||
import com.linkedin.datahub.graphql.QueryContext;
|
||||
import com.linkedin.datahub.graphql.authorization.AuthorizationUtils;
|
||||
import com.linkedin.datahub.graphql.authorization.ConjunctivePrivilegeGroup;
|
||||
import com.linkedin.datahub.graphql.authorization.DisjunctivePrivilegeGroup;
|
||||
import com.linkedin.datahub.graphql.exception.AuthorizationException;
|
||||
import com.linkedin.datahub.graphql.generated.CreateDomainInput;
|
||||
import com.linkedin.domain.DomainProperties;
|
||||
import com.linkedin.entity.client.EntityClient;
|
||||
import com.linkedin.events.metadata.ChangeType;
|
||||
import com.linkedin.metadata.Constants;
|
||||
import com.linkedin.metadata.authorization.PoliciesConfig;
|
||||
import com.linkedin.metadata.entity.EntityService;
|
||||
import com.linkedin.metadata.key.DomainKey;
|
||||
import com.linkedin.metadata.utils.EntityKeyUtils;
|
||||
import com.linkedin.metadata.utils.GenericRecordUtils;
|
||||
import com.linkedin.mxe.MetadataChangeProposal;
|
||||
import graphql.schema.DataFetcher;
|
||||
@ -23,16 +20,17 @@ import java.util.concurrent.CompletableFuture;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import static com.linkedin.datahub.graphql.authorization.AuthorizationUtils.*;
|
||||
import static com.linkedin.datahub.graphql.resolvers.ResolverUtils.*;
|
||||
|
||||
/**
|
||||
* Resolver used for creating a new Domain on DataHub. Requires the MANAGE_DOMAINS privilege.
|
||||
* Resolver used for creating a new Domain on DataHub. Requires the CREATE_DOMAINS or MANAGE_DOMAINS privilege.
|
||||
*/
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class CreateDomainResolver implements DataFetcher<CompletableFuture<String>> {
|
||||
|
||||
private final EntityClient _entityClient;
|
||||
private final EntityService _entityService;
|
||||
|
||||
@Override
|
||||
public CompletableFuture<String> get(DataFetchingEnvironment environment) throws Exception {
|
||||
@ -42,12 +40,10 @@ public class CreateDomainResolver implements DataFetcher<CompletableFuture<Strin
|
||||
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
|
||||
if (!isAuthorizedToCreateDomain(context)) {
|
||||
if (!AuthorizationUtils.canCreateDomains(context)) {
|
||||
throw new AuthorizationException("Unauthorized to perform this action. Please contact your DataHub administrator.");
|
||||
}
|
||||
|
||||
// TODO: Add exists check. Currently this can override previously created domains.
|
||||
|
||||
try {
|
||||
// Create the Domain Key
|
||||
final DomainKey key = new DomainKey();
|
||||
@ -56,6 +52,10 @@ public class CreateDomainResolver implements DataFetcher<CompletableFuture<Strin
|
||||
final String id = input.getId() != null ? input.getId() : UUID.randomUUID().toString();
|
||||
key.setId(id);
|
||||
|
||||
if (_entityService.exists(EntityKeyUtils.convertEntityKeyToUrn(key, Constants.DOMAIN_ENTITY_NAME))) {
|
||||
throw new IllegalArgumentException("This Domain already exists!");
|
||||
}
|
||||
|
||||
// Create the MCP
|
||||
final MetadataChangeProposal proposal = new MetadataChangeProposal();
|
||||
proposal.setEntityKeyAspect(GenericRecordUtils.serializeAspect(key));
|
||||
@ -63,7 +63,8 @@ public class CreateDomainResolver implements DataFetcher<CompletableFuture<Strin
|
||||
proposal.setAspectName(Constants.DOMAIN_PROPERTIES_ASPECT_NAME);
|
||||
proposal.setAspect(GenericRecordUtils.serializeAspect(mapDomainProperties(input)));
|
||||
proposal.setChangeType(ChangeType.UPSERT);
|
||||
return _entityClient.ingestProposal(proposal, context.getAuthentication());
|
||||
|
||||
return _entityService.ingestProposal(proposal, createAuditStamp(context)).getUrn().toString();
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to create Domain with id: {}, name: {}: {}", input.getId(), input.getName(), e.getMessage());
|
||||
throw new RuntimeException(String.format("Failed to create Domain with id: %s, name: %s", input.getId(), input.getName()), e);
|
||||
@ -77,15 +78,4 @@ public class CreateDomainResolver implements DataFetcher<CompletableFuture<Strin
|
||||
result.setDescription(input.getDescription(), SetMode.IGNORE_NULL);
|
||||
return result;
|
||||
}
|
||||
|
||||
private boolean isAuthorizedToCreateDomain(final QueryContext context) {
|
||||
final DisjunctivePrivilegeGroup orPrivilegeGroups = new DisjunctivePrivilegeGroup(ImmutableList.of(
|
||||
new ConjunctivePrivilegeGroup(ImmutableList.of(PoliciesConfig.MANAGE_DOMAINS_PRIVILEGE.getType()))
|
||||
));
|
||||
|
||||
return AuthorizationUtils.isAuthorized(
|
||||
context.getAuthorizer(),
|
||||
context.getActorUrn(),
|
||||
orPrivilegeGroups);
|
||||
}
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
package com.linkedin.datahub.graphql.resolvers.domain;
|
||||
|
||||
import com.linkedin.common.urn.Urn;
|
||||
import com.linkedin.datahub.graphql.QueryContext;
|
||||
import com.linkedin.datahub.graphql.authorization.AuthorizationUtils;
|
||||
import com.linkedin.datahub.graphql.exception.AuthorizationException;
|
||||
import com.linkedin.entity.client.EntityClient;
|
||||
import com.linkedin.r2.RemoteInvocationException;
|
||||
import graphql.schema.DataFetcher;
|
||||
import graphql.schema.DataFetchingEnvironment;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
|
||||
/**
|
||||
* Resolver responsible for hard deleting a particular DataHub Corp Group
|
||||
*/
|
||||
@Slf4j
|
||||
public class DeleteDomainResolver implements DataFetcher<CompletableFuture<Boolean>> {
|
||||
|
||||
private final EntityClient _entityClient;
|
||||
|
||||
public DeleteDomainResolver(final EntityClient entityClient) {
|
||||
_entityClient = entityClient;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Boolean> get(final DataFetchingEnvironment environment) throws Exception {
|
||||
final QueryContext context = environment.getContext();
|
||||
final String domainUrn = environment.getArgument("urn");
|
||||
final Urn urn = Urn.createFromString(domainUrn);
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
|
||||
if (AuthorizationUtils.canManageDomains(context) || AuthorizationUtils.canDeleteEntity(urn, context)) {
|
||||
try {
|
||||
_entityClient.deleteEntity(urn, context.getAuthentication());
|
||||
|
||||
// Asynchronously Delete all references to the entity (to return quickly)
|
||||
CompletableFuture.runAsync(() -> {
|
||||
try {
|
||||
_entityClient.deleteEntityReferences(urn, context.getAuthentication());
|
||||
} catch (RemoteInvocationException e) {
|
||||
log.error(String.format("Caught exception while attempting to clear all entity references for Domain with urn %s", urn), e);
|
||||
}
|
||||
});
|
||||
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(String.format("Failed to perform delete against domain with urn %s", domainUrn), e);
|
||||
}
|
||||
}
|
||||
throw new AuthorizationException("Unauthorized to perform this action. Please contact your DataHub administrator.");
|
||||
});
|
||||
}
|
||||
}
|
@ -45,7 +45,7 @@ public class ListDomainsResolver implements DataFetcher<CompletableFuture<ListDo
|
||||
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
|
||||
if (AuthorizationUtils.canManageDomains(context)) {
|
||||
if (AuthorizationUtils.canCreateDomains(context)) {
|
||||
final ListDomainsInput input = bindArgument(environment.getArgument("input"), ListDomainsInput.class);
|
||||
final Integer start = input.getStart() == null ? DEFAULT_START : input.getStart();
|
||||
final Integer count = input.getCount() == null ? DEFAULT_COUNT : input.getCount();
|
||||
|
@ -0,0 +1,80 @@
|
||||
package com.linkedin.datahub.graphql.resolvers.tag;
|
||||
|
||||
import com.linkedin.data.template.SetMode;
|
||||
import com.linkedin.datahub.graphql.QueryContext;
|
||||
import com.linkedin.datahub.graphql.authorization.AuthorizationUtils;
|
||||
import com.linkedin.datahub.graphql.exception.AuthorizationException;
|
||||
import com.linkedin.datahub.graphql.generated.CreateTagInput;
|
||||
import com.linkedin.events.metadata.ChangeType;
|
||||
import com.linkedin.metadata.Constants;
|
||||
import com.linkedin.metadata.entity.EntityService;
|
||||
import com.linkedin.metadata.key.TagKey;
|
||||
import com.linkedin.metadata.utils.EntityKeyUtils;
|
||||
import com.linkedin.metadata.utils.GenericRecordUtils;
|
||||
import com.linkedin.mxe.MetadataChangeProposal;
|
||||
import com.linkedin.tag.TagProperties;
|
||||
import graphql.schema.DataFetcher;
|
||||
import graphql.schema.DataFetchingEnvironment;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import static com.linkedin.datahub.graphql.authorization.AuthorizationUtils.*;
|
||||
import static com.linkedin.datahub.graphql.resolvers.ResolverUtils.*;
|
||||
|
||||
/**
|
||||
* Resolver used for creating a new Tag on DataHub. Requires the CREATE_TAG or MANAGE_TAGS privilege.
|
||||
*/
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class CreateTagResolver implements DataFetcher<CompletableFuture<String>> {
|
||||
|
||||
private final EntityService _entityService;
|
||||
|
||||
@Override
|
||||
public CompletableFuture<String> get(DataFetchingEnvironment environment) throws Exception {
|
||||
|
||||
final QueryContext context = environment.getContext();
|
||||
final CreateTagInput input = bindArgument(environment.getArgument("input"), CreateTagInput.class);
|
||||
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
|
||||
if (!AuthorizationUtils.canCreateTags(context)) {
|
||||
throw new AuthorizationException("Unauthorized to perform this action. Please contact your DataHub administrator.");
|
||||
}
|
||||
|
||||
try {
|
||||
// Create the Tag Key
|
||||
final TagKey key = new TagKey();
|
||||
|
||||
// Take user provided id OR generate a random UUID for the Tag.
|
||||
final String id = input.getId() != null ? input.getId() : UUID.randomUUID().toString();
|
||||
key.setName(id);
|
||||
|
||||
if (_entityService.exists(EntityKeyUtils.convertEntityKeyToUrn(key, Constants.TAG_ENTITY_NAME))) {
|
||||
throw new IllegalArgumentException("This Tag already exists!");
|
||||
}
|
||||
|
||||
// Create the MCP
|
||||
final MetadataChangeProposal proposal = new MetadataChangeProposal();
|
||||
proposal.setEntityKeyAspect(GenericRecordUtils.serializeAspect(key));
|
||||
proposal.setEntityType(Constants.TAG_ENTITY_NAME);
|
||||
proposal.setAspectName(Constants.TAG_PROPERTIES_ASPECT_NAME);
|
||||
proposal.setAspect(GenericRecordUtils.serializeAspect(mapTagProperties(input)));
|
||||
proposal.setChangeType(ChangeType.UPSERT);
|
||||
return _entityService.ingestProposal(proposal, createAuditStamp(context)).getUrn().toString();
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to create Domain with id: {}, name: {}: {}", input.getId(), input.getName(), e.getMessage());
|
||||
throw new RuntimeException(String.format("Failed to create Domain with id: %s, name: %s", input.getId(), input.getName()), e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private TagProperties mapTagProperties(final CreateTagInput input) {
|
||||
final TagProperties result = new TagProperties();
|
||||
result.setName(input.getName());
|
||||
result.setDescription(input.getDescription(), SetMode.IGNORE_NULL);
|
||||
return result;
|
||||
}
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
package com.linkedin.datahub.graphql.resolvers.tag;
|
||||
|
||||
import com.linkedin.common.urn.Urn;
|
||||
import com.linkedin.common.urn.UrnUtils;
|
||||
import com.linkedin.datahub.graphql.QueryContext;
|
||||
import com.linkedin.datahub.graphql.authorization.AuthorizationUtils;
|
||||
import com.linkedin.datahub.graphql.exception.AuthorizationException;
|
||||
import com.linkedin.entity.client.EntityClient;
|
||||
import com.linkedin.r2.RemoteInvocationException;
|
||||
import graphql.schema.DataFetcher;
|
||||
import graphql.schema.DataFetchingEnvironment;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
|
||||
/**
|
||||
* Resolver responsible for hard deleting a particular DataHub Corp Group
|
||||
*/
|
||||
@Slf4j
|
||||
public class DeleteTagResolver implements DataFetcher<CompletableFuture<Boolean>> {
|
||||
|
||||
private final EntityClient _entityClient;
|
||||
|
||||
public DeleteTagResolver(final EntityClient entityClient) {
|
||||
_entityClient = entityClient;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Boolean> get(final DataFetchingEnvironment environment) throws Exception {
|
||||
final QueryContext context = environment.getContext();
|
||||
final String tagUrn = environment.getArgument("urn");
|
||||
final Urn urn = Urn.createFromString(tagUrn);
|
||||
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
|
||||
if (AuthorizationUtils.canManageTags(context) || AuthorizationUtils.canDeleteEntity(UrnUtils.getUrn(tagUrn), context)) {
|
||||
try {
|
||||
_entityClient.deleteEntity(urn, context.getAuthentication());
|
||||
|
||||
// Asynchronously Delete all references to the entity (to return quickly)
|
||||
CompletableFuture.runAsync(() -> {
|
||||
try {
|
||||
_entityClient.deleteEntityReferences(urn, context.getAuthentication());
|
||||
} catch (RemoteInvocationException e) {
|
||||
log.error(String.format(
|
||||
"Caught exception while attempting to clear all entity references for Tag with urn %s", urn), e);
|
||||
}
|
||||
});
|
||||
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(String.format("Failed to perform delete against domain with urn %s", tagUrn), e);
|
||||
}
|
||||
}
|
||||
throw new AuthorizationException("Unauthorized to perform this action. Please contact your DataHub administrator.");
|
||||
});
|
||||
}
|
||||
}
|
@ -42,7 +42,6 @@ public class TagUpdateInputMapper implements InputModelMapper<TagUpdateInput, Co
|
||||
auditStamp.setActor(actor, SetMode.IGNORE_NULL);
|
||||
auditStamp.setTime(System.currentTimeMillis());
|
||||
|
||||
|
||||
// Creator is the owner.
|
||||
final Ownership ownership = new Ownership();
|
||||
final Owner owner = new Owner();
|
||||
|
@ -52,6 +52,11 @@ type PlatformPrivileges {
|
||||
"""
|
||||
generatePersonalAccessTokens: Boolean!
|
||||
|
||||
"""
|
||||
Whether the user should be able to create new Domains
|
||||
"""
|
||||
createDomains: Boolean!
|
||||
|
||||
"""
|
||||
Whether the user should be able to manage Domains
|
||||
"""
|
||||
@ -86,6 +91,16 @@ type PlatformPrivileges {
|
||||
Whether the user is able to manage user credentials
|
||||
"""
|
||||
manageUserCredentials: Boolean!
|
||||
|
||||
"""
|
||||
Whether the user should be able to create new Tags
|
||||
"""
|
||||
createTags: Boolean!
|
||||
|
||||
"""
|
||||
Whether the user should be able to create and delete all Tags
|
||||
"""
|
||||
manageTags: Boolean!
|
||||
}
|
||||
|
||||
"""
|
||||
|
@ -210,11 +210,26 @@ type Mutation {
|
||||
"""
|
||||
updateDataJob(urn: String!, input: DataJobUpdateInput!): DataJob
|
||||
|
||||
"""
|
||||
Create a new tag. Requires the 'Manage Tags' or 'Create Tags' Platform Privilege. If a Tag with the provided ID already exists,
|
||||
it will be overwritten.
|
||||
"""
|
||||
createTag(
|
||||
"Inputs required to create a new Tag."
|
||||
input: CreateTagInput!): String
|
||||
|
||||
"""
|
||||
Update the information about a particular Entity Tag
|
||||
"""
|
||||
updateTag(urn: String!, input: TagUpdateInput!): Tag
|
||||
|
||||
"""
|
||||
Delete a Tag
|
||||
"""
|
||||
deleteTag(
|
||||
"The urn of the Tag to delete"
|
||||
urn: String!): Boolean
|
||||
|
||||
"""
|
||||
Set the hex color associated with an existing Tag
|
||||
"""
|
||||
@ -326,11 +341,19 @@ type Mutation {
|
||||
createGroup(input: CreateGroupInput!): String
|
||||
|
||||
"""
|
||||
Create a new Domain. Returns the urn of the newly created Domain. Requires the Manage Domains privilege. If a domain with the provided ID already exists,
|
||||
Create a new Domain. Returns the urn of the newly created Domain. Requires the 'Create Domains' or 'Manage Domains' Platform Privilege. If a Domain with the provided ID already exists,
|
||||
it will be overwritten.
|
||||
"""
|
||||
createDomain(input: CreateDomainInput!): String
|
||||
|
||||
"""
|
||||
Delete a Domain
|
||||
"""
|
||||
deleteDomain(
|
||||
"The urn of the Domain to delete"
|
||||
urn: String!): Boolean
|
||||
|
||||
|
||||
"""
|
||||
Sets the Domain for a Dataset, Chart, Dashboard, Data Flow (Pipeline), or Data Job (Task). Returns true if the Domain was successfully added, or already exists. Requires the Edit Domains privilege for the Entity.
|
||||
"""
|
||||
@ -3615,6 +3638,26 @@ input TagUpdateInput {
|
||||
ownership: OwnershipUpdate
|
||||
}
|
||||
|
||||
"""
|
||||
Input required to create a new Tag
|
||||
"""
|
||||
input CreateTagInput {
|
||||
"""
|
||||
Optional! A custom id to use as the primary key identifier for the Tag. If not provided, a random UUID will be generated as the id.
|
||||
"""
|
||||
id: String
|
||||
|
||||
"""
|
||||
Display name for the Tag
|
||||
"""
|
||||
name: String!
|
||||
|
||||
"""
|
||||
Optional description for the Tag
|
||||
"""
|
||||
description: String
|
||||
}
|
||||
|
||||
"""
|
||||
An update for the ownership information for a Metadata Entity
|
||||
"""
|
||||
|
@ -1,16 +1,16 @@
|
||||
package com.linkedin.datahub.graphql.resolvers.domain;
|
||||
|
||||
import com.datahub.authentication.Authentication;
|
||||
import com.linkedin.common.AuditStamp;
|
||||
import com.linkedin.common.urn.UrnUtils;
|
||||
import com.linkedin.datahub.graphql.QueryContext;
|
||||
import com.linkedin.datahub.graphql.generated.CreateDomainInput;
|
||||
import com.linkedin.domain.DomainProperties;
|
||||
import com.linkedin.entity.client.EntityClient;
|
||||
import com.linkedin.events.metadata.ChangeType;
|
||||
import com.linkedin.metadata.Constants;
|
||||
import com.linkedin.metadata.entity.EntityService;
|
||||
import com.linkedin.metadata.key.DomainKey;
|
||||
import com.linkedin.metadata.utils.GenericRecordUtils;
|
||||
import com.linkedin.mxe.MetadataChangeProposal;
|
||||
import com.linkedin.r2.RemoteInvocationException;
|
||||
import graphql.schema.DataFetchingEnvironment;
|
||||
import java.util.concurrent.CompletionException;
|
||||
import org.mockito.Mockito;
|
||||
@ -31,8 +31,12 @@ public class CreateDomainResolverTest {
|
||||
@Test
|
||||
public void testGetSuccess() throws Exception {
|
||||
// Create resolver
|
||||
EntityClient mockClient = Mockito.mock(EntityClient.class);
|
||||
CreateDomainResolver resolver = new CreateDomainResolver(mockClient);
|
||||
EntityService mockService = Mockito.mock(EntityService.class);
|
||||
Mockito.when(mockService.ingestProposal(Mockito.any(MetadataChangeProposal.class), Mockito.any(AuditStamp.class)))
|
||||
.thenReturn(new EntityService.IngestProposalResult(UrnUtils.getUrn(
|
||||
String.format("urn:li:tag:%s",
|
||||
TEST_INPUT.getId())), true));
|
||||
CreateDomainResolver resolver = new CreateDomainResolver(mockService);
|
||||
|
||||
// Execute resolver
|
||||
QueryContext mockContext = getMockAllowContext();
|
||||
@ -55,17 +59,17 @@ public class CreateDomainResolverTest {
|
||||
proposal.setChangeType(ChangeType.UPSERT);
|
||||
|
||||
// Not ideal to match against "any", but we don't know the auto-generated execution request id
|
||||
Mockito.verify(mockClient, Mockito.times(1)).ingestProposal(
|
||||
Mockito.verify(mockService, Mockito.times(1)).ingestProposal(
|
||||
Mockito.eq(proposal),
|
||||
Mockito.any(Authentication.class)
|
||||
Mockito.any(AuditStamp.class)
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetUnauthorized() throws Exception {
|
||||
// Create resolver
|
||||
EntityClient mockClient = Mockito.mock(EntityClient.class);
|
||||
CreateDomainResolver resolver = new CreateDomainResolver(mockClient);
|
||||
EntityService mockService = Mockito.mock(EntityService.class);
|
||||
CreateDomainResolver resolver = new CreateDomainResolver(mockService);
|
||||
|
||||
// Execute resolver
|
||||
DataFetchingEnvironment mockEnv = Mockito.mock(DataFetchingEnvironment.class);
|
||||
@ -74,19 +78,19 @@ public class CreateDomainResolverTest {
|
||||
Mockito.when(mockEnv.getContext()).thenReturn(mockContext);
|
||||
|
||||
assertThrows(CompletionException.class, () -> resolver.get(mockEnv).join());
|
||||
Mockito.verify(mockClient, Mockito.times(0)).ingestProposal(
|
||||
Mockito.verify(mockService, Mockito.times(0)).ingestProposal(
|
||||
Mockito.any(),
|
||||
Mockito.any(Authentication.class));
|
||||
Mockito.any(AuditStamp.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetEntityClientException() throws Exception {
|
||||
// Create resolver
|
||||
EntityClient mockClient = Mockito.mock(EntityClient.class);
|
||||
Mockito.doThrow(RemoteInvocationException.class).when(mockClient).ingestProposal(
|
||||
EntityService mockService = Mockito.mock(EntityService.class);
|
||||
Mockito.doThrow(RuntimeException.class).when(mockService).ingestProposal(
|
||||
Mockito.any(),
|
||||
Mockito.any(Authentication.class));
|
||||
CreateDomainResolver resolver = new CreateDomainResolver(mockClient);
|
||||
Mockito.any(AuditStamp.class));
|
||||
CreateDomainResolver resolver = new CreateDomainResolver(mockService);
|
||||
|
||||
// Execute resolver
|
||||
DataFetchingEnvironment mockEnv = Mockito.mock(DataFetchingEnvironment.class);
|
||||
|
@ -0,0 +1,56 @@
|
||||
package com.linkedin.datahub.graphql.resolvers.domain;
|
||||
|
||||
import com.datahub.authentication.Authentication;
|
||||
import com.linkedin.common.urn.Urn;
|
||||
import com.linkedin.datahub.graphql.QueryContext;
|
||||
import com.linkedin.entity.client.EntityClient;
|
||||
import graphql.schema.DataFetchingEnvironment;
|
||||
import java.util.concurrent.CompletionException;
|
||||
import org.mockito.Mockito;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import static com.linkedin.datahub.graphql.TestUtils.*;
|
||||
import static org.testng.Assert.*;
|
||||
|
||||
|
||||
public class DeleteDomainResolverTest {
|
||||
|
||||
private static final String TEST_URN = "urn:li:domain:test-id";
|
||||
|
||||
@Test
|
||||
public void testGetSuccess() throws Exception {
|
||||
EntityClient mockClient = Mockito.mock(EntityClient.class);
|
||||
DeleteDomainResolver resolver = new DeleteDomainResolver(mockClient);
|
||||
|
||||
// Execute resolver
|
||||
QueryContext mockContext = getMockAllowContext();
|
||||
DataFetchingEnvironment mockEnv = Mockito.mock(DataFetchingEnvironment.class);
|
||||
Mockito.when(mockEnv.getArgument(Mockito.eq("urn"))).thenReturn(TEST_URN);
|
||||
Mockito.when(mockEnv.getContext()).thenReturn(mockContext);
|
||||
|
||||
assertTrue(resolver.get(mockEnv).get());
|
||||
|
||||
Mockito.verify(mockClient, Mockito.times(1)).deleteEntity(
|
||||
Mockito.eq(Urn.createFromString(TEST_URN)),
|
||||
Mockito.any(Authentication.class)
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetUnauthorized() throws Exception {
|
||||
// Create resolver
|
||||
EntityClient mockClient = Mockito.mock(EntityClient.class);
|
||||
DeleteDomainResolver resolver = new DeleteDomainResolver(mockClient);
|
||||
|
||||
// Execute resolver
|
||||
DataFetchingEnvironment mockEnv = Mockito.mock(DataFetchingEnvironment.class);
|
||||
Mockito.when(mockEnv.getArgument(Mockito.eq("urn"))).thenReturn(TEST_URN);
|
||||
QueryContext mockContext = getMockDenyContext();
|
||||
Mockito.when(mockEnv.getContext()).thenReturn(mockContext);
|
||||
|
||||
assertThrows(CompletionException.class, () -> resolver.get(mockEnv).join());
|
||||
Mockito.verify(mockClient, Mockito.times(0)).deleteEntity(
|
||||
Mockito.any(),
|
||||
Mockito.any(Authentication.class));
|
||||
}
|
||||
}
|
@ -0,0 +1,103 @@
|
||||
package com.linkedin.datahub.graphql.resolvers.tag;
|
||||
|
||||
import com.linkedin.common.AuditStamp;
|
||||
import com.linkedin.common.urn.UrnUtils;
|
||||
import com.linkedin.datahub.graphql.QueryContext;
|
||||
import com.linkedin.datahub.graphql.generated.CreateTagInput;
|
||||
import com.linkedin.tag.TagProperties;
|
||||
import com.linkedin.events.metadata.ChangeType;
|
||||
import com.linkedin.metadata.Constants;
|
||||
import com.linkedin.metadata.entity.EntityService;
|
||||
import com.linkedin.metadata.key.TagKey;
|
||||
import com.linkedin.metadata.utils.GenericRecordUtils;
|
||||
import com.linkedin.mxe.MetadataChangeProposal;
|
||||
import graphql.schema.DataFetchingEnvironment;
|
||||
import java.util.concurrent.CompletionException;
|
||||
import org.mockito.Mockito;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import static com.linkedin.datahub.graphql.TestUtils.*;
|
||||
import static org.testng.Assert.*;
|
||||
|
||||
|
||||
public class CreateTagResolverTest {
|
||||
|
||||
private static final CreateTagInput TEST_INPUT = new CreateTagInput(
|
||||
"test-id",
|
||||
"test-name",
|
||||
"test-description"
|
||||
);
|
||||
|
||||
@Test
|
||||
public void testGetSuccess() throws Exception {
|
||||
// Create resolver
|
||||
EntityService mockService = Mockito.mock(EntityService.class);
|
||||
Mockito.when(mockService.ingestProposal(Mockito.any(MetadataChangeProposal.class), Mockito.any(AuditStamp.class)))
|
||||
.thenReturn(new EntityService.IngestProposalResult(UrnUtils.getUrn(
|
||||
String.format("urn:li:tag:%s",
|
||||
TEST_INPUT.getId())), true));
|
||||
CreateTagResolver resolver = new CreateTagResolver(mockService);
|
||||
|
||||
// Execute resolver
|
||||
QueryContext mockContext = getMockAllowContext();
|
||||
DataFetchingEnvironment mockEnv = Mockito.mock(DataFetchingEnvironment.class);
|
||||
Mockito.when(mockEnv.getArgument(Mockito.eq("input"))).thenReturn(TEST_INPUT);
|
||||
Mockito.when(mockEnv.getContext()).thenReturn(mockContext);
|
||||
|
||||
resolver.get(mockEnv).get();
|
||||
|
||||
final TagKey key = new TagKey();
|
||||
key.setName("test-id");
|
||||
final MetadataChangeProposal proposal = new MetadataChangeProposal();
|
||||
proposal.setEntityKeyAspect(GenericRecordUtils.serializeAspect(key));
|
||||
proposal.setEntityType(Constants.TAG_ENTITY_NAME);
|
||||
TagProperties props = new TagProperties();
|
||||
props.setDescription("test-description");
|
||||
props.setName("test-name");
|
||||
proposal.setAspectName(Constants.TAG_PROPERTIES_ASPECT_NAME);
|
||||
proposal.setAspect(GenericRecordUtils.serializeAspect(props));
|
||||
proposal.setChangeType(ChangeType.UPSERT);
|
||||
|
||||
// Not ideal to match against "any", but we don't know the auto-generated execution request id
|
||||
Mockito.verify(mockService, Mockito.times(1)).ingestProposal(
|
||||
Mockito.eq(proposal),
|
||||
Mockito.any(AuditStamp.class)
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetUnauthorized() throws Exception {
|
||||
// Create resolver
|
||||
EntityService mockService = Mockito.mock(EntityService.class);
|
||||
CreateTagResolver resolver = new CreateTagResolver(mockService);
|
||||
|
||||
// Execute resolver
|
||||
DataFetchingEnvironment mockEnv = Mockito.mock(DataFetchingEnvironment.class);
|
||||
QueryContext mockContext = getMockDenyContext();
|
||||
Mockito.when(mockEnv.getArgument(Mockito.eq("input"))).thenReturn(TEST_INPUT);
|
||||
Mockito.when(mockEnv.getContext()).thenReturn(mockContext);
|
||||
|
||||
assertThrows(CompletionException.class, () -> resolver.get(mockEnv).join());
|
||||
Mockito.verify(mockService, Mockito.times(0)).ingestProposal(
|
||||
Mockito.any(),
|
||||
Mockito.any(AuditStamp.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetEntityClientException() throws Exception {
|
||||
// Create resolver
|
||||
EntityService mockService = Mockito.mock(EntityService.class);
|
||||
Mockito.doThrow(RuntimeException.class).when(mockService).ingestProposal(
|
||||
Mockito.any(),
|
||||
Mockito.any(AuditStamp.class));
|
||||
CreateTagResolver resolver = new CreateTagResolver(mockService);
|
||||
|
||||
// Execute resolver
|
||||
DataFetchingEnvironment mockEnv = Mockito.mock(DataFetchingEnvironment.class);
|
||||
QueryContext mockContext = getMockAllowContext();
|
||||
Mockito.when(mockEnv.getArgument(Mockito.eq("input"))).thenReturn(TEST_INPUT);
|
||||
Mockito.when(mockEnv.getContext()).thenReturn(mockContext);
|
||||
|
||||
assertThrows(CompletionException.class, () -> resolver.get(mockEnv).join());
|
||||
}
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
package com.linkedin.datahub.graphql.resolvers.tag;
|
||||
|
||||
import com.datahub.authentication.Authentication;
|
||||
import com.linkedin.common.urn.Urn;
|
||||
import com.linkedin.datahub.graphql.QueryContext;
|
||||
import com.linkedin.entity.client.EntityClient;
|
||||
import graphql.schema.DataFetchingEnvironment;
|
||||
import java.util.concurrent.CompletionException;
|
||||
import org.mockito.Mockito;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import static com.linkedin.datahub.graphql.TestUtils.*;
|
||||
import static org.testng.Assert.*;
|
||||
|
||||
|
||||
public class DeleteTagResolverTest {
|
||||
|
||||
private static final String TEST_URN = "urn:li:tag:test-id";
|
||||
|
||||
@Test
|
||||
public void testGetSuccess() throws Exception {
|
||||
EntityClient mockClient = Mockito.mock(EntityClient.class);
|
||||
DeleteTagResolver resolver = new DeleteTagResolver(mockClient);
|
||||
|
||||
// Execute resolver
|
||||
QueryContext mockContext = getMockAllowContext();
|
||||
DataFetchingEnvironment mockEnv = Mockito.mock(DataFetchingEnvironment.class);
|
||||
Mockito.when(mockEnv.getArgument(Mockito.eq("urn"))).thenReturn(TEST_URN);
|
||||
Mockito.when(mockEnv.getContext()).thenReturn(mockContext);
|
||||
|
||||
assertTrue(resolver.get(mockEnv).get());
|
||||
|
||||
Mockito.verify(mockClient, Mockito.times(1)).deleteEntity(
|
||||
Mockito.eq(Urn.createFromString(TEST_URN)),
|
||||
Mockito.any(Authentication.class)
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetUnauthorized() throws Exception {
|
||||
// Create resolver
|
||||
EntityClient mockClient = Mockito.mock(EntityClient.class);
|
||||
DeleteTagResolver resolver = new DeleteTagResolver(mockClient);
|
||||
|
||||
// Execute resolver
|
||||
DataFetchingEnvironment mockEnv = Mockito.mock(DataFetchingEnvironment.class);
|
||||
Mockito.when(mockEnv.getArgument(Mockito.eq("urn"))).thenReturn(TEST_URN);
|
||||
QueryContext mockContext = getMockDenyContext();
|
||||
Mockito.when(mockEnv.getContext()).thenReturn(mockContext);
|
||||
|
||||
assertThrows(CompletionException.class, () -> resolver.get(mockEnv).join());
|
||||
Mockito.verify(mockClient, Mockito.times(0)).deleteEntity(
|
||||
Mockito.any(),
|
||||
Mockito.any(Authentication.class));
|
||||
}
|
||||
}
|
@ -3084,6 +3084,17 @@ export const mocks = [
|
||||
viewAnalytics: true,
|
||||
managePolicies: true,
|
||||
manageIdentities: true,
|
||||
manageDomains: true,
|
||||
manageTags: true,
|
||||
createDomains: true,
|
||||
createTags: true,
|
||||
manageUserCredentials: true,
|
||||
manageGlossaries: true,
|
||||
manageTests: true,
|
||||
manageTokens: true,
|
||||
manageSecrets: true,
|
||||
manageIngestion: true,
|
||||
generatePersonalAccessTokens: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -3303,4 +3314,7 @@ export const platformPrivileges: PlatformPrivileges = {
|
||||
manageTests: true,
|
||||
manageGlossaries: true,
|
||||
manageUserCredentials: true,
|
||||
manageTags: true,
|
||||
createTags: true,
|
||||
createDomains: true,
|
||||
};
|
||||
|
@ -31,6 +31,9 @@ query getMe {
|
||||
manageTests
|
||||
manageGlossaries
|
||||
manageUserCredentials
|
||||
manageTags
|
||||
createDomains
|
||||
createTags
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -77,11 +77,14 @@ We currently support the following:
|
||||
| Manage Secrets | Allow actor to create & remove secrets stored inside DataHub. |
|
||||
| Manage Users & Groups | Allow actor to create, remove, and update users and groups on DataHub. |
|
||||
| Manage All Access Tokens | Allow actor to create, remove, and list access tokens for all users on DataHub. |
|
||||
| Manage Domains | Allow actor to create and remove Asset Domains. |
|
||||
| Create Domains | Allow the actor to create new Domains |
|
||||
| Manage Domains | Allow actor to create and remove any Domains. |
|
||||
| View Analytics | Allow the actor access to the DataHub analytics dashboard. |
|
||||
| Generate Personal Access Tokens | Allow the actor to generate access tokens for personal use with DataHub APIs. |
|
||||
| Manage User Credentials | Allow the actor to generate invite links for new native DataHub users, and password reset links for existing native users. |
|
||||
| Manage Glossaries | Allow the actor to create, edit, move, and delete Glossary Terms and Term Groups |
|
||||
| Create Tags | Allow the actor to create new Tags |
|
||||
| Manage Tags | Allow the actor to create and remove any Tags |
|
||||
|
||||
**Common metadata privileges** to view & modify any entity within DataHub.
|
||||
|
||||
|
@ -21,7 +21,8 @@
|
||||
"MANAGE_DOMAINS",
|
||||
"MANAGE_TESTS",
|
||||
"MANAGE_GLOSSARIES",
|
||||
"MANAGE_USER_CREDENTIALS"
|
||||
"MANAGE_USER_CREDENTIALS",
|
||||
"MANAGE_TAGS"
|
||||
],
|
||||
"displayName":"Root User - All Platform Privileges",
|
||||
"description":"Grants full platform privileges to root datahub super user.",
|
||||
@ -84,7 +85,8 @@
|
||||
"GENERATE_PERSONAL_ACCESS_TOKENS",
|
||||
"MANAGE_DOMAINS",
|
||||
"MANAGE_TESTS",
|
||||
"MANAGE_GLOSSARIES"
|
||||
"MANAGE_GLOSSARIES",
|
||||
"MANAGE_TAGS"
|
||||
],
|
||||
"displayName":"All Users - All Platform Privileges",
|
||||
"description":"Grants full platform privileges to ALL users of DataHub. Change this policy to alter that behavior.",
|
||||
|
@ -52,7 +52,6 @@ public class PoliciesConfig {
|
||||
"Generate Personal Access Tokens",
|
||||
"Generate personal access tokens for use with DataHub APIs.");
|
||||
|
||||
|
||||
public static final Privilege MANAGE_ACCESS_TOKENS = Privilege.of(
|
||||
"MANAGE_ACCESS_TOKENS",
|
||||
"Manage All Access Tokens",
|
||||
@ -79,6 +78,21 @@ public class PoliciesConfig {
|
||||
Privilege.of("MANAGE_USER_CREDENTIALS", "Manage User Credentials",
|
||||
"Manage credentials for native DataHub users, including inviting new users and resetting passwords");
|
||||
|
||||
public static final Privilege MANAGE_TAGS_PRIVILEGE = Privilege.of(
|
||||
"MANAGE_TAGS",
|
||||
"Manage Tags",
|
||||
"Create and remove Tags.");
|
||||
|
||||
public static final Privilege CREATE_TAGS_PRIVILEGE = Privilege.of(
|
||||
"CREATE_TAGS",
|
||||
"Create Tags",
|
||||
"Create new Tags.");
|
||||
|
||||
public static final Privilege CREATE_DOMAINS_PRIVILEGE = Privilege.of(
|
||||
"CREATE_DOMAINS",
|
||||
"Create Domains",
|
||||
"Create new Domains.");
|
||||
|
||||
public static final List<Privilege> PLATFORM_PRIVILEGES = ImmutableList.of(
|
||||
MANAGE_POLICIES_PRIVILEGE,
|
||||
MANAGE_USERS_AND_GROUPS_PRIVILEGE,
|
||||
@ -90,7 +104,10 @@ public class PoliciesConfig {
|
||||
MANAGE_ACCESS_TOKENS,
|
||||
MANAGE_TESTS_PRIVILEGE,
|
||||
MANAGE_GLOSSARIES_PRIVILEGE,
|
||||
MANAGE_USER_CREDENTIALS_PRIVILEGE
|
||||
MANAGE_USER_CREDENTIALS_PRIVILEGE,
|
||||
MANAGE_TAGS_PRIVILEGE,
|
||||
CREATE_TAGS_PRIVILEGE,
|
||||
CREATE_DOMAINS_PRIVILEGE
|
||||
);
|
||||
|
||||
// Resource Privileges //
|
||||
@ -155,6 +172,11 @@ public class PoliciesConfig {
|
||||
"Edit All",
|
||||
"The ability to edit any information about an entity. Super user privileges.");
|
||||
|
||||
public static final Privilege DELETE_ENTITY_PRIVILEGE = Privilege.of(
|
||||
"DELETE_ENTITY",
|
||||
"Delete",
|
||||
"The ability to delete the delete this entity.");
|
||||
|
||||
public static final List<Privilege> COMMON_ENTITY_PRIVILEGES = ImmutableList.of(
|
||||
VIEW_ENTITY_PAGE_PRIVILEGE,
|
||||
EDIT_ENTITY_TAGS_PRIVILEGE,
|
||||
@ -283,7 +305,7 @@ public class PoliciesConfig {
|
||||
"Tags",
|
||||
"Tags indexed by DataHub",
|
||||
ImmutableList.of(VIEW_ENTITY_PAGE_PRIVILEGE, EDIT_ENTITY_OWNERS_PRIVILEGE, EDIT_TAG_COLOR_PRIVILEGE,
|
||||
EDIT_ENTITY_DOCS_PRIVILEGE, EDIT_ENTITY_PRIVILEGE)
|
||||
EDIT_ENTITY_DOCS_PRIVILEGE, EDIT_ENTITY_PRIVILEGE, DELETE_ENTITY_PRIVILEGE)
|
||||
);
|
||||
|
||||
// Container Privileges
|
||||
@ -300,7 +322,7 @@ public class PoliciesConfig {
|
||||
"Domains",
|
||||
"Domains created on DataHub",
|
||||
ImmutableList.of(VIEW_ENTITY_PAGE_PRIVILEGE, EDIT_ENTITY_OWNERS_PRIVILEGE, EDIT_ENTITY_DOCS_PRIVILEGE,
|
||||
EDIT_ENTITY_DOC_LINKS_PRIVILEGE, EDIT_ENTITY_PRIVILEGE)
|
||||
EDIT_ENTITY_DOC_LINKS_PRIVILEGE, EDIT_ENTITY_PRIVILEGE, DELETE_ENTITY_PRIVILEGE)
|
||||
);
|
||||
|
||||
// Glossary Term Privileges
|
||||
|
Loading…
x
Reference in New Issue
Block a user