mirror of
				https://github.com/zulip/zulip.git
				synced 2025-11-04 05:53:43 +00:00 
			
		
		
		
	invites: Make it possible for non-admins to revoke multiuse invites.
This commit makes changes to allow non-admins to revoke multiuse invitations created by them.
This commit is contained in:
		@@ -33,6 +33,15 @@ format used by the Zulip server that they are interacting with.
 | 
			
		||||
  to create reusable invitation links. Previously, this endpoint was
 | 
			
		||||
  restricted to admin users only.
 | 
			
		||||
 | 
			
		||||
* `GET /invites`: Endpoint response for non-admin users now includes both
 | 
			
		||||
  email invitations and reusable invitation links that they have created.
 | 
			
		||||
  Previously, non-admin users could only create email invitations, and
 | 
			
		||||
  therefore the response did not include reusable invitation links for these users.
 | 
			
		||||
 | 
			
		||||
* `DELETE /invites/multiuse/{invite_id}`: Non-admin users can now revoke
 | 
			
		||||
  reusable invitation links they have created. Previously, only admin users could
 | 
			
		||||
  create and revoke reusable invitation links.
 | 
			
		||||
 | 
			
		||||
**Feature level 208**
 | 
			
		||||
 | 
			
		||||
* [`POST /users/me/subscriptions`](/api/subscribe),
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,9 @@
 | 
			
		||||
{{#if is_multiuse}}
 | 
			
		||||
{{#if referred_by}}
 | 
			
		||||
<p>{{#tr}}Are you sure you want to revoke this invitation link created by <strong>{referred_by}</strong>?{{/tr}}</p>
 | 
			
		||||
{{else}}
 | 
			
		||||
<p>{{#tr}}Are you sure you want to revoke this invitation link?{{/tr}}</p>
 | 
			
		||||
{{/if}}
 | 
			
		||||
{{else}}
 | 
			
		||||
<p>{{#tr}}Are you sure you want to revoke the invitation to <strong>{email}</strong>?{{/tr}}</p>
 | 
			
		||||
{{/if}}
 | 
			
		||||
 
 | 
			
		||||
@@ -362,13 +362,19 @@ def do_get_invites_controlled_by_user(user_profile: UserProfile) -> List[Dict[st
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    if not user_profile.is_realm_admin:
 | 
			
		||||
        # We do not return multiuse invites to non-admin users.
 | 
			
		||||
        return invites
 | 
			
		||||
 | 
			
		||||
    if user_profile.is_realm_admin:
 | 
			
		||||
        multiuse_confirmation_objs = Confirmation.objects.filter(
 | 
			
		||||
            realm=user_profile.realm, type=Confirmation.MULTIUSE_INVITE
 | 
			
		||||
        ).filter(Q(expiry_date__gte=timezone_now()) | Q(expiry_date=None))
 | 
			
		||||
    else:
 | 
			
		||||
        multiuse_invite_ids = MultiuseInvite.objects.filter(referred_by=user_profile).values_list(
 | 
			
		||||
            "id", flat=True
 | 
			
		||||
        )
 | 
			
		||||
        multiuse_confirmation_objs = Confirmation.objects.filter(
 | 
			
		||||
            type=Confirmation.MULTIUSE_INVITE,
 | 
			
		||||
            object_id__in=multiuse_invite_ids,
 | 
			
		||||
        ).filter(Q(expiry_date__gte=timezone_now()) | Q(expiry_date=None))
 | 
			
		||||
 | 
			
		||||
    for confirmation_obj in multiuse_confirmation_objs:
 | 
			
		||||
        invite = confirmation_obj.content_object
 | 
			
		||||
        assert invite is not None
 | 
			
		||||
 
 | 
			
		||||
@@ -1747,8 +1747,11 @@ class InvitationsTestCase(InviteUserBase):
 | 
			
		||||
        do_create_multiuse_invite_link(
 | 
			
		||||
            user_profile, PreregistrationUser.INVITE_AS["MEMBER"], invite_expires_in_minutes
 | 
			
		||||
        )
 | 
			
		||||
        self.assert_length(do_get_invites_controlled_by_user(user_profile), 5)
 | 
			
		||||
        self.assert_length(do_get_invites_controlled_by_user(hamlet), 1)
 | 
			
		||||
        do_create_multiuse_invite_link(
 | 
			
		||||
            hamlet, PreregistrationUser.INVITE_AS["MEMBER"], invite_expires_in_minutes
 | 
			
		||||
        )
 | 
			
		||||
        self.assert_length(do_get_invites_controlled_by_user(user_profile), 6)
 | 
			
		||||
        self.assert_length(do_get_invites_controlled_by_user(hamlet), 2)
 | 
			
		||||
        self.assert_length(do_get_invites_controlled_by_user(othello), 1)
 | 
			
		||||
 | 
			
		||||
    def test_successful_get_open_invitations(self) -> None:
 | 
			
		||||
@@ -1999,6 +2002,26 @@ class InvitationsTestCase(InviteUserBase):
 | 
			
		||||
            confirmation_settings.STATUS_REVOKED,
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        # Test non-admins can only delete invitations created by them.
 | 
			
		||||
        multiuse_invite = MultiuseInvite.objects.create(
 | 
			
		||||
            referred_by=self.example_user("hamlet"), realm=zulip_realm
 | 
			
		||||
        )
 | 
			
		||||
        create_confirmation_link(
 | 
			
		||||
            multiuse_invite, Confirmation.MULTIUSE_INVITE, validity_in_minutes=validity_in_minutes
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        self.login("cordelia")
 | 
			
		||||
        error_result = self.client_delete("/json/invites/multiuse/" + str(multiuse_invite.id))
 | 
			
		||||
        self.assert_json_error(error_result, "Must be an organization administrator")
 | 
			
		||||
 | 
			
		||||
        self.login("hamlet")
 | 
			
		||||
        result = self.client_delete("/json/invites/multiuse/" + str(multiuse_invite.id))
 | 
			
		||||
        self.assertEqual(result.status_code, 200)
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            MultiuseInvite.objects.get(id=multiuse_invite.id).status,
 | 
			
		||||
            confirmation_settings.STATUS_REVOKED,
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        # Test deleting multiuse invite from another realm
 | 
			
		||||
        mit_realm = get_realm("zephyr")
 | 
			
		||||
        multiuse_invite_in_mit = MultiuseInvite.objects.create(
 | 
			
		||||
 
 | 
			
		||||
@@ -14,7 +14,7 @@ from zerver.actions.invites import (
 | 
			
		||||
    do_revoke_multi_use_invite,
 | 
			
		||||
    do_revoke_user_invite,
 | 
			
		||||
)
 | 
			
		||||
from zerver.decorator import require_member_or_admin, require_realm_admin
 | 
			
		||||
from zerver.decorator import require_member_or_admin
 | 
			
		||||
from zerver.lib.exceptions import JsonableError, OrganizationOwnerRequiredError
 | 
			
		||||
from zerver.lib.request import REQ, has_request_variables
 | 
			
		||||
from zerver.lib.response import json_success
 | 
			
		||||
@@ -140,7 +140,7 @@ def revoke_user_invite(
 | 
			
		||||
    return json_success(request)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@require_realm_admin
 | 
			
		||||
@require_member_or_admin
 | 
			
		||||
@has_request_variables
 | 
			
		||||
def revoke_multiuse_invite(
 | 
			
		||||
    request: HttpRequest, user_profile: UserProfile, invite_id: int
 | 
			
		||||
@@ -153,6 +153,7 @@ def revoke_multiuse_invite(
 | 
			
		||||
    if invite.realm != user_profile.realm:
 | 
			
		||||
        raise JsonableError(_("No such invitation"))
 | 
			
		||||
 | 
			
		||||
    if invite.referred_by_id != user_profile.id:
 | 
			
		||||
        check_role_based_permissions(invite.invited_as, user_profile, require_admin=True)
 | 
			
		||||
 | 
			
		||||
    if invite.status == confirmation_settings.STATUS_REVOKED:
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user