auth

package
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 7, 2026 License: Apache-2.0 Imports: 21 Imported by: 0

Documentation

Index

Constants

View Source
const (
	OutcomeSuccess = "success"
	OutcomeDenied  = "denied"
	OutcomeError   = "error"
)

AuditOutcome enumerates the possible outcomes for an audit log entry.

View Source
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.

View Source
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"
)
View Source
const (
	// KeyPrefixLive is the scannable prefix for production API keys.
	KeyPrefixLive = "csk_live_"

	// SessionTokenPrefix marks opaque session tokens.
	SessionTokenPrefix = "css_"
)
View Source
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

View Source
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")
)
View Source
var (
	ErrSessionInvalid = errors.New("session not found")
	ErrSessionRevoked = errors.New("session revoked")
	ErrSessionExpired = errors.New("session expired")
	ErrUserDisabled   = errors.New("user disabled")
)
View Source
var ErrAgentKeyIncidentRequired = errors.New("mint agent session key: incident id required")

ErrAgentKeyIncidentRequired is returned when a mint is attempted without a bound incident.

View Source
var ErrInvalidExternalIdentity = errors.New("invalid external identity")

ErrInvalidExternalIdentity is returned when a provider returns an unusable identity.

View Source
var ErrLoginDenied = errors.New("login denied")

ErrLoginDenied is returned when an external identity is not allowed to log in.

Functions

func CheckScope

func CheckScope(scopeJSON []byte, jobAlias string) bool

CheckScope validates whether the key is allowed to act on the given job alias. A nil/empty scope means unrestricted access.

func DecodeScope

func DecodeScope(scopeJSON []byte) (*models.KeyScope, error)

DecodeScope normalizes the persisted scope payload into a structured model. Nil, empty, or empty-job scopes are treated as unrestricted.

func GenerateKey

func GenerateKey() (plaintext, prefix string, err error)

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

func GenerateToken() (string, error)

GenerateToken produces a new opaque session token. The plaintext is shown to the client once in the cookie; only its hash is stored.

func HasRole

func HasRole(keyRole, required models.Role) bool

HasRole returns true if the key's role is at or above the required level.

func HashKey

func HashKey(plaintext, secret string) (string, error)

HashKey returns the stored API-key hash string: HMAC-SHA256 when a secret is configured, plain SHA-256 otherwise.

func IsScoped

func IsScoped(scopeJSON []byte) (bool, error)

IsScoped reports whether the scope payload restricts access to specific jobs.

func RequiredRole

func RequiredRole(method, path string) (models.Role, bool)

RequiredRole returns the minimum role needed for a given HTTP method + path.

func ScopeJobs

func ScopeJobs(scopeJSON []byte) ([]string, error)

ScopeJobs returns the normalized scoped job aliases or nil when unrestricted.

Types

type AgentClaimView

type AgentClaimView struct {
	IncidentID uuid.UUID
	Jobs       []string
}

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

type CreateKeyResponse struct {
	Plaintext string         `json:"key"`
	Key       *models.APIKey `json:"api_key"`
}

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

func PrincipalFromKey(k *models.APIKey) *Principal

PrincipalFromKey builds a Principal from a validated API key.

func PrincipalFromUser

func PrincipalFromUser(u *models.User) *Principal

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.

func (*RoleMapper) Resolve

func (m *RoleMapper) Resolve(groups []string) (models.Role, bool)

Resolve returns the effective role for groups and whether login is allowed.

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.

func (*SSOService) Complete

func (s *SSOService) Complete(ctx context.Context, ext *ExternalIdentity, method, ip, ua string) (string, *models.Session, error)

Complete turns an authenticated ExternalIdentity into a server-side session.

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

func (s *Service) AdminKeyExists() (bool, error)

AdminKeyExists returns true if at least one non-revoked, non-expired admin key exists.

func (*Service) Bootstrap

func (s *Service) Bootstrap() (string, error)

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

func (s *Service) JobAliasByBackfillID(ctx context.Context, id uuid.UUID) (string, error)

JobAliasByBackfillID resolves the job alias for a backfill identifier.

func (*Service) JobAliasByID

func (s *Service) JobAliasByID(ctx context.Context, id uuid.UUID) (string, error)

JobAliasByID resolves the job alias for a job identifier.

func (*Service) JobAliasByRunID

func (s *Service) JobAliasByRunID(ctx context.Context, id uuid.UUID) (string, error)

JobAliasByRunID resolves the job alias for a job run identifier.

func (*Service) ListKeys

func (s *Service) ListKeys() ([]models.APIKey, error)

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

func (s *Service) RevokeKey(id uuid.UUID) error

RevokeKey sets revoked_at on the specified key.

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

func (s *Service) RunLastUsedFlusher(ctx context.Context)

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.

func (*Service) ValidateKey

func (s *Service) ValidateKey(plaintext string) (_ *models.APIKey, retErr error)

ValidateKey looks up a plaintext API key, verifies it is active, and returns the key record. On success it asynchronously updates last_used_at.

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

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

func (s *SessionStore) Revoke(ctx context.Context, id uuid.UUID) error

Revoke marks a single session revoked.

func (*SessionStore) RevokeAllForUser

func (s *SessionStore) RevokeAllForUser(ctx context.Context, userID uuid.UUID) error

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.

func (*SessionStore) Validate

func (s *SessionStore) Validate(ctx context.Context, plaintext string) (*models.Session, *models.User, error)

Validate resolves a plaintext token to its live session and user.

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

func NewUserStore(db *gorm.DB) *UserStore

NewUserStore creates a user store backed by the given database.

func (*UserStore) Upsert

func (us *UserStore) Upsert(ctx context.Context, ext *ExternalIdentity, role models.Role) (*models.User, error)

Upsert provisions a user on first login and refreshes profile, role, and last-login fields on subsequent logins, keyed on (issuer, subject).

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL