-
Notifications
You must be signed in to change notification settings - Fork 2.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(privileges) Create privileges to allow for managing children of entities #6346
feat(privileges) Create privileges to allow for managing children of entities #6346
Conversation
… terms and nodes with a resolver
case Constants.GLOSSARY_NODE_ENTITY_NAME: | ||
return getGlossaryNodePrivileges(urn, context); | ||
default: | ||
log.warn("Tried to get entity privileges for entity type {} but nothing is implemented for it yet", urn.getEntityType()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice..
} | ||
Urn parentNodeUrn = GlossaryUtils.getTermParentUrn(termUrn, context, _entityClient); | ||
if (parentNodeUrn != null) { | ||
Boolean canManage = GlossaryUtils.canManageGlossaryEntity(context, parentNodeUrn); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this check really be... "can manage children entities" ?
result.setCanManageChildren(true); | ||
return result; | ||
} | ||
Boolean canManageChildren = GlossaryUtils.canManageGlossaryEntity(context, nodeUrn); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NIt: somewhat confusing when reading that canManageGlossaryEntity is related to whether we can manage the children
|
||
return CompletableFuture.supplyAsync(() -> { | ||
if (AuthorizationUtils.canManageGlossaries((context))) { | ||
if (GlossaryUtils.canManageGlossaryEntity(context, parentNode)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah seeing it once more I would expeet to pass the current entity urn into this method
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah these comments make total sense. I think it either needs to be canManageChildrenEntities
and pass in the parentNode
or it should still be canManageGlossaryEntity
and pass in the current entity and an optional parentNode, then the method gets the parent node if none is provided (because sometimes we have access to it like in create while other times we need to fetch it like for delete)
I'm going to go with canManageChildrenEntities
for now since that's how it's currently set up and I think makes the privileges relationship in the glossary a bit more obvious - we look to owners of the parent for permissions and the name of the method tells us that.
If you like the other option though, let me know! i could be convinced.
@@ -62,7 +63,7 @@ private Boolean updateGlossaryTermName( | |||
UpdateNameInput input, | |||
QueryContext context | |||
) { | |||
if (AuthorizationUtils.canManageGlossaries(context)) { | |||
if (GlossaryUtils.canManageGlossaries(context)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So in order to change the name of the entity I've created, I might need "manage glossaries"? What if we just revert back to the same check we ran before around whether we can manage the node?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yup definitely like this more. if you can delete the entity you should be able to rename it...
@@ -86,7 +87,7 @@ private Boolean updateGlossaryNodeName( | |||
UpdateNameInput input, | |||
QueryContext context | |||
) { | |||
if (AuthorizationUtils.canManageGlossaries(context)) { | |||
if (GlossaryUtils.canManageGlossaries(context)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here
if (AuthorizationUtils.canManageGlossaries(environment.getContext())) { | ||
Urn currentParentUrn = GlossaryUtils.getParentUrn(targetUrn, context, _entityClient); | ||
// need to be able to manage current parent node and new parent node | ||
if (GlossaryUtils.canManageGlossaryEntity(context, currentParentUrn) && GlossaryUtils.canManageGlossaryEntity(context, parentNodeUrn)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pretty awesome! Nice catch on this check.
|
||
private GlossaryUtils() { } | ||
|
||
public static boolean canManageGlossaries(@Nonnull QueryContext context) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor: i tend to try to put a java method doc on every public method
} | ||
|
||
/** | ||
* Returns true if the current user is able to create, delete, or move Glossary Terms and Nodes under a parent Node. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Based on description, definitely think this is due for a rename!
} | ||
} | ||
|
||
public static Urn getNodeParentUrn(Urn nodeUrn, QueryContext context, EntityClient entityClient) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
minor: java doc.
We can also annotate this method as @nullable.
Also prefer to annotate input args where possible
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you got it
} | ||
} | ||
|
||
public static Urn getParentUrn(Urn urn, QueryContext context, EntityClient entityClient) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same!
@@ -197,14 +192,14 @@ function EntityDropdown(props: Props) { | |||
{menuItems.has(EntityMenuItems.DELETE) && ( | |||
<StyledMenuItem | |||
key="5" | |||
disabled={isDeleteDisabled || !canManageGlossaries} | |||
disabled={entityHasChildren || !canManageGlossaryEntity} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice!
onClick={onDeleteEntity} | ||
> | ||
<Tooltip | ||
title={`Can't delete ${entityRegistry.getEntityName( | ||
entityType, | ||
)} with child entities.`} | ||
overlayStyle={isDeleteDisabled ? {} : { display: 'none' }} | ||
overlayStyle={entityHasChildren ? {} : { display: 'none' }} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should I be able to see this if I am not allowed to delete?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
great point! i'll make it so we only show this tooltip if you would be able to delete if there were not any children, but there are children.
@@ -249,6 +249,12 @@ public class PoliciesConfig { | |||
"Edit Contact Information", | |||
"The ability to change the contact information such as email & chat handles."); | |||
|
|||
// Glossary Node Privileges | |||
public static final Privilege MANAGE_CHILDREN_PRIVILEGE = Privilege.of( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know we are calling this a "Glossary Node Privilege", but then naming it something really generic (Manage Children). What is the risk of going too generic with this? Would it be better to go more specific with something like "MANAGE_GLOSSARY_CHILDREN"? Disclaimer- I have not deeply thought through it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmm so I think we could certainly reuse this privilege for other things if we start needing to. The risk is that MANAGE_CHILDREN
could mean slightly different things in different contexts. So in the future - does MANAGE_CHILDREN
mean the same thing for Glossary Nodes, Domains, and Containers? maybe? maybe not.
so maybe the least risky move here is to make this more specific and call if MANAGE_GLOSSARY_CHILDREN
so that if we want to have slightly different meanings for different parent contexts.
It would also give more flexibility when creating policies to be more fine-grained - maybe you want to create a policy where users can manage children of nodes they create, but only edit domains they own, but not necessarily their children - they could then add the MANAGE_GLOSSARY_CHILDREN
privilege but omit the MANAGE_DOMAIN_CHILDREN
privilege.
The bummer then is if we start having a lot of these, it feels repetitive. However, I don't really see that happening. Also we're pretty repetitive in our privileges per entity type anyways, this kind of fits in with the current approach of giving very fine-grained control over things.
After talking this out I'm going to go ahead and change the name to be more specific! However if you feel different let me know and I'll be happy to discuss.
import static com.linkedin.datahub.graphql.resolvers.AuthUtils.ALL_PRIVILEGES_GROUP; | ||
|
||
@Slf4j | ||
public class GlossaryUtils { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it'd be great to have a couple unit tests. Usually these static classes are quite easy to test!
|
||
expect(canEditName).toBe(false); | ||
}); | ||
|
||
it('should return false for an unsupported entity', () => { | ||
const canEditName = getCanEditName(EntityType.Chart, platformPrivileges); | ||
const canEditName = getCanEditName(EntityType.Chart, entityDataWithManagePrivileges, platformPrivileges); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice! Thanks for adding these :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall LGTM. Once CI is green we can go on it
…entities (datahub-project#6346) Co-authored-by: Chris Collins <[email protected]> Co-authored-by: Chris Collins <[email protected]>
…entities (datahub-project#6346) Co-authored-by: Chris Collins <[email protected]> Co-authored-by: Chris Collins <[email protected]>
…entities (datahub-project#6346) Co-authored-by: Chris Collins <[email protected]> Co-authored-by: Chris Collins <[email protected]>
This PR does a few things, all with the goal of allowing users to manage children of entities using our permissions framework. Specifically, the desire was to allow a way for users to allow owners of Glossary Nodes to create, delete, and move Terms and Nodes underneath it. So this PR creates the generic privileges than apply to Nodes but could be applied to other entities. Same thing with a new GraphQL privileges object on an entity. Broken down:
Manage Children
privilege that's applied to Glossary Nodes right nowEntityPrivileges
object that can be returned on an entity itselfEntityPrivilegesResolver
to resolve privileges of different entity typesChecklist