Documentation
¶
Index ¶
- Constants
- Variables
- func APIKeySurfaceStrings(surfaces []APIKeySurface) []string
- func ClientIPFromContext(ctx context.Context) (string, bool)
- func HasAPIKeySurface(surfaces []APIKeySurface, surface APIKeySurface) bool
- func HasWorkspaceAccess(access *WorkspaceAccess, workspaceName string) bool
- func ValidAPIKeySurface(surface APIKeySurface) bool
- func ValidateWorkspaceAccess(globalRole Role, access *WorkspaceAccess, workspaceExists func(string) bool) error
- func WithAPIKey(ctx context.Context, key *APIKey) context.Context
- func WithClientIP(ctx context.Context, ip string) context.Context
- func WithUser(ctx context.Context, user *User) context.Context
- func WorkspaceAccessEqual(left, right *WorkspaceAccess) bool
- type APIKey
- type APIKeyAttributionClass
- type APIKeyForStorage
- type APIKeyStore
- type APIKeySurface
- type AuthorizationSyncResult
- type AuthorizationSyncUserStore
- type Role
- type TokenSecret
- func (ts TokenSecret) GoString() string
- func (ts TokenSecret) IsValid() bool
- func (ts TokenSecret) MarshalJSON() ([]byte, error)
- func (ts TokenSecret) MarshalText() ([]byte, error)
- func (ts TokenSecret) SigningKey() []byte
- func (ts TokenSecret) String() string
- func (ts *TokenSecret) UnmarshalJSON([]byte) error
- func (ts *TokenSecret) UnmarshalText([]byte) error
- type TokenSecretProvider
- type User
- type UserForStorage
- type UserPatch
- type UserStore
- type Webhook
- type WebhookAuthMode
- type WebhookForStorage
- type WebhookHMACEnforcementMode
- type WebhookStore
- type WorkspaceAccess
- type WorkspaceGrant
Constants ¶
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" )
const ( WebhookHMACAlgorithm = "HMAC-SHA256" WebhookHMACHeaderName = "X-Dagu-Signature" WebhookHMACHeaderValueFormat = "sha256=<hex>" )
Variables ¶
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.
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.
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.
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.
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 ¶
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 ¶
WithAPIKey returns a new context that carries the provided API key value.
func WithClientIP ¶
WithClientIP returns a new context that carries the client IP address.
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 ¶
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 ¶
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 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 ¶
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 ¶
CanExecute returns true if the role can run or stop DAGs.
func (Role) CanManageAudit ¶
CanManageAudit returns true if the role can view audit logs.
func (Role) IsAdmin ¶
IsAdmin returns true if the role has administrative privileges (user management).
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 ¶
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 ¶
UserForAPIKeyAttribution returns the subject user represented by an API key.
func UserFromContext ¶
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 ¶
CanUsePassword reports whether password authentication is available to the user.
func (*User) IsOIDCUser ¶
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 ¶
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 (*Webhook) HMACSecretConfigured ¶
func (*Webhook) MarshalJSON ¶
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 ¶
WorkspaceGrant assigns a role for one workspace.