2020-06-11 00:54:34 +02:00
|
|
|
import re
|
2020-06-13 05:24:42 +02:00
|
|
|
from typing import List, Sequence, Set
|
2020-06-11 00:54:34 +02:00
|
|
|
|
2016-10-12 05:13:32 +02:00
|
|
|
from django.http import HttpRequest, HttpResponse
|
|
|
|
from django.utils.translation import ugettext as _
|
|
|
|
|
2020-06-11 00:54:34 +02:00
|
|
|
from zerver.decorator import require_member_or_admin, require_realm_admin
|
|
|
|
from zerver.lib.actions import (
|
|
|
|
do_create_multiuse_invite_link,
|
|
|
|
do_get_user_invites,
|
|
|
|
do_invite_users,
|
|
|
|
do_resend_user_invite_email,
|
|
|
|
do_revoke_multi_use_invite,
|
|
|
|
do_revoke_user_invite,
|
|
|
|
)
|
2020-06-18 13:03:06 +02:00
|
|
|
from zerver.lib.exceptions import OrganizationAdministratorRequired, OrganizationOwnerRequired
|
2020-06-11 00:54:34 +02:00
|
|
|
from zerver.lib.request import REQ, JsonableError, has_request_variables
|
|
|
|
from zerver.lib.response import json_error, json_success
|
2019-02-02 23:53:22 +01:00
|
|
|
from zerver.lib.streams import access_stream_by_id
|
2020-06-11 00:54:34 +02:00
|
|
|
from zerver.lib.validator import check_int, check_list
|
|
|
|
from zerver.models import MultiuseInvite, PreregistrationUser, Stream, UserProfile
|
2016-10-12 05:13:32 +02:00
|
|
|
|
|
|
|
|
2020-06-18 13:03:06 +02:00
|
|
|
def check_if_owner_required(invited_as: int, user_profile: UserProfile) -> None:
|
2021-02-12 08:19:30 +01:00
|
|
|
if (
|
2021-02-12 08:20:45 +01:00
|
|
|
invited_as == PreregistrationUser.INVITE_AS["REALM_OWNER"]
|
2021-02-12 08:19:30 +01:00
|
|
|
and not user_profile.is_realm_owner
|
|
|
|
):
|
2020-06-18 13:03:06 +02:00
|
|
|
raise OrganizationOwnerRequired()
|
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
|
2019-06-18 16:43:22 +02:00
|
|
|
@require_member_or_admin
|
2016-10-12 05:13:32 +02:00
|
|
|
@has_request_variables
|
2021-02-12 08:19:30 +01:00
|
|
|
def invite_users_backend(
|
|
|
|
request: HttpRequest,
|
|
|
|
user_profile: UserProfile,
|
|
|
|
invitee_emails_raw: str = REQ("invitee_emails"),
|
2021-02-12 08:20:45 +01:00
|
|
|
invite_as: int = REQ(validator=check_int, default=PreregistrationUser.INVITE_AS["MEMBER"]),
|
2021-02-12 08:19:30 +01:00
|
|
|
stream_ids: List[int] = REQ(validator=check_list(check_int)),
|
|
|
|
) -> HttpResponse:
|
2017-10-15 18:34:47 +02:00
|
|
|
|
2017-05-18 02:45:20 +02:00
|
|
|
if user_profile.realm.invite_by_admins_only and not user_profile.is_realm_admin:
|
2019-11-16 15:53:56 +01:00
|
|
|
raise OrganizationAdministratorRequired()
|
2018-12-30 11:06:12 +01:00
|
|
|
if invite_as not in PreregistrationUser.INVITE_AS.values():
|
|
|
|
return json_error(_("Must be invited as an valid type of user"))
|
2020-06-18 13:03:06 +02:00
|
|
|
check_if_owner_required(invite_as, user_profile)
|
2021-02-12 08:19:30 +01:00
|
|
|
if (
|
2021-02-12 08:20:45 +01:00
|
|
|
invite_as == PreregistrationUser.INVITE_AS["REALM_ADMIN"]
|
2021-02-12 08:19:30 +01:00
|
|
|
and not user_profile.is_realm_admin
|
|
|
|
):
|
2018-03-08 01:47:17 +01:00
|
|
|
return json_error(_("Must be an organization administrator"))
|
2016-10-12 05:13:32 +02:00
|
|
|
if not invitee_emails_raw:
|
|
|
|
return json_error(_("You must specify at least one email address."))
|
2018-12-22 05:41:54 +01:00
|
|
|
if not stream_ids:
|
|
|
|
return json_error(_("You must specify at least one stream for invitees to join."))
|
2016-10-12 05:13:32 +02:00
|
|
|
|
|
|
|
invitee_emails = get_invitee_emails_set(invitee_emails_raw)
|
|
|
|
|
python: Convert assignment type annotations to Python 3.6 style.
This commit was split by tabbott; this piece covers the vast majority
of files in Zulip, but excludes scripts/, tools/, and puppet/ to help
ensure we at least show the right error messages for Xenial systems.
We can likely further refine the remaining pieces with some testing.
Generated by com2ann, with whitespace fixes and various manual fixes
for runtime issues:
- invoiced_through: Optional[LicenseLedger] = models.ForeignKey(
+ invoiced_through: Optional["LicenseLedger"] = models.ForeignKey(
-_apns_client: Optional[APNsClient] = None
+_apns_client: Optional["APNsClient"] = None
- notifications_stream: Optional[Stream] = models.ForeignKey('Stream', related_name='+', null=True, blank=True, on_delete=CASCADE)
- signup_notifications_stream: Optional[Stream] = models.ForeignKey('Stream', related_name='+', null=True, blank=True, on_delete=CASCADE)
+ notifications_stream: Optional["Stream"] = models.ForeignKey('Stream', related_name='+', null=True, blank=True, on_delete=CASCADE)
+ signup_notifications_stream: Optional["Stream"] = models.ForeignKey('Stream', related_name='+', null=True, blank=True, on_delete=CASCADE)
- author: Optional[UserProfile] = models.ForeignKey('UserProfile', blank=True, null=True, on_delete=CASCADE)
+ author: Optional["UserProfile"] = models.ForeignKey('UserProfile', blank=True, null=True, on_delete=CASCADE)
- bot_owner: Optional[UserProfile] = models.ForeignKey('self', null=True, on_delete=models.SET_NULL)
+ bot_owner: Optional["UserProfile"] = models.ForeignKey('self', null=True, on_delete=models.SET_NULL)
- default_sending_stream: Optional[Stream] = models.ForeignKey('zerver.Stream', null=True, related_name='+', on_delete=CASCADE)
- default_events_register_stream: Optional[Stream] = models.ForeignKey('zerver.Stream', null=True, related_name='+', on_delete=CASCADE)
+ default_sending_stream: Optional["Stream"] = models.ForeignKey('zerver.Stream', null=True, related_name='+', on_delete=CASCADE)
+ default_events_register_stream: Optional["Stream"] = models.ForeignKey('zerver.Stream', null=True, related_name='+', on_delete=CASCADE)
-descriptors_by_handler_id: Dict[int, ClientDescriptor] = {}
+descriptors_by_handler_id: Dict[int, "ClientDescriptor"] = {}
-worker_classes: Dict[str, Type[QueueProcessingWorker]] = {}
-queues: Dict[str, Dict[str, Type[QueueProcessingWorker]]] = {}
+worker_classes: Dict[str, Type["QueueProcessingWorker"]] = {}
+queues: Dict[str, Dict[str, Type["QueueProcessingWorker"]]] = {}
-AUTH_LDAP_REVERSE_EMAIL_SEARCH: Optional[LDAPSearch] = None
+AUTH_LDAP_REVERSE_EMAIL_SEARCH: Optional["LDAPSearch"] = None
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-04-22 01:09:50 +02:00
|
|
|
streams: List[Stream] = []
|
2018-12-22 05:41:54 +01:00
|
|
|
for stream_id in stream_ids:
|
2017-01-30 03:09:04 +01:00
|
|
|
try:
|
2020-10-16 17:25:48 +02:00
|
|
|
(stream, sub) = access_stream_by_id(user_profile, stream_id)
|
2017-01-30 03:09:04 +01:00
|
|
|
except JsonableError:
|
2018-12-22 05:41:54 +01:00
|
|
|
return json_error(
|
2021-02-12 08:19:30 +01:00
|
|
|
_("Stream does not exist with id: {}. No invites were sent.").format(stream_id)
|
|
|
|
)
|
2016-10-12 05:13:32 +02:00
|
|
|
streams.append(stream)
|
|
|
|
|
2018-12-30 11:06:12 +01:00
|
|
|
do_invite_users(user_profile, invitee_emails, streams, invite_as)
|
2017-07-25 02:02:30 +02:00
|
|
|
return json_success()
|
2016-10-12 05:13:32 +02:00
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
|
2017-11-27 09:28:57 +01:00
|
|
|
def get_invitee_emails_set(invitee_emails_raw: str) -> Set[str]:
|
2021-02-12 08:20:45 +01:00
|
|
|
invitee_emails_list = set(re.split(r"[,\n]", invitee_emails_raw))
|
2016-10-12 05:13:32 +02:00
|
|
|
invitee_emails = set()
|
|
|
|
for email in invitee_emails_list:
|
2021-02-12 08:20:45 +01:00
|
|
|
is_email_with_name = re.search(r"<(?P<email>.*)>", email)
|
2016-10-12 05:13:32 +02:00
|
|
|
if is_email_with_name:
|
2021-02-12 08:20:45 +01:00
|
|
|
email = is_email_with_name.group("email")
|
2016-10-12 05:13:32 +02:00
|
|
|
invitee_emails.add(email.strip())
|
|
|
|
return invitee_emails
|
2017-10-21 03:15:12 +02:00
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
|
2020-04-30 21:41:21 +02:00
|
|
|
@require_member_or_admin
|
2017-11-27 09:28:57 +01:00
|
|
|
def get_user_invites(request: HttpRequest, user_profile: UserProfile) -> HttpResponse:
|
2017-10-21 03:15:12 +02:00
|
|
|
all_users = do_get_user_invites(user_profile)
|
2021-02-12 08:20:45 +01:00
|
|
|
return json_success({"invites": all_users})
|
2017-10-21 03:15:12 +02:00
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
|
2020-04-30 21:41:21 +02:00
|
|
|
@require_member_or_admin
|
2017-10-21 03:15:12 +02:00
|
|
|
@has_request_variables
|
2021-02-12 08:19:30 +01:00
|
|
|
def revoke_user_invite(
|
|
|
|
request: HttpRequest, user_profile: UserProfile, prereg_id: int
|
|
|
|
) -> HttpResponse:
|
2017-12-05 20:01:55 +01:00
|
|
|
try:
|
|
|
|
prereg_user = PreregistrationUser.objects.get(id=prereg_id)
|
|
|
|
except PreregistrationUser.DoesNotExist:
|
2017-12-05 20:05:17 +01:00
|
|
|
raise JsonableError(_("No such invitation"))
|
2017-12-05 20:01:55 +01:00
|
|
|
|
|
|
|
if prereg_user.referred_by.realm != user_profile.realm:
|
2017-12-05 20:05:17 +01:00
|
|
|
raise JsonableError(_("No such invitation"))
|
2017-12-05 20:01:55 +01:00
|
|
|
|
2020-06-18 13:03:06 +02:00
|
|
|
if prereg_user.referred_by_id != user_profile.id:
|
|
|
|
check_if_owner_required(prereg_user.invited_as, user_profile)
|
|
|
|
if not user_profile.is_realm_admin:
|
|
|
|
raise JsonableError(_("Must be an organization administrator"))
|
2020-04-30 21:41:21 +02:00
|
|
|
|
2017-12-05 20:01:55 +01:00
|
|
|
do_revoke_user_invite(prereg_user)
|
2017-10-21 03:15:12 +02:00
|
|
|
return json_success()
|
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
|
2019-02-15 19:09:25 +01:00
|
|
|
@require_realm_admin
|
|
|
|
@has_request_variables
|
2021-02-12 08:19:30 +01:00
|
|
|
def revoke_multiuse_invite(
|
|
|
|
request: HttpRequest, user_profile: UserProfile, invite_id: int
|
|
|
|
) -> HttpResponse:
|
2019-02-15 19:09:25 +01:00
|
|
|
|
|
|
|
try:
|
|
|
|
invite = MultiuseInvite.objects.get(id=invite_id)
|
|
|
|
except MultiuseInvite.DoesNotExist:
|
|
|
|
raise JsonableError(_("No such invitation"))
|
|
|
|
|
|
|
|
if invite.realm != user_profile.realm:
|
|
|
|
raise JsonableError(_("No such invitation"))
|
|
|
|
|
2020-06-18 13:03:06 +02:00
|
|
|
check_if_owner_required(invite.invited_as, user_profile)
|
|
|
|
|
2019-02-15 19:09:25 +01:00
|
|
|
do_revoke_multi_use_invite(invite)
|
|
|
|
return json_success()
|
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
|
2020-04-30 21:41:21 +02:00
|
|
|
@require_member_or_admin
|
2017-10-21 03:15:12 +02:00
|
|
|
@has_request_variables
|
2021-02-12 08:19:30 +01:00
|
|
|
def resend_user_invite_email(
|
|
|
|
request: HttpRequest, user_profile: UserProfile, prereg_id: int
|
|
|
|
) -> HttpResponse:
|
2017-12-05 20:01:55 +01:00
|
|
|
try:
|
|
|
|
prereg_user = PreregistrationUser.objects.get(id=prereg_id)
|
|
|
|
except PreregistrationUser.DoesNotExist:
|
2017-12-05 20:05:17 +01:00
|
|
|
raise JsonableError(_("No such invitation"))
|
2017-12-05 20:01:55 +01:00
|
|
|
|
2019-03-21 23:37:15 +01:00
|
|
|
# Structurally, any invitation the user can actually access should
|
|
|
|
# have a referred_by set for the user who created it.
|
|
|
|
if prereg_user.referred_by is None or prereg_user.referred_by.realm != user_profile.realm:
|
2017-12-05 20:05:17 +01:00
|
|
|
raise JsonableError(_("No such invitation"))
|
2017-12-05 20:01:55 +01:00
|
|
|
|
2020-06-18 13:03:06 +02:00
|
|
|
if prereg_user.referred_by_id != user_profile.id:
|
|
|
|
check_if_owner_required(prereg_user.invited_as, user_profile)
|
|
|
|
if not user_profile.is_realm_admin:
|
|
|
|
raise JsonableError(_("Must be an organization administrator"))
|
2020-04-30 21:41:21 +02:00
|
|
|
|
2017-12-05 20:01:55 +01:00
|
|
|
timestamp = do_resend_user_invite_email(prereg_user)
|
2021-02-12 08:20:45 +01:00
|
|
|
return json_success({"timestamp": timestamp})
|
2018-03-02 12:27:57 +01:00
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
|
2018-06-16 15:32:59 +02:00
|
|
|
@require_realm_admin
|
2018-03-02 12:27:57 +01:00
|
|
|
@has_request_variables
|
2019-02-06 22:57:14 +01:00
|
|
|
def generate_multiuse_invite_backend(
|
2021-02-12 08:19:30 +01:00
|
|
|
request: HttpRequest,
|
|
|
|
user_profile: UserProfile,
|
2021-02-12 08:20:45 +01:00
|
|
|
invite_as: int = REQ(validator=check_int, default=PreregistrationUser.INVITE_AS["MEMBER"]),
|
2021-02-12 08:19:30 +01:00
|
|
|
stream_ids: Sequence[int] = REQ(validator=check_list(check_int), default=[]),
|
|
|
|
) -> HttpResponse:
|
2020-06-18 13:03:06 +02:00
|
|
|
check_if_owner_required(invite_as, user_profile)
|
|
|
|
|
2018-03-02 12:27:57 +01:00
|
|
|
streams = []
|
|
|
|
for stream_id in stream_ids:
|
|
|
|
try:
|
2020-10-16 17:25:48 +02:00
|
|
|
(stream, sub) = access_stream_by_id(user_profile, stream_id)
|
2018-03-02 12:27:57 +01:00
|
|
|
except JsonableError:
|
2019-04-20 03:49:03 +02:00
|
|
|
return json_error(_("Invalid stream id {}. No invites were sent.").format(stream_id))
|
2018-03-02 12:27:57 +01:00
|
|
|
streams.append(stream)
|
|
|
|
|
2019-02-06 22:57:14 +01:00
|
|
|
invite_link = do_create_multiuse_invite_link(user_profile, invite_as, streams)
|
2021-02-12 08:20:45 +01:00
|
|
|
return json_success({"invite_link": invite_link})
|