Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ private Constants() {}
public static final String LOGICAL_SCHEMA_FILE = "logical.graphql";
public static final String SETTINGS_SCHEMA_FILE = "settings.graphql";
public static final String FILES_SCHEMA_FILE = "files.graphql";
public static final String DOCUMENTS_SCHEMA_FILE = "documents.graphql";

public static final String QUERY_SCHEMA_FILE = "query.graphql";
public static final String TEMPLATE_SCHEMA_FILE = "template.graphql";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,7 @@
import com.linkedin.datahub.graphql.types.incident.IncidentType;
import com.linkedin.datahub.graphql.types.ingestion.ExecutionRequestType;
import com.linkedin.datahub.graphql.types.ingestion.IngestionSourceType;
import com.linkedin.datahub.graphql.types.knowledge.DocumentType;
import com.linkedin.datahub.graphql.types.mlmodel.MLFeatureTableType;
import com.linkedin.datahub.graphql.types.mlmodel.MLFeatureType;
import com.linkedin.datahub.graphql.types.mlmodel.MLModelGroupType;
Expand Down Expand Up @@ -341,6 +342,7 @@
import com.linkedin.metadata.service.BusinessAttributeService;
import com.linkedin.metadata.service.DataHubFileService;
import com.linkedin.metadata.service.DataProductService;
import com.linkedin.metadata.service.DocumentService;
import com.linkedin.metadata.service.ERModelRelationshipService;
import com.linkedin.metadata.service.FormService;
import com.linkedin.metadata.service.LineageService;
Expand Down Expand Up @@ -424,6 +426,7 @@ public class GmsGraphQLEngine {
private final RestrictedService restrictedService;
private ConnectionService connectionService;
private AssertionService assertionService;
private final DocumentService documentService;
private final EntityVersioningService entityVersioningService;
private final ApplicationService applicationService;
private final PageTemplateService pageTemplateService;
Expand Down Expand Up @@ -470,6 +473,7 @@ public class GmsGraphQLEngine {
private final DataHubConnectionType connectionType;
private final ContainerType containerType;
private final DomainType domainType;
private final DocumentType documentType;
private final NotebookType notebookType;
private final AssertionType assertionType;
private final VersionedDatasetType versionedDatasetType;
Expand Down Expand Up @@ -574,6 +578,7 @@ public GmsGraphQLEngine(final GmsGraphQLEngineArgs args) {
this.restrictedService = args.restrictedService;
this.connectionService = args.connectionService;
this.assertionService = args.assertionService;
this.documentService = args.documentService;
this.entityVersioningService = args.entityVersioningService;

this.businessAttributeService = args.businessAttributeService;
Expand Down Expand Up @@ -613,6 +618,7 @@ public GmsGraphQLEngine(final GmsGraphQLEngineArgs args) {
this.connectionType = new DataHubConnectionType(entityClient, secretService);
this.containerType = new ContainerType(entityClient);
this.domainType = new DomainType(entityClient);
this.documentType = new DocumentType(entityClient);
this.notebookType = new NotebookType(entityClient);
this.assertionType = new AssertionType(entityClient);
this.versionedDatasetType = new VersionedDatasetType(entityClient);
Expand Down Expand Up @@ -672,6 +678,7 @@ public GmsGraphQLEngine(final GmsGraphQLEngineArgs args) {
containerType,
notebookType,
domainType,
documentType,
assertionType,
versionedDatasetType,
dataPlatformInstanceType,
Expand Down Expand Up @@ -775,6 +782,7 @@ public void configureRuntimeWiring(final RuntimeWiring.Builder builder) {
configureOrganisationRoleResolvers(builder);
configureGlossaryNodeResolvers(builder);
configureDomainResolvers(builder);
configureDocumentResolvers(builder);
configureDataProductResolvers(builder);
configureApplicationResolvers(builder);
configureAssertionResolvers(builder);
Expand Down Expand Up @@ -873,7 +881,8 @@ public GraphQLEngine.Builder builder() {
.addSchema(fileBasedSchema(MODULE_SCHEMA_FILE))
.addSchema(fileBasedSchema(PATCH_SCHEMA_FILE))
.addSchema(fileBasedSchema(SETTINGS_SCHEMA_FILE))
.addSchema(fileBasedSchema(FILES_SCHEMA_FILE));
.addSchema(fileBasedSchema(FILES_SCHEMA_FILE))
.addSchema(fileBasedSchema(DOCUMENTS_SCHEMA_FILE));

for (GmsGraphQLPlugin plugin : this.graphQLPlugins) {
List<String> pluginSchemaFiles = plugin.getSchemaFiles();
Expand Down Expand Up @@ -2956,6 +2965,20 @@ private void configureDomainResolvers(final RuntimeWiring.Builder builder) {
.getUrn())));
}

private void configureDocumentResolvers(final RuntimeWiring.Builder builder) {
// Delegate Knowledge Article wiring to consolidated resolver class
new com.linkedin.datahub.graphql.resolvers.knowledge.DocumentResolvers(
this.documentService,
entityTypes,
documentType,
entityClient,
this.entityService,
this.graphClient,
entityRegistry,
this.timelineService)
.configureResolvers(builder);
}

private void configureFormResolvers(final RuntimeWiring.Builder builder) {
builder.type(
"FormAssociation",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import com.linkedin.metadata.service.BusinessAttributeService;
import com.linkedin.metadata.service.DataHubFileService;
import com.linkedin.metadata.service.DataProductService;
import com.linkedin.metadata.service.DocumentService;
import com.linkedin.metadata.service.ERModelRelationshipService;
import com.linkedin.metadata.service.FormService;
import com.linkedin.metadata.service.LineageService;
Expand Down Expand Up @@ -94,6 +95,7 @@ public class GmsGraphQLEngineArgs {
ChromeExtensionConfiguration chromeExtensionConfiguration;
ConnectionService connectionService;
AssertionService assertionService;
DocumentService documentService;
EntityVersioningService entityVersioningService;
ApplicationService applicationService;
PageTemplateService pageTemplateService;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,86 @@ public static boolean canManageHomePageTemplates(@Nonnull QueryContext context)
context.getOperationContext(), PoliciesConfig.MANAGE_HOME_PAGE_TEMPLATES_PRIVILEGE);
}

/**
* Returns true if the current user is able to create Knowledge Articles. This is true if the user
* has the 'Create Entity' privilege for Knowledge Articles or 'Manage Knowledge Articles'
* platform privilege.
*/
public static boolean canCreateDocument(@Nonnull QueryContext context) {
final DisjunctivePrivilegeGroup orPrivilegeGroups =
new DisjunctivePrivilegeGroup(
ImmutableList.of(
new ConjunctivePrivilegeGroup(
ImmutableList.of(PoliciesConfig.MANAGE_DOCUMENTS_PRIVILEGE.getType()))));

return AuthUtil.isAuthorized(context.getOperationContext(), orPrivilegeGroups, null);
}

/**
* Returns true if the current user is able to edit a specific Document. This is true if the user
* has the 'Edit Entity Docs' or 'Edit Entity' metadata privilege on the document, or the 'Manage
* Documents' platform privilege.
*/
public static boolean canEditDocument(@Nonnull Urn documentUrn, @Nonnull QueryContext context) {
final DisjunctivePrivilegeGroup orPrivilegeGroups =
new DisjunctivePrivilegeGroup(
ImmutableList.of(
new ConjunctivePrivilegeGroup(
ImmutableList.of(PoliciesConfig.EDIT_ENTITY_PRIVILEGE.getType())),
new ConjunctivePrivilegeGroup(
ImmutableList.of(PoliciesConfig.MANAGE_DOCUMENTS_PRIVILEGE.getType()))));

return isAuthorized(
context, documentUrn.getEntityType(), documentUrn.toString(), orPrivilegeGroups);
}

/**
* Returns true if the current user is able to read a specific Document. This is true if the user
* has the 'Get Entity' metadata privilege on the document or the 'Manage Documents' platform
* privilege.
*/
public static boolean canGetDocument(@Nonnull Urn documentUrn, @Nonnull QueryContext context) {
final DisjunctivePrivilegeGroup orPrivilegeGroups =
new DisjunctivePrivilegeGroup(
ImmutableList.of(
new ConjunctivePrivilegeGroup(
ImmutableList.of(PoliciesConfig.VIEW_ENTITY_PAGE_PRIVILEGE.getType())),
new ConjunctivePrivilegeGroup(
ImmutableList.of(PoliciesConfig.MANAGE_DOCUMENTS_PRIVILEGE.getType()))));

return isAuthorized(
context, documentUrn.getEntityType(), documentUrn.toString(), orPrivilegeGroups);
}

/**
* Returns true if the current user is able to delete a specific Document. This is true if the
* user has the delete entity authorization on the document or the 'Manage Documents' platform
* privilege.
*/
public static boolean canDeleteDocument(@Nonnull Urn documentUrn, @Nonnull QueryContext context) {
// Check if user can delete entity using standard delete authorization
if (AuthUtil.isAuthorizedEntityUrns(
context.getOperationContext(), DELETE, List.of(documentUrn))) {
return true;
}

// Fallback to document-specific management privilege
final DisjunctivePrivilegeGroup orPrivilegeGroups =
new DisjunctivePrivilegeGroup(
ImmutableList.of(
new ConjunctivePrivilegeGroup(
ImmutableList.of(PoliciesConfig.MANAGE_DOCUMENTS_PRIVILEGE.getType()))));

return isAuthorized(
context, documentUrn.getEntityType(), documentUrn.toString(), orPrivilegeGroups);
}

/** Returns true if the current user has the platform-level 'Manage Documents' privilege. */
public static boolean canManageDocuments(@Nonnull QueryContext context) {
return AuthUtil.isAuthorized(
context.getOperationContext(), PoliciesConfig.MANAGE_DOCUMENTS_PRIVILEGE);
}

public static boolean isAuthorized(
@Nonnull QueryContext context,
@Nonnull String resourceType,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ public CompletableFuture<AuthenticatedUser> get(DataFetchingEnvironment environm
platformPrivileges.setViewTests(canViewTests(context));
platformPrivileges.setManageTests(canManageTests(context));
platformPrivileges.setManageGlossaries(canManageGlossaries(context));
platformPrivileges.setManageDocuments(AuthorizationUtils.canManageDocuments(context));
platformPrivileges.setManageUserCredentials(canManageUserCredentials(context));
platformPrivileges.setCreateDomains(AuthorizationUtils.canCreateDomains(context));
platformPrivileges.setCreateTags(AuthorizationUtils.canCreateTags(context));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,7 @@ public CompletableFuture<AppConfig> get(final DataFetchingEnvironment environmen
.setAssetSummaryPageV1(_featureFlags.isAssetSummaryPageV1())
.setDatasetSummaryPageV1(_featureFlags.isDatasetSummaryPageV1())
.setDocumentationFileUploadV1(isDocumentationFileUploadV1Enabled())
.setContextDocumentsEnabled(_featureFlags.isContextDocumentsEnabled())
.build();

appConfig.setFeatureFlags(featureFlagsConfig);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ public CompletableFuture<EntityPrivileges> get(DataFetchingEnvironment environme
return getDashboardPrivileges(urn, context);
case Constants.DATA_JOB_ENTITY_NAME:
return getDataJobPrivileges(urn, context);
case Constants.DOCUMENT_ENTITY_NAME:
return getDocumentPrivileges(urn, context);
default:
log.warn(
"Tried to get entity privileges for entity type {}. Adding common privileges only.",
Expand Down Expand Up @@ -161,6 +163,14 @@ private EntityPrivileges getDataJobPrivileges(Urn urn, QueryContext context) {
return result;
}

private EntityPrivileges getDocumentPrivileges(Urn urn, QueryContext context) {
final EntityPrivileges result = new EntityPrivileges();
addCommonPrivileges(result, urn, context);
// Document-specific: canManageEntity includes ability to delete/move documents
result.setCanManageEntity(AuthorizationUtils.canEditDocument(urn, context));
return result;
}

private void addCommonPrivileges(
@Nonnull EntityPrivileges result, @Nonnull Urn urn, @Nonnull QueryContext context) {
result.setCanEditLineage(canEditEntityLineage(urn, context));
Expand Down
Loading
Loading