mirror of
https://github.com/datahub-project/datahub.git
synced 2025-12-24 08:28:12 +00:00
fix(businessAttribute): add businessAttribute-dataset missing permission (#12650)
This commit is contained in:
parent
bed7cfb298
commit
b8987f2769
@ -29,6 +29,7 @@ dependencies {
|
||||
|
||||
testImplementation externalDependency.mockito
|
||||
testImplementation externalDependency.testng
|
||||
testImplementation externalDependency.mockitoInline
|
||||
}
|
||||
|
||||
graphqlCodegen {
|
||||
|
||||
@ -2,6 +2,7 @@ package com.linkedin.datahub.graphql.resolvers.businessattribute;
|
||||
|
||||
import static com.linkedin.datahub.graphql.resolvers.ResolverUtils.bindArgument;
|
||||
import static com.linkedin.datahub.graphql.resolvers.mutate.MutationUtils.buildMetadataChangeProposalWithUrn;
|
||||
import static com.linkedin.datahub.graphql.resolvers.mutate.util.BusinessAttributeUtils.validateInputResources;
|
||||
import static com.linkedin.metadata.Constants.BUSINESS_ATTRIBUTE_ASPECT;
|
||||
|
||||
import com.linkedin.businessattribute.BusinessAttributeAssociation;
|
||||
@ -43,6 +44,7 @@ public class AddBusinessAttributeResolver implements DataFetcher<CompletableFutu
|
||||
return GraphQLConcurrencyUtils.supplyAsync(
|
||||
() -> {
|
||||
try {
|
||||
validateInputResources(resourceRefInputs, context);
|
||||
addBusinessAttributeToResource(
|
||||
context.getOperationContext(),
|
||||
businessAttributeUrn,
|
||||
|
||||
@ -1,11 +1,16 @@
|
||||
package com.linkedin.datahub.graphql.resolvers.businessattribute;
|
||||
|
||||
import static com.linkedin.datahub.graphql.authorization.AuthorizationUtils.ALL_PRIVILEGES_GROUP;
|
||||
|
||||
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.datahub.graphql.QueryContext;
|
||||
import com.linkedin.datahub.graphql.authorization.AuthorizationUtils;
|
||||
import com.linkedin.metadata.authorization.PoliciesConfig;
|
||||
import java.net.URISyntaxException;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class BusinessAttributeAuthorizationUtils {
|
||||
@ -32,4 +37,20 @@ public class BusinessAttributeAuthorizationUtils {
|
||||
PoliciesConfig.MANAGE_BUSINESS_ATTRIBUTE_PRIVILEGE.getType()))));
|
||||
return AuthUtil.isAuthorized(context.getOperationContext(), orPrivilegeGroups, null);
|
||||
}
|
||||
|
||||
public static boolean isAuthorizedToEditBusinessAttribute(
|
||||
@Nonnull QueryContext context, String targetUrn) throws URISyntaxException {
|
||||
Urn schemaFieldUrn = Urn.createFromString(targetUrn);
|
||||
Urn datasetUrn = schemaFieldUrn.getIdAsUrn();
|
||||
final DisjunctivePrivilegeGroup orPrivilegeGroups =
|
||||
new DisjunctivePrivilegeGroup(
|
||||
ImmutableList.of(
|
||||
ALL_PRIVILEGES_GROUP,
|
||||
new ConjunctivePrivilegeGroup(
|
||||
ImmutableList.of(
|
||||
PoliciesConfig.EDIT_DATASET_COL_BUSINESS_ATTRIBUTE_PRIVILEGE.getType()))));
|
||||
|
||||
return AuthorizationUtils.isAuthorized(
|
||||
context, datasetUrn.getEntityType(), datasetUrn.toString(), orPrivilegeGroups);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,6 +2,7 @@ package com.linkedin.datahub.graphql.resolvers.businessattribute;
|
||||
|
||||
import static com.linkedin.datahub.graphql.resolvers.ResolverUtils.bindArgument;
|
||||
import static com.linkedin.datahub.graphql.resolvers.mutate.MutationUtils.buildMetadataChangeProposalWithUrn;
|
||||
import static com.linkedin.datahub.graphql.resolvers.mutate.util.BusinessAttributeUtils.validateInputResources;
|
||||
import static com.linkedin.metadata.Constants.BUSINESS_ATTRIBUTE_ASPECT;
|
||||
|
||||
import com.linkedin.businessattribute.BusinessAttributes;
|
||||
@ -40,6 +41,7 @@ public class RemoveBusinessAttributeResolver implements DataFetcher<CompletableF
|
||||
return GraphQLConcurrencyUtils.supplyAsync(
|
||||
() -> {
|
||||
try {
|
||||
validateInputResources(resourceRefInputs, context);
|
||||
removeBusinessAttribute(
|
||||
context.getOperationContext(),
|
||||
resourceRefInputs,
|
||||
|
||||
@ -1,8 +1,11 @@
|
||||
package com.linkedin.datahub.graphql.resolvers.mutate.util;
|
||||
|
||||
import static com.linkedin.datahub.graphql.resolvers.businessattribute.BusinessAttributeAuthorizationUtils.isAuthorizedToEditBusinessAttribute;
|
||||
import static com.linkedin.metadata.utils.CriterionUtils.buildCriterion;
|
||||
|
||||
import com.linkedin.datahub.graphql.QueryContext;
|
||||
import com.linkedin.datahub.graphql.exception.AuthorizationException;
|
||||
import com.linkedin.datahub.graphql.generated.ResourceRefInput;
|
||||
import com.linkedin.entity.client.EntityClient;
|
||||
import com.linkedin.metadata.Constants;
|
||||
import com.linkedin.metadata.query.filter.Condition;
|
||||
@ -23,6 +26,8 @@ import com.linkedin.schema.NumberType;
|
||||
import com.linkedin.schema.SchemaFieldDataType;
|
||||
import com.linkedin.schema.StringType;
|
||||
import com.linkedin.schema.TimeType;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import javax.annotation.Nonnull;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@ -105,4 +110,14 @@ public class BusinessAttributeUtils {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static void validateInputResources(List<ResourceRefInput> resources, QueryContext context)
|
||||
throws URISyntaxException {
|
||||
for (ResourceRefInput resource : resources) {
|
||||
if (!isAuthorizedToEditBusinessAttribute(context, resource.getResourceUrn())) {
|
||||
throw new AuthorizationException(
|
||||
"Unauthorized to perform this action. Please contact your DataHub administrator.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,24 +4,30 @@ import static com.linkedin.datahub.graphql.TestUtils.getMockAllowContext;
|
||||
import static com.linkedin.datahub.graphql.TestUtils.getMockEntityService;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mockStatic;
|
||||
import static org.testng.Assert.assertThrows;
|
||||
import static org.testng.Assert.assertTrue;
|
||||
import static org.testng.Assert.expectThrows;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.linkedin.businessattribute.BusinessAttributeAssociation;
|
||||
import com.linkedin.businessattribute.BusinessAttributes;
|
||||
import com.linkedin.common.urn.BusinessAttributeUrn;
|
||||
import com.linkedin.common.urn.Urn;
|
||||
import com.linkedin.datahub.graphql.QueryContext;
|
||||
import com.linkedin.datahub.graphql.exception.AuthorizationException;
|
||||
import com.linkedin.datahub.graphql.generated.AddBusinessAttributeInput;
|
||||
import com.linkedin.datahub.graphql.generated.ResourceRefInput;
|
||||
import com.linkedin.datahub.graphql.resolvers.mutate.util.BusinessAttributeUtils;
|
||||
import com.linkedin.metadata.Constants;
|
||||
import com.linkedin.metadata.entity.EntityService;
|
||||
import com.linkedin.metadata.entity.ebean.batch.AspectsBatchImpl;
|
||||
import graphql.schema.DataFetchingEnvironment;
|
||||
import io.datahubproject.metadata.context.OperationContext;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.List;
|
||||
import org.mockito.MockedStatic;
|
||||
import org.mockito.Mockito;
|
||||
import org.testng.annotations.BeforeMethod;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
public class AddBusinessAttributeResolverTest {
|
||||
@ -33,21 +39,16 @@ public class AddBusinessAttributeResolverTest {
|
||||
private QueryContext mockContext;
|
||||
private DataFetchingEnvironment mockEnv;
|
||||
|
||||
@BeforeMethod
|
||||
private void init() {
|
||||
mockService = getMockEntityService();
|
||||
mockEnv = Mockito.mock(DataFetchingEnvironment.class);
|
||||
}
|
||||
|
||||
private void setupAllowContext() {
|
||||
mockContext = getMockAllowContext();
|
||||
Mockito.when(mockEnv.getContext()).thenReturn(mockContext);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSuccess() throws Exception {
|
||||
init();
|
||||
setupAllowContext();
|
||||
|
||||
Mockito.when(mockEnv.getArgument(Mockito.eq("input"))).thenReturn(addBusinessAttributeInput());
|
||||
Mockito.when(
|
||||
mockService.exists(
|
||||
@ -74,9 +75,6 @@ public class AddBusinessAttributeResolverTest {
|
||||
|
||||
@Test
|
||||
public void testBusinessAttributeAlreadyAdded() throws Exception {
|
||||
init();
|
||||
setupAllowContext();
|
||||
|
||||
Mockito.when(mockEnv.getArgument(Mockito.eq("input"))).thenReturn(addBusinessAttributeInput());
|
||||
Mockito.when(
|
||||
mockService.exists(
|
||||
@ -102,9 +100,6 @@ public class AddBusinessAttributeResolverTest {
|
||||
|
||||
@Test
|
||||
public void testBusinessAttributeNotExists() throws Exception {
|
||||
init();
|
||||
setupAllowContext();
|
||||
|
||||
Mockito.when(mockEnv.getArgument(Mockito.eq("input"))).thenReturn(addBusinessAttributeInput());
|
||||
Mockito.when(
|
||||
mockService.exists(
|
||||
@ -129,6 +124,42 @@ public class AddBusinessAttributeResolverTest {
|
||||
.ingestProposal(any(OperationContext.class), any(AspectsBatchImpl.class), eq(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testActorNotHavePermissionToAddBusinessAttribute() throws Exception {
|
||||
Mockito.when(mockEnv.getArgument(Mockito.eq("input"))).thenReturn(addBusinessAttributeInput());
|
||||
Mockito.when(
|
||||
mockService.exists(
|
||||
any(OperationContext.class),
|
||||
eq(Urn.createFromString((BUSINESS_ATTRIBUTE_URN))),
|
||||
eq(true)))
|
||||
.thenReturn(true);
|
||||
|
||||
AddBusinessAttributeInput businessAttributeInput = addBusinessAttributeInput();
|
||||
List<ResourceRefInput> resourceRefInputs = businessAttributeInput.getResourceUrn();
|
||||
|
||||
try (MockedStatic<BusinessAttributeUtils> mockedStatic =
|
||||
mockStatic(BusinessAttributeUtils.class)) {
|
||||
mockedStatic
|
||||
.when(
|
||||
() ->
|
||||
BusinessAttributeUtils.validateInputResources(
|
||||
resourceRefInputs, mockEnv.getContext()))
|
||||
.thenThrow(
|
||||
new AuthorizationException(
|
||||
"Unauthorized to perform this action. Please contact your DataHub administrator."));
|
||||
|
||||
AddBusinessAttributeResolver addBusinessAttributeResolver =
|
||||
new AddBusinessAttributeResolver(mockService);
|
||||
addBusinessAttributeResolver.get(mockEnv).get();
|
||||
|
||||
assertThrows(
|
||||
AuthorizationException.class,
|
||||
() -> {
|
||||
BusinessAttributeUtils.validateInputResources(resourceRefInputs, mockEnv.getContext());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public AddBusinessAttributeInput addBusinessAttributeInput() {
|
||||
AddBusinessAttributeInput addBusinessAttributeInput = new AddBusinessAttributeInput();
|
||||
addBusinessAttributeInput.setBusinessAttributeUrn(BUSINESS_ATTRIBUTE_URN);
|
||||
@ -136,10 +167,10 @@ public class AddBusinessAttributeResolverTest {
|
||||
return addBusinessAttributeInput;
|
||||
}
|
||||
|
||||
private ImmutableList<ResourceRefInput> resourceRefInput() {
|
||||
private List<ResourceRefInput> resourceRefInput() {
|
||||
ResourceRefInput resourceRefInput = new ResourceRefInput();
|
||||
resourceRefInput.setResourceUrn(RESOURCE_URN);
|
||||
return ImmutableList.of(resourceRefInput);
|
||||
return List.of(resourceRefInput);
|
||||
}
|
||||
|
||||
private BusinessAttributes businessAttributes() throws URISyntaxException {
|
||||
|
||||
@ -4,6 +4,8 @@ import static com.linkedin.datahub.graphql.TestUtils.getMockAllowContext;
|
||||
import static com.linkedin.datahub.graphql.TestUtils.getMockEntityService;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mockStatic;
|
||||
import static org.testng.Assert.assertThrows;
|
||||
import static org.testng.Assert.assertTrue;
|
||||
import static org.testng.Assert.expectThrows;
|
||||
|
||||
@ -13,15 +15,19 @@ import com.linkedin.businessattribute.BusinessAttributes;
|
||||
import com.linkedin.common.urn.BusinessAttributeUrn;
|
||||
import com.linkedin.common.urn.Urn;
|
||||
import com.linkedin.datahub.graphql.QueryContext;
|
||||
import com.linkedin.datahub.graphql.exception.AuthorizationException;
|
||||
import com.linkedin.datahub.graphql.generated.AddBusinessAttributeInput;
|
||||
import com.linkedin.datahub.graphql.generated.ResourceRefInput;
|
||||
import com.linkedin.datahub.graphql.resolvers.mutate.util.BusinessAttributeUtils;
|
||||
import com.linkedin.metadata.Constants;
|
||||
import com.linkedin.metadata.entity.EntityService;
|
||||
import com.linkedin.metadata.entity.ebean.batch.AspectsBatchImpl;
|
||||
import graphql.schema.DataFetchingEnvironment;
|
||||
import io.datahubproject.metadata.context.OperationContext;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import org.mockito.MockedStatic;
|
||||
import org.mockito.Mockito;
|
||||
import org.testng.annotations.BeforeMethod;
|
||||
import org.testng.annotations.Test;
|
||||
@ -39,16 +45,12 @@ public class RemoveBusinessAttributeResolverTest {
|
||||
private void init() {
|
||||
mockService = getMockEntityService();
|
||||
mockEnv = Mockito.mock(DataFetchingEnvironment.class);
|
||||
}
|
||||
|
||||
private void setupAllowContext() {
|
||||
mockContext = getMockAllowContext();
|
||||
Mockito.when(mockEnv.getContext()).thenReturn(mockContext);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSuccess() throws Exception {
|
||||
setupAllowContext();
|
||||
|
||||
Mockito.when(mockEnv.getArgument(Mockito.eq("input"))).thenReturn(addBusinessAttributeInput());
|
||||
|
||||
@ -70,7 +72,6 @@ public class RemoveBusinessAttributeResolverTest {
|
||||
|
||||
@Test
|
||||
public void testBusinessAttributeNotAdded() throws Exception {
|
||||
setupAllowContext();
|
||||
AddBusinessAttributeInput input = addBusinessAttributeInput();
|
||||
Mockito.when(mockEnv.getArgument(Mockito.eq("input"))).thenReturn(input);
|
||||
Mockito.when(
|
||||
@ -98,6 +99,42 @@ public class RemoveBusinessAttributeResolverTest {
|
||||
any(OperationContext.class), Mockito.any(AspectsBatchImpl.class), eq(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testActorNotHavePermissionToRemoveBusinessAttribute() throws Exception {
|
||||
Mockito.when(mockEnv.getArgument(Mockito.eq("input"))).thenReturn(addBusinessAttributeInput());
|
||||
Mockito.when(
|
||||
mockService.exists(
|
||||
any(OperationContext.class),
|
||||
eq(Urn.createFromString((BUSINESS_ATTRIBUTE_URN))),
|
||||
eq(true)))
|
||||
.thenReturn(true);
|
||||
|
||||
AddBusinessAttributeInput businessAttributeInput = addBusinessAttributeInput();
|
||||
List<ResourceRefInput> resourceRefInputs = businessAttributeInput.getResourceUrn();
|
||||
|
||||
try (MockedStatic<BusinessAttributeUtils> mockedStatic =
|
||||
mockStatic(BusinessAttributeUtils.class)) {
|
||||
mockedStatic
|
||||
.when(
|
||||
() ->
|
||||
BusinessAttributeUtils.validateInputResources(
|
||||
resourceRefInputs, mockEnv.getContext()))
|
||||
.thenThrow(
|
||||
new AuthorizationException(
|
||||
"Unauthorized to perform this action. Please contact your DataHub administrator."));
|
||||
|
||||
AddBusinessAttributeResolver addBusinessAttributeResolver =
|
||||
new AddBusinessAttributeResolver(mockService);
|
||||
addBusinessAttributeResolver.get(mockEnv).get();
|
||||
|
||||
assertThrows(
|
||||
AuthorizationException.class,
|
||||
() -> {
|
||||
BusinessAttributeUtils.validateInputResources(resourceRefInputs, mockEnv.getContext());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public AddBusinessAttributeInput addBusinessAttributeInput() {
|
||||
AddBusinessAttributeInput addBusinessAttributeInput = new AddBusinessAttributeInput();
|
||||
addBusinessAttributeInput.setBusinessAttributeUrn(BUSINESS_ATTRIBUTE_URN);
|
||||
|
||||
@ -550,6 +550,12 @@ public class PoliciesConfig {
|
||||
"Produce Platform Event API",
|
||||
"The ability to produce Platform Events using the API.");
|
||||
|
||||
public static final Privilege EDIT_DATASET_COL_BUSINESS_ATTRIBUTE_PRIVILEGE =
|
||||
Privilege.of(
|
||||
"EDIT_DATASET_COL_BUSINESS_ATTRIBUTE_PRIVILEGE",
|
||||
"Edit Dataset Column Business Attribute",
|
||||
"The ability to edit the column (field) Business Attribute associated with a dataset schema.");
|
||||
|
||||
public static final ResourcePrivileges DATASET_PRIVILEGES =
|
||||
ResourcePrivileges.of(
|
||||
"dataset",
|
||||
@ -570,7 +576,8 @@ public class PoliciesConfig {
|
||||
EDIT_QUERIES_PRIVILEGE,
|
||||
CREATE_ER_MODEL_RELATIONSHIP_PRIVILEGE,
|
||||
DATA_READ_ONLY_PRIVILEGE,
|
||||
DATA_READ_WRITE_PRIVILEGE))
|
||||
DATA_READ_WRITE_PRIVILEGE,
|
||||
EDIT_DATASET_COL_BUSINESS_ATTRIBUTE_PRIVILEGE))
|
||||
.flatMap(Collection::stream)
|
||||
.collect(Collectors.toList()));
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user