Documentation
¶
Index ¶
- Constants
- Variables
- func CheckPassword(hash, plain string) error
- func ClearSessionCookie(w http.ResponseWriter)
- func GetSessionCookie(r *http.Request) (string, error)
- func HashPassword(plain string) (string, error)
- func IsLinkCode(s string) bool
- func NewSessionID() string
- func SeedPolicies(ctx context.Context, store AuthStore) error
- func SetSessionCookie(w http.ResponseWriter, sessionID string, secure bool)
- type AccessRequest
- type Action
- type AuthStore
- type AuthUser
- type Identity
- type LinkCodeStore
- type Policy
- type PolicyEngine
- type ProvisionRequest
- type RateLimiter
- type Resource
- type ResourceType
- type Session
- type Subject
- type TokenService
- type UserToken
- type VaultWriter
Constants ¶
const ( OpEq = "eq" OpNeq = "neq" OpIn = "in" OpNotIn = "not_in" OpContains = "contains" )
Condition operators.
const ( // SessionCookieName is the HTTP cookie name for session tokens. SessionCookieName = "anna_session" // SessionDuration is the default session lifetime. SessionDuration = 7 * 24 * time.Hour )
const ( EffectAllow = "allow" EffectDeny = "deny" )
Policy effect constants.
const ( RoleAdmin = "admin" RoleUser = "user" )
RoleAdmin and RoleUser are the built-in role IDs.
const ( // AnnaTokenName is re-exported from the vault package for callers that only // import auth. AnnaTokenName = vault.AnnaTokenName )
Variables ¶
var ( ErrRateLimitIP = errors.New("too many requests from this IP, try again later") ErrRateLimitUsername = errors.New("too many failed attempts for this account, try again in 30 seconds") )
Rate limiting errors.
var ErrAccessDenied = errors.New("access denied")
ErrAccessDenied is returned by Must when the request is denied.
var ErrNoSession = errors.New("no session cookie")
ErrNoSession is returned when no session cookie is present.
Functions ¶
func CheckPassword ¶
CheckPassword verifies a plaintext password against a bcrypt hash.
func ClearSessionCookie ¶
func ClearSessionCookie(w http.ResponseWriter)
ClearSessionCookie removes the session cookie from the response. Setting Secure=true is harmless on HTTP and ensures the cookie is properly cleared on HTTPS deployments.
func GetSessionCookie ¶
GetSessionCookie extracts the session ID from the request cookie. Returns ErrNoSession if the cookie is missing or empty.
func HashPassword ¶
HashPassword hashes a plaintext password using bcrypt with cost 12.
func IsLinkCode ¶
IsLinkCode returns true if the string looks like a valid link code format (6 alphanumeric characters). This is a quick check before attempting Consume.
func NewSessionID ¶
func NewSessionID() string
NewSessionID generates a cryptographically random hex-encoded session ID (32 bytes = 64 hex characters).
func SeedPolicies ¶
SeedPolicies ensures the built-in policies exist in the store. It uses an idempotent pattern: existing entries are skipped.
func SetSessionCookie ¶
func SetSessionCookie(w http.ResponseWriter, sessionID string, secure bool)
SetSessionCookie writes the session cookie to the response. The Secure flag is set when secure is true (i.e., not localhost/dev).
Types ¶
type AccessRequest ¶
type AccessRequest struct {
Subject Subject `json:"subject"`
Action Action `json:"action"`
Resource Resource `json:"resource"`
Context map[string]any `json:"context,omitempty"`
}
AccessRequest represents a request to check authorization.
type AuthStore ¶
type AuthStore interface {
// Users
CreateUser(ctx context.Context, username, passwordHash string) (AuthUser, error)
GetUser(ctx context.Context, id int64) (AuthUser, error)
GetUserByUsername(ctx context.Context, username string) (AuthUser, error)
ListUsers(ctx context.Context) ([]AuthUser, error)
UpdateUser(ctx context.Context, u AuthUser) error
UpdateUserRole(ctx context.Context, userID int64, role string) error
UpdateUserDefaultAgent(ctx context.Context, userID int64, agentID string) error
UpdateUserNotifyIdentity(ctx context.Context, userID int64, identityID *int64) error
UpdateUserAgeKeys(ctx context.Context, userID int64, publicKey, privateKey string) error
DeleteUser(ctx context.Context, id int64) error
CountUsers(ctx context.Context) (int64, error)
// Identities (linked channel accounts)
CreateIdentity(ctx context.Context, i Identity) (Identity, error)
GetIdentity(ctx context.Context, id int64) (Identity, error)
GetIdentityByPlatform(ctx context.Context, platform, externalID string) (Identity, error)
UpdateIdentityExternalID(ctx context.Context, id int64, externalID string) error
ListIdentitiesByUser(ctx context.Context, userID int64) ([]Identity, error)
DeleteIdentity(ctx context.Context, id int64) error
// Policies
CreatePolicy(ctx context.Context, p Policy) (Policy, error)
GetPolicy(ctx context.Context, id string) (Policy, error)
ListPolicies(ctx context.Context) ([]Policy, error)
ListEnabledPolicies(ctx context.Context) ([]Policy, error)
UpdatePolicy(ctx context.Context, p Policy) error
DeletePolicy(ctx context.Context, id string) error
// User-Agent assignments
AssignAgent(ctx context.Context, userID int64, agentID string) error
RemoveAgent(ctx context.Context, userID int64, agentID string) error
ListUserAgentIDs(ctx context.Context, userID int64) ([]string, error)
ListAgentUserIDs(ctx context.Context, agentID string) ([]int64, error)
// Sessions
CreateSession(ctx context.Context, s Session) (Session, error)
GetSession(ctx context.Context, id string) (Session, error)
DeleteSession(ctx context.Context, id string) error
DeleteExpiredSessions(ctx context.Context) error
DeleteUserSessions(ctx context.Context, userID int64) error
UpdateSessionExpiry(ctx context.Context, id string, expiresAt time.Time) error
// User tokens
CreateUserToken(ctx context.Context, token UserToken) (UserToken, error)
GetUserTokenByHash(ctx context.Context, tokenHash string) (UserToken, error)
GetActiveUserTokenByHash(ctx context.Context, tokenHash string) (UserToken, error)
GetActiveAutoUserToken(ctx context.Context, userID int64) (UserToken, error)
RotateUserToken(ctx context.Context, id int64) (int64, error)
RevokeUserToken(ctx context.Context, id int64) (int64, error)
UpdateUserTokenLastUsed(ctx context.Context, id int64) (int64, error)
}
AuthStore provides typed access to auth-related data in the database. This is separate from config.Store — auth methods are NOT mixed in.
type AuthUser ¶
type AuthUser struct {
ID int64 `json:"id"`
Username string `json:"username"`
PasswordHash string `json:"-"`
Role string `json:"role"`
IsActive bool `json:"is_active"`
DefaultAgentID string `json:"default_agent_id,omitempty"`
NotifyIdentityID *int64 `json:"notify_identity_id,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
AuthUser represents a system user with login credentials and preferences.
func ProvisionIdentityUser ¶ added in v0.14.0
func ProvisionIdentityUser(ctx context.Context, store AuthStore, req ProvisionRequest) (AuthUser, error)
ProvisionIdentityUser creates a new user + identity pair atomically. It is idempotent: if the (platform, externalID) identity already exists, the existing user is returned without creating anything new.
On a concurrent race where two callers both miss the initial identity lookup and one loses the unique-constraint insert, the loser re-reads the winning identity/user and returns it rather than propagating an error.
type Identity ¶
type Identity struct {
ID int64 `json:"id"`
UserID int64 `json:"user_id"`
Platform string `json:"platform"`
ExternalID string `json:"external_id"`
Name string `json:"name"`
LinkedAt time.Time `json:"linked_at"`
}
Identity represents a linked channel identity.
type LinkCodeStore ¶
type LinkCodeStore struct {
// contains filtered or unexported fields
}
LinkCodeStore manages single-use link codes for channel account linking. Codes expire after 5 minutes. The default constructor keeps them in-memory; the shared constructor persists them to the Anna DB for cross-process use.
func NewLinkCodeStore ¶
func NewLinkCodeStore() *LinkCodeStore
NewLinkCodeStore creates a new link code store.
func NewSharedLinkCodeStore ¶
NewSharedLinkCodeStore creates a link code store backed by the shared Anna DB so admin and channel subprocesses can exchange codes across processes.
type Policy ¶
type Policy struct {
ID string `json:"id"`
Name string `json:"name"`
Effect string `json:"effect"`
Subjects string `json:"subjects"`
Actions string `json:"actions"`
Resources string `json:"resources"`
Conditions string `json:"conditions"`
Priority int `json:"priority"`
IsSystem bool `json:"is_system"`
Enabled bool `json:"enabled"`
CreatedAt time.Time `json:"created_at"`
}
Policy represents an ABAC policy with JSON conditions.
type PolicyEngine ¶
type PolicyEngine struct {
// contains filtered or unexported fields
}
PolicyEngine evaluates access requests against loaded policies using a deny-overrides algorithm. Policies are loaded once at startup.
func NewEngine ¶
func NewEngine(ctx context.Context, store AuthStore) (*PolicyEngine, error)
NewEngine creates a PolicyEngine by loading all enabled policies from the store. Policies are sorted by priority (descending) for deterministic evaluation.
func NewEngineFromPolicies ¶
func NewEngineFromPolicies(policies []Policy) *PolicyEngine
NewEngineFromPolicies creates a PolicyEngine from a pre-loaded set of policies. Useful for testing.
func (*PolicyEngine) Can ¶
func (e *PolicyEngine) Can(_ context.Context, req AccessRequest) bool
Can returns true if the access request is allowed by the loaded policies. It uses the deny-overrides algorithm:
- Find all policies matching subject roles, action, and resource type
- Evaluate conditions (ABAC) for matching policies
- If ANY matching policy has effect=deny -> deny
- If at least one matching policy has effect=allow -> allow
- No match -> deny (default deny)
func (*PolicyEngine) Must ¶
func (e *PolicyEngine) Must(ctx context.Context, req AccessRequest) error
Must is like Can but returns ErrAccessDenied when the request is denied.
type ProvisionRequest ¶ added in v0.14.0
type ProvisionRequest struct {
Platform string
ExternalID string
Name string
EmailHint string
// OnUserCreated is invoked after CreateUser succeeds and before the identity
// is created. Callers can use it to provision optional user-scoped resources
// such as vault keys without adding auth->resource-package dependencies.
OnUserCreated func(ctx context.Context, userID int64) error
}
ProvisionRequest carries the information needed to provision a new user.
type RateLimiter ¶
type RateLimiter struct {
// contains filtered or unexported fields
}
RateLimiter provides per-IP and per-username rate limiting for login attempts.
func (*RateLimiter) CheckIP ¶
func (rl *RateLimiter) CheckIP(ip string) error
CheckIP verifies the IP has not exceeded the request limit. Does not increment the counter — call RecordIPAttempt after a failed attempt.
func (*RateLimiter) CheckUsername ¶
func (rl *RateLimiter) CheckUsername(username string) error
CheckUsername verifies the username is not in a cooldown period.
func (*RateLimiter) RecordIPAttempt ¶
func (rl *RateLimiter) RecordIPAttempt(ip string)
RecordIPAttempt records a failed attempt for rate limiting by IP.
func (*RateLimiter) RecordLoginFailure ¶
func (rl *RateLimiter) RecordLoginFailure(username string)
RecordLoginFailure records a failed login attempt for a username.
func (*RateLimiter) RecordLoginSuccess ¶
func (rl *RateLimiter) RecordLoginSuccess(username string)
RecordLoginSuccess resets the failure counter for a username.
type Resource ¶
type Resource struct {
Type ResourceType `json:"type"`
ID string `json:"id"`
OwnerID int64 `json:"owner_id"`
Attrs map[string]any `json:"attrs,omitempty"`
}
Resource represents the target of an authorization request.
type ResourceType ¶
type ResourceType string
ResourceType is a string alias for resource types.
const ( ResourceAgent ResourceType = "agent" ResourceAgentList ResourceType = "agent_list" ResourceProvider ResourceType = "provider" ResourceChannel ResourceType = "channel" ResourceSession ResourceType = "session" ResourceUser ResourceType = "user" ResourceUserData ResourceType = "user_data" ResourceSkill ResourceType = "skill" ResourceScheduler ResourceType = "scheduler" ResourceSetting ResourceType = "setting" )
ResourceType constants.
type Session ¶
type Session struct {
ID string `json:"id"`
UserID int64 `json:"user_id"`
ExpiresAt time.Time `json:"expires_at"`
CreatedAt time.Time `json:"created_at"`
}
Session represents an HTTP session.
type Subject ¶
type Subject struct {
UserID int64 `json:"user_id"`
Roles []string `json:"roles"`
AgentIDs []string `json:"agent_ids"`
Attrs map[string]any `json:"attrs,omitempty"`
}
Subject represents the entity requesting access.
type TokenService ¶ added in v0.20.0
type TokenService struct {
// contains filtered or unexported fields
}
TokenService owns API token lifecycle and authentication.
func NewTokenService ¶ added in v0.20.0
func NewTokenService(store tokenStore, vault VaultWriter) *TokenService
NewTokenService creates a token service backed by auth persistence and vault writes.
func (*TokenService) Authenticate ¶ added in v0.20.0
Authenticate returns the active user identified by rawToken.
func (*TokenService) EnsureAutoToken ¶ added in v0.20.0
func (s *TokenService) EnsureAutoToken(ctx context.Context, userID int64) error
EnsureAutoToken ensures userID has one active auto-generated token in the vault and token table. Concurrent calls for the same user are safe: the unique partial index on (user_id) WHERE auto_generated=1 AND revoked_at IS NULL prevents duplicate rows; the losing writer gets a constraint error that the caller is expected to log and ignore (see buildSandboxEnv).
type UserToken ¶ added in v0.20.0
type UserToken struct {
ID int64 `json:"id"`
UserID int64 `json:"user_id"`
Name string `json:"name"`
TokenHash string `json:"-"`
TokenPrefix string `json:"token_prefix"`
AutoGenerated bool `json:"auto_generated"`
LastUsedAt *time.Time `json:"last_used_at,omitempty"`
ExpiresAt *time.Time `json:"expires_at,omitempty"`
RotatedAt *time.Time `json:"rotated_at,omitempty"`
RevokedAt *time.Time `json:"revoked_at,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
UserToken represents an API token owned by a user.