Skip to content

Commit 2624e19

Browse files
feat(access): Improve external role retrieval (#10160)
Co-authored-by: Chris Collins <[email protected]>
1 parent 98c0bf6 commit 2624e19

File tree

8 files changed

+170
-251
lines changed

8 files changed

+170
-251
lines changed

datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/GmsGraphQLEngine.java

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@
130130
import com.linkedin.datahub.graphql.resolvers.dataproduct.UpdateDataProductResolver;
131131
import com.linkedin.datahub.graphql.resolvers.dataset.DatasetStatsSummaryResolver;
132132
import com.linkedin.datahub.graphql.resolvers.dataset.DatasetUsageStatsResolver;
133+
import com.linkedin.datahub.graphql.resolvers.dataset.IsAssignedToMeResolver;
133134
import com.linkedin.datahub.graphql.resolvers.deprecation.UpdateDeprecationResolver;
134135
import com.linkedin.datahub.graphql.resolvers.domain.CreateDomainResolver;
135136
import com.linkedin.datahub.graphql.resolvers.domain.DeleteDomainResolver;
@@ -389,7 +390,7 @@
389390
import org.dataloader.DataLoaderOptions;
390391

391392
/**
392-
* A {@link GraphQLEngine} configured to provide access to the entities and aspects on the the GMS
393+
* A {@link GraphQLEngine} configured to provide access to the entities and aspects on the GMS
393394
* graph.
394395
*/
395396
@Slf4j
@@ -716,7 +717,7 @@ public void configureRuntimeWiring(final RuntimeWiring.Builder builder) {
716717
configureVersionedDatasetResolvers(builder);
717718
configureAccessAccessTokenMetadataResolvers(builder);
718719
configureTestResultResolvers(builder);
719-
configureRoleResolvers(builder);
720+
configureDataHubRoleResolvers(builder);
720721
configureSchemaFieldResolvers(builder);
721722
configureERModelRelationshipResolvers(builder);
722723
configureEntityPathResolvers(builder);
@@ -729,6 +730,7 @@ public void configureRuntimeWiring(final RuntimeWiring.Builder builder) {
729730
configureFormResolvers(builder);
730731
configureIncidentResolvers(builder);
731732
configureRestrictedResolvers(builder);
733+
configureRoleResolvers(builder);
732734
}
733735

734736
private void configureOrganisationRoleResolvers(RuntimeWiring.Builder builder) {
@@ -2690,7 +2692,7 @@ private void configurePolicyResolvers(final RuntimeWiring.Builder builder) {
26902692
})));
26912693
}
26922694

2693-
private void configureRoleResolvers(final RuntimeWiring.Builder builder) {
2695+
private void configureDataHubRoleResolvers(final RuntimeWiring.Builder builder) {
26942696
builder.type(
26952697
"DataHubRole",
26962698
typeWiring ->
@@ -2925,4 +2927,10 @@ private void configureRestrictedResolvers(final RuntimeWiring.Builder builder) {
29252927
siblingGraphService, restrictedService, this.authorizationConfiguration))
29262928
.dataFetcher("relationships", new EntityRelationshipsResultResolver(graphClient)));
29272929
}
2930+
2931+
private void configureRoleResolvers(final RuntimeWiring.Builder builder) {
2932+
builder.type(
2933+
"Role",
2934+
typeWiring -> typeWiring.dataFetcher("isAssignedToMe", new IsAssignedToMeResolver()));
2935+
}
29282936
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package com.linkedin.datahub.graphql.resolvers.dataset;
2+
3+
import com.linkedin.datahub.graphql.QueryContext;
4+
import com.linkedin.datahub.graphql.generated.CorpUser;
5+
import com.linkedin.datahub.graphql.generated.Role;
6+
import com.linkedin.datahub.graphql.generated.RoleUser;
7+
import graphql.schema.DataFetcher;
8+
import graphql.schema.DataFetchingEnvironment;
9+
import java.util.Collections;
10+
import java.util.Set;
11+
import java.util.concurrent.CompletableFuture;
12+
import java.util.stream.Collectors;
13+
import lombok.extern.slf4j.Slf4j;
14+
15+
@Slf4j
16+
public class IsAssignedToMeResolver implements DataFetcher<CompletableFuture<Boolean>> {
17+
18+
@Override
19+
public CompletableFuture<Boolean> get(final DataFetchingEnvironment environment)
20+
throws Exception {
21+
final QueryContext context = environment.getContext();
22+
final Role role = environment.getSource();
23+
return CompletableFuture.supplyAsync(
24+
() -> {
25+
try {
26+
final Set<String> assignedUserUrns =
27+
role.getActors() != null && role.getActors().getUsers() != null
28+
? role.getActors().getUsers().stream()
29+
.map(RoleUser::getUser)
30+
.map(CorpUser::getUrn)
31+
.collect(Collectors.toSet())
32+
: Collections.emptySet();
33+
return assignedUserUrns.contains(context.getActorUrn());
34+
} catch (Exception e) {
35+
throw new RuntimeException(
36+
"Failed to determine if current user is assigned to Role", e);
37+
}
38+
});
39+
}
40+
}

datahub-graphql-core/src/main/resources/entity.graphql

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1720,6 +1720,7 @@ type Role implements Entity {
17201720
"""
17211721
actors: Actor
17221722

1723+
isAssignedToMe: Boolean!
17231724

17241725
}
17251726

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
package com.linkedin.datahub.graphql.resolvers.role;
2+
3+
import static com.linkedin.datahub.graphql.TestUtils.getMockAllowContext;
4+
import static org.testng.Assert.assertFalse;
5+
import static org.testng.Assert.assertTrue;
6+
7+
import com.linkedin.common.urn.Urn;
8+
import com.linkedin.common.urn.UrnUtils;
9+
import com.linkedin.datahub.graphql.QueryContext;
10+
import com.linkedin.datahub.graphql.generated.*;
11+
import com.linkedin.datahub.graphql.resolvers.dataset.IsAssignedToMeResolver;
12+
import graphql.schema.DataFetchingEnvironment;
13+
import java.util.ArrayList;
14+
import org.mockito.Mockito;
15+
import org.testng.annotations.Test;
16+
17+
public class IsAssignedToMeResolverTest {
18+
19+
private static final Urn TEST_CORP_USER_URN_1 = UrnUtils.getUrn("urn:li:corpuser:test-user-1");
20+
private static final Urn TEST_CORP_USER_URN_2 = UrnUtils.getUrn("urn:li:corpuser:test-user-2");
21+
private static final Urn TEST_CORP_USER_URN_3 = UrnUtils.getUrn("urn:li:corpuser:test-user-3");
22+
23+
@Test
24+
public void testReturnsTrueIfCurrentUserIsAssignedToRole() throws Exception {
25+
26+
CorpUser corpUser1 = new CorpUser();
27+
corpUser1.setUrn(TEST_CORP_USER_URN_1.toString());
28+
CorpUser corpUser2 = new CorpUser();
29+
corpUser2.setUrn(TEST_CORP_USER_URN_2.toString());
30+
CorpUser corpUser3 = new CorpUser();
31+
corpUser3.setUrn(TEST_CORP_USER_URN_3.toString());
32+
33+
ArrayList<RoleUser> roleUsers = new ArrayList<>();
34+
roleUsers.add(new RoleUser(corpUser1));
35+
roleUsers.add(new RoleUser(corpUser2));
36+
roleUsers.add(new RoleUser(corpUser3));
37+
38+
Actor actor = new Actor();
39+
actor.setUsers(roleUsers);
40+
Role role = new Role();
41+
role.setUrn("urn:li:role:fake-role");
42+
role.setActors(actor);
43+
44+
QueryContext mockContext = getMockAllowContext(TEST_CORP_USER_URN_1.toString());
45+
DataFetchingEnvironment mockEnv = Mockito.mock(DataFetchingEnvironment.class);
46+
Mockito.when(mockEnv.getContext()).thenReturn(mockContext);
47+
Mockito.when(mockEnv.getSource()).thenReturn(role);
48+
49+
IsAssignedToMeResolver resolver = new IsAssignedToMeResolver();
50+
assertTrue(resolver.get(mockEnv).get());
51+
}
52+
53+
@Test
54+
public void testReturnsFalseIfCurrentUserIsNotAssignedToRole() throws Exception {
55+
56+
CorpUser corpUser1 = new CorpUser();
57+
corpUser1.setUrn(TEST_CORP_USER_URN_1.toString());
58+
CorpUser corpUser2 = new CorpUser();
59+
corpUser2.setUrn(TEST_CORP_USER_URN_2.toString());
60+
CorpUser corpUser3 = new CorpUser();
61+
corpUser3.setUrn(TEST_CORP_USER_URN_3.toString());
62+
63+
ArrayList<RoleUser> roleUsers = new ArrayList<>();
64+
roleUsers.add(new RoleUser(corpUser2));
65+
roleUsers.add(new RoleUser(corpUser3));
66+
67+
Actor actor = new Actor();
68+
actor.setUsers(roleUsers);
69+
Role role = new Role();
70+
role.setUrn("urn:li:role:fake-role");
71+
role.setActors(actor);
72+
73+
QueryContext mockContext = getMockAllowContext(TEST_CORP_USER_URN_1.toString());
74+
DataFetchingEnvironment mockEnv = Mockito.mock(DataFetchingEnvironment.class);
75+
Mockito.when(mockEnv.getContext()).thenReturn(mockContext);
76+
Mockito.when(mockEnv.getSource()).thenReturn(role);
77+
78+
IsAssignedToMeResolver resolver = new IsAssignedToMeResolver();
79+
assertFalse(resolver.get(mockEnv).get());
80+
}
81+
}

datahub-web-react/src/app/entity/shared/tabs/Dataset/AccessManagement/AccessManagement.tsx

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import { SpinProps } from 'antd/es/spin';
55
import { LoadingOutlined } from '@ant-design/icons';
66
import { useBaseEntity } from '../../../EntityContext';
77
import { GetDatasetQuery, useGetExternalRolesQuery } from '../../../../../../graphql/dataset.generated';
8-
import { useGetMeQuery } from '../../../../../../graphql/me.generated';
98
import { handleAccessRoles } from './utils';
109
import AccessManagerDescription from './AccessManagerDescription';
1110

@@ -59,10 +58,10 @@ const AccessButton = styled(Button)`
5958
`;
6059

6160
export default function AccessManagement() {
62-
const { data: loggedInUser } = useGetMeQuery({ fetchPolicy: 'cache-first' });
6361
const baseEntity = useBaseEntity<GetDatasetQuery>();
62+
6463
const { data: externalRoles, loading: isLoading } = useGetExternalRolesQuery({
65-
variables: { urn: baseEntity?.dataset?.urn as string },
64+
variables: { urn: baseEntity?.dataset?.urn as string, },
6665
skip: !baseEntity?.dataset?.urn,
6766
});
6867

@@ -114,7 +113,7 @@ export default function AccessManagement() {
114113
return (
115114
<StyledTable
116115
loading={isLoading ? spinProps : false}
117-
dataSource={handleAccessRoles(externalRoles, loggedInUser)}
116+
dataSource={handleAccessRoles(externalRoles)}
118117
columns={columns} pagination={false}
119118
/>
120119
)

0 commit comments

Comments
 (0)