Best-effort serve for bad stored config + admin self-lockout guards
Serving never refuses to start because of stored realm config: the registry build sanitizes best-effort and warns — misfiled origin entries are reclassified (a cross-domain origins entry is served as a related origin) or dropped, collisions resolve first-come-wins, over-cap related lists truncate, unsalvageable realms are skipped. Fixing the stored config stays the admin interface's job, and it stays reachable on any working realm. Only a config with no servable realm at all is fatal. Admin realm writes stay strict and gain self-lockout guards: an update that would leave the admin's current host unable to run ceremonies for the realm they are on is refused (unless an auth host takes over ceremonies), and deleting the realm currently in use is refused.
This commit is contained in:
@@ -251,6 +251,125 @@ class TestValidateConfig:
|
||||
)
|
||||
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Best-effort serving: stored config sanitization
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestSanitizeConfig:
|
||||
"""Serving never fails on stored config problems; it degrades + warns."""
|
||||
|
||||
def test_cross_domain_origin_moved_to_related(self):
|
||||
config, warnings = realms.sanitize_config(
|
||||
Config(
|
||||
realms=[RealmConfig(rp_id="localhost", origins=["https://example.com"])]
|
||||
)
|
||||
)
|
||||
realm = config.realms[0]
|
||||
assert realm.origins is None
|
||||
assert realm.related_origins == ["https://example.com"]
|
||||
assert any("related origin" in w for w in warnings)
|
||||
realms.validate_config(config) # sanitized config is strict-clean
|
||||
|
||||
def test_malformed_origin_dropped(self):
|
||||
config, warnings = realms.sanitize_config(
|
||||
Config(realms=[RealmConfig(rp_id="a.com", origins=["not a url"])])
|
||||
)
|
||||
assert config.realms[0].origins is None
|
||||
assert warnings
|
||||
|
||||
def test_invalid_rp_id_realm_dropped(self):
|
||||
config, warnings = realms.sanitize_config(
|
||||
Config(
|
||||
realms=[RealmConfig(rp_id="not a domain!"), RealmConfig(rp_id="ok.com")]
|
||||
)
|
||||
)
|
||||
assert [r.rp_id for r in config.realms] == ["ok.com"]
|
||||
assert any("dropped" in w for w in warnings)
|
||||
|
||||
def test_duplicate_rp_id_first_wins(self):
|
||||
config, _warnings = realms.sanitize_config(
|
||||
Config(
|
||||
realms=[
|
||||
RealmConfig(rp_id="a.com", rp_name="First"),
|
||||
RealmConfig(rp_id="a.com"),
|
||||
]
|
||||
)
|
||||
)
|
||||
assert len(config.realms) == 1
|
||||
assert config.realms[0].rp_name == "First"
|
||||
|
||||
def test_related_inside_own_domain_dropped(self):
|
||||
config, _ = realms.sanitize_config(
|
||||
Config(
|
||||
realms=[
|
||||
RealmConfig(rp_id="a.com", related_origins=["https://app.a.com"])
|
||||
]
|
||||
)
|
||||
)
|
||||
assert config.realms[0].related_origins is None
|
||||
|
||||
def test_cap_exceeded_truncated(self):
|
||||
config, warnings = realms.sanitize_config(
|
||||
Config(
|
||||
realms=[
|
||||
RealmConfig(
|
||||
rp_id="a.com",
|
||||
related_origins=[f"https://app{i}.com" for i in range(6)],
|
||||
)
|
||||
]
|
||||
)
|
||||
)
|
||||
assert len(config.realms[0].related_origins) == 5
|
||||
assert any("maximum" in w for w in warnings)
|
||||
|
||||
def test_auth_host_outside_domain_ignored(self):
|
||||
config, warnings = realms.sanitize_config(
|
||||
Config(realms=[RealmConfig(rp_id="a.com", auth_host="https://auth.b.com")])
|
||||
)
|
||||
assert config.realms[0].auth_host is None
|
||||
assert any("auth host ignored" in w for w in warnings)
|
||||
|
||||
def test_auth_host_colliding_with_rp_id_ignored(self):
|
||||
config, warnings = realms.sanitize_config(
|
||||
Config(
|
||||
realms=[
|
||||
RealmConfig(rp_id="a.com", auth_host="https://auth.a.com"),
|
||||
RealmConfig(rp_id="auth.a.com"),
|
||||
]
|
||||
)
|
||||
)
|
||||
assert config.realms[0].auth_host is None
|
||||
assert any("collides with an rp-id" in w for w in warnings)
|
||||
|
||||
def test_related_colliding_with_other_realm_dropped(self):
|
||||
config, _ = realms.sanitize_config(
|
||||
Config(
|
||||
realms=[
|
||||
RealmConfig(rp_id="a.com", related_origins=["https://app.b.com"]),
|
||||
RealmConfig(rp_id="b.com"),
|
||||
]
|
||||
)
|
||||
)
|
||||
assert config.realms[0].related_origins is None
|
||||
|
||||
def test_no_realms_is_fatal(self):
|
||||
with pytest.raises(ValueError, match="realm"):
|
||||
realms.sanitize_config(Config(realms=[]))
|
||||
with pytest.raises(ValueError, match="No servable realm"):
|
||||
realms.sanitize_config(Config(realms=[RealmConfig(rp_id="not a domain!")]))
|
||||
|
||||
def test_build_tolerates_and_serves(self):
|
||||
# Cross-domain entry stored in origins: served as a related origin
|
||||
reg = build_registry(
|
||||
RealmConfig(rp_id="localhost", origins=["https://example.com"])
|
||||
)
|
||||
assert reg.warnings
|
||||
realm = reg.get("localhost")
|
||||
assert realm.related_origins == ["https://example.com"]
|
||||
realm.passkey.validate_origin("https://example.com")
|
||||
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Origin validation semantics (Passkey)
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user