fastapi-gsap/gsap_broker/credentials/stub_backend.py
Tyler J King 24eefe1699 feat(credentials): add Entra and Stub credential backends
Entra backend resolves OAuth tokens via MSAL client_credentials
(OBO flow wired in future sprint) and passes ACs through for
Bascule. Kerberos stubbed pending hybrid environment config.
Stub backend for dev/testing without real IdP.

Signed-off-by: Tyler King <tking@guildhouse.dev>
2026-04-14 05:57:52 -04:00

84 lines
2.6 KiB
Python

# Copyright 2026 Guildhouse Dev
# SPDX-License-Identifier: Apache-2.0
"""Stub credential backend for development and testing.
Returns valid-looking credentials with short TTLs. NEVER use in
production — these credentials grant no actual access.
Useful for:
- Running the connector framework locally without Entra/Vault
- Integration tests that verify the credential lifecycle
(acquire → use → discard) without real secrets infrastructure
- Verifying that transports handle credential types correctly
"""
import logging
from datetime import datetime, timedelta, UTC
from gsap_broker.credentials.resolver import (
BasculeCredential,
Credential,
CredentialBackend,
KerberosCredential,
OAuthCredential,
SSHCertCredential,
)
logger = logging.getLogger(__name__)
class StubCredentialBackend(CredentialBackend):
"""Development/testing backend that returns mock credentials."""
@property
def supported_types(self) -> list[str]:
return ["bascule_ac", "kerberos", "oauth", "ssh_cert"]
async def resolve(
self,
credential_type: str,
target: str,
ac_context: dict,
) -> Credential:
expires_at = datetime.now(UTC) + timedelta(minutes=5)
scoped_to = ac_context.get("accord", {}).get("template", "stub")
if credential_type == "bascule_ac":
return BasculeCredential(
target=target,
expires_at=expires_at,
scoped_to=scoped_to,
authorization_context=ac_context,
)
if credential_type == "kerberos":
return KerberosCredential(
target=target,
expires_at=expires_at,
scoped_to=scoped_to,
ticket=b"STUB_TICKET",
)
if credential_type == "oauth":
return OAuthCredential(
target=target,
expires_at=expires_at,
scoped_to=scoped_to,
access_token="stub-access-token",
)
if credential_type == "ssh_cert":
return SSHCertCredential(
target=target,
expires_at=expires_at,
scoped_to=scoped_to,
certificate="stub-cert",
private_key="stub-key",
)
# Should not happen if supported_types is checked first
raise ValueError(f"StubBackend: unsupported type '{credential_type}'")
async def revoke(self, credential: Credential) -> None:
logger.debug("Stub revoke: %s for %s (no-op)", credential.credential_type, credential.target)