CLI: positional rp-id/rp-name; init adds domains to an existing database

- 'paskia init [rp-id] [rp-name]' and 'paskia migrate [rp-id]' are now
  positional; comma separation and the --rp-id/--rp-name flags are gone.
- With an existing paskia.kantadb, init adds the rp-id as a new domain
  (seeding its OIDC provider) or updates an existing domain's rp-name.
- Origin allow-list semantics clarified: the bare '*' entry allows
  anything within the rp-id domain on any scheme and port (also the
  empty-list default and its display in the admin UI, replacing the
  synthetic '*.rp-id' row); '*.x' wildcards are https-only; exact entries
  match scheme, host and port. Legacy '*.rp-id' wildcards migrate to '*'
  to preserve their any-scheme meaning.
This commit is contained in:
2026-09-07 03:07:38 +00:00
parent 476ce996ad
commit 7ff8869e1d
16 changed files with 293 additions and 129 deletions
+42 -2
View File
@@ -227,6 +227,21 @@ class TestValidateConfig:
)
)
def test_star_origin_accepted_not_auth_host(self):
domains.validate_config(
Config(domains={"a.com": DomainConfig(origins={"*": True})})
)
with pytest.raises(ValueError, match="cannot be the auth host"):
domains.validate_config(
Config(
domains={
"a.com": DomainConfig(
origins={"*": OriginEntry(auth_host=True)}
)
}
)
)
def test_auth_host_collision(self):
with pytest.raises(ValueError, match="collides with a related origin"):
domains.validate_config(
@@ -417,6 +432,30 @@ class TestOriginValidation:
with pytest.raises(ValueError, match="not allowed"):
p.validate_origin("https://other.com")
def test_wildcard_is_https_only(self):
"""A '*.example.com' entry does not fall back to other schemes."""
p = Passkey(rp_id="example.com", origins=["*.example.com"])
with pytest.raises(ValueError, match="not allowed"):
p.validate_origin("http://example.com")
with pytest.raises(ValueError, match="not allowed"):
p.validate_origin("http://app.example.com:8080")
def test_star_entry_matches_any_scheme_and_port(self):
"""The bare '*' entry allows anything within the rp-id domain."""
p = Passkey(rp_id="example.com", origins=["*"])
assert p.validate_origin("https://example.com")
assert p.validate_origin("http://app.example.com:8080")
with pytest.raises(ValueError, match="not allowed"):
p.validate_origin("https://other.com")
def test_exact_entry_matches_scheme_and_port(self):
p = Passkey(rp_id="localhost", origins=["http://localhost:4403"])
assert p.validate_origin("http://localhost:4403")
with pytest.raises(ValueError, match="not allowed"):
p.validate_origin("https://localhost:4403")
with pytest.raises(ValueError, match="not allowed"):
p.validate_origin("http://localhost:4404")
def test_sub_wildcard_matches_only_its_subtree(self):
p = Passkey(rp_id="example.com", origins=["*.app.example.com"])
assert p.validate_origin("https://app.example.com")
@@ -643,7 +682,7 @@ class TestLegacyConversion:
kanta.data.config = LegacyConfig(
rp_id="example.com",
rp_name="Example",
origins=["https://app.example.com"],
origins=["https://app.example.com", "*.example.com"],
)
kanta.data.credentials[cred_uuid] = LegacyCredential(
credential_id=b"credential-id",
@@ -668,7 +707,8 @@ class TestLegacyConversion:
config = convert_legacy_database(src_file, tmp_path / "paskia.kantadb")
domain = config.domains["example.com"]
assert domain.rp_name == "Example"
assert domain.origins == {"app.example.com": True}
# A legacy wildcard over the rp-id itself becomes the bare '*'
assert domain.origins == {"app.example.com": True, "*": True}
converted = _read_db(tmp_path / "paskia.kantadb")
assert converted.credentials[cred_uuid].rp_id == "example.com"