-
Notifications
You must be signed in to change notification settings - Fork 5
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
Organization member-management #1414
base: main
Are you sure you want to change the base?
Organization member-management #1414
Conversation
…h explicit names for groups
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
…e-members-in-an-organization-on-the-organizations-member-page
Codecov Report
@@ Coverage Diff @@
## main #1414 +/- ##
==========================================
- Coverage 83.11% 82.79% -0.32%
==========================================
Files 100 100
Lines 3524 3569 +45
==========================================
+ Hits 2929 2955 +26
- Misses 595 614 +19
Flags with carried forward coverage won't be shown. Click here to find out more.
|
Current problem: Clientside doesnt get updated when changes are made. (i.e when user is deleted or added). Not sure how to fix this |
Merge branch 'main' into 1360-admins-can-add-delete-promote-and-demote-members-in-an-organization-on-the-organizations-member-page
Kudos, SonarCloud Quality Gate passed! 0 Bugs No Coverage information |
…e-members-in-an-organization-on-the-organizations-member-page
SonarCloud Quality Gate failed. 0 Bugs No Coverage information Catch issues before they fail your Quality Gate with our IDE extension SonarLint |
Passing run #2913 ↗︎
Details:
Review all test suite changes for PR #1414 ↗︎ |
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.
These are fairly old comments that I've forgotten to finish up, but hopefully there's some value here. Don't hesitate to ask if there are any questions 😊
class Arguments: | ||
membership_id = graphene.ID() | ||
|
||
@permission_required("organizations.manage_organization") |
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.
Unfortunately, some poor decision making on my part to add a lot of complexity with permissions comes back to bite us 🦷 This permission check must include the ID of the organization the membership belongs to. Otherwise, you can delete memberships from other organizations than your own.
Look to get_organization_from_data
for inspiration.
class Arguments: | ||
membership_data = ChangeMembershipInput(required=True) | ||
|
||
@permission_required("organizations.manage_organization") |
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, the permission check must include the organization ID
# Change ResponsibleGroup.group_type from "HR" or "PRIMARY" to "ADMIN" or "MEMBER" | ||
responsible_group.name = f"{responsible_group.organization.name}:{ADMIN_GROUP_TYPE}" | ||
responsible_group.group.name = ( | ||
f"{responsible_group.organization.name}:{ADMIN_GROUP_TYPE}:git{responsible_group.group.uuid}" |
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.
Is the git
part intentional?
f"{responsible_group.organization.name}:{ADMIN_GROUP_TYPE}:git{responsible_group.group.uuid}" | |
f"{responsible_group.organization.name}:{ADMIN_GROUP_TYPE}:{responsible_group.group.uuid}" |
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.
This is great, looks solid
const [AssignMembershipWithUsername] = useMutation(AssignMembershipWithUsernameDocument); | ||
const [DeleteMembership] = useMutation(DeleteMembershipDocument); | ||
const [ChangeMembership] = useMutation(ChangeMembershipDocument); |
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.
Nitpicking
const [AssignMembershipWithUsername] = useMutation(AssignMembershipWithUsernameDocument); | |
const [DeleteMembership] = useMutation(DeleteMembershipDocument); | |
const [ChangeMembership] = useMutation(ChangeMembershipDocument); | |
const [assignMembershipWithUsername] = useMutation(AssignMembershipWithUsernameDocument); | |
const [deleteMembership] = useMutation(DeleteMembershipDocument); | |
const [changeMembership] = useMutation(ChangeMembershipDocument); |
group_id = graphene.ID() | ||
|
||
|
||
class AssignMembershipWithUsername(graphene.Mutation): |
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'm going to do a longer write up here with a general suggestion, so be warned: wall of text incoming.
Early on in the life span of this project, I got carried away with permissions, thinking it would be cool to have a super-flexible permission structure that could handle as many use cases as I could imagine. This resulted in us adding the complex permission structure of the project today, which are way too complex. In reality, we just need, at most, a way to tell if a user is and ADMIN
or a MEMBER
in an organization.
As a result, my suggestion here is to try to hide some of the complexity of our permission structure, to prevent it from leaking out through the API, and make it easier to work with in the future. It doesn't require a huge structural change, we can just change this API slightly.
Rather than forcing the users of the API (us) to have to figure out which group_id
a user should be added to, we can hide that complexity behind a choice with two options: A user should either be added as an ADMIN
or as a MEMBER
.
Specifically, something like this:
class MembershipType(graphene.Enum):
MEMBER = "MEMBER"
ADMIN = "ADMIN"
class AssignMembershipWithUsernameInput(graphene.InputObjectType):
username = graphene.String(required=True)
organization_id = graphene.ID(required=True)
membership_type = MembershipType # Not 100% on the syntax here, this is my best guess.
class AssignMembershipWithUsername(graphene.Mutation):
membership = graphene.Field(MembershipType)
# If you want to avoid having to deal with cache mutations frontend, you can also add this
organization = graphene.Field(OrganizationType)
@permission_required("organizations.manage_organization", fn=get_organization_from_data)
def mutate(self, _, membership_data):
# Implement it similar to today, but instead of getting `group_id` directly, do something along the lines of
if membership_data["membership_type"] == MembershipType.ADMIN:
group = organization.admin_group
elif membership_data["membership_type"] == MembershipType.MEMBER:
group = organization.member_group
# followed by more or less the same logic as before
The overall goal here is to reduce the complexity for the end-user of the API, placing more of the responsibility on the backend to handle the underlying (and in this case, unnecessary 😢 ) complexity.
} | ||
}; | ||
|
||
const handleRemoveMembership = (membership: MembershipType | any) => { |
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.
Don't use any
here, define it properly. If it's possibly undefined, use | undefined
instead.
const handleRemoveMembership = (membership: MembershipType | any) => { | |
const handleRemoveMembership = (membership: MembershipType) => { |
setUserInput(""); | ||
}; | ||
|
||
const handleGroupChange = (membership: MembershipType | any) => { |
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, don't use any
const handleGroupChange = (membership: MembershipType | any) => { | |
const handleGroupChange = (membership: MembershipType) => { |
startIcon={<AdminPanelSettings />} | ||
sx={{ mr: 1 }} | ||
> | ||
{membership?.group?.uuid == organization.adminGroup?.uuid ? "Demoter" : "Promoter"} |
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.
Promoter/demoter aren't very obvious, how about or something along those lines?
{membership?.group?.uuid == organization.adminGroup?.uuid ? "Demoter" : "Promoter"} | |
{membership?.group?.uuid == organization.adminGroup?.uuid ? "Fjern som admin" : "Gjør til admin"} |
) | ||
assign_perm("forms.add_form", hr_group.group) | ||
print(admin_group.group, instance) |
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.
print(admin_group.group, instance) |
Changes
Issues