Documentation
¶
Index ¶
- Constants
- Variables
- func CheckScope(scopeJSON []byte, jobAlias string) bool
- func DecodeScope(scopeJSON []byte) (*models.KeyScope, error)
- func GenerateKey() (plaintext, prefix string, err error)
- func GenerateToken() (string, error)
- func HasRole(keyRole, required models.Role) bool
- func HashKey(plaintext, secret string) (string, error)
- func IsScoped(scopeJSON []byte) (bool, error)
- func RequiredRole(method, path string) (models.Role, bool)
- func ScopeJobs(scopeJSON []byte) ([]string, error)
- type AgentClaimView
- type AuditEntry
- type AuditLogger
- type AuditQueryRequest
- type CreateKeyRequest
- type CreateKeyResponse
- type CreateSessionRequest
- type CredentialAuthenticator
- type ExternalIdentity
- type Principal
- type PrincipalKind
- type RateLimiter
- type RedirectAuthenticator
- type RoleMapper
- type SSOService
- type SSOServiceOption
- type Service
- func (s *Service) AdminKeyExists() (bool, error)
- func (s *Service) Bootstrap() (string, error)
- func (s *Service) CreateKey(req *CreateKeyRequest) (*CreateKeyResponse, error)
- func (s *Service) JobAliasByBackfillID(ctx context.Context, id uuid.UUID) (string, error)
- func (s *Service) JobAliasByID(ctx context.Context, id uuid.UUID) (string, error)
- func (s *Service) JobAliasByRunID(ctx context.Context, id uuid.UUID) (string, error)
- func (s *Service) ListKeys() ([]models.APIKey, error)
- func (s *Service) MintAgentSessionKey(incidentID uuid.UUID, allowlist []string, ttl time.Duration) (*CreateKeyResponse, error)
- func (s *Service) RevokeKey(id uuid.UUID) error
- func (s *Service) RotateKey(id uuid.UUID, gracePeriod time.Duration, actor string) (*CreateKeyResponse, error)
- func (s *Service) RunLastUsedFlusher(ctx context.Context)
- func (s *Service) ValidateKey(plaintext string) (_ *models.APIKey, retErr error)
- type ServiceOption
- type SessionStore
- func (s *SessionStore) Create(ctx context.Context, req CreateSessionRequest) (string, *models.Session, error)
- func (s *SessionStore) Reap(ctx context.Context) (int64, error)
- func (s *SessionStore) Revoke(ctx context.Context, id uuid.UUID) error
- func (s *SessionStore) RevokeAllForUser(ctx context.Context, userID uuid.UUID) error
- func (s *SessionStore) RunLastSeenFlusher(ctx context.Context)
- func (s *SessionStore) RunReaper(ctx context.Context)
- func (s *SessionStore) Validate(ctx context.Context, plaintext string) (*models.Session, *models.User, error)
- type SessionStoreOption
- type UserStore
Constants ¶
const ( OutcomeSuccess = "success" OutcomeDenied = "denied" OutcomeError = "error" )
AuditOutcome enumerates the possible outcomes for an audit log entry.
const ( ActionAuthAttempt = "auth.attempt" ActionAuthDenied = "auth.denied" ActionAuthLogin = "auth.login" ActionAuthLogout = "auth.logout" ActionAuthLoginDenied = "auth.login_denied" ActionAuthSessionRevoked = "auth.session_revoked" ActionUserProvisioned = "user.provisioned" ActionKeyCreate = "api_key.create" ActionKeyRevoke = "api_key.revoke" ActionKeyRotate = "api_key.rotate" ActionJobCreate = "job.create" ActionJobDelete = "job.delete" ActionJobPause = "job.pause" ActionJobUnpause = "job.unpause" ActionRunTrigger = "run.trigger" ActionRunRetry = "run.retry" ActionRunQueueRead = "run_queue.read" ActionRunQueueCancel = "run_queue.cancel" ActionBackfill = "run.backfill" ActionJobdefApply = "jobdef.apply" ActionCachePrune = "cache.prune" ActionCacheDelete = "cache.delete" ActionLogLevel = "log.set_level" ActionDBQuery = "database.query" ActionWebhookDenied = "webhook.denied" )
AuditAction enumerates well-known auditable actions.
const ( // KeyHashSchemeSHA256 is the unkeyed hash format (used when no secret is configured). KeyHashSchemeSHA256 = "sha256" // KeyHashSchemeHMACSHA256 is the keyed production hash format. KeyHashSchemeHMACSHA256 = "hmac-sha256" )
const ( // KeyPrefixLive is the scannable prefix for production API keys. KeyPrefixLive = "csk_live_" // SessionTokenPrefix marks opaque session tokens. SessionTokenPrefix = "css_" )
const AgentSessionKeyRole = models.RoleRunner
AgentSessionKeyRole is the RBAC role an agent-session credential is minted with. It is the minimum role that satisfies the /v1/agent/* route policy (read context at viewer level, propose/execute actions and append notes at runner level). The credential is additionally scope-locked to a single incident's agent routes by its AgentClaim, so the role alone never grants reach beyond that incident — RBAC and scope both gate every request.
Variables ¶
var ( ErrKeyNotFound = errors.New("api key not found") ErrKeyRevoked = errors.New("api key revoked") ErrKeyExpired = errors.New("api key expired") ErrForbidden = errors.New("insufficient permissions") )
var ( ErrSessionInvalid = errors.New("session not found") ErrSessionRevoked = errors.New("session revoked") ErrSessionExpired = errors.New("session expired") ErrUserDisabled = errors.New("user disabled") )
var ErrAgentKeyIncidentRequired = errors.New("mint agent session key: incident id required")
ErrAgentKeyIncidentRequired is returned when a mint is attempted without a bound incident.
var ErrInvalidExternalIdentity = errors.New("invalid external identity")
ErrInvalidExternalIdentity is returned when a provider returns an unusable identity.
var ErrLoginDenied = errors.New("login denied")
ErrLoginDenied is returned when an external identity is not allowed to log in.
Functions ¶
func CheckScope ¶
CheckScope validates whether the key is allowed to act on the given job alias. A nil/empty scope means unrestricted access.
func DecodeScope ¶
DecodeScope normalizes the persisted scope payload into a structured model. Nil, empty, or empty-job scopes are treated as unrestricted.
func GenerateKey ¶
GenerateKey produces a new API key and its display prefix. Returns (plaintext_key, key_prefix, error). The plaintext key must be shown exactly once at creation time.
func GenerateToken ¶
GenerateToken produces a new opaque session token. The plaintext is shown to the client once in the cookie; only its hash is stored.
func HashKey ¶
HashKey returns the stored API-key hash string: HMAC-SHA256 when a secret is configured, plain SHA-256 otherwise.
func RequiredRole ¶
RequiredRole returns the minimum role needed for a given HTTP method + path.
Types ¶
type AgentClaimView ¶
AgentClaimView is the normalized, package-local view of an agent-session claim returned to callers (the auth middleware) so they need not import the models package to enforce the incident binding.
func DecodeAgentClaim ¶
func DecodeAgentClaim(scopeJSON []byte) (*AgentClaimView, error)
DecodeAgentClaim returns the agent-session claim carried by a scope payload, or nil when the key is not an agent-session credential. It deliberately does NOT run the job-normalization early-return that DecodeScope applies (a scope with an agent claim and no Jobs is a valid, maximally-restricted agent key, not an unrestricted key), so callers MUST check for an agent claim before treating an empty ScopeJobs result as "unrestricted".
type AuditEntry ¶
type AuditEntry struct {
Actor string
Action string
ResourceType string
ResourceID string
SourceIP string
Outcome string
Metadata map[string]any
}
AuditEntry holds the fields for a single audit log write.
type AuditLogger ¶
type AuditLogger struct {
// contains filtered or unexported fields
}
AuditLogger writes structured audit log entries to the database.
func NewAuditLogger ¶
func NewAuditLogger(db *gorm.DB) *AuditLogger
NewAuditLogger creates a new audit logger.
func (*AuditLogger) Log ¶
func (a *AuditLogger) Log(entry AuditEntry) error
Log writes an audit entry to the database.
func (*AuditLogger) Query ¶
func (a *AuditLogger) Query(req *AuditQueryRequest) ([]models.AuditLog, error)
Query returns audit log entries matching the given filters.
type AuditQueryRequest ¶
type AuditQueryRequest struct {
Since *time.Time
Until *time.Time
Actor string
Action string
Limit int
Offset int
}
AuditQueryRequest holds filters for querying the audit log.
type CreateKeyRequest ¶
type CreateKeyRequest struct {
Description string
Role models.Role
Scope *models.KeyScope
CreatedBy string
ExpiresAt *time.Time
}
CreateKeyRequest holds the parameters for creating a new API key.
type CreateKeyResponse ¶
CreateKeyResponse is returned on key creation — the only time the plaintext is available.
type CreateSessionRequest ¶
type CreateSessionRequest struct {
UserID uuid.UUID
AuthMethod string
SourceIP string
UserAgent string
}
CreateSessionRequest holds parameters for minting a session.
type CredentialAuthenticator ¶
type CredentialAuthenticator interface {
Name() string
Authenticate(ctx context.Context, username, password string) (*ExternalIdentity, error)
}
CredentialAuthenticator is implemented by credential providers.
type ExternalIdentity ¶
type ExternalIdentity struct {
Issuer string
Subject string
Email string
DisplayName string
Groups []string
}
ExternalIdentity is the normalized identity every provider produces.
type Principal ¶
type Principal struct {
Kind PrincipalKind
Role models.Role
Scope []byte // raw KeyScope JSON; nil/empty == unrestricted.
Subject string // audit actor: key prefix or user email.
UserID *uuid.UUID
KeyID *uuid.UUID
}
Principal is the unified authenticated identity used by RBAC, scope checks, and audit. It is produced from either an API key or a user session.
func PrincipalFromKey ¶
PrincipalFromKey builds a Principal from a validated API key.
func PrincipalFromUser ¶
PrincipalFromUser builds a Principal from an authenticated user. SSO users are unscoped (nil Scope) in v1.
type PrincipalKind ¶
type PrincipalKind string
PrincipalKind distinguishes the credential type behind an authenticated request.
const ( PrincipalAPIKey PrincipalKind = "api_key" PrincipalUser PrincipalKind = "user" )
type RateLimiter ¶
type RateLimiter struct {
// contains filtered or unexported fields
}
RateLimiter tracks failed authentication attempts per source IP using a sliding window counter.
func NewRateLimiter ¶
func NewRateLimiter(limit int, interval time.Duration) *RateLimiter
NewRateLimiter creates a rate limiter that allows `limit` failures per `interval` per source IP.
func (*RateLimiter) Cleanup ¶
func (r *RateLimiter) Cleanup()
Cleanup removes expired windows. Call periodically to prevent memory growth.
func (*RateLimiter) IsLimited ¶
func (r *RateLimiter) IsLimited(ip string) bool
IsLimited returns true if the IP has exceeded the failure threshold.
func (*RateLimiter) RecordFailure ¶
func (r *RateLimiter) RecordFailure(ip string) bool
RecordFailure increments the failure count for the given IP. Returns true if the IP is now rate-limited.
func (*RateLimiter) RetryAfter ¶
func (r *RateLimiter) RetryAfter(ip string) int
RetryAfter returns the number of seconds until the rate limit window resets for the given IP. Returns 0 if not limited.
func (*RateLimiter) RunCleanup ¶
func (r *RateLimiter) RunCleanup(done <-chan struct{})
RunCleanup periodically removes expired windows until done is closed. The caller owns the goroutine lifecycle so shutdown can wait for the loop.
type RedirectAuthenticator ¶
type RedirectAuthenticator interface {
Name() string
Begin(w http.ResponseWriter, r *http.Request, returnTo string) (redirectURL string, err error)
Complete(r *http.Request) (*ExternalIdentity, error)
}
RedirectAuthenticator is implemented by browser-redirect providers.
type RoleMapper ¶
type RoleMapper struct {
// contains filtered or unexported fields
}
RoleMapper resolves IdP groups to Caesium roles. Highest matched role wins. The wildcard entry "*" matches every login; defaultRole is only a fallback when no explicit or wildcard mapping applies.
func NewRoleMapper ¶
func NewRoleMapper(mapping, defaultRole string) (*RoleMapper, error)
NewRoleMapper parses a semicolon-separated group=role mapping and optional default role. Entries split on the last '=' so LDAP DNs can be used as keys.
type SSOService ¶
type SSOService struct {
// contains filtered or unexported fields
}
SSOService is the shared tail: provision the user, map a role, mint a session.
func NewSSOService ¶
func NewSSOService(users *UserStore, sessions *SessionStore, roles *RoleMapper, opts ...SSOServiceOption) *SSOService
NewSSOService creates a shared SSO login pipeline.
type SSOServiceOption ¶
type SSOServiceOption func(*SSOService)
SSOServiceOption customizes the shared SSO login pipeline.
func WithSSOAuditLogger ¶
func WithSSOAuditLogger(auditor *AuditLogger) SSOServiceOption
WithSSOAuditLogger records SSO login audit entries during completion.
type Service ¶
type Service struct {
// contains filtered or unexported fields
}
Service provides API key management and validation.
func NewService ¶
func NewService(db *gorm.DB, opts ...ServiceOption) *Service
NewService creates a new auth service backed by the given database.
func (*Service) AdminKeyExists ¶
AdminKeyExists returns true if at least one non-revoked, non-expired admin key exists.
func (*Service) Bootstrap ¶
Bootstrap generates the initial admin key on first startup with auth enabled. Returns the plaintext key (to be printed to stdout once) or empty string if an admin key already exists.
func (*Service) CreateKey ¶
func (s *Service) CreateKey(req *CreateKeyRequest) (*CreateKeyResponse, error)
CreateKey generates a new API key and persists its hash.
func (*Service) JobAliasByBackfillID ¶
JobAliasByBackfillID resolves the job alias for a backfill identifier.
func (*Service) JobAliasByID ¶
JobAliasByID resolves the job alias for a job identifier.
func (*Service) JobAliasByRunID ¶
JobAliasByRunID resolves the job alias for a job run identifier.
func (*Service) ListKeys ¶
ListKeys returns all API keys (without hashes, which are excluded by the model JSON tag).
func (*Service) MintAgentSessionKey ¶
func (s *Service) MintAgentSessionKey(incidentID uuid.UUID, allowlist []string, ttl time.Duration) (*CreateKeyResponse, error)
MintAgentSessionKey creates a scoped, short-lived API key bound to a single incident's /v1/agent/* tool surface. It is the credential-minting site the incident manager (an unscoped, server-side principal) calls once per agent session: the caller supplies the FROZEN job allowlist snapshotted at incident open, and the returned key can never widen it. The plaintext is returned once (to be injected into the agent container) and only its hash is persisted.
The key:
- carries an AgentClaim, so the deny-by-default route-scope switch treats it as valid ONLY for this incident's agent routes and 403s everything else;
- is minted at the runner role (the minimum the agent routes require);
- expires after ttl, so the credential dies with the session even if the supervisor never gets to revoke it explicitly.
func (*Service) RotateKey ¶
func (s *Service) RotateKey(id uuid.UUID, gracePeriod time.Duration, actor string) (*CreateKeyResponse, error)
RotateKey creates a new key and sets expires_at on the old key to allow a grace period.
func (*Service) RunLastUsedFlusher ¶
RunLastUsedFlusher periodically flushes buffered last_used_at timestamps to the database until ctx is cancelled. This keeps the hot auth path free of writes while allowing the caller to own goroutine lifecycle.
type ServiceOption ¶
type ServiceOption func(*Service)
ServiceOption customizes auth service behavior.
func WithKeyHashSecret ¶
func WithKeyHashSecret(secret string) ServiceOption
WithKeyHashSecret configures the server-side secret used for HMAC-SHA256 key hashes.
func WithNow ¶
func WithNow(now func() time.Time) ServiceOption
WithNow overrides the service clock. Intended for tests.
func WithSleep ¶
func WithSleep(sleep func(time.Duration)) ServiceOption
WithSleep overrides the service sleep function. Intended for tests.
func WithValidationFailureMinLatency ¶
func WithValidationFailureMinLatency(d time.Duration) ServiceOption
WithValidationFailureMinLatency configures the minimum latency for auth failures.
type SessionStore ¶
type SessionStore struct {
// contains filtered or unexported fields
}
SessionStore manages server-side login sessions in the catalog DB. Tokens are hashed at rest and last-seen updates are coalesced.
func NewSessionStore ¶
func NewSessionStore(db *gorm.DB, opts ...SessionStoreOption) *SessionStore
NewSessionStore creates a new session store backed by the given database.
func (*SessionStore) Create ¶
func (s *SessionStore) Create(ctx context.Context, req CreateSessionRequest) (string, *models.Session, error)
Create mints a new session and returns the plaintext token for the cookie.
func (*SessionStore) Reap ¶
func (s *SessionStore) Reap(ctx context.Context) (int64, error)
Reap deletes expired sessions and sessions revoked more than an hour ago.
func (*SessionStore) RevokeAllForUser ¶
RevokeAllForUser revokes every live session for a user.
func (*SessionStore) RunLastSeenFlusher ¶
func (s *SessionStore) RunLastSeenFlusher(ctx context.Context)
RunLastSeenFlusher periodically flushes buffered session activity.
func (*SessionStore) RunReaper ¶
func (s *SessionStore) RunReaper(ctx context.Context)
RunReaper sweeps expired sessions until ctx is cancelled.
type SessionStoreOption ¶
type SessionStoreOption func(*SessionStore)
SessionStoreOption customizes session-store behavior.
func WithSessionHashSecret ¶
func WithSessionHashSecret(secret string) SessionStoreOption
WithSessionHashSecret configures the server-side secret for session-token hashes.
func WithSessionNow ¶
func WithSessionNow(now func() time.Time) SessionStoreOption
WithSessionNow overrides the session-store clock. Intended for tests.
func WithSessionTTLs ¶
func WithSessionTTLs(idle, absolute time.Duration) SessionStoreOption
WithSessionTTLs configures idle and absolute session lifetimes.
type UserStore ¶
type UserStore struct {
// contains filtered or unexported fields
}
UserStore provisions and updates user identities.
func NewUserStore ¶
NewUserStore creates a user store backed by the given database.