secrets-engine/tests/test_service_auth.py
tegwick b8094f701b
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 2s
Read the secrets-engine-openbao client secret in memory from OpenBao
Founder direction 2026-10-06 and ops-mason plan
keycape-secrets-engine-openbao-client-custody (658cdbc): no workstation copy.
service-jwt now gets CLIENT_SECRET from platform/workloads/keycape/
secrets-engine-openbao through one attended OIDC login on
keycape-secrets-engine-openbao-operator-workload-kv-read (-no-store, token from
a pipe, header-only GET, unconditional revoke-self). The consumer file is
refused for this profile and kept for the approval client.
service-auth probe-openbao prints value-free first-use evidence.

SECRETS-WP-0012: T02 cancelled (D1-D4 superseded), T04 done, T03 is the
founder-run first live use. Synthetic tests only; 524 passed, 1 skipped.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 132360@bnt-lap001
Assistant-Session: 769b1afd-4330-4c86-96b2-e510e26db42b
2026-10-06 14:13:41 +02:00

175 lines
5.5 KiB
Python

import base64
import json
from datetime import datetime, timezone
from pathlib import Path
import pytest
from secrets_engine.errors import BackendError
from secrets_engine.service_auth import (
KeyCapeServiceAuthConfig,
KeyCapeServiceAuthProvider,
preflight_service_jwt,
)
NOW = datetime(2026, 8, 23, 12, 0, tzinfo=timezone.utc)
def _segment(value):
return base64.urlsafe_b64encode(
json.dumps(value, separators=(",", ":")).encode()
).decode().rstrip("=")
def _jwt(**overrides):
now = int(NOW.timestamp())
claims = {
"iss": "https://keycape.example.test",
"sub": "service:secrets-engine",
"aud": "secrets-engine-openbao",
"iat": now,
"exp": now + 900,
"principal_type": "service",
"tenant": "tenant:platform",
"roles": ["secrets-engine"],
"groups": [],
"scope": "openbao:login",
"assurance": {
"level": "aal1",
"methods": ["client_secret"],
"mfa": False,
"source": "key-cape",
},
}
claims.update(overrides)
return f"{_segment({'alg': 'RS256', 'typ': 'JWT'})}.{_segment(claims)}.signature"
def _config(_unused=None):
return KeyCapeServiceAuthConfig(
token_url="https://keycape.example.test/token",
issuer="https://keycape.example.test",
)
def test_preflight_accepts_exact_service_contract_and_renewal_window():
token = _jwt()
result = preflight_service_jwt(token, _config(), now=NOW)
assert result.expires_at - result.issued_at == 900
assert not result.needs_renewal(NOW)
assert result.needs_renewal(datetime.fromtimestamp(result.expires_at - 180, timezone.utc))
assert token not in repr(result)
@pytest.mark.parametrize(
("override", "match"),
[
({"iss": "https://attacker.test"}, "'iss'"),
({"sub": "service:operator"}, "'sub'"),
({"aud": "different"}, "'aud'"),
({"tenant": "tenant:coulomb"}, "'tenant'"),
({"tenant": "tenant:other"}, "'tenant'"),
({"roles": ["admin"]}, "roles"),
({"scope": "openbao:login admin"}, "scope"),
({"exp": int(NOW.timestamp()) + 901}, "15-minute"),
],
)
def test_preflight_rejects_claim_or_lifetime_drift(override, match):
with pytest.raises(BackendError, match=match):
preflight_service_jwt(_jwt(**override), _config(), now=NOW)
class _Response:
status = 200
def __init__(self, payload):
self.payload = payload
def __enter__(self):
return self
def __exit__(self, *_args):
return False
def read(self, _size=-1):
return json.dumps(self.payload).encode()
def test_exchange_uses_basic_auth_and_never_places_secret_in_body(tmp_path):
secret_file = tmp_path / "client.secret"
secret_file.write_text("client-secret-value")
secret_file.chmod(0o600)
captured = {}
def transport(request, *, timeout):
captured["authorization"] = request.get_header("Authorization")
captured["body"] = request.data.decode()
captured["timeout"] = timeout
return _Response(
{
"access_token": _jwt(),
"token_type": "Bearer",
"expires_in": 900,
}
)
provider = KeyCapeServiceAuthProvider(
_config(), transport=transport, secret_source=lambda: "client-secret-value"
)
result = provider.exchange(now=NOW)
assert result.expires_at == int(NOW.timestamp()) + 900
assert captured["authorization"].startswith("Basic ")
assert "client-secret-value" not in captured["body"]
assert captured["body"] == "grant_type=client_credentials&scope=openbao%3Alogin"
def test_exchange_rejects_refresh_or_id_tokens_without_exposing_secret(tmp_path):
secret = "do-not-leak-this-client-secret"
secret_file = tmp_path / "client.secret"
secret_file.write_text(secret)
secret_file.chmod(0o600)
def transport(_request, *, timeout):
return _Response(
{
"access_token": _jwt(),
"token_type": "Bearer",
"expires_in": 900,
"refresh_token": "also-secret",
}
)
provider = KeyCapeServiceAuthProvider(
_config(), transport=transport, secret_source=lambda: secret
)
with pytest.raises(BackendError, match="forbidden extra token") as error:
provider.exchange(now=NOW)
assert secret not in str(error.value)
def test_provider_config_is_exact_and_has_no_bootstrap_fallback():
with pytest.raises(BackendError, match="HTTPS"):
KeyCapeServiceAuthConfig(
token_url="http://keycape.example.test/token",
issuer="https://keycape.example.test",
)
with pytest.raises(BackendError, match="accepted contract"):
KeyCapeServiceAuthConfig(
token_url="https://keycape.example.test/token",
issuer="https://keycape.example.test",
client_id="operator",
)
def test_openbao_profile_refuses_file_source(tmp_path):
with pytest.raises(BackendError, match="file source is refused"):
KeyCapeServiceAuthConfig(
token_url="https://keycape.example.test/token",
issuer="https://keycape.example.test",
client_secret_file=tmp_path / "client.secret",
)
provider = KeyCapeServiceAuthProvider(
_config(), transport=lambda *_a, **_k: pytest.fail("no exchange")
)
with pytest.raises(BackendError, match="no file fallback"):
provider.exchange(now=NOW)