Exemple #1
0
    def test_change_email_deactivated_user_realm(self) -> None:
        data = {"email": "*****@*****.**"}
        user_profile = self.example_user("hamlet")
        self.login_user(user_profile)
        url = "/json/settings"
        self.assert_length(mail.outbox, 0)
        result = self.client_patch(url, data)
        self.assert_length(mail.outbox, 1)
        self.assert_json_success(result)
        email_message = mail.outbox[0]
        self.assertEqual(
            email_message.subject,
            "Verify your new email address",
        )
        body = email_message.body
        self.assertIn("We received a request to change the email", body)

        activation_url = [s for s in body.split("\n") if s][2]

        do_deactivate_user(user_profile, acting_user=None)
        response = self.client_get(activation_url)
        self.assertEqual(response.status_code, 401)

        do_deactivate_realm(user_profile.realm, acting_user=None)

        response = self.client_get(activation_url)
        self.assertEqual(response.status_code, 302)
        self.assertTrue(
            response["Location"].endswith("/accounts/deactivated/"))
Exemple #2
0
    def test_bulk_handle_digest_email_skips_deactivated_users(self) -> None:
        """
        A user id may be added to the queue before the user is deactivated. In such a case,
        the function responsible for sending the email should correctly skip them.
        """
        realm = get_realm("zulip")
        hamlet = self.example_user("hamlet")
        user_ids = list(
            UserProfile.objects.filter(is_bot=False,
                                       realm=realm).values_list("id",
                                                                flat=True))

        do_deactivate_user(hamlet, acting_user=None)

        with mock.patch(
                "zerver.lib.digest.enough_traffic",
                return_value=True), mock.patch(
                    "zerver.lib.digest.send_future_email") as mock_send_email:
            bulk_handle_digest_email(user_ids, 1)

        emailed_user_ids = [
            call_args[1]["to_user_ids"][0]
            for call_args in mock_send_email.call_args_list
        ]

        self.assertEqual(
            set(emailed_user_ids),
            {user_id
             for user_id in user_ids if user_id != hamlet.id})
Exemple #3
0
def deactivate_user_own_backend(request: HttpRequest,
                                user_profile: UserProfile) -> HttpResponse:
    if UserProfile.objects.filter(realm=user_profile.realm,
                                  is_active=True).count() == 1:
        raise CannotDeactivateLastUserError(is_last_owner=False)
    if user_profile.is_realm_owner and check_last_owner(user_profile):
        raise CannotDeactivateLastUserError(is_last_owner=True)

    do_deactivate_user(user_profile, acting_user=user_profile)
    return json_success(request)
Exemple #4
0
    def _test_remove_muted_user_valid_data(self,
                                           deactivate_user: bool = False
                                           ) -> None:
        hamlet = self.example_user("hamlet")
        self.login_user(hamlet)
        cordelia = self.example_user("cordelia")
        mute_time = datetime(2021, 1, 1, tzinfo=timezone.utc)

        if deactivate_user:
            do_deactivate_user(cordelia, acting_user=None)

        with mock.patch("zerver.views.muting.timezone_now",
                        return_value=mute_time):
            url = f"/api/v1/users/me/muted_users/{cordelia.id}"
            result = self.api_post(hamlet, url)
            self.assert_json_success(result)

        with mock.patch("zerver.actions.muted_users.timezone_now",
                        return_value=mute_time):
            # To test that `RealmAuditLog` entry has correct `event_time`.
            url = f"/api/v1/users/me/muted_users/{cordelia.id}"
            result = self.api_delete(hamlet, url)

        self.assert_json_success(result)
        self.assertNotIn(
            {
                "id": cordelia.id,
                "timestamp": datetime_to_timestamp(mute_time),
            },
            get_user_mutes(hamlet),
        )
        self.assertIsNone(get_mute_object(hamlet, cordelia))

        audit_log_entries = list(
            RealmAuditLog.objects.filter(acting_user=hamlet,
                                         modified_user=hamlet).values_list(
                                             "event_type", "event_time",
                                             "extra_data").order_by("id"))
        self.assert_length(audit_log_entries, 2)
        audit_log_entry = audit_log_entries[1]
        self.assertEqual(
            audit_log_entry,
            (
                RealmAuditLog.USER_UNMUTED,
                mute_time,
                orjson.dumps({
                    "unmuted_user_id": cordelia.id
                }).decode(),
            ),
        )
 def test_user_activation(self) -> None:
     realm = get_realm("zulip")
     now = timezone_now()
     user = do_create_user("email",
                           "password",
                           realm,
                           "full_name",
                           acting_user=None)
     do_deactivate_user(user, acting_user=user)
     do_activate_mirror_dummy_user(user, acting_user=user)
     do_deactivate_user(user, acting_user=user)
     do_reactivate_user(user, acting_user=user)
     self.assertEqual(
         RealmAuditLog.objects.filter(event_time__gte=now).count(), 6)
     event_types = list(
         RealmAuditLog.objects.filter(
             realm=realm,
             acting_user=user,
             modified_user=user,
             modified_stream=None,
             event_time__gte=now,
             event_time__lte=now + timedelta(minutes=60),
         ).order_by("event_time").values_list("event_type", flat=True))
     self.assertEqual(
         event_types,
         [
             RealmAuditLog.USER_CREATED,
             RealmAuditLog.USER_DEACTIVATED,
             RealmAuditLog.USER_ACTIVATED,
             RealmAuditLog.USER_DEACTIVATED,
             RealmAuditLog.USER_REACTIVATED,
         ],
     )
     for event in RealmAuditLog.objects.filter(
             realm=realm,
             acting_user=user,
             modified_user=user,
             modified_stream=None,
             event_time__gte=now,
             event_time__lte=now + timedelta(minutes=60),
     ):
         extra_data = orjson.loads(assert_is_not_none(event.extra_data))
         self.check_role_count_schema(extra_data[RealmAuditLog.ROLE_COUNT])
         self.assertNotIn(RealmAuditLog.OLD_VALUE, extra_data)
Exemple #6
0
    def handle(self, *args: Any, **options: Any) -> None:
        realm = self.get_realm(options)
        user_profile = self.get_user(options["email"], realm)

        print(
            f"Deactivating {user_profile.full_name} ({user_profile.delivery_email}) - {user_profile.realm.string_id}"
        )
        print(
            f"{user_profile.delivery_email} has the following active sessions:"
        )
        for session in user_sessions(user_profile):
            print(session.expire_date, session.get_decoded())
        print("")
        print("{} has {} active bots that will also be deactivated.".format(
            user_profile.delivery_email,
            get_active_bots_owned_by_user(user_profile).count(),
        ))

        if not options["for_real"]:
            raise CommandError(
                "This was a dry run. Pass -f to actually deactivate.")

        do_deactivate_user(user_profile, acting_user=None)
        print("Sessions deleted, user deactivated.")
Exemple #7
0
    def test_single_user_get(self) -> None:
        reset_emails_in_zulip_realm()

        # First, we set up the test with some data
        user = self.example_user("othello")
        self.login_user(user)
        result = self.client_post("/json/users/me/presence",
                                  {"status": "active"})
        result = self.client_post("/json/users/me/presence",
                                  {"status": "active"},
                                  HTTP_USER_AGENT="ZulipDesktop/1.0")
        result = self.api_post(
            user,
            "/api/v1/users/me/presence",
            {"status": "idle"},
            HTTP_USER_AGENT="ZulipAndroid/1.0",
        )
        self.assert_json_success(result)

        # Check some error conditions
        result = self.client_get("/json/users/[email protected]/presence")
        self.assert_json_error(result, "No such user")

        result = self.client_get("/json/users/[email protected]/presence")
        self.assert_json_error(result,
                               "No presence data for [email protected]")

        cordelia = self.example_user("cordelia")
        result = self.client_get(f"/json/users/{cordelia.id}/presence")
        self.assert_json_error(result, f"No presence data for {cordelia.id}")

        do_deactivate_user(self.example_user("cordelia"), acting_user=None)
        result = self.client_get("/json/users/[email protected]/presence")
        self.assert_json_error(result, "No such user")

        result = self.client_get(f"/json/users/{cordelia.id}/presence")
        self.assert_json_error(result, "No such user")

        result = self.client_get("/json/users/[email protected]/presence")
        self.assert_json_error(result,
                               "Presence is not supported for bot users.")

        sipbtest = self.mit_user("sipbtest")
        self.login_user(sipbtest)
        result = self.client_get("/json/users/[email protected]/presence",
                                 subdomain="zephyr")
        self.assert_json_error(result, "No such user")

        othello = self.example_user("othello")
        result = self.client_get(f"/json/users/{othello.id}/presence",
                                 subdomain="zephyr")
        self.assert_json_error(result, "No such user")

        # Then, we check everything works
        self.login("hamlet")
        result = self.client_get("/json/users/[email protected]/presence")
        result_dict = result.json()
        self.assertEqual(set(result_dict["presence"].keys()),
                         {"ZulipAndroid", "website", "aggregated"})
        self.assertEqual(set(result_dict["presence"]["website"].keys()),
                         {"status", "timestamp"})

        result = self.client_get(f"/json/users/{othello.id}/presence")
        result_dict = result.json()
        self.assertEqual(set(result_dict["presence"].keys()),
                         {"ZulipAndroid", "website", "aggregated"})
        self.assertEqual(set(result_dict["presence"]["website"].keys()),
                         {"status", "timestamp"})
Exemple #8
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)
Exemple #9
0
def _deactivate_user_profile_backend(request: HttpRequest,
                                     user_profile: UserProfile,
                                     target: UserProfile) -> HttpResponse:
    do_deactivate_user(target, acting_user=user_profile)
    return json_success(request)