Esempio n. 1
0
def json_change_notify_settings(
        request: HttpRequest, user_profile: UserProfile,
        enable_stream_desktop_notifications: Optional[bool]=REQ(validator=check_bool, default=None),
        enable_stream_email_notifications: Optional[bool]=REQ(validator=check_bool, default=None),
        enable_stream_push_notifications: Optional[bool]=REQ(validator=check_bool, default=None),
        enable_stream_audible_notifications: Optional[bool]=REQ(validator=check_bool, default=None),
        notification_sound: Optional[str]=REQ(validator=check_string, default=None),
        enable_desktop_notifications: Optional[bool]=REQ(validator=check_bool, default=None),
        enable_sounds: Optional[bool]=REQ(validator=check_bool, default=None),
        enable_offline_email_notifications: Optional[bool]=REQ(validator=check_bool, default=None),
        enable_offline_push_notifications: Optional[bool]=REQ(validator=check_bool, default=None),
        enable_online_push_notifications: Optional[bool]=REQ(validator=check_bool, default=None),
        enable_digest_emails: Optional[bool]=REQ(validator=check_bool, default=None),
        enable_login_emails: Optional[bool]=REQ(validator=check_bool, default=None),
        message_content_in_email_notifications: Optional[bool]=REQ(validator=check_bool, default=None),
        pm_content_in_desktop_notifications: Optional[bool]=REQ(validator=check_bool, default=None),
        desktop_icon_count_display: Optional[int]=REQ(validator=check_int, default=None),
        realm_name_in_notifications: Optional[bool]=REQ(validator=check_bool, default=None)
) -> HttpResponse:
    result = {}

    # Stream notification settings.

    if (notification_sound is not None and
            notification_sound not in get_available_notification_sounds()):
        raise JsonableError(_("Invalid notification sound '%s'") % (notification_sound,))

    req_vars = {k: v for k, v in list(locals().items()) if k in user_profile.notification_setting_types}

    for k, v in list(req_vars.items()):
        if v is not None and getattr(user_profile, k) != v:
            do_change_notification_settings(user_profile, k, v)
            result[k] = v

    return json_success(result)
Esempio n. 2
0
def json_change_notify_settings(request, user_profile,
                                enable_stream_desktop_notifications=REQ(validator=check_bool,
                                                                        default=None),
                                enable_stream_push_notifications=REQ(validator=check_bool,
                                                                     default=None),
                                enable_stream_sounds=REQ(validator=check_bool,
                                                         default=None),
                                enable_desktop_notifications=REQ(validator=check_bool,
                                                                 default=None),
                                enable_sounds=REQ(validator=check_bool,
                                                  default=None),
                                enable_offline_email_notifications=REQ(validator=check_bool,
                                                                       default=None),
                                enable_offline_push_notifications=REQ(validator=check_bool,
                                                                      default=None),
                                enable_online_push_notifications=REQ(validator=check_bool,
                                                                     default=None),
                                enable_digest_emails=REQ(validator=check_bool,
                                                         default=None),
                                pm_content_in_desktop_notifications=REQ(validator=check_bool,
                                                                        default=None)):
    # type: (HttpRequest, UserProfile, Optional[bool], Optional[bool], Optional[bool], Optional[bool], Optional[bool], Optional[bool], Optional[bool], Optional[bool], Optional[bool], Optional[bool]) -> HttpResponse
    result = {}

    # Stream notification settings.

    req_vars = {k: v for k, v in list(locals().items()) if k in user_profile.notification_setting_types}

    for k, v in list(req_vars.items()):
        if v is not None and getattr(user_profile, k) != v:
            do_change_notification_settings(user_profile, k, v)
            result[k] = v

    return json_success(result)
Esempio n. 3
0
    def test_change_notification_settings(self) -> None:
        user = self.example_user("hamlet")
        value: Union[bool, int, str]
        for setting, v in user.notification_setting_types.items():
            if setting == "notification_sound":
                value = "ding"
            elif setting == "desktop_icon_count_display":
                value = 3
            else:
                value = False
            now = timezone_now()

            old_value = getattr(user, setting)
            do_change_notification_settings(user, setting, value, acting_user=user)
            expected_extra_data = {
                RealmAuditLog.OLD_VALUE: old_value,
                RealmAuditLog.NEW_VALUE: value,
                "property": setting,
            }
            self.assertEqual(
                RealmAuditLog.objects.filter(
                    realm=user.realm,
                    event_type=RealmAuditLog.USER_NOTIFICATION_SETTINGS_CHANGED,
                    event_time__gte=now,
                    acting_user=user,
                    modified_user=user,
                    extra_data=orjson.dumps(expected_extra_data).decode(),
                ).count(),
                1,
            )
            self.assertEqual(getattr(user, setting), value)
Esempio n. 4
0
def json_change_notify_settings(
        request: HttpRequest, user_profile: UserProfile,
        enable_stream_desktop_notifications: Optional[bool]=REQ(validator=check_bool, default=None),
        enable_stream_email_notifications: Optional[bool]=REQ(validator=check_bool, default=None),
        enable_stream_push_notifications: Optional[bool]=REQ(validator=check_bool, default=None),
        enable_stream_sounds: Optional[bool]=REQ(validator=check_bool, default=None),
        enable_desktop_notifications: Optional[bool]=REQ(validator=check_bool, default=None),
        enable_sounds: Optional[bool]=REQ(validator=check_bool, default=None),
        enable_offline_email_notifications: Optional[bool]=REQ(validator=check_bool, default=None),
        enable_offline_push_notifications: Optional[bool]=REQ(validator=check_bool, default=None),
        enable_online_push_notifications: Optional[bool]=REQ(validator=check_bool, default=None),
        enable_digest_emails: Optional[bool]=REQ(validator=check_bool, default=None),
        enable_login_emails: Optional[bool]=REQ(validator=check_bool, default=None),
        message_content_in_email_notifications: Optional[bool]=REQ(validator=check_bool, default=None),
        pm_content_in_desktop_notifications: Optional[bool]=REQ(validator=check_bool, default=None),
        realm_name_in_notifications: Optional[bool]=REQ(validator=check_bool, default=None)
) -> HttpResponse:
    result = {}

    # Stream notification settings.

    req_vars = {k: v for k, v in list(locals().items()) if k in user_profile.notification_setting_types}

    for k, v in list(req_vars.items()):
        if v is not None and getattr(user_profile, k) != v:
            do_change_notification_settings(user_profile, k, v)
            result[k] = v

    return json_success(result)
Esempio n. 5
0
    def handle(self, **options):
        # type: (**str) -> None
        realm = self.get_realm(options)
        if realm is None and options["users"] is None:
            self.print_help("./manage.py", "turn_off_digests")
            exit(1)

        if realm and not options["users"]:
            user_profiles = UserProfile.objects.filter(realm=realm)
        else:
            emails = set(
                [email.strip() for email in options["users"].split(",")])
            user_profiles = []
            for email in emails:
                user_profiles.append(self.get_user(email, realm))

        print("Turned off digest emails for:")
        for user_profile in user_profiles:
            already_disabled_prefix = ""
            if user_profile.enable_digest_emails:
                do_change_notification_settings(user_profile,
                                                'enable_digest_emails', False)
            else:
                already_disabled_prefix = "(already off) "
            print("%s%s <%s>" % (already_disabled_prefix,
                                 user_profile.full_name, user_profile.email))
Esempio n. 6
0
    def test_enable_login_emails_user_setting(self) -> None:
        user = self.example_user("hamlet")
        mock_time = datetime.datetime(year=2018,
                                      month=1,
                                      day=1,
                                      tzinfo=datetime.timezone.utc)

        user.timezone = "US/Pacific"
        user.date_joined = mock_time - datetime.timedelta(
            seconds=JUST_CREATED_THRESHOLD + 1)
        user.save()

        do_change_notification_settings(user,
                                        "enable_login_emails",
                                        False,
                                        acting_user=None)
        self.assertFalse(user.enable_login_emails)
        with mock.patch("zerver.signals.timezone_now", return_value=mock_time):
            self.login_user(user)
        self.assert_length(mail.outbox, 0)

        do_change_notification_settings(user,
                                        "enable_login_emails",
                                        True,
                                        acting_user=None)
        self.assertTrue(user.enable_login_emails)
        with mock.patch("zerver.signals.timezone_now", return_value=mock_time):
            self.login_user(user)
        self.assert_length(mail.outbox, 1)
Esempio n. 7
0
 def test_message_content_disabled_in_missed_message_notifications(self) -> None:
     # Test when user disabled message content in email notifications.
     do_change_notification_settings(self.example_user("hamlet"),
                                     "message_content_in_email_notifications", False)
     self._extra_context_in_missed_stream_messages_mention(False, show_message_content=False)
     mail.outbox = []
     self._extra_context_in_personal_missed_stream_messages(False, show_message_content=False)
     mail.outbox = []
     self._extra_context_in_huddle_missed_stream_messages_two_others(False, show_message_content=False)
Esempio n. 8
0
 def test_message_content_disabled_in_missed_message_notifications(self) -> None:
     # Test when user disabled message content in email notifications.
     do_change_notification_settings(self.example_user("hamlet"),
                                     "message_content_in_email_notifications", False)
     self._extra_context_in_missed_stream_messages_mention(False, show_message_content=False)
     mail.outbox = []
     self._extra_context_in_personal_missed_stream_messages(False, show_message_content=False)
     mail.outbox = []
     self._extra_context_in_huddle_missed_stream_messages_two_others(False, show_message_content=False)
    def handle(self, **options: str) -> None:
        realm = self.get_realm(options)
        user_profiles = self.get_users(options, realm)

        print("Turned off digest emails for:")
        for user_profile in user_profiles:
            already_disabled_prefix = ""
            if user_profile.enable_digest_emails:
                do_change_notification_settings(user_profile, 'enable_digest_emails', False)
            else:
                already_disabled_prefix = "(already off) "
            print(f"{already_disabled_prefix}{user_profile.full_name} <{user_profile.delivery_email}>")
Esempio n. 10
0
    def handle(self, **options: str) -> None:
        realm = self.get_realm(options)
        user_profiles = self.get_users(options, realm)

        print("Turned off digest emails for:")
        for user_profile in user_profiles:
            already_disabled_prefix = ""
            if user_profile.enable_digest_emails:
                do_change_notification_settings(user_profile, 'enable_digest_emails', False)
            else:
                already_disabled_prefix = "(already off) "
            print("%s%s <%s>" % (already_disabled_prefix, user_profile.full_name,
                                 user_profile.email))
Esempio n. 11
0
    def handle(self, **options):
        # type: (**str) -> None
        realm = self.get_realm(options)
        user_profiles = self.get_users(options, realm)

        print("Turned off digest emails for:")
        for user_profile in user_profiles:
            already_disabled_prefix = ""
            if user_profile.enable_digest_emails:
                do_change_notification_settings(user_profile, 'enable_digest_emails', False)
            else:
                already_disabled_prefix = "(already off) "
            print("%s%s <%s>" % (already_disabled_prefix, user_profile.full_name,
                                 user_profile.email))
Esempio n. 12
0
    def test_realm_message_content_allowed_in_email_notifications(self) -> None:
        user = self.example_user("hamlet")

        # When message content is allowed at realm level
        realm = get_realm("zulip")
        realm.message_content_allowed_in_email_notifications = True
        realm.save(update_fields=['message_content_allowed_in_email_notifications'])

        # Emails have missed message content when message content is enabled by the user
        do_change_notification_settings(user, "message_content_in_email_notifications", True)
        mail.outbox = []
        self._extra_context_in_personal_missed_stream_messages(False, show_message_content=True)

        # Emails don't have missed message content when message content is disabled by the user
        do_change_notification_settings(user, "message_content_in_email_notifications", False)
        mail.outbox = []
        self._extra_context_in_personal_missed_stream_messages(False, show_message_content=False)

        # When message content is not allowed at realm level
        # Emails don't have missed message irrespective of message content setting of the user
        realm = get_realm("zulip")
        realm.message_content_allowed_in_email_notifications = False
        realm.save(update_fields=['message_content_allowed_in_email_notifications'])

        do_change_notification_settings(user, "message_content_in_email_notifications", True)
        mail.outbox = []
        self._extra_context_in_personal_missed_stream_messages(False, show_message_content=False)

        do_change_notification_settings(user, "message_content_in_email_notifications", False)
        mail.outbox = []
        self._extra_context_in_personal_missed_stream_messages(False, show_message_content=False)
Esempio n. 13
0
    def test_realm_message_content_allowed_in_email_notifications(self) -> None:
        user = self.example_user("hamlet")

        # When message content is allowed at realm level
        realm = get_realm("zulip")
        realm.message_content_allowed_in_email_notifications = True
        realm.save(update_fields=['message_content_allowed_in_email_notifications'])

        # Emails have missed message content when message content is enabled by the user
        do_change_notification_settings(user, "message_content_in_email_notifications", True)
        mail.outbox = []
        self._extra_context_in_personal_missed_stream_messages(False, show_message_content=True)

        # Emails don't have missed message content when message content is disabled by the user
        do_change_notification_settings(user, "message_content_in_email_notifications", False)
        mail.outbox = []
        self._extra_context_in_personal_missed_stream_messages(False, show_message_content=False)

        # When message content is not allowed at realm level
        # Emails don't have missed message irrespective of message content setting of the user
        realm = get_realm("zulip")
        realm.message_content_allowed_in_email_notifications = False
        realm.save(update_fields=['message_content_allowed_in_email_notifications'])

        do_change_notification_settings(user, "message_content_in_email_notifications", True)
        mail.outbox = []
        self._extra_context_in_personal_missed_stream_messages(False, show_message_content=False)

        do_change_notification_settings(user, "message_content_in_email_notifications", False)
        mail.outbox = []
        self._extra_context_in_personal_missed_stream_messages(False, show_message_content=False)
Esempio n. 14
0
    def test_enable_login_emails_user_setting(self) -> None:
        user = self.example_user('hamlet')
        utc = get_timezone('utc')
        mock_time = datetime.datetime(year=2018, month=1, day=1, tzinfo=utc)

        user.timezone = 'US/Pacific'
        user.date_joined = mock_time - datetime.timedelta(seconds=JUST_CREATED_THRESHOLD + 1)
        user.save()

        do_change_notification_settings(user, "enable_login_emails", False)
        self.assertFalse(user.enable_login_emails)
        with mock.patch('zerver.signals.timezone_now', return_value=mock_time):
            self.login(user.email)
        self.assertEqual(len(mail.outbox), 0)

        do_change_notification_settings(user, "enable_login_emails", True)
        self.assertTrue(user.enable_login_emails)
        with mock.patch('zerver.signals.timezone_now', return_value=mock_time):
            self.login(user.email)
        self.assertEqual(len(mail.outbox), 1)
Esempio n. 15
0
    def handle(self, **options):
        # type: (**str) -> None
        if options["string_id"] is None and options["users"] is None:
            self.print_help("./manage.py", "turn_off_digests")
            exit(1)

        if options["string_id"]:
            realm = get_realm(options["string_id"])
            user_profiles = UserProfile.objects.filter(realm=realm)
        else:
            emails = set([email.strip() for email in options["users"].split(",")])
            user_profiles = []
            for email in emails:
                user_profiles.append(get_user_profile_by_email(email))

        print("Turned off digest emails for:")
        for user_profile in user_profiles:
            already_disabled_prefix = ""
            if user_profile.enable_digest_emails:
                do_change_notification_settings(user_profile, 'enable_digest_emails', False)
            else:
                already_disabled_prefix = "(already off) "
            print("%s%s <%s>" % (already_disabled_prefix, user_profile.full_name,
                                 user_profile.email))
Esempio n. 16
0
def do_missedmessage_unsubscribe(user_profile):
    # type: (UserProfile) -> None
    do_change_notification_settings(user_profile, 'enable_offline_email_notifications', False)
Esempio n. 17
0
def do_login_unsubscribe(user_profile: UserProfile) -> None:
    do_change_notification_settings(user_profile, 'enable_login_emails', False, acting_user=user_profile)
Esempio n. 18
0
def do_missedmessage_unsubscribe(user_profile: UserProfile) -> None:
    do_change_notification_settings(user_profile, 'enable_offline_email_notifications', False, acting_user=user_profile)
Esempio n. 19
0
def do_marketing_unsubscribe(user_profile: UserProfile) -> None:
    do_change_notification_settings(user_profile,
                                    "enable_marketing_emails",
                                    False,
                                    acting_user=user_profile)
Esempio n. 20
0
def json_change_settings(
    request: HttpRequest,
    user_profile: UserProfile,
    full_name: str = REQ(default=""),
    email: str = REQ(default=""),
    old_password: str = REQ(default=""),
    new_password: str = REQ(default=""),
    twenty_four_hour_time: Optional[bool] = REQ(json_validator=check_bool,
                                                default=None),
    dense_mode: Optional[bool] = REQ(json_validator=check_bool, default=None),
    starred_message_counts: Optional[bool] = REQ(json_validator=check_bool,
                                                 default=None),
    fluid_layout_width: Optional[bool] = REQ(json_validator=check_bool,
                                             default=None),
    high_contrast_mode: Optional[bool] = REQ(json_validator=check_bool,
                                             default=None),
    color_scheme: Optional[int] = REQ(json_validator=check_int_in(
        UserProfile.COLOR_SCHEME_CHOICES),
                                      default=None),
    translate_emoticons: Optional[bool] = REQ(json_validator=check_bool,
                                              default=None),
    default_language: Optional[str] = REQ(default=None),
    default_view: Optional[str] = REQ(
        str_validator=check_string_in(default_view_options), default=None),
    left_side_userlist: Optional[bool] = REQ(json_validator=check_bool,
                                             default=None),
    emojiset: Optional[str] = REQ(
        str_validator=check_string_in(emojiset_choices), default=None),
    demote_inactive_streams: Optional[int] = REQ(json_validator=check_int_in(
        UserProfile.DEMOTE_STREAMS_CHOICES),
                                                 default=None),
    timezone: Optional[str] = REQ(str_validator=check_string_in(
        pytz.all_timezones_set),
                                  default=None),
    email_notifications_batching_period_seconds: Optional[int] = REQ(
        json_validator=check_int, default=None),
    enable_stream_desktop_notifications: Optional[bool] = REQ(
        json_validator=check_bool, default=None),
    enable_stream_email_notifications: Optional[bool] = REQ(
        json_validator=check_bool, default=None),
    enable_stream_push_notifications: Optional[bool] = REQ(
        json_validator=check_bool, default=None),
    enable_stream_audible_notifications: Optional[bool] = REQ(
        json_validator=check_bool, default=None),
    wildcard_mentions_notify: Optional[bool] = REQ(json_validator=check_bool,
                                                   default=None),
    notification_sound: Optional[str] = REQ(default=None),
    enable_desktop_notifications: Optional[bool] = REQ(
        json_validator=check_bool, default=None),
    enable_sounds: Optional[bool] = REQ(json_validator=check_bool,
                                        default=None),
    enable_offline_email_notifications: Optional[bool] = REQ(
        json_validator=check_bool, default=None),
    enable_offline_push_notifications: Optional[bool] = REQ(
        json_validator=check_bool, default=None),
    enable_online_push_notifications: Optional[bool] = REQ(
        json_validator=check_bool, default=None),
    enable_digest_emails: Optional[bool] = REQ(json_validator=check_bool,
                                               default=None),
    enable_login_emails: Optional[bool] = REQ(json_validator=check_bool,
                                              default=None),
    enable_marketing_emails: Optional[bool] = REQ(json_validator=check_bool,
                                                  default=None),
    message_content_in_email_notifications: Optional[bool] = REQ(
        json_validator=check_bool, default=None),
    pm_content_in_desktop_notifications: Optional[bool] = REQ(
        json_validator=check_bool, default=None),
    desktop_icon_count_display: Optional[int] = REQ(json_validator=check_int,
                                                    default=None),
    realm_name_in_notifications: Optional[bool] = REQ(
        json_validator=check_bool, default=None),
    presence_enabled: Optional[bool] = REQ(json_validator=check_bool,
                                           default=None),
    enter_sends: Optional[bool] = REQ(json_validator=check_bool, default=None),
) -> HttpResponse:
    # We can't use REQ for this widget because
    # get_available_language_codes requires provisioning to be
    # complete.
    if default_language is not None and default_language not in get_available_language_codes(
    ):
        raise JsonableError(_("Invalid default_language"))

    if (notification_sound is not None
            and notification_sound not in get_available_notification_sounds()
            and notification_sound != "none"):
        raise JsonableError(
            _("Invalid notification sound '{}'").format(notification_sound))

    if email_notifications_batching_period_seconds is not None and (
            email_notifications_batching_period_seconds <= 0
            or email_notifications_batching_period_seconds > 7 * 24 * 60 * 60):
        # We set a limit of one week for the batching period
        raise JsonableError(
            _("Invalid email batching period: {} seconds").format(
                email_notifications_batching_period_seconds))

    if new_password != "":
        return_data: Dict[str, Any] = {}
        if email_belongs_to_ldap(user_profile.realm,
                                 user_profile.delivery_email):
            raise JsonableError(_("Your Zulip password is managed in LDAP"))

        try:
            if not authenticate(
                    request,
                    username=user_profile.delivery_email,
                    password=old_password,
                    realm=user_profile.realm,
                    return_data=return_data,
            ):
                raise JsonableError(_("Wrong password!"))
        except RateLimited as e:
            assert e.secs_to_freedom is not None
            secs_to_freedom = int(e.secs_to_freedom)
            raise JsonableError(
                _("You're making too many attempts! Try again in {} seconds.").
                format(secs_to_freedom), )

        if not check_password_strength(new_password):
            raise JsonableError(_("New password is too weak!"))

        do_change_password(user_profile, new_password)
        # In Django 1.10, password changes invalidates sessions, see
        # https://docs.djangoproject.com/en/1.10/topics/auth/default/#session-invalidation-on-password-change
        # for details. To avoid this logging the user out of their own
        # session (which would provide a confusing UX at best), we
        # update the session hash here.
        update_session_auth_hash(request, user_profile)
        # We also save the session to the DB immediately to mitigate
        # race conditions. In theory, there is still a race condition
        # and to completely avoid it we will have to use some kind of
        # mutex lock in `django.contrib.auth.get_user` where session
        # is verified. To make that lock work we will have to control
        # the AuthenticationMiddleware which is currently controlled
        # by Django,
        request.session.save()

    result: Dict[str, Any] = {}
    new_email = email.strip()
    if user_profile.delivery_email != new_email and new_email != "":
        if user_profile.realm.email_changes_disabled and not user_profile.is_realm_admin:
            raise JsonableError(
                _("Email address changes are disabled in this organization."))

        error = validate_email_is_valid(
            new_email,
            get_realm_email_validator(user_profile.realm),
        )
        if error:
            raise JsonableError(error)

        try:
            validate_email_not_already_in_realm(
                user_profile.realm,
                new_email,
                verbose=False,
            )
        except ValidationError as e:
            raise JsonableError(e.message)

        do_start_email_change_process(user_profile, new_email)

    if user_profile.full_name != full_name and full_name.strip() != "":
        if name_changes_disabled(
                user_profile.realm) and not user_profile.is_realm_admin:
            # Failingly silently is fine -- they can't do it through the UI, so
            # they'd have to be trying to break the rules.
            pass
        else:
            # Note that check_change_full_name strips the passed name automatically
            check_change_full_name(user_profile, full_name, user_profile)

    # Loop over user_profile.property_types
    request_settings = {
        k: v
        for k, v in list(locals().items()) if k in user_profile.property_types
    }
    for k, v in list(request_settings.items()):
        if v is not None and getattr(user_profile, k) != v:
            do_set_user_display_setting(user_profile, k, v)

    req_vars = {
        k: v
        for k, v in list(locals().items())
        if k in user_profile.notification_setting_types
    }

    for k, v in list(req_vars.items()):
        if v is not None and getattr(user_profile, k) != v:
            do_change_notification_settings(user_profile,
                                            k,
                                            v,
                                            acting_user=user_profile)

    if timezone is not None and user_profile.timezone != timezone:
        do_set_user_display_setting(user_profile, "timezone", timezone)

    # TODO: Do this more generally.
    from zerver.lib.request import get_request_notes

    request_notes = get_request_notes(request)
    for req_var in request.POST:
        if req_var not in request_notes.processed_parameters:
            request_notes.ignored_parameters.add(req_var)

    if len(request_notes.ignored_parameters) > 0:
        result["ignored_parameters_unsupported"] = list(
            request_notes.ignored_parameters)

    return json_success(result)
Esempio n. 21
0
def do_digest_unsubscribe(user_profile: UserProfile) -> None:
    do_change_notification_settings(user_profile, 'enable_digest_emails', False)
Esempio n. 22
0
def do_digest_unsubscribe(user_profile):
    # type: (UserProfile) -> None
    do_change_notification_settings(user_profile, 'enable_digest_emails', False)
Esempio n. 23
0
def do_login_unsubscribe(user_profile: UserProfile) -> None:
    do_change_notification_settings(user_profile, 'enable_login_emails', False)