Example #1
0
    def handle(self, *args: Any, **options: str) -> None:
        old_email = options["old_email"]
        new_email = options["new_email"]

        realm = self.get_realm(options)
        user_profile = self.get_user(old_email, realm)

        do_change_user_delivery_email(user_profile, new_email)
Example #2
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 #3
0
    def test_change_email(self) -> None:
        now = timezone_now()
        user = self.example_user("hamlet")
        new_email = "*****@*****.**"
        do_change_user_delivery_email(user, new_email)
        self.assertEqual(
            RealmAuditLog.objects.filter(
                event_type=RealmAuditLog.USER_EMAIL_CHANGED,
                event_time__gte=now).count(),
            1,
        )
        self.assertEqual(new_email, user.delivery_email)

        # Test the RealmAuditLog stringification
        audit_entry = RealmAuditLog.objects.get(
            event_type=RealmAuditLog.USER_EMAIL_CHANGED, event_time__gte=now)
        self.assertTrue(
            str(audit_entry).startswith(
                f"<RealmAuditLog: <UserProfile: {user.email} {user.realm}> {RealmAuditLog.USER_EMAIL_CHANGED} "
            ))
Example #4
0
    def save(self) -> None:
        """
        This method is called at the end of operations modifying a user,
        and is responsible for actually applying the requested changes,
        writing them to the database.
        """
        realm = RequestNotes.get_notes(self._request).realm
        assert realm is not None

        email_new_value = getattr(self, "_email_new_value", None)
        is_active_new_value = getattr(self, "_is_active_new_value", None)
        full_name_new_value = getattr(self, "_full_name_new_value", None)
        password = getattr(self, "_password_set_to", None)

        # Clean up the internal "pending change" state, now that we've
        # fetched the values:
        self._email_new_value = None
        self._is_active_new_value = None
        self._full_name_new_value = None
        self._password_set_to = None

        if email_new_value:
            try:
                # Note that the validate_email check that usually
                # appears adjacent to email_allowed_for_realm is
                # present in save().
                email_allowed_for_realm(email_new_value, realm)
            except DomainNotAllowedForRealmError:
                raise scim_exceptions.BadRequestError(
                    "This email domain isn't allowed in this organization.")
            except DisposableEmailError:  # nocoverage
                raise scim_exceptions.BadRequestError(
                    "Disposable email domains are not allowed for this realm.")
            except EmailContainsPlusError:  # nocoverage
                raise scim_exceptions.BadRequestError(
                    "Email address can't contain + characters.")

            try:
                validate_email_not_already_in_realm(realm, email_new_value)
            except ValidationError as e:
                raise ConflictError("Email address already in use: " + str(e))

        if self.is_new_user():
            assert full_name_new_value is not None
            self.obj = do_create_user(
                email_new_value,
                password,
                realm,
                full_name_new_value,
                acting_user=None,
            )
            return

        # TODO: The below operations should ideally be executed in a single
        # atomic block to avoid failing with partial changes getting saved.
        # This can be fixed once we figure out how do_deactivate_user can be run
        # inside an atomic block.

        # We process full_name first here, since it's the only one that can fail.
        if full_name_new_value:
            check_change_full_name(self.obj,
                                   full_name_new_value,
                                   acting_user=None)

        if email_new_value:
            do_change_user_delivery_email(self.obj, email_new_value)

        if is_active_new_value is not None and is_active_new_value:
            do_reactivate_user(self.obj, acting_user=None)
        elif is_active_new_value is not None and not is_active_new_value:
            do_deactivate_user(self.obj, acting_user=None)
Example #5
0
def generate_all_emails(request: HttpRequest) -> HttpResponse:
    if not settings.TEST_SUITE:  # nocoverage
        # It's really convenient to automatically inline the email CSS
        # here, since that saves a step when testing out changes to
        # the email CSS.  But we don't run this inside the test suite,
        # because by role, the tests shouldn't be doing a provision-like thing.
        subprocess.check_call(["./scripts/setup/inline_email_css.py"])

    # We import the Django test client inside the view function,
    # because it isn't needed in production elsewhere, and not
    # importing it saves ~50ms of unnecessary manage.py startup time.

    from django.test import Client

    client = Client()

    # write fake data for all variables
    registered_email = "*****@*****.**"
    unregistered_email_1 = "*****@*****.**"
    unregistered_email_2 = "*****@*****.**"
    invite_expires_in_minutes = settings.INVITATION_LINK_VALIDITY_MINUTES
    realm = get_realm("zulip")
    other_realm = Realm.objects.exclude(string_id="zulip").first()
    user = get_user_by_delivery_email(registered_email, realm)
    host_kwargs = {"HTTP_HOST": realm.host}

    # Password reset emails
    # active account in realm
    result = client.post("/accounts/password/reset/", {"email": registered_email}, **host_kwargs)
    assert result.status_code == 302
    # deactivated user
    change_user_is_active(user, False)
    result = client.post("/accounts/password/reset/", {"email": registered_email}, **host_kwargs)
    assert result.status_code == 302
    change_user_is_active(user, True)
    # account on different realm
    assert other_realm is not None
    result = client.post(
        "/accounts/password/reset/", {"email": registered_email}, HTTP_HOST=other_realm.host
    )
    assert result.status_code == 302
    # no account anywhere
    result = client.post(
        "/accounts/password/reset/", {"email": unregistered_email_1}, **host_kwargs
    )
    assert result.status_code == 302

    # Confirm account email
    result = client.post("/accounts/home/", {"email": unregistered_email_1}, **host_kwargs)
    assert result.status_code == 302

    # Find account email
    result = client.post("/accounts/find/", {"emails": registered_email}, **host_kwargs)
    assert result.status_code == 302

    # New login email
    logged_in = client.login(dev_auth_username=registered_email, realm=realm)
    assert logged_in

    # New user invite and reminder emails
    stream = get_realm_stream("Denmark", user.realm.id)
    result = client.post(
        "/json/invites",
        {
            "invitee_emails": unregistered_email_2,
            "invite_expires_in_minutes": invite_expires_in_minutes,
            "stream_ids": orjson.dumps([stream.id]).decode(),
        },
        **host_kwargs,
    )
    assert result.status_code == 200

    # Verification for new email
    result = client.patch(
        "/json/settings", urllib.parse.urlencode({"email": "*****@*****.**"}), **host_kwargs
    )
    assert result.status_code == 200

    # Email change successful
    key = Confirmation.objects.filter(type=Confirmation.EMAIL_CHANGE).latest("id").confirmation_key
    url = confirmation_url(key, realm, Confirmation.EMAIL_CHANGE)
    user_profile = get_user_by_delivery_email(registered_email, realm)
    result = client.get(url)
    assert result.status_code == 200

    # Reset the email value so we can run this again
    do_change_user_delivery_email(user_profile, registered_email)

    # Follow up day1 day2 emails for normal user
    enqueue_welcome_emails(user_profile)

    # Follow up day1 day2 emails for admin user
    enqueue_welcome_emails(get_user_by_delivery_email("*****@*****.**", realm), realm_creation=True)

    # Realm reactivation email
    do_send_realm_reactivation_email(realm, acting_user=None)

    return redirect(email_page)