Skip to content
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

Open
wants to merge 30 commits into
base: main
Choose a base branch
from

Conversation

simensandhaug
Copy link
Contributor

@simensandhaug simensandhaug commented Jan 30, 2023

Changes

  • Implements changes from PR(change permission groups hr_group, primary_group -> admin_group, member_group)
  • Add mutation to delete membership
  • Add mutation to add member by username
  • Ability to change membership group from member -> administrator and vice versa

Issues

  • No backend tests
  • Have to refresh page after delete and assign membership to see changes. Fix by updating local cache when mutating? Not sure how to do this.
  • Button alignment
    image

@vercel
Copy link

vercel bot commented Jan 30, 2023

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
indok-web ✅ Ready (Inspect) Visit Preview 💬 Add feedback Sep 28, 2023 4:50pm

…e-members-in-an-organization-on-the-organizations-member-page
@codecov
Copy link

codecov bot commented Jan 30, 2023

Codecov Report

Merging #1414 (3af4c03) into main (5bfd2a4) will decrease coverage by 0.32%.
Report is 6 commits behind head on main.
The diff coverage is 68.57%.

@@            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     
Flag Coverage Δ
apitests 82.79% <68.57%> (-0.32%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files Coverage Δ
backend/apps/forms/signals.py 100.00% <100.00%> (ø)
backend/apps/organizations/models.py 87.50% <100.00%> (+2.50%) ⬆️
backend/apps/organizations/schema.py 100.00% <100.00%> (ø)
backend/apps/organizations/types.py 73.77% <100.00%> (ø)
backend/apps/permissions/constants.py 100.00% <100.00%> (ø)
backend/apps/permissions/signals.py 97.50% <100.00%> (-0.07%) ⬇️
backend/apps/organizations/signals.py 80.00% <80.00%> (+0.58%) ⬆️
backend/apps/organizations/mutations.py 60.65% <57.14%> (-4.35%) ⬇️

@simensandhaug
Copy link
Contributor Author

Current problem: Clientside doesnt get updated when changes are made. (i.e when user is deleted or added). Not sure how to fix this

@simensandhaug simensandhaug changed the title 1360 admins can add delete promote and demote members in an organization on the organizations member page Organization member-management Feb 6, 2023
Merge branch 'main' into 1360-admins-can-add-delete-promote-and-demote-members-in-an-organization-on-the-organizations-member-page
@sonarcloud
Copy link

sonarcloud bot commented Feb 13, 2023

Kudos, SonarCloud Quality Gate passed!    Quality Gate passed

Bug A 0 Bugs
Vulnerability A 0 Vulnerabilities
Security Hotspot A 0 Security Hotspots
Code Smell A 1 Code Smell

No Coverage information No Coverage information
0.0% 0.0% Duplication

…e-members-in-an-organization-on-the-organizations-member-page
@sonarcloud
Copy link

sonarcloud bot commented Sep 28, 2023

SonarCloud Quality Gate failed.    Quality Gate failed

Bug A 0 Bugs
Vulnerability A 0 Vulnerabilities
Security Hotspot A 0 Security Hotspots
Code Smell A 3 Code Smells

No Coverage information No Coverage information
19.0% 19.0% Duplication

idea Catch issues before they fail your Quality Gate with our IDE extension sonarlint SonarLint

@cypress
Copy link

cypress bot commented Sep 28, 2023

Passing run #2913 ↗︎

0 2 0 0 Flakiness 0

Details:

Merge 3af4c03 into 5bfd2a4...
Project: Indøk Web Commit: 472f7ec1e3 ℹ️
Status: Passed Duration: 01:45 💡
Started: Sep 28, 2023 4:57 PM Ended: Sep 28, 2023 4:58 PM

Review all test suite changes for PR #1414 ↗︎

@MagnusHafstad MagnusHafstad requested review from larwaa and removed request for larwaa November 9, 2023 20:24
Copy link
Member

@larwaa larwaa left a 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")
Copy link
Member

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")
Copy link
Member

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}"
Copy link
Member

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?

Suggested change
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}"

Copy link
Member

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

Comment on lines +36 to +38
const [AssignMembershipWithUsername] = useMutation(AssignMembershipWithUsernameDocument);
const [DeleteMembership] = useMutation(DeleteMembershipDocument);
const [ChangeMembership] = useMutation(ChangeMembershipDocument);
Copy link
Member

Choose a reason for hiding this comment

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

Nitpicking

Suggested change
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):
Copy link
Member

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) => {
Copy link
Member

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.

Suggested change
const handleRemoveMembership = (membership: MembershipType | any) => {
const handleRemoveMembership = (membership: MembershipType) => {

setUserInput("");
};

const handleGroupChange = (membership: MembershipType | any) => {
Copy link
Member

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

Suggested change
const handleGroupChange = (membership: MembershipType | any) => {
const handleGroupChange = (membership: MembershipType) => {

startIcon={<AdminPanelSettings />}
sx={{ mr: 1 }}
>
{membership?.group?.uuid == organization.adminGroup?.uuid ? "Demoter" : "Promoter"}
Copy link
Member

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?

Suggested change
{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)
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
print(admin_group.group, instance)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Admins can Add, Delete, Promote and Demote members in an organization on the organizations member-page
3 participants