Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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 @@ -28,6 +28,7 @@
import com.linkedin.datahub.graphql.resolvers.MeResolver;
import com.linkedin.datahub.graphql.resolvers.ResolverUtils;
import com.linkedin.datahub.graphql.resolvers.application.BatchSetApplicationResolver;
import com.linkedin.datahub.graphql.resolvers.application.BatchUnsetApplicationResolver;
import com.linkedin.datahub.graphql.resolvers.application.CreateApplicationResolver;
import com.linkedin.datahub.graphql.resolvers.application.DeleteApplicationResolver;
import com.linkedin.datahub.graphql.resolvers.assertion.AssertionRunEventResolver;
Expand Down Expand Up @@ -967,7 +968,17 @@ private void configureContainerResolvers(final RuntimeWiring.Builder builder) {
return container.getDataPlatformInstance() != null
? container.getDataPlatformInstance().getUrn()
: null;
})));
}))
.dataFetcher(
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@chriscollins3456 - ptal of the resolver changes here to support application. i made updates here instead of making changes to all the mappers to support setApplication.

"application",
(env) -> {
final Container container = env.getSource();
if (container.getApplications() != null
&& !container.getApplications().isEmpty()) {
return container.getApplications().get(0);
}
return null;
}));
}

private void configureDataPlatformInstanceResolvers(final RuntimeWiring.Builder builder) {
Expand Down Expand Up @@ -1394,6 +1405,9 @@ private void configureMutationResolvers(final RuntimeWiring.Builder builder) {
new DeleteApplicationResolver(this.entityClient, this.applicationService))
.dataFetcher(
"batchSetApplication", new BatchSetApplicationResolver(this.applicationService))
.dataFetcher(
"batchUnsetApplication",
new BatchUnsetApplicationResolver(this.applicationService))
.dataFetcher(
"createOwnershipType", new CreateOwnershipTypeResolver(this.ownershipTypeService))
.dataFetcher(
Expand Down Expand Up @@ -1819,7 +1833,17 @@ private void configureDatasetResolvers(final RuntimeWiring.Builder builder) {
(env) ->
Optional.ofNullable((Dataset) env.getSource())
.map(Dataset::getLogicalParent)
.orElse(null))))
.orElse(null)))
.dataFetcher(
"application",
(env) -> {
final Dataset dataset = env.getSource();
if (dataset.getApplications() != null
&& !dataset.getApplications().isEmpty()) {
return dataset.getApplications().get(0);
}
return null;
}))
.type(
"Owner",
typeWiring ->
Expand Down Expand Up @@ -1922,7 +1946,19 @@ private void configureDatasetResolvers(final RuntimeWiring.Builder builder) {
private void configureVersionedDatasetResolvers(final RuntimeWiring.Builder builder) {
builder.type(
"VersionedDataset",
typeWiring -> typeWiring.dataFetcher("relationships", new StaticDataFetcher(null)));
typeWiring ->
typeWiring
.dataFetcher("relationships", new StaticDataFetcher(null))
.dataFetcher(
"application",
(env) -> {
final VersionedDataset dataset = env.getSource();
if (dataset.getApplications() != null
&& !dataset.getApplications().isEmpty()) {
return dataset.getApplications().get(0);
}
return null;
}));
}

/**
Expand Down Expand Up @@ -1962,7 +1998,17 @@ private void configureGlossaryTermResolvers(final RuntimeWiring.Builder builder)
.dataFetcher("privileges", new EntityPrivilegesResolver(entityClient))
.dataFetcher(
"aspects", new WeaklyTypedAspectsResolver(entityClient, entityRegistry))
.dataFetcher("exists", new EntityExistsResolver(entityService)));
.dataFetcher("exists", new EntityExistsResolver(entityService))
.dataFetcher(
"application",
(env) -> {
final GlossaryTerm glossaryTerm = env.getSource();
if (glossaryTerm.getApplications() != null
&& !glossaryTerm.getApplications().isEmpty()) {
return glossaryTerm.getApplications().get(0);
}
return null;
}));
}

private void configureGlossaryNodeResolvers(final RuntimeWiring.Builder builder) {
Expand Down Expand Up @@ -2190,7 +2236,17 @@ private void configureNotebookResolvers(final RuntimeWiring.Builder builder) {
return notebook.getDataPlatformInstance() != null
? notebook.getDataPlatformInstance().getUrn()
: null;
})));
}))
.dataFetcher(
"application",
(env) -> {
final Notebook notebook = env.getSource();
if (notebook.getApplications() != null
&& !notebook.getApplications().isEmpty()) {
return notebook.getApplications().get(0);
}
return null;
}));
}

/**
Expand Down Expand Up @@ -2239,6 +2295,16 @@ private void configureDashboardResolvers(final RuntimeWiring.Builder builder) {
.dataFetcher("usageStats", new DashboardUsageStatsResolver(timeseriesAspectService))
.dataFetcher(
"statsSummary", new DashboardStatsSummaryResolver(timeseriesAspectService))
.dataFetcher(
"application",
(env) -> {
final Dashboard dashboard = env.getSource();
if (dashboard.getApplications() != null
&& !dashboard.getApplications().isEmpty()) {
return dashboard.getApplications().get(0);
}
return null;
})
.dataFetcher("privileges", new EntityPrivilegesResolver(entityClient))
.dataFetcher("exists", new EntityExistsResolver(entityService))
.dataFetcher(
Expand Down Expand Up @@ -2369,6 +2435,15 @@ private void configureChartResolvers(final RuntimeWiring.Builder builder) {
.dataFetcher("parentContainers", new ParentContainersResolver(entityClient))
.dataFetcher(
"statsSummary", new ChartStatsSummaryResolver(this.timeseriesAspectService))
.dataFetcher(
"application",
(env) -> {
final Chart chart = env.getSource();
if (chart.getApplications() != null && !chart.getApplications().isEmpty()) {
return chart.getApplications().get(0);
}
return null;
})
.dataFetcher("privileges", new EntityPrivilegesResolver(entityClient))
.dataFetcher("exists", new EntityExistsResolver(entityService))
.dataFetcher(
Expand Down Expand Up @@ -2593,7 +2668,17 @@ private void configureDataJobResolvers(final RuntimeWiring.Builder builder) {
entityClient,
graphClient,
timeseriesAspectService,
new EntityHealthResolver.Config(false, true))))
new EntityHealthResolver.Config(false, true)))
.dataFetcher(
"application",
(env) -> {
final DataJob dataJob = env.getSource();
if (dataJob.getApplications() != null
&& !dataJob.getApplications().isEmpty()) {
return dataJob.getApplications().get(0);
}
return null;
}))
.type(
"DataJobInputOutput",
typeWiring ->
Expand Down Expand Up @@ -2678,7 +2763,17 @@ private void configureDataFlowResolvers(final RuntimeWiring.Builder builder) {
entityClient,
graphClient,
timeseriesAspectService,
new EntityHealthResolver.Config(false, true))));
new EntityHealthResolver.Config(false, true)))
.dataFetcher(
"application",
(env) -> {
final DataFlow dataFlow = env.getSource();
if (dataFlow.getApplications() != null
&& !dataFlow.getApplications().isEmpty()) {
return dataFlow.getApplications().get(0);
}
return null;
}));
}

/**
Expand Down Expand Up @@ -2719,7 +2814,17 @@ private void configureMLFeatureTableResolvers(final RuntimeWiring.Builder builde
return entity.getDataPlatformInstance() != null
? entity.getDataPlatformInstance().getUrn()
: null;
})))
}))
.dataFetcher(
"application",
(env) -> {
final MLFeatureTable mlFeatureTable = env.getSource();
if (mlFeatureTable.getApplications() != null
&& !mlFeatureTable.getApplications().isEmpty()) {
return mlFeatureTable.getApplications().get(0);
}
return null;
}))
.type(
"MLFeatureTableProperties",
typeWiring ->
Expand Down Expand Up @@ -2810,7 +2915,17 @@ private void configureMLFeatureTableResolvers(final RuntimeWiring.Builder builde
return mlModel.getDataPlatformInstance() != null
? mlModel.getDataPlatformInstance().getUrn()
: null;
})))
}))
.dataFetcher(
"application",
(env) -> {
final MLModel mlModel = env.getSource();
if (mlModel.getApplications() != null
&& !mlModel.getApplications().isEmpty()) {
return mlModel.getApplications().get(0);
}
return null;
}))
.type(
"MLModelProperties",
typeWiring ->
Expand Down Expand Up @@ -2859,7 +2974,17 @@ private void configureMLFeatureTableResolvers(final RuntimeWiring.Builder builde
return entity.getDataPlatformInstance() != null
? entity.getDataPlatformInstance().getUrn()
: null;
})))
}))
.dataFetcher(
"application",
(env) -> {
final MLModelGroup mlModelGroup = env.getSource();
if (mlModelGroup.getApplications() != null
&& !mlModelGroup.getApplications().isEmpty()) {
return mlModelGroup.getApplications().get(0);
}
return null;
}))
.type(
"MLFeature",
typeWiring ->
Expand All @@ -2885,7 +3010,17 @@ private void configureMLFeatureTableResolvers(final RuntimeWiring.Builder builde
return entity.getDataPlatformInstance() != null
? entity.getDataPlatformInstance().getUrn()
: null;
})))
}))
.dataFetcher(
"application",
(env) -> {
final MLFeature mlFeature = env.getSource();
if (mlFeature.getApplications() != null
&& !mlFeature.getApplications().isEmpty()) {
return mlFeature.getApplications().get(0);
}
return null;
}))
.type(
"MLPrimaryKey",
typeWiring ->
Expand All @@ -2911,7 +3046,17 @@ private void configureMLFeatureTableResolvers(final RuntimeWiring.Builder builde
return entity.getDataPlatformInstance() != null
? entity.getDataPlatformInstance().getUrn()
: null;
})));
}))
.dataFetcher(
"application",
(env) -> {
final MLPrimaryKey mlPrimaryKey = env.getSource();
if (mlPrimaryKey.getApplications() != null
&& !mlPrimaryKey.getApplications().isEmpty()) {
return mlPrimaryKey.getApplications().get(0);
}
return null;
}));
}

private void configureGlossaryRelationshipResolvers(final RuntimeWiring.Builder builder) {
Expand Down Expand Up @@ -3015,7 +3160,17 @@ private void configureDataProductResolvers(final RuntimeWiring.Builder builder)
.dataFetcher("privileges", new EntityPrivilegesResolver(entityClient))
.dataFetcher(
"aspects", new WeaklyTypedAspectsResolver(entityClient, entityRegistry))
.dataFetcher("relationships", new EntityRelationshipsResultResolver(graphClient)));
.dataFetcher("relationships", new EntityRelationshipsResultResolver(graphClient))
.dataFetcher(
"application",
(env) -> {
final DataProduct dataProduct = env.getSource();
if (dataProduct.getApplications() != null
&& !dataProduct.getApplications().isEmpty()) {
return dataProduct.getApplications().get(0);
}
return null;
}));
}

private void configureApplicationResolvers(final RuntimeWiring.Builder builder) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,16 @@
import static com.linkedin.metadata.authorization.ApiOperation.MANAGE;

import com.datahub.authorization.AuthUtil;
import com.datahub.authorization.ConjunctivePrivilegeGroup;
import com.datahub.authorization.DisjunctivePrivilegeGroup;
import com.google.common.collect.ImmutableList;
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.metadata.authorization.PoliciesConfig;
import com.linkedin.metadata.service.ApplicationService;
import java.util.List;
import javax.annotation.Nonnull;
import lombok.extern.slf4j.Slf4j;
Expand All @@ -22,4 +31,53 @@ public static boolean canManageApplications(@Nonnull QueryContext context) {
return AuthUtil.isAuthorizedEntityType(
context.getOperationContext(), MANAGE, List.of(APPLICATION_ENTITY_NAME));
}

/**
* Verifies that the current user is authorized to edit applications on a specific resource
* entity.
*
* @throws AuthorizationException if the user is not authorized
*/
public static void verifyEditApplicationsAuthorization(
@Nonnull Urn resourceUrn, @Nonnull QueryContext context) {
if (!AuthorizationUtils.isAuthorized(
context,
resourceUrn.getEntityType(),
resourceUrn.toString(),
new DisjunctivePrivilegeGroup(
ImmutableList.of(
new ConjunctivePrivilegeGroup(
ImmutableList.of(PoliciesConfig.EDIT_ENTITY_APPLICATIONS_PRIVILEGE.getType())),
new ConjunctivePrivilegeGroup(
ImmutableList.of(PoliciesConfig.EDIT_ENTITY_PRIVILEGE.getType())))))) {
throw new AuthorizationException(
"Unauthorized to perform this action. Please contact your DataHub administrator.");
}
}

/**
* Verifies that all resources exist and that the current user is authorized to edit applications
* on them.
*
* @param resources List of resource URN strings to verify
* @param applicationService Service to verify entity existence
* @param context Query context with operation context and authorization info
* @param operationName Name of the operation being performed (for error messages)
* @throws RuntimeException if any resource does not exist
* @throws AuthorizationException if the user is not authorized for any resource
*/
public static void verifyResourcesExistAndAuthorized(
@Nonnull List<String> resources,
@Nonnull ApplicationService applicationService,
@Nonnull QueryContext context,
@Nonnull String operationName) {
for (String resource : resources) {
Urn resourceUrn = UrnUtils.getUrn(resource);
if (!applicationService.verifyEntityExists(context.getOperationContext(), resourceUrn)) {
throw new RuntimeException(
String.format("Failed to %s, %s in resources does not exist", operationName, resource));
}
verifyEditApplicationsAuthorization(resourceUrn, context);
}
}
}
Loading
Loading