from collections.abc import Callable from datetime import timedelta from typing import Any import time_machine from django.utils.timezone import now as timezone_now from typing_extensions import override from zerver.actions.realm_settings import do_set_realm_property from zerver.actions.users import change_user_is_active from zerver.lib.sessions import ( delete_all_deactivated_user_sessions, delete_all_user_sessions, delete_realm_user_sessions, delete_session, delete_user_sessions, get_expirable_session_var, set_expirable_session_var, user_sessions, ) from zerver.lib.test_classes import ZulipTestCase from zerver.models import Realm, UserProfile from zerver.models.realms import get_realm class TestSessions(ZulipTestCase): def do_test_session( self, user: UserProfile, action: Callable[[], Any], realm: Realm, expected_result: bool ) -> None: self.login_user(user) self.assertIn("_auth_user_id", self.client.session) action() if expected_result: result = self.client_get("/", subdomain=realm.subdomain) self.assertEqual(200, result.status_code) self.assertTrue('is_spectator":true' in str(result.content)) else: self.assertIn("_auth_user_id", self.client.session) def test_delete_session(self) -> None: user_profile = self.example_user("hamlet") self.login_user(user_profile) self.assertIn("_auth_user_id", self.client.session) for session in user_sessions(user_profile): delete_session(session) result = self.client_get("/") self.assertEqual(result.status_code, 200) self.assertTrue('is_spectator":true' in str(result.content)) def test_delete_user_sessions(self) -> None: user_profile = self.example_user("hamlet") self.do_test_session( user_profile, lambda: delete_user_sessions(user_profile), get_realm("zulip"), True ) self.do_test_session( self.example_user("othello"), lambda: delete_user_sessions(user_profile), get_realm("zulip"), False, ) def test_delete_realm_user_sessions(self) -> None: realm = get_realm("zulip") self.do_test_session( self.example_user("hamlet"), lambda: delete_realm_user_sessions(realm), get_realm("zulip"), True, ) self.do_test_session( self.mit_user("sipbtest"), lambda: delete_realm_user_sessions(realm), get_realm("zephyr"), False, ) def test_delete_all_user_sessions(self) -> None: self.do_test_session( self.example_user("hamlet"), delete_all_user_sessions, get_realm("zulip"), True, ) lear_realm = get_realm("lear") do_set_realm_property(lear_realm, "enable_spectator_access", True, acting_user=None) self.make_stream( "web_public_stream", realm=lear_realm, is_web_public=True, ) self.do_test_session( self.lear_user("cordelia"), delete_all_user_sessions, lear_realm, True, ) def test_delete_all_deactivated_user_sessions(self) -> None: # Test that no exception is thrown with a logged-out session self.login("othello") self.assertIn("_auth_user_id", self.client.session) self.client_post("/accounts/logout/") delete_all_deactivated_user_sessions() result = self.client_get("/") self.assertEqual(result.status_code, 200) self.assertTrue('is_spectator":true' in str(result.content)) # Test nothing happens to an active user's session self.login("othello") self.assertIn("_auth_user_id", self.client.session) delete_all_deactivated_user_sessions() self.assertIn("_auth_user_id", self.client.session) # Test that a deactivated session gets logged out user_profile_3 = self.example_user("cordelia") self.login_user(user_profile_3) self.assertIn("_auth_user_id", self.client.session) change_user_is_active(user_profile_3, False) with self.assertLogs(level="INFO") as info_logs: delete_all_deactivated_user_sessions() self.assertEqual( info_logs.output, [f"INFO:root:Deactivating session for deactivated user {user_profile_3.id}"], ) result = self.client_get("/") self.assertEqual(result.status_code, 200) self.assertTrue('is_spectator":true' in str(result.content)) class TestExpirableSessionVars(ZulipTestCase): @override def setUp(self) -> None: self.session = self.client.session super().setUp() def test_set_and_get_basic(self) -> None: start_time = timezone_now() with time_machine.travel(start_time, tick=False): set_expirable_session_var( self.session, "test_set_and_get_basic", "some_value", expiry_seconds=10 ) value = get_expirable_session_var(self.session, "test_set_and_get_basic") self.assertEqual(value, "some_value") with time_machine.travel((start_time + timedelta(seconds=11)), tick=False): value = get_expirable_session_var(self.session, "test_set_and_get_basic") self.assertEqual(value, None) def test_set_and_get_with_delete(self) -> None: set_expirable_session_var( self.session, "test_set_and_get_with_delete", "some_value", expiry_seconds=10 ) value = get_expirable_session_var(self.session, "test_set_and_get_with_delete", delete=True) self.assertEqual(value, "some_value") self.assertEqual( get_expirable_session_var(self.session, "test_set_and_get_with_delete"), None ) def test_get_var_not_set(self) -> None: value = get_expirable_session_var( self.session, "test_get_var_not_set", default_value="default" ) self.assertEqual(value, "default") def test_get_var_is_not_expirable(self) -> None: self.session["test_get_var_is_not_expirable"] = 0 with self.assertLogs(level="WARNING") as m: value = get_expirable_session_var( self.session, "test_get_var_is_not_expirable", default_value="default" ) self.assertEqual(value, "default") self.assertIn( "WARNING:root:get_expirable_session_var: error getting test_get_var_is_not_expirable", m.output[0], )