# 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)