39 lines
1.8 KiB
Python
39 lines
1.8 KiB
Python
|
|
"""Single-key invalidation guarantees needed by the platform rotation fence."""
|
||
|
|
from datetime import datetime, timedelta, timezone
|
||
|
|
|
||
|
|
import pytest
|
||
|
|
|
||
|
|
from api.config import settings
|
||
|
|
from api.services.repository_rename import (
|
||
|
|
RenamePreconditionFailed, RenameServiceUnavailable,
|
||
|
|
_sign_preflight, _verify_preflight_token,
|
||
|
|
)
|
||
|
|
|
||
|
|
|
||
|
|
def test_rotation_invalidates_predecessor_and_accepts_successor(monkeypatch):
|
||
|
|
payload = {'expires_at': (datetime.now(timezone.utc) + timedelta(minutes=15)).isoformat()}
|
||
|
|
monkeypatch.setattr(settings, 'repository_rename_preflight_secret', 'fixture-old-key')
|
||
|
|
predecessor = _sign_preflight(payload)
|
||
|
|
assert _verify_preflight_token(predecessor) == payload
|
||
|
|
monkeypatch.setattr(settings, 'repository_rename_preflight_secret', 'fixture-new-key')
|
||
|
|
with pytest.raises(RenamePreconditionFailed):
|
||
|
|
_verify_preflight_token(predecessor)
|
||
|
|
assert _verify_preflight_token(_sign_preflight(payload)) == payload
|
||
|
|
|
||
|
|
|
||
|
|
def test_tamper_expiry_and_no_key_fail_closed(monkeypatch):
|
||
|
|
monkeypatch.setattr(settings, 'repository_rename_preflight_secret', 'fixture-key')
|
||
|
|
future = {'expires_at': (datetime.now(timezone.utc) + timedelta(minutes=15)).isoformat()}
|
||
|
|
token = _sign_preflight(future)
|
||
|
|
encoded, signature = token.split('.')
|
||
|
|
with pytest.raises(RenamePreconditionFailed):
|
||
|
|
_verify_preflight_token(encoded + '.' + ('A' if signature[0] != 'A' else 'B') + signature[1:])
|
||
|
|
past = {'expires_at': (datetime.now(timezone.utc) - timedelta(seconds=1)).isoformat()}
|
||
|
|
with pytest.raises(RenamePreconditionFailed, match='expired'):
|
||
|
|
_verify_preflight_token(_sign_preflight(past))
|
||
|
|
monkeypatch.setattr(settings, 'repository_rename_preflight_secret', None)
|
||
|
|
with pytest.raises(RenameServiceUnavailable):
|
||
|
|
_sign_preflight(future)
|
||
|
|
with pytest.raises(RenameServiceUnavailable):
|
||
|
|
_verify_preflight_token(token)
|