feat: migrate refimpls from draft-00 to draft-01 claim names

- Rename `par` to `pred` (predecessor) in types, serialization, tests
- Remove `pol`, `pol_decision` from core payload; move to `ect_ext`
- Remove `sub` from payload (not part of ECT spec)
- Update `typ` from `wimse-exec+jwt` to `exec+jwt` (accept both)
- Rename MaxParLength to MaxPredLength everywhere
- Update testdata, demos, READMEs with migration table
- All Go tests pass, all 56 Python tests pass (90% coverage)
This commit is contained in:
2026-04-03 10:55:58 +02:00
parent ba044f6626
commit 884d2dc836
33 changed files with 416 additions and 481 deletions

View File

@@ -4,7 +4,7 @@ import time
import pytest
from ect import Payload, create, generate_key, CreateOptions, default_create_options, POL_DECISION_APPROVED
from ect import Payload, create, generate_key, CreateOptions, default_create_options
def test_default_create_options():
@@ -14,7 +14,7 @@ def test_default_create_options():
def test_create_errors():
key = generate_key()
p = Payload(iss="i", aud=["a"], iat=1, exp=2, jti="j", exec_act="e", par=[], pol="p", pol_decision=POL_DECISION_APPROVED)
p = Payload(iss="i", aud=["a"], iat=1, exp=2, jti="j", exec_act="e", pred=[])
with pytest.raises(ValueError, match="KeyID|required"):
create(p, key, CreateOptions(key_id=""))
with pytest.raises((ValueError, TypeError, AttributeError)):
@@ -26,7 +26,7 @@ def test_create_optional_pol():
now = int(time.time())
p = Payload(
iss="iss", aud=["a"], iat=now, exp=now + 3600,
jti="jti-nopol", exec_act="act", par=[],
jti="jti-nopol", exec_act="act", pred=[],
)
compact = create(p, key, CreateOptions(key_id="kid"))
assert compact
@@ -34,7 +34,7 @@ def test_create_optional_pol():
def test_create_validation_errors():
key = generate_key()
base = dict(iss="i", aud=["a"], iat=1, exp=2, jti="j", exec_act="e", par=[])
base = dict(iss="i", aud=["a"], iat=1, exp=2, jti="j", exec_act="e", pred=[])
with pytest.raises(ValueError, match="iss"):
create(Payload(**{**base, "iss": ""}), key, CreateOptions(key_id="k"))
with pytest.raises(ValueError, match="aud"):
@@ -43,16 +43,12 @@ def test_create_validation_errors():
create(Payload(**{**base, "jti": ""}), key, CreateOptions(key_id="k"))
with pytest.raises(ValueError, match="exec_act"):
create(Payload(**{**base, "exec_act": ""}), key, CreateOptions(key_id="k"))
with pytest.raises(ValueError, match="pol and pol_decision"):
create(Payload(**{**base, "pol": "p", "pol_decision": ""}), key, CreateOptions(key_id="k"))
with pytest.raises(ValueError, match="pol_decision"):
create(Payload(**{**base, "pol": "p", "pol_decision": "bad"}), key, CreateOptions(key_id="k"))
def test_create_ext_compensation_reason_requires_required():
key = generate_key()
p = Payload(
iss="i", aud=["a"], iat=1, exp=2, jti="j", exec_act="e", par=[],
iss="i", aud=["a"], iat=1, exp=2, jti="j", exec_act="e", pred=[],
ext={"compensation_reason": "rollback", "compensation_required": False},
)
with pytest.raises(ValueError, match="compensation_required"):
@@ -61,7 +57,7 @@ def test_create_ext_compensation_reason_requires_required():
def test_create_zero_expiry_uses_default():
key = generate_key()
p = Payload(iss="i", aud=["a"], iat=0, exp=0, jti="j", exec_act="e", par=[])
p = Payload(iss="i", aud=["a"], iat=0, exp=0, jti="j", exec_act="e", pred=[])
compact = create(p, key, CreateOptions(key_id="k", default_expiry_sec=300))
assert compact
# create() works on a copy; decode the token to verify defaults were applied
@@ -73,17 +69,17 @@ def test_create_zero_expiry_uses_default():
def test_create_validate_uuids_rejects_non_uuid_jti():
key = generate_key()
now = int(time.time())
p = Payload(iss="i", aud=["a"], iat=now, exp=now + 3600, jti="not-a-uuid", exec_act="e", par=[])
p = Payload(iss="i", aud=["a"], iat=now, exp=now + 3600, jti="not-a-uuid", exec_act="e", pred=[])
with pytest.raises(ValueError, match="jti must be UUID"):
create(p, key, CreateOptions(key_id="k", validate_uuids=True))
def test_create_max_par_length():
def test_create_max_pred_length():
key = generate_key()
now = int(time.time())
p = Payload(iss="i", aud=["a"], iat=now, exp=now + 3600, jti="550e8400-e29b-41d4-a716-446655440000", exec_act="e", par=["p1", "p2"])
with pytest.raises(ValueError, match="par exceeds max length"):
create(p, key, CreateOptions(key_id="k", max_par_length=1))
p = Payload(iss="i", aud=["a"], iat=now, exp=now + 3600, jti="550e8400-e29b-41d4-a716-446655440000", exec_act="e", pred=["p1", "p2"])
with pytest.raises(ValueError, match="pred exceeds max length"):
create(p, key, CreateOptions(key_id="k", max_pred_length=1))
def test_create_ext_size_rejected():
@@ -91,7 +87,7 @@ def test_create_ext_size_rejected():
key = generate_key()
now = int(time.time())
p = Payload(
iss="i", aud=["a"], iat=now, exp=now + 3600, jti="550e8400-e29b-41d4-a716-446655440000", exec_act="e", par=[],
iss="i", aud=["a"], iat=now, exp=now + 3600, jti="550e8400-e29b-41d4-a716-446655440000", exec_act="e", pred=[],
ext={"x": "y" * (EXT_MAX_SIZE - 5)},
)
with pytest.raises(ValueError, match="ext exceeds max size"):