Skip to content

feat(org member invite): OrganizationMemberInviteDetails DELETE endpoint #88587

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

Open
wants to merge 5 commits into
base: master
Choose a base branch
from

Conversation

mifu67
Copy link
Contributor

@mifu67 mifu67 commented Apr 2, 2025

Delete an OrganizationMemberInvite object from the database. This is used to delete invites and reject invite requests.

@github-actions github-actions bot added the Scope: Backend Automatically applied to PRs that change backend components label Apr 2, 2025
audit_data = invited_member.get_audit_log_data()
event_name = "INVITE_REMOVE" if invited_member.invite_approved else "INVITE_REQUEST_REMOVE"
with transaction.atomic(router.db_for_write(OrganizationMemberInvite)):
invited_member.delete()
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think I unfortunately need to move this to a method on the model to support rejecting an invite request via slack ;_;

Copy link

codecov bot commented Apr 2, 2025

Codecov Report

Attention: Patch coverage is 96.80000% with 4 lines in your changes missing coverage. Please review.

✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
...pi/endpoints/organization_member_invite/details.py 90.47% 4 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master   #88587      +/-   ##
==========================================
+ Coverage   81.79%   82.93%   +1.13%     
==========================================
  Files       10204    10206       +2     
  Lines      575357   575882     +525     
  Branches    22662    22662              
==========================================
+ Hits       470626   477619    +6993     
+ Misses     103802    97334    -6468     
  Partials      929      929              

@mifu67 mifu67 marked this pull request as ready for review April 8, 2025 16:38
@mifu67 mifu67 requested a review from cathteng April 8, 2025 16:38
Comment on lines 262 to 190
if not request.access.has_scope("member:admin"):
if not organization.flags.disable_member_invite and request.access.has_scope(
"member:invite"
):
Copy link
Member

Choose a reason for hiding this comment

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

can we pull these out into their own variables? then checking them is a bit easier to grok

Base automatically changed from mifu67/member-invite/omi-details-put to master April 15, 2025 23:04
Copy link
Member

@leedongwei leedongwei left a comment

Choose a reason for hiding this comment

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

Code quality issues

Comment on lines +187 to +204
if request.user.is_authenticated and not is_active_superuser(request):
try:
acting_member = OrganizationMember.objects.get(
organization=organization, user_id=request.user.id
)
except OrganizationMember.DoesNotExist:
return Response({"detail": ERR_INSUFFICIENT_ROLE}, status=400)

has_member_admin_scope = request.access.has_scope("member:admin")
if not has_member_admin_scope:
can_invite_members = request.access.has_scope("member:invite")
if can_invite_members:
return self._handle_deletion_by_member(
request, organization, invited_member, acting_member
)
return Response({"detail": ERR_INSUFFICIENT_SCOPE}, status=400)
else:
can_manage = roles.can_manage(acting_member.role, invited_member.role)
Copy link
Member

Choose a reason for hiding this comment

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

I think you can move this further down the method, and do all the low-hanging fruit checks and return early from there. It will make this section a lot less nested.

Comment on lines +195 to +201
has_member_admin_scope = request.access.has_scope("member:admin")
if not has_member_admin_scope:
can_invite_members = request.access.has_scope("member:invite")
if can_invite_members:
return self._handle_deletion_by_member(
request, organization, invited_member, acting_member
)
Copy link
Member

@leedongwei leedongwei Apr 29, 2025

Choose a reason for hiding this comment

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

Is this more readable?

    has_member_admin_scope = request.access.has_scope("member:admin")
    can_invite_members = request.access.has_scope("member:invite")

    if not has_member_admin_scope or can_invite_members
        return self._handle_deletion_by_member(
            request, organization, invited_member, acting_member
        )

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This isn't the same logic, because users with org:admin have member:invite. We would enter the if statement erroneously.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'll make it a little cleaner.

"""
Members can remove invites that they sent
"""
self.login_as(self.regular_user)
Copy link
Member

Choose a reason for hiding this comment

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

how is this working if the user is not a member of the org?

i think you might need to update MemberInvitePermission's scope_map for DELETE

return self._handle_deletion_by_member(
request, organization, invited_member, acting_member
)
return Response({"detail": ERR_INSUFFICIENT_SCOPE}, status=400)
Copy link
Member

Choose a reason for hiding this comment

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

should these be responding with 403 if you're not allowed to perform this action?

Comment on lines +209 to +211
# prevents superuser read from deleting an invite or invite request
elif not request.access.has_scope("member:admin"):
return Response({"detail": ERR_INSUFFICIENT_SCOPE}, status=400)
Copy link
Member

Choose a reason for hiding this comment

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

rather than preventing superuser read (since they can't hit the endpoint anyway), i think this is preventing unauthenticated users from hitting this endpoint

raise NotImplementedError
# with superuser read write separation, superuser read cannot hit this endpoint
# so we can keep this as is_active_superuser
if request.user.is_authenticated and not is_active_superuser(request):
Copy link
Member

Choose a reason for hiding this comment

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

maybe check the user is authenticated separately and early return? they should always be authenticated to proceed

@@ -140,7 +145,90 @@ def put(

return Response(serialize(invited_member, request.user), status=200)

def _remove_invite_from_db(
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 almost the same as reject_invite_request on the OrganizationMemberInvite model. could they be collapsed into a single function?

@getsantry
Copy link
Contributor

getsantry bot commented Jun 6, 2025

This issue has gone three weeks without activity. In another week, I will close it.

But! If you comment or otherwise update it, I will reset the clock, and if you remove the label Waiting for: Community, I will leave it alone ... forever!


"A weed is but an unloved flower." ― Ella Wheeler Wilcox 🥀

@getsantry getsantry bot added the Stale label Jun 6, 2025
@mifu67 mifu67 added WIP and removed Stale labels Jun 6, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Scope: Backend Automatically applied to PRs that change backend components WIP
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants