fix: preserve OAuth users across config reload; fix test isolation
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -146,9 +146,14 @@ def load_users(config: dict) -> dict:
|
|||||||
Returns the new ``users`` dict.
|
Returns the new ``users`` dict.
|
||||||
"""
|
"""
|
||||||
global users
|
global users
|
||||||
|
old_users = dict(users) # snapshot before rebuild
|
||||||
users_cfg = config.get("users", {})
|
users_cfg = config.get("users", {})
|
||||||
if not isinstance(users_cfg, dict):
|
if not isinstance(users_cfg, dict):
|
||||||
users = {}
|
users = {}
|
||||||
|
# Preserve OAuth-provisioned users (password_hash == "") that aren't in config.
|
||||||
|
for username, existing_user in old_users.items():
|
||||||
|
if not existing_user.password_hash and username not in users:
|
||||||
|
users[username] = existing_user
|
||||||
return users
|
return users
|
||||||
|
|
||||||
result: dict = {}
|
result: dict = {}
|
||||||
@@ -166,6 +171,10 @@ def load_users(config: dict) -> dict:
|
|||||||
)
|
)
|
||||||
|
|
||||||
users = result
|
users = result
|
||||||
|
# Preserve OAuth-provisioned users (password_hash == "") that aren't in config.
|
||||||
|
for username, existing_user in old_users.items():
|
||||||
|
if not existing_user.password_hash and username not in users:
|
||||||
|
users[username] = existing_user
|
||||||
logger.info("Loaded %d user(s) from config", len(users))
|
logger.info("Loaded %d user(s) from config", len(users))
|
||||||
return users
|
return users
|
||||||
|
|
||||||
|
|||||||
+18
-4
@@ -3,6 +3,8 @@ import time as time_mod
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from hbd.server import oauth
|
from hbd.server import oauth
|
||||||
|
from hbd.server import users as users_mod
|
||||||
|
from hbd.server.users import User
|
||||||
|
|
||||||
|
|
||||||
CFG_OFF = {}
|
CFG_OFF = {}
|
||||||
@@ -25,6 +27,13 @@ def clear_oauth_states():
|
|||||||
oauth._states.clear()
|
oauth._states.clear()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(autouse=True)
|
||||||
|
def reset_users_dict():
|
||||||
|
original = dict(users_mod.users)
|
||||||
|
yield
|
||||||
|
users_mod.users = original
|
||||||
|
|
||||||
|
|
||||||
def test_is_enabled_when_all_keys_present():
|
def test_is_enabled_when_all_keys_present():
|
||||||
assert oauth.is_enabled(CFG_ON) is True
|
assert oauth.is_enabled(CFG_ON) is True
|
||||||
|
|
||||||
@@ -66,10 +75,6 @@ def test_validate_state_expired(monkeypatch):
|
|||||||
assert oauth.validate_state(state) is False
|
assert oauth.validate_state(state) is False
|
||||||
|
|
||||||
|
|
||||||
from hbd.server import users as users_mod
|
|
||||||
from hbd.server.users import User
|
|
||||||
|
|
||||||
|
|
||||||
def _reset_users(entries=None):
|
def _reset_users(entries=None):
|
||||||
users_mod.users = entries or {}
|
users_mod.users = entries or {}
|
||||||
|
|
||||||
@@ -116,3 +121,12 @@ def test_provision_oauth_user_does_not_overwrite_with_empty():
|
|||||||
user = users_mod.provision_oauth_user("bob", "", "")
|
user = users_mod.provision_oauth_user("bob", "", "")
|
||||||
assert user.full_name == "Bob"
|
assert user.full_name == "Bob"
|
||||||
assert user.avatar == "bob.png"
|
assert user.avatar == "bob.png"
|
||||||
|
|
||||||
|
|
||||||
|
def test_provision_oauth_user_survives_config_reload():
|
||||||
|
_reset_users()
|
||||||
|
users_mod.provision_oauth_user("oauthonly", "OAuth Only", "https://example.com/a.png")
|
||||||
|
assert "oauthonly" in users_mod.users
|
||||||
|
# Reload with empty config — OAuth user should survive
|
||||||
|
users_mod.load_users({})
|
||||||
|
assert "oauthonly" in users_mod.users
|
||||||
|
|||||||
Reference in New Issue
Block a user