Example #1
0
def downgrade_small_realms_behind_on_payments_as_needed() -> None:
    customers = Customer.objects.all().exclude(stripe_customer_id=None)
    for customer in customers:
        realm = customer.realm

        # For larger realms, we generally want to talk to the customer
        # before downgrading or cancelling invoices; so this logic only applies with 5.
        if get_latest_seat_count(realm) >= 5:
            continue

        if get_current_plan_by_customer(customer) is not None:
            # Only customers with last 2 invoices open should be downgraded.
            if not customer_has_last_n_invoices_open(customer, 2):
                continue

            # We've now decided to downgrade this customer and void all invoices, and the below will execute this.

            downgrade_now_without_creating_additional_invoices(realm)
            void_all_open_invoices(realm)
            context: Dict[str, Union[str, Realm]] = {
                "upgrade_url": f"{realm.uri}{reverse('initial_upgrade')}",
                "realm": realm,
            }
            send_email_to_billing_admins_and_realm_owners(
                "zerver/emails/realm_auto_downgraded",
                realm,
                from_name=FromAddress.security_email_from_name(language=realm.default_language),
                from_address=FromAddress.tokenized_no_reply_address(),
                language=realm.default_language,
                context=context,
            )
        else:
            if customer_has_last_n_invoices_open(customer, 1):
                void_all_open_invoices(realm)
Example #2
0
def do_start_email_change_process(user_profile: UserProfile,
                                  new_email: str) -> None:
    old_email = user_profile.delivery_email
    obj = EmailChangeStatus.objects.create(
        new_email=new_email,
        old_email=old_email,
        user_profile=user_profile,
        realm=user_profile.realm,
    )

    activation_url = create_confirmation_link(obj, Confirmation.EMAIL_CHANGE)
    from zerver.context_processors import common_context

    context = common_context(user_profile)
    context.update(
        old_email=old_email,
        new_email=new_email,
        activate_url=activation_url,
    )
    language = user_profile.default_language
    send_email(
        "zerver/emails/confirm_new_email",
        to_emails=[new_email],
        from_name=FromAddress.security_email_from_name(language=language),
        from_address=FromAddress.tokenized_no_reply_address(),
        language=language,
        context=context,
        realm=user_profile.realm,
    )
Example #3
0
def do_send_confirmation_email(
    invitee: PreregistrationUser,
    referrer: UserProfile,
    email_language: str,
    invite_expires_in_minutes: Union[Optional[int],
                                     UnspecifiedValue] = UnspecifiedValue(),
) -> str:
    """
    Send the confirmation/welcome e-mail to an invited user.
    """
    activation_url = create_confirmation_link(
        invitee,
        Confirmation.INVITATION,
        validity_in_minutes=invite_expires_in_minutes)
    context = {
        "referrer_full_name": referrer.full_name,
        "referrer_email": referrer.delivery_email,
        "activate_url": activation_url,
        "referrer_realm_name": referrer.realm.name,
    }
    send_email(
        "zerver/emails/invitation",
        to_emails=[invitee.email],
        from_address=FromAddress.tokenized_no_reply_address(),
        language=email_language,
        context=context,
        realm=referrer.realm,
    )
    return activation_url
Example #4
0
    def consume(self, data: Mapping[str, Any]) -> None:
        if "email" in data:
            # When upgrading from a version up through 1.7.1, there may be
            # existing items in the queue with `email` instead of `prereg_id`.
            invitee = PreregistrationUser.objects.filter(
                email__iexact=data["email"].strip()).latest("invited_at")
        else:
            invitee = PreregistrationUser.objects.filter(id=data["prereg_id"]).first()
            if invitee is None:
                # The invitation could have been revoked
                return

        referrer = get_user_profile_by_id(data["referrer_id"])
        logger.info("Sending invitation for realm %s to %s" % (referrer.realm.string_id, invitee.email))
        do_send_confirmation_email(invitee, referrer)

        # queue invitation reminder for two days from now.
        link = create_confirmation_link(invitee, referrer.realm.host, Confirmation.INVITATION)
        context = common_context(referrer)
        context.update({
            'activate_url': link,
            'referrer_name': referrer.full_name,
            'referrer_email': referrer.email,
            'referrer_realm_name': referrer.realm.name,
        })
        send_future_email(
            "zerver/emails/invitation_reminder",
            referrer.realm,
            to_emails=[invitee.email],
            from_address=FromAddress.tokenized_no_reply_address(),
            language=referrer.realm.default_language,
            context=context,
            delay=datetime.timedelta(days=2))
Example #5
0
def confirm_email_change(request: HttpRequest,
                         confirmation_key: str) -> HttpResponse:
    try:
        email_change_object = get_object_from_key(confirmation_key,
                                                  Confirmation.EMAIL_CHANGE)
    except ConfirmationKeyException as exception:
        return render_confirmation_key_error(request, exception)

    new_email = email_change_object.new_email
    old_email = email_change_object.old_email
    user_profile = email_change_object.user_profile

    if user_profile.realm.email_changes_disabled and not user_profile.is_realm_admin:
        raise JsonableError(
            _("Email address changes are disabled in this organization."))

    do_change_user_delivery_email(user_profile, new_email)

    context = {'realm_name': user_profile.realm.name, 'new_email': new_email}
    language = user_profile.default_language
    send_email('zerver/emails/notify_change_in_email',
               to_emails=[old_email],
               from_name=FromAddress.security_email_from_name(
                   user_profile=user_profile),
               from_address=FromAddress.SUPPORT,
               language=language,
               context=context)

    ctx = {
        'new_email': new_email,
        'old_email': old_email,
    }
    return render(request,
                  'confirmation/confirm_email_change.html',
                  context=ctx)
Example #6
0
    def send(self, users: List[UserProfile]) -> None:
        """Sends one-use only links for resetting password to target users

        """
        for user_profile in users:
            context = {
                'email': user_profile.delivery_email,
                'reset_url': generate_password_reset_url(user_profile, default_token_generator),
                'realm_uri': user_profile.realm.uri,
                'realm_name': user_profile.realm.name,
                'active_account_in_realm': True,
            }
            send_email('zerver/emails/password_reset', to_user_ids=[user_profile.id],
                       from_address=FromAddress.tokenized_no_reply_address(),
                       from_name=FromAddress.security_email_from_name(user_profile=user_profile),
                       context=context)
Example #7
0
def send_confirm_registration_email(email: str, activation_url: str,
                                    language: str) -> None:
    send_email('zerver/emails/confirm_registration',
               to_emails=[email],
               from_address=FromAddress.tokenized_no_reply_address(),
               language=language,
               context={'activate_url': activation_url})
Example #8
0
    def consume(self, data: Mapping[str, Any]) -> None:
        if "email" in data:
            # When upgrading from a version up through 1.7.1, there may be
            # existing items in the queue with `email` instead of `prereg_id`.
            invitee = PreregistrationUser.objects.filter(
                email__iexact=data["email"].strip()).latest("invited_at")
        else:
            invitee = PreregistrationUser.objects.filter(
                id=data["prereg_id"]).first()
            if invitee is None:
                # The invitation could have been revoked
                return

        referrer = get_user_profile_by_id(data["referrer_id"])
        logger.info("Sending invitation for realm %s to %s" %
                    (referrer.realm.string_id, invitee.email))
        do_send_confirmation_email(invitee, referrer)

        # queue invitation reminder for two days from now.
        link = create_confirmation_link(invitee, referrer.realm.host,
                                        Confirmation.INVITATION)
        context = common_context(referrer)
        context.update({
            'activate_url': link,
            'referrer_name': referrer.full_name,
            'referrer_email': referrer.email,
            'referrer_realm_name': referrer.realm.name,
        })
        send_future_email(
            "zerver/emails/invitation_reminder",
            referrer.realm,
            to_emails=[invitee.email],
            from_address=FromAddress.tokenized_no_reply_address(),
            context=context,
            delay=datetime.timedelta(days=2))
Example #9
0
    def handle(self, *args: Any, **kwargs: str) -> None:
        if settings.WARN_NO_EMAIL:
            raise CommandError(
                "Outgoing email not yet configured, see\n  "
                "https://zulip.readthedocs.io/en/latest/production/email.html")
        if len(kwargs["email"]) == 0:
            raise CommandError(
                "Usage: /home/zulip/deployments/current/manage.py "
                "send_test_email [email protected]")

        print("If you run into any trouble, read:")
        print()
        print(
            "  https://zulip.readthedocs.io/en/latest/production/email.html#troubleshooting"
        )
        print()
        print(
            "The most common error is not setting `ADD_TOKENS_TO_NOREPLY_ADDRESS=False` when"
        )
        print("using an email provider that doesn't support that feature.")
        print()
        print("Sending 2 test emails from:")

        message = (
            "Success!  If you receive this message (and a second with a different subject), "
            "you've successfully configured sending emails from your Zulip server.  "
            "Remember that you need to restart "
            "the Zulip server with /home/zulip/deployments/current/scripts/restart-server "
            "after changing the settings in /etc/zulip before your changes will take effect."
        )
        with redirect_stderr(io.StringIO()) as f:
            smtplib.SMTP.debuglevel = 1
            try:
                sender = FromAddress.SUPPORT
                print(f"  * {sender}")
                send_mail("Zulip email test", message, sender, kwargs["email"])

                noreply_sender = FromAddress.tokenized_no_reply_address()
                print(f"  * {noreply_sender}")
                send_mail("Zulip noreply email test", message, noreply_sender,
                          kwargs["email"])
            except smtplib.SMTPException as e:
                print(f"Failed to send mails: {e}")
                print()
                print("Full SMTP log follows:")
                print(f.getvalue())
                raise CommandError("Email sending failed!")
        print()
        print("Successfully sent 2 emails to {}!".format(", ".join(
            kwargs["email"])))

        if kwargs["managers"]:
            mail_managers("Zulip manager email test",
                          "This email was sent to the site managers.")

        if kwargs["admins"]:
            mail_admins("Zulip admins email test",
                        "This email was sent to the site admins.")
Example #10
0
def send_confirm_registration_email(
    email: str, activation_url: str, language: str, realm: Optional[Realm] = None
) -> None:
    send_email(
        "zerver/emails/confirm_registration",
        to_emails=[email],
        from_address=FromAddress.tokenized_no_reply_address(),
        language=language,
        context={"activate_url": activation_url},
        realm=realm,
    )
Example #11
0
 def test_build_SES_compatible_From_field(self) -> None:
     hamlet = self.example_user("hamlet")
     from_name = FromAddress.security_email_from_name(language="en")
     mail = build_email(
         "zerver/emails/password_reset",
         to_emails=[hamlet],
         from_name=from_name,
         from_address=FromAddress.NOREPLY,
         language="en",
     )
     self.assertEqual(mail.extra_headers["From"],
                      "{} <{}>".format(from_name, FromAddress.NOREPLY))
Example #12
0
def downgrade_small_realms_behind_on_payments_as_needed() -> None:
    customers = Customer.objects.all()
    for customer in customers:
        realm = customer.realm

        # For larger realms, we generally want to talk to the customer
        # before downgrading; so this logic only applies with 5.
        if get_latest_seat_count(realm) >= 5:
            continue

        if get_current_plan_by_customer(customer) is None:
            continue

        due_invoice_count = 0
        for invoice in stripe.Invoice.list(
                customer=customer.stripe_customer_id, limit=2):
            if invoice.status == "open":
                due_invoice_count += 1

        # Customers with only 1 overdue invoice are ignored.
        if due_invoice_count < 2:
            continue

        # We've now decided to downgrade this customer and void all invoices, and the below will execute this.

        downgrade_now_without_creating_additional_invoices(realm)
        void_all_open_invoices(realm)
        context: Dict[str, str] = {
            "upgrade_url": f"{realm.uri}{reverse('initial_upgrade')}",
            "realm": realm,
        }
        send_email_to_billing_admins_and_realm_owners(
            "zerver/emails/realm_auto_downgraded",
            realm,
            from_name=FromAddress.security_email_from_name(
                language=realm.default_language),
            from_address=FromAddress.tokenized_no_reply_address(),
            language=realm.default_language,
            context=context,
        )
    def send(self, users: List[UserProfile]) -> None:
        """Sends one-use only links for resetting password to target users

        """
        for user_profile in users:
            context = {
                'email': user_profile.email,
                'reset_url': generate_password_reset_url(user_profile, default_token_generator),
                'realm_uri': user_profile.realm.uri,
                'active_account_in_realm': True,
            }
            send_email('zerver/emails/password_reset', to_user_id=user_profile.id,
                       from_address=FromAddress.tokenized_no_reply_address(),
                       from_name="Zulip Account Security", context=context)
Example #14
0
def do_send_realm_reactivation_email(
        realm: Realm, *, acting_user: Optional[UserProfile]) -> None:
    url = create_confirmation_link(realm, Confirmation.REALM_REACTIVATION)
    RealmAuditLog.objects.create(
        realm=realm,
        acting_user=acting_user,
        event_type=RealmAuditLog.REALM_REACTIVATION_EMAIL_SENT,
        event_time=timezone_now(),
    )
    context = {
        "confirmation_url": url,
        "realm_uri": realm.uri,
        "realm_name": realm.name
    }
    language = realm.default_language
    send_email_to_admins(
        "zerver/emails/realm_reactivation",
        realm,
        from_address=FromAddress.tokenized_no_reply_address(),
        from_name=FromAddress.security_email_from_name(language=language),
        language=language,
        context=context,
    )
Example #15
0
def confirm_email_change(request: HttpRequest,
                         confirmation_key: str) -> HttpResponse:
    try:
        email_change_object = get_object_from_key(confirmation_key,
                                                  [Confirmation.EMAIL_CHANGE])
    except ConfirmationKeyException as exception:
        return render_confirmation_key_error(request, exception)

    new_email = email_change_object.new_email
    old_email = email_change_object.old_email
    user_profile = email_change_object.user_profile

    if user_profile.realm.deactivated:
        return redirect_to_deactivation_notice()

    if not user_profile.is_active:
        # TODO: Make this into a user-facing error, not JSON
        raise UserDeactivatedError()

    if user_profile.realm.email_changes_disabled and not user_profile.is_realm_admin:
        raise JsonableError(
            _("Email address changes are disabled in this organization."))

    do_change_user_delivery_email(user_profile, new_email)

    context = {"realm_name": user_profile.realm.name, "new_email": new_email}
    language = user_profile.default_language
    send_email(
        "zerver/emails/notify_change_in_email",
        to_emails=[old_email],
        from_name=FromAddress.security_email_from_name(
            user_profile=user_profile),
        from_address=FromAddress.SUPPORT,
        language=language,
        context=context,
        realm=user_profile.realm,
    )

    ctx = {
        "new_email_html_tag":
        SafeString(
            f'<a href="mailto:{escape(new_email)}">{escape(new_email)}</a>'),
        "old_email_html_tag":
        SafeString(
            f'<a href="mailto:{escape(old_email)}">{escape(old_email)}</a>'),
    }
    return render(request,
                  "confirmation/confirm_email_change.html",
                  context=ctx)
Example #16
0
    def test_send_email_exceptions(self) -> None:
        hamlet = self.example_user("hamlet")
        from_name = FromAddress.security_email_from_name(language="en")
        address = FromAddress.NOREPLY
        # Used to check the output
        mail = build_email(
            "zerver/emails/password_reset",
            to_emails=[hamlet],
            from_name=from_name,
            from_address=address,
            language="en",
        )
        self.assertEqual(mail.extra_headers["From"],
                         f"{from_name} <{FromAddress.NOREPLY}>")

        # We test the cases that should raise an EmailNotDeliveredException
        errors = {
            f"Unknown error sending password_reset email to {mail.to}": [0],
            f"Error sending password_reset email to {mail.to}":
            [SMTPException()],
            f"Error sending password_reset email to {mail.to}: {{'{address}': (550, b'User unknown')}}":
            [
                SMTPRecipientsRefused(
                    recipients={address: (550, b"User unknown")})
            ],
            f"Error sending password_reset email to {mail.to} with error code 242: From field too long":
            [SMTPDataError(242, "From field too long.")],
        }

        for message, side_effect in errors.items():
            with mock.patch.object(EmailBackend,
                                   "send_messages",
                                   side_effect=side_effect):
                with self.assertLogs(logger=logger) as info_log:
                    with self.assertRaises(EmailNotDeliveredException):
                        send_email(
                            "zerver/emails/password_reset",
                            to_emails=[hamlet],
                            from_name=from_name,
                            from_address=FromAddress.NOREPLY,
                            language="en",
                        )
                self.assert_length(info_log.records, 2)
                self.assertEqual(
                    info_log.output[0],
                    f"INFO:{logger.name}:Sending password_reset email to {mail.to}",
                )
                self.assertTrue(info_log.output[1].startswith(
                    f"ERROR:zulip.send_email:{message}"))
Example #17
0
def sponsorship(
    request: HttpRequest,
    user: UserProfile,
    organization_type: str = REQ("organization-type", validator=check_string),
    website: str = REQ("website", validator=check_string),
    description: str = REQ("description", validator=check_string)
) -> HttpResponse:
    realm = user.realm

    requested_by = user.full_name

    role_id_to_name_map = {
        UserProfile.ROLE_REALM_OWNER: "Realm owner",
        UserProfile.ROLE_REALM_ADMINISTRATOR: "Realm adminstrator",
        UserProfile.ROLE_MEMBER: "Member",
        UserProfile.ROLE_GUEST: "Guest"
    }
    user_role = role_id_to_name_map[user.role]

    support_realm_uri = get_realm(settings.STAFF_SUBDOMAIN).uri
    support_url = urljoin(
        support_realm_uri,
        urlunsplit(("", "", reverse('analytics.views.support'),
                    urlencode({"q": realm.string_id}), "")))

    context = {
        "requested_by": requested_by,
        "user_role": user_role,
        "string_id": realm.string_id,
        "support_url": support_url,
        "organization_type": organization_type,
        "website": website,
        "description": description,
    }
    send_email(
        "zerver/emails/sponsorship_request",
        to_emails=[FromAddress.SUPPORT],
        from_name="Zulip sponsorship",
        from_address=FromAddress.tokenized_no_reply_address(),
        reply_to_email=user.delivery_email,
        context=context,
    )

    update_sponsorship_status(realm, True)
    user.is_billing_admin = True
    user.save(update_fields=["is_billing_admin"])

    return json_success()
Example #18
0
def email_on_new_login(sender: Any, user: UserProfile, request: Any,
                       **kwargs: Any) -> None:
    if not user.enable_login_emails:
        return
    # We import here to minimize the dependencies of this module,
    # since it runs as part of `manage.py` initialization
    from zerver.context_processors import common_context

    if not settings.SEND_LOGIN_EMAILS:
        return

    if request:
        # If the user's account was just created, avoid sending an email.
        if (timezone_now() -
                user.date_joined).total_seconds() <= JUST_CREATED_THRESHOLD:
            return

        user_agent = request.META.get('HTTP_USER_AGENT', "").lower()

        context = common_context(user)
        context['user_email'] = user.delivery_email
        user_tz = user.timezone
        if user_tz == '':
            user_tz = timezone_get_current_timezone_name()
        local_time = timezone_now().astimezone(get_timezone(user_tz))
        if user.twenty_four_hour_time:
            hhmm_string = local_time.strftime('%H:%M')
        else:
            hhmm_string = local_time.strftime('%I:%M%p')
        context['login_time'] = local_time.strftime(
            '%A, %B %d, %Y at {} %Z'.format(hhmm_string))
        context['device_ip'] = request.META.get('REMOTE_ADDR') or _(
            "Unknown IP address")
        context['device_os'] = get_device_os(user_agent) or _(
            "an unknown operating system")
        context['device_browser'] = get_device_browser(user_agent) or _(
            "An unknown browser")
        context['unsubscribe_link'] = one_click_unsubscribe_link(user, 'login')

        email_dict = {
            'template_prefix': 'zerver/emails/notify_new_login',
            'to_user_ids': [user.id],
            'from_name':
            FromAddress.security_email_from_name(user_profile=user),
            'from_address': FromAddress.NOREPLY,
            'context': context
        }
        queue_json_publish("email_senders", email_dict)
Example #19
0
def email_on_new_login(sender: Any, user: UserProfile, request: Any,
                       **kwargs: Any) -> None:
    if not user.enable_login_emails:
        return
    # We import here to minimize the dependencies of this module,
    # since it runs as part of `manage.py` initialization
    from zerver.context_processors import common_context

    if not settings.SEND_LOGIN_EMAILS:
        return

    if request:
        # If the user's account was just created, avoid sending an email.
        if (timezone_now() -
                user.date_joined).total_seconds() <= JUST_CREATED_THRESHOLD:
            return

        user_agent = request.META.get("HTTP_USER_AGENT", "").lower()

        context = common_context(user)
        context["user_email"] = user.delivery_email
        user_tz = user.timezone
        if user_tz == "":
            user_tz = timezone_get_current_timezone_name()
        local_time = timezone_now().astimezone(pytz.timezone(user_tz))
        if user.twenty_four_hour_time:
            hhmm_string = local_time.strftime("%H:%M")
        else:
            hhmm_string = local_time.strftime("%I:%M%p")
        context["login_time"] = local_time.strftime(
            f"%A, %B %d, %Y at {hhmm_string} %Z")
        context["device_ip"] = request.META.get("REMOTE_ADDR") or _(
            "Unknown IP address")
        context["device_os"] = get_device_os(user_agent) or _(
            "an unknown operating system")
        context["device_browser"] = get_device_browser(user_agent) or _(
            "An unknown browser")
        context["unsubscribe_link"] = one_click_unsubscribe_link(user, "login")

        email_dict = {
            "template_prefix": "zerver/emails/notify_new_login",
            "to_user_ids": [user.id],
            "from_name":
            FromAddress.security_email_from_name(user_profile=user),
            "from_address": FromAddress.NOREPLY,
            "context": context,
        }
        queue_json_publish("email_senders", email_dict)
Example #20
0
    def handle(self, *args: Any, **kwargs: str) -> None:
        if settings.WARN_NO_EMAIL:
            raise CommandError(
                "Outgoing email not yet configured, see\n  "
                "https://zulip.readthedocs.io/en/latest/production/email.html")
        if len(kwargs['email']) == 0:
            raise CommandError(
                "Usage: /home/zulip/deployments/current/manage.py "
                "send_test_email [email protected]")

        print("If you run into any trouble, read:")
        print()
        print(
            "  https://zulip.readthedocs.io/en/latest/production/email.html#troubleshooting"
        )
        print()
        print(
            "The most common error is not setting `ADD_TOKENS_TO_NOREPLY_ADDRESS=False` when"
        )
        print("using an email provider that doesn't support that feature.")
        print()
        print("Sending 2 test emails from:")

        message = (
            "Success!  If you receive this message (and a second with a different subject), "
            "you've successfully configured sending emails from your Zulip server.  "
            "Remember that you need to restart "
            "the Zulip server with /home/zulip/deployments/current/scripts/restart-server "
            "after changing the settings in /etc/zulip before your changes will take effect."
        )
        sender = FromAddress.SUPPORT
        print("  * %s" % (sender, ))
        send_mail("Zulip email test", message, sender, kwargs['email'])
        noreply_sender = FromAddress.tokenized_no_reply_address()
        print("  * %s" % (noreply_sender, ))
        send_mail("Zulip noreply email test", message, noreply_sender,
                  kwargs['email'])
        print()
        print("Successfully sent 2 emails to %s!" %
              (", ".join(kwargs['email'])))

        if kwargs['managers']:
            mail_managers("Zulip manager email test",
                          "This email was sent to the site managers.")

        if kwargs['admins']:
            mail_admins("Zulip admins email test",
                        "This email was sent to the site admins.")
Example #21
0
    def handle(self, *args: Any, **kwargs: str) -> None:
        if settings.WARN_NO_EMAIL:
            raise CommandError("Outgoing email not yet configured, see\n  "
                               "https://zulip.readthedocs.io/en/latest/production/email.html")
        message = ("Success!  If you receive this message, you've "
                   "successfully configured sending email from your "
                   "Zulip server.  Remember that you need to restart "
                   "the Zulip server with /home/zulip/deployments/current/scripts/restart-server "
                   "after changing the settings in /etc/zulip before your changes will take effect.")
        send_mail("Zulip email test", message, FromAddress.SUPPORT, kwargs['email'])
        send_mail("Zulip noreply email test", message, FromAddress.tokenized_no_reply_address(), kwargs['email'])

        if kwargs['managers']:
            mail_managers("Zulip manager email test", "This email was sent to the site managers.")

        if kwargs['admins']:
            mail_admins("Zulip admins email test", "This email was sent to the site admins.")
Example #22
0
def sponsorship(
    request: HttpRequest,
    user: UserProfile,
    organization_type: str = REQ("organization-type",
                                 json_validator=check_string),
    website: str = REQ("website", json_validator=check_string),
    description: str = REQ("description", json_validator=check_string),
) -> HttpResponse:
    realm = user.realm

    requested_by = user.full_name
    user_role = user.get_role_name()

    support_realm_uri = get_realm(settings.STAFF_SUBDOMAIN).uri
    support_url = urljoin(
        support_realm_uri,
        urlunsplit(
            ("", "", reverse("support"), urlencode({"q":
                                                    realm.string_id}), "")),
    )

    context = {
        "requested_by": requested_by,
        "user_role": user_role,
        "string_id": realm.string_id,
        "support_url": support_url,
        "organization_type": organization_type,
        "website": website,
        "description": description,
    }
    send_email(
        "zerver/emails/sponsorship_request",
        to_emails=[FromAddress.SUPPORT],
        from_name="Zulip sponsorship",
        from_address=FromAddress.tokenized_no_reply_address(),
        reply_to_email=user.delivery_email,
        context=context,
    )

    update_sponsorship_status(realm, True, acting_user=user)
    user.is_billing_admin = True
    user.save(update_fields=["is_billing_admin"])

    return json_success()
Example #23
0
def send_confirm_registration_email(
    email: str,
    activation_url: str,
    *,
    realm: Optional[Realm] = None,
    request: Optional[HttpRequest] = None,
) -> None:
    send_email(
        "zerver/emails/confirm_registration",
        to_emails=[email],
        from_address=FromAddress.tokenized_no_reply_address(),
        language=request.LANGUAGE_CODE if request is not None else None,
        context={
            "create_realm": (realm is None),
            "activate_url": activation_url,
        },
        realm=realm,
        request=request,
    )
Example #24
0
    def test_send_email_exceptions(self) -> None:
        hamlet = self.example_user("hamlet")
        from_name = FromAddress.security_email_from_name(language="en")
        # Used to check the output
        mail = build_email(
            "zerver/emails/password_reset",
            to_emails=[hamlet],
            from_name=from_name,
            from_address=FromAddress.NOREPLY,
            language="en",
        )
        self.assertEqual(mail.extra_headers["From"],
                         "{} <{}>".format(from_name, FromAddress.NOREPLY))

        # We test the two cases that should raise an EmailNotDeliveredException
        errors = {
            f"Unknown error sending password_reset email to {mail.to}": [0],
            f"Error sending password_reset email to {mail.to}":
            [SMTPException()],
        }

        for message, side_effect in errors.items():
            with mock.patch.object(EmailBackend,
                                   "send_messages",
                                   side_effect=side_effect):
                with self.assertLogs(logger=logger) as info_log:
                    with self.assertRaises(EmailNotDeliveredException):
                        send_email(
                            "zerver/emails/password_reset",
                            to_emails=[hamlet],
                            from_name=from_name,
                            from_address=FromAddress.NOREPLY,
                            language="en",
                        )
                self.assert_length(info_log.records, 2)
                self.assertEqual(
                    info_log.output[0],
                    f"INFO:{logger.name}:Sending password_reset email to {mail.to}",
                )
                self.assertTrue(info_log.output[1].startswith(
                    f"ERROR:zulip.send_email:{message}"))
Example #25
0
def support_request(request: HttpRequest) -> HttpResponse:
    user = request.user
    assert user.is_authenticated

    context = {
        "email": user.delivery_email,
        "realm_name": user.realm.name,
        "MAX_SUBJECT_LENGTH": SupportRequestForm.MAX_SUBJECT_LENGTH,
    }

    if request.POST:
        post_data = request.POST.copy()
        form = SupportRequestForm(post_data)

        if form.is_valid():
            email_context = {
                "requested_by": user.full_name,
                "realm_string_id": user.realm.string_id,
                "request_subject": form.cleaned_data["request_subject"],
                "request_message": form.cleaned_data["request_message"],
                "support_url": get_support_url(user.realm),
                "user_role": user.get_role_name(),
            }

            send_email(
                "zerver/emails/support_request",
                to_emails=[FromAddress.SUPPORT],
                from_name="Zulip Support",
                from_address=FromAddress.tokenized_no_reply_address(),
                reply_to_email=user.delivery_email,
                context=email_context,
            )

            response = render(request, "corporate/support_request_thanks.html", context=context)
            return response

    response = render(request, "corporate/support_request.html", context=context)
    return response
Example #26
0
    def test_send_email_exceptions(self) -> None:
        hamlet = self.example_user("hamlet")
        from_name = FromAddress.security_email_from_name(language="en")
        # Used to check the output
        mail = build_email(
            "zerver/emails/password_reset",
            to_emails=[hamlet],
            from_name=from_name,
            from_address=FromAddress.NOREPLY,
            language="en",
        )
        self.assertEqual(mail.extra_headers["From"],
                         "{} <{}>".format(from_name, FromAddress.NOREPLY))

        # We test the two cases that should raise an EmailNotDeliveredException
        side_effect = [0, SMTPException]

        with mock.patch.object(EmailBackend,
                               "send_messages",
                               side_effect=side_effect):
            for i in range(len(side_effect)):
                with self.assertLogs(logger=logger) as info_log:
                    with self.assertRaises(EmailNotDeliveredException):
                        send_email(
                            "zerver/emails/password_reset",
                            to_emails=[hamlet],
                            from_name=from_name,
                            from_address=FromAddress.NOREPLY,
                            language="en",
                        )
                self.assertEqual(len(info_log.records), 2)
                self.assertEqual(
                    info_log.output,
                    [
                        f"INFO:{logger.name}:Sending password_reset email to {mail.to}",
                        f"ERROR:{logger.name}:Error sending password_reset email to {mail.to}",
                    ],
                )
Example #27
0
def send_confirm_registration_email(email: str, activation_url: str) -> None:
    send_email('zerver/emails/confirm_registration', to_email=email,
               from_address=FromAddress.tokenized_no_reply_address(),
               context={'activate_url': activation_url})
Example #28
0
    def save(
            self,
            domain_override: Optional[bool] = 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: 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"]

        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

        user = None  # type: Optional[UserProfile]
        try:
            user = get_active_user(email, realm)
        except UserProfile.DoesNotExist:
            pass

        context = {
            'email': email,
            'realm_uri': realm.uri,
        }

        if user is not None:
            token = token_generator.make_token(user)
            uid = urlsafe_base64_encode(force_bytes(user.id)).decode('ascii')
            endpoint = reverse(
                'django.contrib.auth.views.password_reset_confirm',
                kwargs=dict(uidb64=uid, token=token))

            context['no_account_in_realm'] = False
            context['reset_url'] = "{}{}".format(user.realm.uri, endpoint)
            send_email('zerver/emails/password_reset',
                       to_user_id=user.id,
                       from_name="Zulip Account Security",
                       from_address=FromAddress.tokenized_no_reply_address(),
                       context=context)
        else:
            context['no_account_in_realm'] = True
            active_accounts = UserProfile.objects.filter(email__iexact=email,
                                                         is_active=True)
            if active_accounts:
                context['active_accounts'] = active_accounts
                context['multiple_active_accounts'] = active_accounts.count(
                ) != 1
            send_email('zerver/emails/password_reset',
                       to_email=email,
                       from_name="Zulip Account Security",
                       from_address=FromAddress.tokenized_no_reply_address(),
                       context=context)
Example #29
0
    def save(self,
             domain_override: Optional[bool]=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: 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"]

        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

        user = None  # type: Optional[UserProfile]
        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)
            send_email('zerver/emails/password_reset', to_user_ids=[user.id],
                       from_name="Zulip Account Security",
                       from_address=FromAddress.tokenized_no_reply_address(),
                       context=context)
        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
            send_email('zerver/emails/password_reset', to_emails=[email],
                       from_name="Zulip Account Security",
                       from_address=FromAddress.tokenized_no_reply_address(),
                       language=request.LANGUAGE_CODE,
                       context=context)
Example #30
0
    def save(self,
             domain_override: Optional[bool]=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: 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"]

        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

        user = None  # type: Optional[UserProfile]
        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)
            send_email('zerver/emails/password_reset', to_user_ids=[user.id],
                       from_name="Zulip Account Security",
                       from_address=FromAddress.tokenized_no_reply_address(),
                       context=context)
        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
            send_email('zerver/emails/password_reset', to_emails=[email],
                       from_name="Zulip Account Security",
                       from_address=FromAddress.tokenized_no_reply_address(),
                       language=request.LANGUAGE_CODE,
                       context=context)
Example #31
0
    def save(self,
             domain_override: Optional[bool]=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: 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"]

        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

        user = None  # type: Optional[UserProfile]
        try:
            user = get_active_user(email, realm)
        except UserProfile.DoesNotExist:
            pass

        context = {
            'email': email,
            'realm_uri': realm.uri,
        }

        if user is not None:
            token = token_generator.make_token(user)
            uid = urlsafe_base64_encode(force_bytes(user.id)).decode('ascii')
            endpoint = reverse('django.contrib.auth.views.password_reset_confirm',
                               kwargs=dict(uidb64=uid, token=token))

            context['no_account_in_realm'] = False
            context['reset_url'] = "{}{}".format(user.realm.uri, endpoint)
            send_email('zerver/emails/password_reset', to_user_id=user.id,
                       from_name="Zulip Account Security",
                       from_address=FromAddress.tokenized_no_reply_address(),
                       context=context)
        else:
            context['no_account_in_realm'] = True
            accounts = UserProfile.objects.filter(email__iexact=email)
            if accounts:
                context['accounts'] = accounts
                context['multiple_accounts'] = accounts.count() != 1
            send_email('zerver/emails/password_reset', to_email=email,
                       from_name="Zulip Account Security",
                       from_address=FromAddress.tokenized_no_reply_address(),
                       context=context)
Example #32
0
def sponsorship(
        request: HttpRequest,
        user: UserProfile,
        organization_type: str = REQ("organization-type"),
        website: str = REQ(),
        description: str = REQ(),
) -> HttpResponse:
    realm = user.realm

    requested_by = user.full_name
    user_role = user.get_role_name()
    support_url = get_support_url(realm)

    post_data = request.POST.copy()
    # We need to do this because the field name in the template
    # for organization type contains a hyphen and the form expects
    # an underscore.
    post_data.update(organization_type=organization_type)
    form = SponsorshipRequestForm(post_data)

    if form.is_valid():
        with transaction.atomic():
            sponsorship_request = ZulipSponsorshipRequest(
                realm=realm,
                requested_by=user,
                org_website=form.cleaned_data["website"],
                org_description=form.cleaned_data["description"],
                org_type=form.cleaned_data["organization_type"],
            )
            sponsorship_request.save()

            org_type = form.cleaned_data["organization_type"]
            if realm.org_type != org_type:
                realm.org_type = org_type
                realm.save(update_fields=["org_type"])

            update_sponsorship_status(realm, True, acting_user=user)
            do_make_user_billing_admin(user)

            org_type_display_name = get_org_type_display_name(org_type)

        context = {
            "requested_by": requested_by,
            "user_role": user_role,
            "string_id": realm.string_id,
            "support_url": support_url,
            "organization_type": org_type_display_name,
            "website": website,
            "description": description,
        }
        send_email(
            "zerver/emails/sponsorship_request",
            to_emails=[FromAddress.SUPPORT],
            from_name="Zulip sponsorship",
            from_address=FromAddress.tokenized_no_reply_address(),
            reply_to_email=user.delivery_email,
            context=context,
        )

        return json_success(request)
    else:
        messages = []
        for error_list in form.errors.get_json_data().values():
            for error in error_list:
                messages.append(error["message"])
        message = " ".join(messages)
        raise BillingError("Form validation error", message=message)
Example #33
0
def sponsorship(
    request: HttpRequest,
    user: UserProfile,
    organization_type: str = REQ("organization-type"),
    website: str = REQ(),
    description: str = REQ(),
) -> HttpResponse:
    realm = user.realm

    requested_by = user.full_name
    user_role = user.get_role_name()

    support_realm_uri = get_realm(settings.STAFF_SUBDOMAIN).uri
    support_url = urljoin(
        support_realm_uri,
        urlunsplit(("", "", reverse("support"), urlencode({"q": realm.string_id}), "")),
    )

    post_data = request.POST.copy()
    # We need to do this because the field name in the template
    # for organization type contains a hyphen and the form expects
    # an underscore.
    post_data.update(organization_type=organization_type)
    form = SponsorshipRequestForm(post_data)

    with transaction.atomic():
        if form.is_valid():
            sponsorship_request = ZulipSponsorshipRequest(
                realm=realm,
                requested_by=user,
                org_website=form.cleaned_data["website"],
                org_description=form.cleaned_data["description"],
                org_type=form.cleaned_data["organization_type"],
            )
            sponsorship_request.save()

            org_type = form.cleaned_data["organization_type"]
            if realm.org_type != org_type:
                realm.org_type = org_type
                realm.save(update_fields=["org_type"])

        update_sponsorship_status(realm, True, acting_user=user)
        do_make_user_billing_admin(user)

    org_type_display_name = get_org_type_display_name(org_type)

    context = {
        "requested_by": requested_by,
        "user_role": user_role,
        "string_id": realm.string_id,
        "support_url": support_url,
        "organization_type": org_type_display_name,
        "website": website,
        "description": description,
    }
    send_email(
        "zerver/emails/sponsorship_request",
        to_emails=[FromAddress.SUPPORT],
        from_name="Zulip sponsorship",
        from_address=FromAddress.tokenized_no_reply_address(),
        reply_to_email=user.delivery_email,
        context=context,
    )

    return json_success()
Example #34
0
    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,
            )