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/"))
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})
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)
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)
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.")
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"})
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)
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)