Files
jarca-ledger-mcp/tests/test_dashboard_persistent_authentication.py

180 lines
5.6 KiB
Python

from __future__ import annotations
import json
import sys
from dataclasses import replace
from pathlib import Path
import pytest
ROOT_DIR = Path(__file__).resolve().parents[1]
if str(ROOT_DIR) not in sys.path:
sys.path.insert(0, str(ROOT_DIR))
from dashboard import authentication
ADMIN_ID = "admin"
ADMIN_PASSWORD = "correct horse battery staple"
SIGNING_SECRET = "s" * 64
def _settings() -> authentication.AuthSettings:
return authentication.AuthSettings(
admin_id=ADMIN_ID,
admin_password=ADMIN_PASSWORD,
signing_secret=SIGNING_SECRET,
)
def test_load_auth_settings_requires_signing_secret(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setenv(authentication.ADMIN_ID_ENV, ADMIN_ID)
monkeypatch.setenv(authentication.ADMIN_PASSWORD_ENV, ADMIN_PASSWORD)
monkeypatch.delenv(authentication.SIGNING_SECRET_ENV, raising=False)
assert authentication.load_auth_settings() is None
monkeypatch.setenv(authentication.SIGNING_SECRET_ENV, SIGNING_SECRET)
assert authentication.load_auth_settings() == _settings()
def test_issue_and_verify_default_token_without_refresh() -> None:
settings = _settings()
issued = authentication.issue_persistent_token(
settings,
remember_me=False,
now=1_700_000_000,
token_id="fixed-token-id",
)
verified = authentication.verify_persistent_token(
settings,
issued.value,
now=1_700_000_001,
)
assert verified is not None
assert verified.issued_at == 1_700_000_000
assert verified.expires_at == 1_700_086_400
assert issued.max_age == authentication.DEFAULT_TOKEN_TTL_SECONDS
assert verified.token_id == "fixed-token-id"
assert ADMIN_PASSWORD not in issued.value
assert SIGNING_SECRET not in issued.value
def test_remember_me_token_uses_30_day_absolute_expiry() -> None:
issued = authentication.issue_persistent_token(
_settings(),
remember_me=True,
now=1_700_000_000,
token_id="fixed-token-id",
)
assert issued.expires_at == 1_702_592_000
assert issued.max_age == authentication.REMEMBER_ME_TOKEN_TTL_SECONDS
def test_token_expiry_is_absolute_and_not_sliding() -> None:
settings = _settings()
issued = authentication.issue_persistent_token(
settings,
remember_me=False,
now=1_700_000_000,
token_id="fixed-token-id",
)
assert authentication.verify_persistent_token(
settings, issued.value, now=issued.expires_at - 1
) is not None
assert authentication.verify_persistent_token(
settings, issued.value, now=issued.expires_at
) is None
def test_tampering_and_rotations_invalidate_token() -> None:
settings = _settings()
issued = authentication.issue_persistent_token(
settings,
remember_me=True,
now=1_700_000_000,
token_id="fixed-token-id",
)
replacement = "A" if issued.value[-1] != "A" else "B"
tampered = issued.value[:-1] + replacement
assert authentication.verify_persistent_token(
settings, tampered, now=1_700_000_001
) is None
assert authentication.verify_persistent_token(
replace(settings, signing_secret="n" * 64),
issued.value,
now=1_700_000_001,
) is None
assert authentication.verify_persistent_token(
replace(settings, admin_password="rotated password"),
issued.value,
now=1_700_000_001,
) is None
assert authentication.verify_persistent_token(
replace(settings, admin_id="rotated-admin"),
issued.value,
now=1_700_000_001,
) is None
@pytest.mark.parametrize(
"payload",
[
{},
{"v": 2, "sub": ADMIN_ID, "iat": 1, "exp": 2, "jti": "x"},
{"v": 1, "sub": "", "iat": 1, "exp": 2, "jti": "x"},
{"v": 1, "sub": ADMIN_ID, "iat": "1", "exp": 2, "jti": "x"},
{"v": 1, "sub": ADMIN_ID, "iat": 2, "exp": 2, "jti": "x"},
{"v": 1, "sub": ADMIN_ID, "iat": 1, "exp": 2, "jti": ""},
{"v": 1, "sub": ADMIN_ID, "iat": 1, "exp": 2, "jti": "x", "extra": 1},
],
)
def test_claim_schema_is_strict(payload: dict[str, object]) -> None:
token = authentication._serializer_for(_settings()).dumps(payload)
assert authentication.verify_persistent_token(
_settings(), token, now=1
) is None
def test_oversized_and_malformed_tokens_are_rejected() -> None:
settings = _settings()
assert authentication.verify_persistent_token(settings, "x" * 10_000, now=1) is None
assert authentication.verify_persistent_token(settings, "not-a-token", now=1) is None
def test_payload_does_not_encode_secrets() -> None:
settings = replace(_settings(), admin_id="configured-admin-identity")
issued = authentication.issue_persistent_token(
settings,
remember_me=False,
now=1_700_000_000,
token_id="fixed-token-id",
)
payload = authentication._serializer_for(settings).loads(issued.value)
assert set(payload) == {"v", "sub", "iat", "exp", "jti"}
assert "password" not in json.dumps(payload).lower()
assert "secret" not in json.dumps(payload).lower()
assert settings.admin_id not in json.dumps(payload)
def test_remember_me_token_expires_at_exact_30_day_boundary() -> None:
settings = _settings()
issued = authentication.issue_persistent_token(
settings, remember_me=True, now=1_700_000_000, token_id="boundary-token-id"
)
assert authentication.verify_persistent_token(
settings, issued.value, now=issued.expires_at - 1
) is not None
assert authentication.verify_persistent_token(
settings, issued.value, now=issued.expires_at
) is None