def find_account( request: HttpRequest, raw_emails: Optional[str] = REQ("emails", default=None) ) -> HttpResponse: url = reverse("find_account") emails: List[str] = [] if request.method == "POST": form = FindMyTeamForm(request.POST) if form.is_valid(): emails = form.cleaned_data["emails"] for i in range(len(emails)): try: rate_limit_request_by_ip(request, domain="sends_email_by_ip") except RateLimited as e: assert e.secs_to_freedom is not None return render( request, "zerver/rate_limit_exceeded.html", context={"retry_after": int(e.secs_to_freedom)}, status=429, ) # Django doesn't support __iexact__in lookup with EmailField, so we have # to use Qs to get around that without needing to do multiple queries. emails_q = Q() for email in emails: emails_q |= Q(delivery_email__iexact=email) user_profiles = UserProfile.objects.filter( emails_q, is_active=True, is_bot=False, realm__deactivated=False ) # We organize the data in preparation for sending exactly # one outgoing email per provided email address, with each # email listing all of the accounts that email address has # with the current Zulip server. context: Dict[str, Dict[str, Any]] = {} for user in user_profiles: key = user.delivery_email.lower() context.setdefault(key, {}) context[key].setdefault("realms", []) context[key]["realms"].append(user.realm) context[key]["external_host"] = settings.EXTERNAL_HOST # This value will end up being the last user ID among # matching accounts; since it's only used for minor # details like language, that arbitrary choice is OK. context[key]["to_user_id"] = user.id for delivery_email, realm_context in context.items(): realm_context["email"] = delivery_email send_email( "zerver/emails/find_team", to_user_ids=[realm_context["to_user_id"]], context=realm_context, from_address=FromAddress.SUPPORT, request=request, ) # Note: Show all the emails in the result otherwise this # feature can be used to ascertain which email addresses # are associated with Zulip. data = urllib.parse.urlencode({"emails": ",".join(emails)}) return redirect(append_url_query_string(url, data)) else: form = FindMyTeamForm() # The below validation is perhaps unnecessary, in that we # shouldn't get able to get here with an invalid email unless # the user hand-edits the URLs. if raw_emails: for email in raw_emails.split(","): try: validators.validate_email(email) emails.append(email) except ValidationError: pass return render( request, "zerver/find_account.html", context={"form": form, "current_url": lambda: url, "emails": emails}, )
def accounts_home( request: HttpRequest, multiuse_object_key: str = "", multiuse_object: Optional[MultiuseInvite] = None, ) -> HttpResponse: try: realm = get_realm(get_subdomain(request)) except Realm.DoesNotExist: return HttpResponseRedirect(reverse(find_account)) if realm.deactivated: return redirect_to_deactivation_notice() from_multiuse_invite = False streams_to_subscribe = None invited_as = None if multiuse_object: realm = multiuse_object.realm streams_to_subscribe = multiuse_object.streams.all() from_multiuse_invite = True invited_as = multiuse_object.invited_as if request.method == "POST": form = HomepageForm(request.POST, realm=realm, from_multiuse_invite=from_multiuse_invite) if form.is_valid(): try: rate_limit_request_by_ip(request, domain="sends_email_by_ip") except RateLimited as e: assert e.secs_to_freedom is not None return render( request, "zerver/rate_limit_exceeded.html", context={"retry_after": int(e.secs_to_freedom)}, status=429, ) email = form.cleaned_data["email"] try: validate_email_not_already_in_realm(realm, email) except ValidationError: return redirect_to_email_login_url(email) activation_url = prepare_activation_url( email, request, streams=streams_to_subscribe, invited_as=invited_as ) try: send_confirm_registration_email(email, activation_url, request=request, realm=realm) except EmailNotDeliveredException: logging.error("Error in accounts_home") return HttpResponseRedirect("/config-error/smtp") return HttpResponseRedirect(reverse("signup_send_confirm", kwargs={"email": email})) else: form = HomepageForm(realm=realm) context = login_context(request) context.update( form=form, current_url=request.get_full_path, multiuse_object_key=multiuse_object_key, from_multiuse_invite=from_multiuse_invite, ) return render(request, "zerver/accounts_home.html", context=context)
def find_account( request: HttpRequest, raw_emails: Optional[str] = REQ("emails", default=None) ) -> HttpResponse: from zerver.context_processors import common_context url = reverse("find_account") emails: List[str] = [] if request.method == "POST": form = FindMyTeamForm(request.POST) if form.is_valid(): emails = form.cleaned_data["emails"] for i in range(len(emails)): try: rate_limit_request_by_ip(request, domain="find_account_by_ip") except RateLimited as e: assert e.secs_to_freedom is not None return render( request, "zerver/rate_limit_exceeded.html", context={"retry_after": int(e.secs_to_freedom)}, status=429, ) # Django doesn't support __iexact__in lookup with EmailField, so we have # to use Qs to get around that without needing to do multiple queries. emails_q = Q() for email in emails: emails_q |= Q(delivery_email__iexact=email) for user in UserProfile.objects.filter(emails_q, is_active=True, is_bot=False, realm__deactivated=False): context = common_context(user) context.update(email=user.delivery_email, ) send_email( "zerver/emails/find_team", to_user_ids=[user.id], context=context, from_address=FromAddress.SUPPORT, ) # Note: Show all the emails in the result otherwise this # feature can be used to ascertain which email addresses # are associated with Zulip. data = urllib.parse.urlencode({"emails": ",".join(emails)}) return redirect(add_query_to_redirect_url(url, data)) else: form = FindMyTeamForm() # The below validation is perhaps unnecessary, in that we # shouldn't get able to get here with an invalid email unless # the user hand-edits the URLs. if raw_emails: for email in raw_emails.split(","): try: validators.validate_email(email) emails.append(email) except ValidationError: pass return render( request, "zerver/find_account.html", context={ "form": form, "current_url": lambda: url, "emails": emails }, )
def create_realm(request: HttpRequest, creation_key: Optional[str] = None) -> HttpResponse: try: key_record = validate_key(creation_key) except RealmCreationKey.Invalid: return render( request, "zerver/realm_creation_failed.html", context={ "message": _("The organization creation link has expired" " or is not valid.") }, ) if not settings.OPEN_REALM_CREATION: if key_record is None: return render( request, "zerver/realm_creation_failed.html", context={"message": _("New organization creation disabled")}, ) # When settings.OPEN_REALM_CREATION is enabled, anyone can create a new realm, # with a few restrictions on their email address. if request.method == "POST": form = RealmCreationForm(request.POST) if form.is_valid(): try: rate_limit_request_by_ip(request, domain="sends_email_by_ip") except RateLimited as e: assert e.secs_to_freedom is not None return render( request, "zerver/rate_limit_exceeded.html", context={"retry_after": int(e.secs_to_freedom)}, status=429, ) email = form.cleaned_data["email"] activation_url = prepare_activation_url(email, request, realm_creation=True) if key_record is not None and key_record.presume_email_valid: # The user has a token created from the server command line; # skip confirming the email is theirs, taking their word for it. # This is essential on first install if the admin hasn't stopped # to configure outbound email up front, or it isn't working yet. key_record.delete() return HttpResponseRedirect(activation_url) try: send_confirm_registration_email(email, activation_url, request=request) except EmailNotDeliveredException: logging.error("Error in create_realm") return HttpResponseRedirect("/config-error/smtp") if key_record is not None: key_record.delete() return HttpResponseRedirect(reverse("new_realm_send_confirm", kwargs={"email": email})) else: form = RealmCreationForm() return render( request, "zerver/create_realm.html", context={"form": form, "current_url": request.get_full_path}, )
def save( self, domain_override: Optional[str] = None, subject_template_name: str = "registration/password_reset_subject.txt", email_template_name: str = "registration/password_reset_email.html", use_https: bool = False, token_generator: PasswordResetTokenGenerator = default_token_generator, from_email: Optional[str] = None, request: Optional[HttpRequest] = None, html_email_template_name: Optional[str] = None, extra_email_context: Optional[Dict[str, Any]] = None, ) -> None: """ If the email address has an account in the target realm, generates a one-use only link for resetting password and sends to the user. We send a different email if an associated account does not exist in the database, or an account does exist, but not in the realm. Note: We ignore protocol and the various email template arguments (those are an artifact of using Django's password reset framework). """ email = self.cleaned_data["email"] # The form is only used in zerver.views.auth.password_rest, we know that # the request must not be None assert request is not None realm = get_realm(get_subdomain(request)) if not email_auth_enabled(realm): logging.info( "Password reset attempted for %s even though password auth is disabled.", email) return if email_belongs_to_ldap(realm, email): # TODO: Ideally, we'd provide a user-facing error here # about the fact that they aren't allowed to have a # password in the Zulip server and should change it in LDAP. logging.info("Password reset not allowed for user in LDAP domain") return if realm.deactivated: logging.info("Realm is deactivated") return if settings.RATE_LIMITING: try: rate_limit_password_reset_form_by_email(email) rate_limit_request_by_ip(request, domain="sends_email_by_ip") except RateLimited: logging.info( "Too many password reset attempts for email %s from %s", email, request.META["REMOTE_ADDR"], ) # The view will handle the RateLimit exception and render an appropriate page raise user: Optional[UserProfile] = None try: user = get_user_by_delivery_email(email, realm) except UserProfile.DoesNotExist: pass context = { "email": email, "realm_uri": realm.uri, "realm_name": realm.name, } if user is not None and not user.is_active: context["user_deactivated"] = True user = None if user is not None: context["active_account_in_realm"] = True context["reset_url"] = generate_password_reset_url( user, token_generator) queue_soft_reactivation(user.id) send_email( "zerver/emails/password_reset", to_user_ids=[user.id], from_name=FromAddress.security_email_from_name( user_profile=user), from_address=FromAddress.tokenized_no_reply_address(), context=context, realm=realm, request=request, ) else: context["active_account_in_realm"] = False active_accounts_in_other_realms = UserProfile.objects.filter( delivery_email__iexact=email, is_active=True) if active_accounts_in_other_realms: context[ "active_accounts_in_other_realms"] = active_accounts_in_other_realms language = get_language() send_email( "zerver/emails/password_reset", to_emails=[email], from_name=FromAddress.security_email_from_name( language=language), from_address=FromAddress.tokenized_no_reply_address(), language=language, context=context, realm=realm, request=request, )