auth

package
v2.11.4 Latest Latest
Warning

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

Go to latest
Published: Aug 3, 2026 License: GPL-3.0 Imports: 8 Imported by: 0

Documentation

Index

Constants

View Source
const (
	// AuthProviderBuiltin identifies users authenticated with a Dagu password.
	AuthProviderBuiltin = "builtin"
	// AuthProviderOIDC identifies users authenticated through OIDC.
	AuthProviderOIDC = "oidc"
	// AuthProviderProxy identifies users authenticated through proxy authentication.
	AuthProviderProxy = "proxy"
)
View Source
const (
	WebhookHMACAlgorithm         = "HMAC-SHA256"
	WebhookHMACHeaderName        = "X-Dagu-Signature"
	WebhookHMACHeaderValueFormat = "sha256=<hex>"
)

Variables

View Source
var (
	// ErrUserNotFound is returned when a user cannot be found.
	ErrUserNotFound = errors.New("user not found")
	// ErrUserAlreadyExists is returned when attempting to create a user
	// with a username that already exists.
	ErrUserAlreadyExists = errors.New("user already exists")
	// ErrUserDisabled is returned when an operation requires an enabled user.
	ErrUserDisabled = errors.New("your account has been disabled, contact administrator")
	// ErrInvalidUsername is returned when the username is invalid.
	ErrInvalidUsername = errors.New("invalid username")
	// ErrInvalidUserID is returned when the user ID is invalid.
	ErrInvalidUserID = errors.New("invalid user ID")
	// ErrOIDCIdentityNotFound is returned when an OIDC identity cannot be found.
	ErrOIDCIdentityNotFound = errors.New("OIDC identity not found")
	// ErrOIDCIdentityAlreadyExists is returned when attempting to create a user
	// with an OIDC identity that already exists.
	ErrOIDCIdentityAlreadyExists = errors.New("OIDC identity already exists")
	// ErrTrustedProxyIdentityNotFound is returned when a proxy identity cannot be found.
	ErrTrustedProxyIdentityNotFound = errors.New("proxy identity not found")
	// ErrTrustedProxyIdentityAlreadyExists is returned when attempting to create a user
	// with a proxy identity that already exists.
	ErrTrustedProxyIdentityAlreadyExists = errors.New("proxy identity already exists")
	// ErrInvalidTrustedProxyIdentity is returned when the provider and trusted identity are inconsistent.
	ErrInvalidTrustedProxyIdentity = errors.New("invalid proxy identity")
	// ErrTrustedProxyIdentityImmutable is returned when an existing trusted identity is changed.
	ErrTrustedProxyIdentityImmutable = errors.New("proxy identity is immutable")
)

Common errors for user store operations.

View Source
var (
	// ErrAPIKeyNotFound is returned when an API key cannot be found.
	ErrAPIKeyNotFound = errors.New("API key not found")
	// ErrAPIKeyAlreadyExists is returned when attempting to create an API key
	// with a name that already exists.
	ErrAPIKeyAlreadyExists = errors.New("API key already exists")
	// ErrInvalidAPIKeyName is returned when the API key name is invalid.
	ErrInvalidAPIKeyName = errors.New("invalid API key name")
	// ErrInvalidAPIKeyID is returned when the API key ID is invalid.
	ErrInvalidAPIKeyID = errors.New("invalid API key ID")
	// ErrInvalidAPIKeyHash is returned when the API key hash is empty.
	ErrInvalidAPIKeyHash = errors.New("invalid API key hash")
	// ErrInvalidAPIKeySurface is returned when the API key surface allowlist is invalid.
	ErrInvalidAPIKeySurface = errors.New("invalid API key surface")
	// ErrInvalidAPIKeyAttribution is returned when API key attribution metadata is invalid.
	ErrInvalidAPIKeyAttribution = errors.New("invalid API key attribution")
	// ErrInvalidRole is returned when the role is not a valid role.
	ErrInvalidRole = errors.New("invalid role")
)

Common errors for API key store operations.

View Source
var (
	// ErrWebhookNotFound is returned when a webhook cannot be found.
	ErrWebhookNotFound = errors.New("webhook not found")
	// ErrWebhookAlreadyExists is returned when attempting to create a webhook
	// for a DAG that already has one.
	ErrWebhookAlreadyExists = errors.New("webhook already exists for this DAG")
	// ErrInvalidWebhookDAGName is returned when the DAG name is invalid.
	ErrInvalidWebhookDAGName = errors.New("invalid webhook DAG name")
	// ErrInvalidWebhookID is returned when the webhook ID is invalid.
	ErrInvalidWebhookID = errors.New("invalid webhook ID")
	// ErrInvalidWebhookTokenHash is returned when the webhook token hash is empty.
	ErrInvalidWebhookTokenHash = errors.New("invalid webhook token hash")
	// ErrWebhookHMACEncryptorRequired is returned when encrypted HMAC secret
	// storage is required but no encryptor is available.
	ErrWebhookHMACEncryptorRequired = errors.New("webhook HMAC secret requires encryptor")
	// ErrWebhookHMACDecryptFailed is returned when an encrypted HMAC secret
	// cannot be decrypted from storage.
	ErrWebhookHMACDecryptFailed = errors.New("failed to decrypt webhook HMAC secret")
)

Common errors for webhook store operations.

View Source
var ErrInvalidTokenSecret = errors.New("invalid token secret")

ErrInvalidTokenSecret indicates the token secret is empty or otherwise unusable. Used by TokenSecretProvider implementations to signal that the next provider in a chain should be tried.

View Source
var (
	// ErrInvalidWorkspaceAccess is returned when workspace grants are malformed.
	ErrInvalidWorkspaceAccess = errors.New("invalid workspace access")
)

Functions

func APIKeySurfaceStrings

func APIKeySurfaceStrings(surfaces []APIKeySurface) []string

APIKeySurfaceStrings returns normalized surface values as strings.

func ClientIPFromContext

func ClientIPFromContext(ctx context.Context) (string, bool)

ClientIPFromContext retrieves the client IP address from the context. It returns the IP address and true if present, or empty string and false otherwise.

func HasAPIKeySurface

func HasAPIKeySurface(surfaces []APIKeySurface, surface APIKeySurface) bool

HasAPIKeySurface reports whether a normalized surface allowlist contains a surface.

func HasWorkspaceAccess

func HasWorkspaceAccess(access *WorkspaceAccess, workspaceName string) bool

HasWorkspaceAccess reports whether a workspace is visible to the policy.

func ValidAPIKeySurface

func ValidAPIKeySurface(surface APIKeySurface) bool

ValidAPIKeySurface reports whether surface is a known API-key surface.

func ValidateWorkspaceAccess

func ValidateWorkspaceAccess(globalRole Role, access *WorkspaceAccess, workspaceExists func(string) bool) error

ValidateWorkspaceAccess validates role invariants and workspace names.

func WithAPIKey

func WithAPIKey(ctx context.Context, key *APIKey) context.Context

WithAPIKey returns a new context that carries the provided API key value.

func WithClientIP

func WithClientIP(ctx context.Context, ip string) context.Context

WithClientIP returns a new context that carries the client IP address.

func WithUser

func WithUser(ctx context.Context, user *User) context.Context

WithUser returns a new context that carries the provided user value.

func WorkspaceAccessEqual

func WorkspaceAccessEqual(left, right *WorkspaceAccess) bool

WorkspaceAccessEqual reports whether two policies grant the same workspace access. Grant order and surrounding workspace whitespace do not affect equality.

Types

type APIKey

type APIKey struct {
	// ID is the unique identifier for the API key (UUID).
	ID string `json:"id"`
	// Name is a human-readable name for the API key (required).
	Name string `json:"name"`
	// Description is an optional description of the API key's purpose.
	Description string `json:"description,omitempty"`
	// Role determines the API key's permissions.
	Role Role `json:"role"`
	// WorkspaceAccess restricts access to selected workspaces.
	// Nil is treated as all-workspaces for backward compatibility.
	WorkspaceAccess *WorkspaceAccess `json:"workspace_access,omitempty"`
	// AllowedSurfaces controls which public interfaces accept this key.
	// Empty is treated as REST API and MCP for backward compatibility.
	AllowedSurfaces []APIKeySurface `json:"allowed_surfaces,omitempty"`
	// AttributionClass determines whether the key is user-owned or a service account.
	AttributionClass APIKeyAttributionClass `json:"attribution_class,omitempty"`
	// OwnerUserID is populated when AttributionClass is user_owned.
	OwnerUserID string `json:"owner_user_id,omitempty"`
	// OwnerUsername is populated when AttributionClass is user_owned.
	OwnerUsername string `json:"owner_username,omitempty"`
	// ServiceAccountID is populated when AttributionClass is service_account.
	ServiceAccountID string `json:"service_account_id,omitempty"`
	// ServiceAccountName is populated when AttributionClass is service_account.
	ServiceAccountName string `json:"service_account_name,omitempty"`
	// MigratedAsServiceAccount is true when a legacy key had no attribution class
	// and was defaulted to service_account.
	MigratedAsServiceAccount bool `json:"migrated_as_service_account,omitempty"`
	// KeyHash is the bcrypt hash of the API key secret.
	// Excluded from JSON serialization for security.
	KeyHash string `json:"-"`
	// KeyDigest is the versioned digest used for API key lookup.
	// Excluded from JSON serialization for security.
	KeyDigest string `json:"-"`
	// KeyPrefix stores the first 8 characters of the key for identification.
	KeyPrefix string `json:"key_prefix"`
	// CreatedAt is the timestamp when the API key was created.
	CreatedAt time.Time `json:"created_at"`
	// UpdatedAt is the timestamp when the API key was last modified.
	UpdatedAt time.Time `json:"updated_at"`
	// CreatedBy is the user ID of the admin who created the API key.
	CreatedBy string `json:"created_by"`
	// LastUsedAt is the timestamp when the API key was last used for authentication.
	LastUsedAt *time.Time `json:"last_used_at,omitempty"`
}

APIKey represents a standalone API key in the system. API keys are independent entities with their own role assignment, enabling programmatic access with fine-grained permissions.

func APIKeyFromContext

func APIKeyFromContext(ctx context.Context) (*APIKey, bool)

APIKeyFromContext retrieves the API key used for authentication from context.

func NewAPIKey

func NewAPIKey(name, description string, role Role, keyHash, keyPrefix, createdBy string) (*APIKey, error)

NewAPIKey creates an APIKey with a new UUID and sets CreatedAt and UpdatedAt to the current UTC time. It validates that required fields are not empty and the role is valid. Returns an error if validation fails.

func NormalizeAPIKeyMetadata

func NormalizeAPIKeyMetadata(key *APIKey) *APIKey

NormalizeAPIKeyMetadata returns a copy with legacy attribution and surface defaults applied.

func (*APIKey) ToStorage

func (k *APIKey) ToStorage() *APIKeyForStorage

ToStorage converts an APIKey to APIKeyForStorage for persistence. NOTE: When adding new fields to APIKey or APIKeyForStorage, ensure both ToStorage and ToAPIKey are updated to maintain field synchronization.

type APIKeyAttributionClass

type APIKeyAttributionClass string

APIKeyAttributionClass describes the identity quality of an API key.

const (
	// APIKeyAttributionUserOwned means the key is attributable to a user owner.
	APIKeyAttributionUserOwned APIKeyAttributionClass = "user_owned"
	// APIKeyAttributionServiceAccount means the key is attributable to a service identity.
	APIKeyAttributionServiceAccount APIKeyAttributionClass = "service_account"
)

type APIKeyForStorage

type APIKeyForStorage struct {
	ID                       string                 `json:"id"`
	Name                     string                 `json:"name"`
	Description              string                 `json:"description,omitempty"`
	Role                     Role                   `json:"role"`
	WorkspaceAccess          *WorkspaceAccess       `json:"workspace_access,omitempty"`
	AllowedSurfaces          []APIKeySurface        `json:"allowed_surfaces,omitempty"`
	AttributionClass         APIKeyAttributionClass `json:"attribution_class,omitempty"`
	OwnerUserID              string                 `json:"owner_user_id,omitempty"`
	OwnerUsername            string                 `json:"owner_username,omitempty"`
	ServiceAccountID         string                 `json:"service_account_id,omitempty"`
	ServiceAccountName       string                 `json:"service_account_name,omitempty"`
	MigratedAsServiceAccount bool                   `json:"migrated_as_service_account,omitempty"`
	KeyHash                  string                 `json:"key_hash"`
	KeyDigest                string                 `json:"key_digest,omitempty"`
	KeyPrefix                string                 `json:"key_prefix"`
	CreatedAt                time.Time              `json:"created_at"`
	UpdatedAt                time.Time              `json:"updated_at"`
	CreatedBy                string                 `json:"created_by"`
	LastUsedAt               *time.Time             `json:"last_used_at,omitempty"`
}

APIKeyForStorage is used for JSON serialization to persistent storage. It includes credential fields excluded from the regular APIKey JSON.

func (*APIKeyForStorage) ToAPIKey

func (s *APIKeyForStorage) ToAPIKey() *APIKey

ToAPIKey converts APIKeyForStorage back to APIKey. NOTE: When adding new fields to APIKey or APIKeyForStorage, ensure both ToStorage and ToAPIKey are updated to maintain field synchronization.

type APIKeyStore

type APIKeyStore interface {
	// Create stores a new API key.
	// Returns ErrAPIKeyAlreadyExists if an API key with the same name exists.
	Create(ctx context.Context, key *APIKey) error

	// GetByID retrieves an API key by its unique ID.
	// Returns ErrAPIKeyNotFound if the API key does not exist.
	GetByID(ctx context.Context, id string) (*APIKey, error)

	// GetByDigest retrieves an API key by its credential digest.
	// Returns ErrAPIKeyNotFound if the API key does not exist.
	GetByDigest(ctx context.Context, digest string) (*APIKey, error)

	// List returns all API keys in the store.
	List(ctx context.Context) ([]*APIKey, error)

	// Update modifies an existing API key.
	// Credential fields and a newer LastUsedAt value in storage are preserved.
	// Returns ErrAPIKeyNotFound if the API key does not exist.
	Update(ctx context.Context, key *APIKey) error

	// Delete removes an API key by its ID.
	// Returns ErrAPIKeyNotFound if the API key does not exist.
	Delete(ctx context.Context, id string) error

	// PromoteDigest atomically assigns a credential digest to an API key.
	// Repeating the promotion with the same digest is idempotent.
	PromoteDigest(ctx context.Context, id, digest string) error

	// UpdateLastUsed records recent API key use without persisting more than once per minute.
	UpdateLastUsed(ctx context.Context, id string) error
}

APIKeyStore defines the interface for API key persistence operations. Implementations must be safe for concurrent use.

type APIKeySurface

type APIKeySurface string

APIKeySurface is an interface where an API key may be accepted.

const (
	// APIKeySurfaceREST allows use against the REST API and related HTTP surfaces.
	APIKeySurfaceREST APIKeySurface = "rest_api"
	// APIKeySurfaceMCP allows use against the MCP endpoint.
	APIKeySurfaceMCP APIKeySurface = "mcp"
)

func CloneAPIKeySurfaces

func CloneAPIKeySurfaces(surfaces []APIKeySurface) []APIKeySurface

CloneAPIKeySurfaces returns a normalized copy of API key surfaces.

func DefaultAPIKeySurfaces

func DefaultAPIKeySurfaces() []APIKeySurface

DefaultAPIKeySurfaces returns the legacy-compatible surface allowlist.

func NormalizeAPIKeySurfaces

func NormalizeAPIKeySurfaces(surfaces []APIKeySurface) []APIKeySurface

NormalizeAPIKeySurfaces returns a stable allowlist, defaulting only missing legacy metadata to both surfaces. Unknown non-empty values are preserved so corrupted metadata cannot be silently broadened into the legacy default.

type AuthorizationSyncResult

type AuthorizationSyncResult struct {
	// User is the current stored user.
	User *User
	// Changed reports whether the synchronization modified authorization.
	Changed bool
	// PreviousRole is the role observed immediately before synchronization.
	PreviousRole Role
	// PreviousWorkspaceAccess is the workspace policy observed immediately before synchronization.
	PreviousWorkspaceAccess *WorkspaceAccess
}

AuthorizationSyncResult describes an atomic authorization synchronization.

type AuthorizationSyncUserStore

type AuthorizationSyncUserStore interface {
	UserStore

	// SyncAuthorization updates role and, when non-nil, workspace access on an enabled user.
	// It returns the current user and the authorization state observed under the same lock.
	SyncAuthorization(ctx context.Context, id string, role Role, workspaceAccess *WorkspaceAccess) (AuthorizationSyncResult, error)
}

AuthorizationSyncUserStore atomically synchronizes externally managed authorization.

type Role

type Role string

Role represents a user's role in the system. Roles determine what actions a user can perform.

Role hierarchy (most to least privileged):

  • admin: Full system access including user management
  • manager: Can create, edit, delete, run, and stop DAGs, and view audit logs
  • developer: Can create, edit, delete, run, and stop DAGs
  • operator: Can run and stop DAGs (execute only)
  • viewer: Read-only access to DAGs and status
const (
	// RoleAdmin has full access to all resources including user management.
	RoleAdmin Role = "admin"
	// RoleManager can create, edit, delete, run, and stop DAGs, and view audit logs.
	RoleManager Role = "manager"
	// RoleDeveloper can create, edit, delete, run, and stop DAGs.
	RoleDeveloper Role = "developer"
	// RoleOperator can run and stop DAGs (execute only, no edit).
	RoleOperator Role = "operator"
	// RoleViewer can only view DAGs and execution history (read-only).
	RoleViewer Role = "viewer"
	// RoleNone represents an unset or unauthenticated role.
	RoleNone Role = ""
)

func AllRoles

func AllRoles() []Role

AllRoles returns a copy of all valid roles.

func EffectiveRole

func EffectiveRole(globalRole Role, access *WorkspaceAccess, workspaceName string) (Role, bool)

EffectiveRole returns the role that applies to a workspace.

Empty workspace names represent unlabelled resources and are governed by the global role so non-workspace workflows remain visible to all authenticated users.

func ParseRole

func ParseRole(s string) (Role, error)

ParseRole converts a string to a Role. If the input is not "admin", "manager", "developer", "operator", or "viewer", it returns an error describing the valid options.

func (Role) CanExecute

func (r Role) CanExecute() bool

CanExecute returns true if the role can run or stop DAGs.

func (Role) CanManageAudit

func (r Role) CanManageAudit() bool

CanManageAudit returns true if the role can view audit logs.

func (Role) CanWrite

func (r Role) CanWrite() bool

CanWrite returns true if the role can create, edit, or delete DAGs.

func (Role) IsAdmin

func (r Role) IsAdmin() bool

IsAdmin returns true if the role has administrative privileges (user management).

func (Role) IsSet

func (r Role) IsSet() bool

IsSet returns true if the role has been assigned (is not empty).

func (Role) String

func (r Role) String() string

String returns the string representation of the role.

func (Role) Valid

func (r Role) Valid() bool

Valid returns true if the role is a known valid role.

type TokenSecret

type TokenSecret struct {
	// contains filtered or unexported fields
}

TokenSecret is an opaque handle to JWT signing key material. The zero value is invalid, forcing callers through constructors.

func NewTokenSecret

func NewTokenSecret(key []byte) (TokenSecret, error)

NewTokenSecret creates a TokenSecret from raw key bytes. Returns ErrInvalidTokenSecret if key is nil or empty. The input is defensively copied to prevent mutation after construction.

func NewTokenSecretFromString

func NewTokenSecretFromString(s string) (TokenSecret, error)

NewTokenSecretFromString creates a TokenSecret from a string value. Returns ErrInvalidTokenSecret if the string is empty.

func (TokenSecret) GoString

func (ts TokenSecret) GoString() string

GoString implements fmt.GoStringer. Returns a safe representation for %#v.

func (TokenSecret) IsValid

func (ts TokenSecret) IsValid() bool

IsValid reports whether the TokenSecret holds usable key material.

func (TokenSecret) MarshalJSON

func (ts TokenSecret) MarshalJSON() ([]byte, error)

MarshalJSON prevents the secret from being serialized into JSON.

func (TokenSecret) MarshalText

func (ts TokenSecret) MarshalText() ([]byte, error)

MarshalText prevents the secret from being serialized as text.

func (TokenSecret) SigningKey

func (ts TokenSecret) SigningKey() []byte

SigningKey returns a copy of the raw key material for JWT signing. This is the ONLY way to access the key bytes. Returns a defensive copy to prevent callers from mutating the internal key.

func (TokenSecret) String

func (ts TokenSecret) String() string

String implements fmt.Stringer. Always returns "[REDACTED]" to prevent accidental logging of secret material.

func (*TokenSecret) UnmarshalJSON

func (ts *TokenSecret) UnmarshalJSON([]byte) error

UnmarshalJSON rejects deserialization to prevent silent zero-value creation.

func (*TokenSecret) UnmarshalText

func (ts *TokenSecret) UnmarshalText([]byte) error

UnmarshalText rejects deserialization to prevent silent zero-value creation.

type TokenSecretProvider

type TokenSecretProvider interface {
	Resolve(ctx context.Context) (TokenSecret, error)
}

TokenSecretProvider resolves the JWT signing secret from a configured source. Implementations may perform I/O (file reads, auto-generation) and should respect context cancellation.

type User

type User struct {
	// ID is the unique identifier for the user (UUID).
	ID string `json:"id"`
	// Username is the unique login name.
	Username string `json:"username"`
	// PasswordHash is the bcrypt hash of the password.
	// Excluded from JSON serialization for security.
	PasswordHash string `json:"-"`
	// Role determines the user's permissions.
	Role Role `json:"role"`
	// WorkspaceAccess restricts access to selected workspaces.
	// Nil is treated as all-workspaces for backward compatibility.
	WorkspaceAccess *WorkspaceAccess `json:"workspace_access,omitempty"`
	// CreatedAt is the timestamp when the user was created.
	CreatedAt time.Time `json:"created_at"`
	// UpdatedAt is the timestamp when the user was last modified.
	UpdatedAt time.Time `json:"updated_at"`

	// AuthProvider indicates how the user authenticates.
	// Empty string is treated as "builtin" for backward compatibility.
	AuthProvider string `json:"auth_provider,omitempty"`
	// OIDCIssuer is the OIDC provider issuer URL (set when AuthProvider is "oidc").
	OIDCIssuer string `json:"oidc_issuer,omitempty"`
	// OIDCSubject is the unique subject identifier from the OIDC provider.
	OIDCSubject string `json:"oidc_subject,omitempty"`
	// TrustedProxySource identifies the proxy identity source.
	TrustedProxySource string `json:"trusted_proxy_source,omitempty"`
	// TrustedProxyUser is the opaque identity supplied by the authenticating proxy.
	TrustedProxyUser string `json:"trusted_proxy_user,omitempty"`
	// IsDisabled indicates if the user account is disabled.
	// Disabled users cannot log in.
	IsDisabled bool `json:"is_disabled,omitempty"`
	// PasswordChangedAt records when the password was last changed.
	// Tokens issued before this time are rejected. Nil means never changed.
	PasswordChangedAt *time.Time `json:"password_changed_at,omitempty"`
}

User represents a user in the system.

func NewUser

func NewUser(username string, passwordHash string, role Role) *User

NewUser creates a User with a new UUID and sets CreatedAt and UpdatedAt to the current UTC time. The provided username, passwordHash, and role are assigned to the corresponding fields.

func UserForAPIKeyAttribution

func UserForAPIKeyAttribution(apiKey *APIKey) (*User, bool)

UserForAPIKeyAttribution returns the subject user represented by an API key.

func UserFromContext

func UserFromContext(ctx context.Context) (*User, bool)

UserFromContext retrieves the authenticated user from the context. It returns the user and true if a *User value is present for the package's userContextKey, or nil and false otherwise.

func (*User) CanUsePassword

func (u *User) CanUsePassword() bool

CanUsePassword reports whether password authentication is available to the user.

func (*User) IsOIDCUser

func (u *User) IsOIDCUser() bool

IsOIDCUser returns true if the user was created via OIDC auto-signup.

func (*User) ToStorage

func (u *User) ToStorage() *UserForStorage

ToStorage converts a User to UserForStorage for persistence.

type UserForStorage

type UserForStorage struct {
	ID                 string           `json:"id"`
	Username           string           `json:"username"`
	PasswordHash       string           `json:"password_hash"`
	Role               Role             `json:"role"`
	WorkspaceAccess    *WorkspaceAccess `json:"workspace_access,omitempty"`
	CreatedAt          time.Time        `json:"created_at"`
	UpdatedAt          time.Time        `json:"updated_at"`
	AuthProvider       string           `json:"auth_provider,omitempty"`
	OIDCIssuer         string           `json:"oidc_issuer,omitempty"`
	OIDCSubject        string           `json:"oidc_subject,omitempty"`
	TrustedProxySource string           `json:"trusted_proxy_source,omitempty"`
	TrustedProxyUser   string           `json:"trusted_proxy_user,omitempty"`
	IsDisabled         bool             `json:"is_disabled,omitempty"`
	PasswordChangedAt  *time.Time       `json:"password_changed_at,omitempty"`
}

UserForStorage is used for JSON serialization to persistent storage. It includes the password hash which is excluded from the regular User JSON.

func (*UserForStorage) ToUser

func (s *UserForStorage) ToUser() *User

ToUser converts UserForStorage back to User.

type UserPatch

type UserPatch struct {
	Username        *string
	Role            *Role
	WorkspaceAccess *WorkspaceAccess
	PasswordHash    *string
	IsDisabled      *bool
}

UserPatch contains independently updateable account fields.

type UserStore

type UserStore interface {
	// Create stores a new user.
	// Returns ErrUserAlreadyExists if a user with the same username exists.
	Create(ctx context.Context, user *User) error

	// GetByID retrieves a user by their unique ID.
	// Returns ErrUserNotFound if the user does not exist.
	GetByID(ctx context.Context, id string) (*User, error)

	// GetByUsername retrieves a user by their username.
	// Returns ErrUserNotFound if the user does not exist.
	GetByUsername(ctx context.Context, username string) (*User, error)

	// GetByOIDCIdentity retrieves a user by their OIDC identity (issuer + subject).
	// Returns ErrOIDCIdentityNotFound if no user exists with the given OIDC identity.
	GetByOIDCIdentity(ctx context.Context, issuer, subject string) (*User, error)

	// GetByTrustedProxyIdentity retrieves a user by their proxy identity source and user.
	// Returns ErrTrustedProxyIdentityNotFound if no user exists with the given identity.
	GetByTrustedProxyIdentity(ctx context.Context, source, user string) (*User, error)

	// List returns all users in the store.
	List(ctx context.Context) ([]*User, error)

	// Update modifies an existing user.
	// Returns ErrUserNotFound if the user does not exist.
	Update(ctx context.Context, user *User) error

	// Patch atomically applies the non-nil fields in patch to an existing user.
	// Returns the stored user after the update.
	Patch(ctx context.Context, id string, patch UserPatch) (*User, error)

	// Delete removes a user by their ID.
	// Returns ErrUserNotFound if the user does not exist.
	Delete(ctx context.Context, id string) error

	// Count returns the total number of users.
	Count(ctx context.Context) (int64, error)
}

UserStore defines the interface for user persistence operations. Implementations must be safe for concurrent use.

type Webhook

type Webhook struct {
	// ID is the unique identifier for the webhook (UUID).
	ID string `json:"id"`
	// DAGName is the file name of the DAG this webhook triggers.
	// This serves as a unique constraint - one webhook per DAG.
	DAGName string `json:"dagName"`
	// TokenHash is the bcrypt hash of the webhook token secret.
	// Excluded from JSON serialization for security.
	TokenHash string `json:"-"`
	// TokenPrefix stores the first 8 characters of the token for identification.
	TokenPrefix string `json:"tokenPrefix"`
	// Enabled indicates whether the webhook is active.
	Enabled bool `json:"enabled"`
	// CreatedAt is the timestamp when the webhook was created.
	CreatedAt time.Time `json:"createdAt"`
	// UpdatedAt is the timestamp when the webhook was last modified.
	UpdatedAt time.Time `json:"updatedAt"`
	// CreatedBy is the user ID of the admin who created the webhook.
	CreatedBy string `json:"createdBy"`
	// LastUsedAt is the timestamp when the webhook was last triggered.
	LastUsedAt *time.Time `json:"lastUsedAt,omitempty"`
	// AuthMode controls which request authentication mode the webhook uses.
	AuthMode WebhookAuthMode `json:"-"`
	// HMACEnforcementMode controls whether HMAC failures block requests when HMAC is enabled.
	HMACEnforcementMode WebhookHMACEnforcementMode `json:"-"`
	// HMACSecret is stored decrypted in memory and encrypted at rest.
	HMACSecret string `json:"-"`
	// HMACSecretGeneratedAt records when the HMAC secret was last generated.
	HMACSecretGeneratedAt *time.Time `json:"-"`
}

Webhook represents a webhook configuration for triggering a specific DAG. Each DAG can have at most one webhook. The token is stored as a bcrypt hash.

func NewWebhook

func NewWebhook(dagName, tokenHash, tokenPrefix, createdBy string) (*Webhook, error)

NewWebhook creates a Webhook with a new UUID and sets CreatedAt and UpdatedAt to the current UTC time. It validates that required fields are not empty. Returns an error if validation fails.

func (*Webhook) EffectiveAuthMode

func (w *Webhook) EffectiveAuthMode() WebhookAuthMode

func (*Webhook) HMACEnabled

func (w *Webhook) HMACEnabled() bool

func (*Webhook) HMACSecretConfigured

func (w *Webhook) HMACSecretConfigured() bool

func (*Webhook) MarshalJSON

func (w *Webhook) MarshalJSON() ([]byte, error)

MarshalJSON exposes defaulted auth mode and public HMAC status while keeping secret material and token hash out of the JSON payload.

func (*Webhook) ToStorage

func (w *Webhook) ToStorage() *WebhookForStorage

ToStorage converts a Webhook to WebhookForStorage for persistence. NOTE: When adding new fields to Webhook or WebhookForStorage, ensure both ToStorage and ToWebhook are updated to maintain field synchronization.

type WebhookAuthMode

type WebhookAuthMode string
const (
	WebhookAuthModeTokenOnly    WebhookAuthMode = "token_only"
	WebhookAuthModeTokenAndHMAC WebhookAuthMode = "token_and_hmac"
	WebhookAuthModeHMACOnly     WebhookAuthMode = "hmac_only"
)

func (WebhookAuthMode) OrDefault

func (m WebhookAuthMode) OrDefault() WebhookAuthMode

type WebhookForStorage

type WebhookForStorage struct {
	ID                    string                     `json:"id"`
	DAGName               string                     `json:"dagName"`
	TokenHash             string                     `json:"tokenHash"`
	TokenPrefix           string                     `json:"tokenPrefix"`
	Enabled               bool                       `json:"enabled"`
	CreatedAt             time.Time                  `json:"createdAt"`
	UpdatedAt             time.Time                  `json:"updatedAt"`
	CreatedBy             string                     `json:"createdBy"`
	LastUsedAt            *time.Time                 `json:"lastUsedAt,omitempty"`
	AuthMode              WebhookAuthMode            `json:"authMode,omitempty"`
	HMACEnforcementMode   WebhookHMACEnforcementMode `json:"hmacEnforcementMode,omitempty"`
	HMACSecretEnc         string                     `json:"hmacSecretEnc,omitempty"`
	HMACSecretGeneratedAt *time.Time                 `json:"hmacSecretGeneratedAt,omitempty"`
}

WebhookForStorage is used for JSON serialization to persistent storage. It includes the token hash which is excluded from the regular Webhook JSON.

func (*WebhookForStorage) ToWebhook

func (s *WebhookForStorage) ToWebhook() *Webhook

ToWebhook converts WebhookForStorage back to Webhook. NOTE: When adding new fields to Webhook or WebhookForStorage, ensure both ToStorage and ToWebhook are updated to maintain field synchronization.

type WebhookHMACEnforcementMode

type WebhookHMACEnforcementMode string
const (
	WebhookHMACEnforcementModeStrict  WebhookHMACEnforcementMode = "strict"
	WebhookHMACEnforcementModeObserve WebhookHMACEnforcementMode = "observe"
)

type WebhookStore

type WebhookStore interface {
	// Create stores a new webhook.
	// Returns ErrWebhookAlreadyExists if a webhook for the DAG already exists.
	Create(ctx context.Context, webhook *Webhook) error

	// GetByID retrieves a webhook by its unique ID.
	// Returns ErrWebhookNotFound if the webhook does not exist.
	GetByID(ctx context.Context, id string) (*Webhook, error)

	// GetByDAGName retrieves the webhook for a specific DAG.
	// Returns ErrWebhookNotFound if no webhook exists for the DAG.
	GetByDAGName(ctx context.Context, dagName string) (*Webhook, error)

	// List returns all webhooks in the store.
	List(ctx context.Context) ([]*Webhook, error)

	// Update modifies an existing webhook.
	// Returns ErrWebhookNotFound if the webhook does not exist.
	Update(ctx context.Context, webhook *Webhook) error

	// Delete removes a webhook by its ID.
	// Returns ErrWebhookNotFound if the webhook does not exist.
	Delete(ctx context.Context, id string) error

	// DeleteByDAGName removes a webhook by its DAG name.
	// Returns ErrWebhookNotFound if no webhook exists for the DAG.
	DeleteByDAGName(ctx context.Context, dagName string) error

	// UpdateLastUsed updates the LastUsedAt timestamp for a webhook.
	// This is called when the webhook is triggered.
	UpdateLastUsed(ctx context.Context, id string) error
}

WebhookStore defines the interface for webhook persistence operations. Implementations must be safe for concurrent use. Each DAG can have at most one webhook (1:1 relationship).

type WorkspaceAccess

type WorkspaceAccess struct {
	All    bool             `json:"all"`
	Grants []WorkspaceGrant `json:"grants,omitempty"`
}

WorkspaceAccess controls which workspaces a user or API key can access.

Missing workspace access is treated as All=true by NormalizeWorkspaceAccess for backward compatibility with existing users and API keys.

func AllWorkspaceAccess

func AllWorkspaceAccess() *WorkspaceAccess

AllWorkspaceAccess returns an all-workspaces access policy.

func CloneWorkspaceAccess

func CloneWorkspaceAccess(access *WorkspaceAccess) *WorkspaceAccess

CloneWorkspaceAccess returns a normalized copy suitable for storage.

func NormalizeWorkspaceAccess

func NormalizeWorkspaceAccess(access *WorkspaceAccess) WorkspaceAccess

NormalizeWorkspaceAccess returns a stable, non-nil workspace access value.

type WorkspaceGrant

type WorkspaceGrant struct {
	Workspace string `json:"workspace"`
	Role      Role   `json:"role"`
}

WorkspaceGrant assigns a role for one workspace.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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