From 1db5e92ca3e3568ed1b4b93fd513703aa07b2db7 Mon Sep 17 00:00:00 2001 From: ievgenii vdovenko Date: Sat, 26 Jul 2025 15:08:32 +0200 Subject: [PATCH] tests: adds test for auth ghost user; refactors accessing default/ghost user --- rhodecode/model/db.py | 23 ++++++++++---------- rhodecode/tests/lib/test_auth.py | 36 +++++++++++++++++++++++++++++++- 2 files changed, 46 insertions(+), 13 deletions(-) diff --git a/rhodecode/model/db.py b/rhodecode/model/db.py index f00f9169..21fb8a6a 100644 --- a/rhodecode/model/db.py +++ b/rhodecode/model/db.py @@ -1261,15 +1261,6 @@ class User(Base, BaseModel): raise Exception("FATAL: Missing administrative account!") return user - @classmethod - def get_ghost_user(cls): - stmt = cls.select().where(User.username == User.GHOST_USER) - user = cls.scalars(stmt).first() - - if user is None: - raise Exception("FATAL: Missing ghost user!") - return user - @classmethod def get_all_super_admins(cls, only_active=False): """ @@ -1291,13 +1282,21 @@ class User(Base, BaseModel): qry = qry.filter(User.active == true()) return [x.user_id for x in qry] + @classmethod + def get_ghost_user(cls, cache=False, refresh=False): + return cls._get_system_user(username=User.GHOST_USER, cache=cache, refresh=refresh) + @classmethod def get_default_user(cls, cache=False, refresh=False): - user = User.get_by_username(User.DEFAULT_USER, cache=cache) + return cls._get_system_user(username=User.DEFAULT_USER, cache=cache, refresh=refresh) + + @classmethod + def _get_system_user(cls, username, cache=False, refresh=False): + user = User.get_by_username(username, cache=cache) if user is None: - raise Exception("FATAL: Missing default account!") + raise Exception("FATAL: Missing %s account!" % username) if refresh: - # The default user might be based on outdated state which + # The 'system' user might be based on outdated state which # has been loaded from the cache. # A call to refresh() ensures that the # latest state from the database is used. diff --git a/rhodecode/tests/lib/test_auth.py b/rhodecode/tests/lib/test_auth.py index 707afbaf..40247865 100644 --- a/rhodecode/tests/lib/test_auth.py +++ b/rhodecode/tests/lib/test_auth.py @@ -572,7 +572,12 @@ def test_auth_user_get_cookie_store_for_normal_user(user_util): assert auth_user.get_cookie_store() == expected_data -def test_auth_user_get_cookie_store_for_default_user(): +def test_auth_user_get_cookie_store_for_default_user(baseapp): + # NOTE: Importing baseapp is important—if this test runs in isolation, + # the User class may not be bound to the Session, resulting in an error. + # Importing baseapp ensures the session is explicitly bound for this test. + # When tests are run in batch, this binding often happens implicitly as a side effect of other tests or fixtures. + default_user = User.get_default_user() auth_user = auth.AuthUser() expected_data = { @@ -584,6 +589,35 @@ def test_auth_user_get_cookie_store_for_default_user(): assert auth_user.get_cookie_store() == expected_data +def test_auth_not_allowed_for_ghost_user(baseapp): + # NOTE: Importing baseapp is important—if this test runs in isolation, + # the User class may not be bound to the Session, resulting in an error. + # Importing baseapp ensures the session is explicitly bound for this test. + # When tests are run in batch, this binding often happens implicitly as a side effect of other tests or fixtures. + ghost = User.get_ghost_user() + + try: + # Under normal conditions, the ghost user should not have a password and should remain inactive. + ghost.password = "" + ghost.active = True + Session().commit() + + auth_user = auth.AuthUser(user_id=ghost.user_id) + expected_data = { + "username": User.GHOST_USER, + "user_id": ghost.user_id, + "password": md5_safe(ghost.password), + "is_authenticated": False, + } + assert auth_user.get_cookie_store() == expected_data + finally: + # Important: Clean up after this test since the ghost user is unique in the system. + # Not cleaning up could affect other tests or parts of the application. + ghost.password = None + ghost.active = False + Session().commit() + + def get_permissions(user, **kwargs): """ Utility filling in useful defaults into the call to `_cached_perms_data`.