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)
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, )
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
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))
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)
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)
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})
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))
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.")
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, )
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))
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)
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, )
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)
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}"))
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()
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)
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)
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.")
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.")
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()
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, )
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}"))
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
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}", ], )
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})
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)
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)
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)
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)
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()
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, )