security

package
v0.20.0 Latest Latest
Warning

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

Go to latest
Published: Mar 9, 2026 License: Apache-2.0 Imports: 29 Imported by: 0

Documentation

Index

Constants

View Source
const (
	AuthSchemeBearer    = "Bearer"
	QueryKeyAccessToken = "__accessToken"
)

Authentication constants.

View Source
const (
	TokenTypeAccess    = "access"
	TokenTypeRefresh   = "refresh"
	TokenTypeChallenge = "challenge"
)

JWT token type constants.

View Source
const (
	// PrioritySelf indicates access only to data created by the user themselves.
	// This is the most restrictive data scope.
	PrioritySelf = 10

	// PriorityDepartment indicates access to data within the user's department.
	PriorityDepartment = 20

	// PriorityDepartmentAndSub indicates access to data within the user's department and all sub-departments.
	PriorityDepartmentAndSub = 30

	// PriorityOrganization indicates access to data within the user's organization.
	PriorityOrganization = 40

	// PriorityOrganizationAndSub indicates access to data within the user's organization and all sub-organizations.
	PriorityOrganizationAndSub = 50

	// PriorityCustom indicates access to data within the user's custom data scope.
	PriorityCustom = 60

	// PriorityAll indicates unrestricted access to all data.
	// This is the broadest data scope, typically used for system administrators.
	PriorityAll = 10000
)
View Source
const (
	JWTIssuer          = "vef"                                                              // Issuer
	DefaultJWTAudience = "vef-app"                                                          // Audience
	DefaultJWTSecret   = "af6675678bd81ad7c93c4a51d122ef61e9750fe5d42ceac1c33b293f36bc14c2" // Secret
)
View Source
const (
	ChallengeTokenExpires       = 5 * time.Minute
	ClaimChallengePending       = "pnd"
	ClaimChallengePrincipalType = "ptp"
	ClaimChallengeResolved      = "rsd"
)
View Source
const (
	ChallengeTypeSMS   = "sms_otp"
	ChallengeTypeEmail = "email_otp"
)

Challenge type constants for convenience constructors.

View Source
const (
	PasswordChangeReasonFirstLogin = "first_login"
	PasswordChangeReasonExpired    = "expired"
)

Predefined password change reason constants for use in PasswordChangeChecker implementations.

View Source
const (
	ChallengeTypeTOTP      = "totp"
	TOTPDefaultDestination = "Authenticator App"
)
View Source
const ChallengeTypeDepartmentSelection = "department_selection"

ChallengeTypeDepartmentSelection is the challenge type identifier for department selection.

View Source
const ChallengeTypePasswordChange = "password_change"

ChallengeTypePasswordChange is the challenge type identifier for forced password change.

Variables

View Source
var (
	ErrDecodeJWTSecretFailed = errors.New("failed to decode jwt secret")

	ErrDecodeSignatureSecretFailed = errors.New("failed to decode signature secret")
	ErrSignatureSecretRequired     = errors.New("signature secret is required")
	ErrSignatureAppIDRequired      = errors.New("signature appID is required")
	ErrSignatureNonceRequired      = errors.New("signature nonce is required")
	ErrSignatureRequired           = errors.New("signature is required")
	ErrSignatureInvalid            = errors.New("signature is invalid")
	ErrSignatureExpired            = errors.New("signature has expired")
	ErrSignatureNonceUsed          = errors.New("signature nonce has already been used")

	ErrUserDetailsNotStruct        = errors.New("user details type must be a struct or struct pointer")
	ErrExternalAppDetailsNotStruct = errors.New("external app details type must be a struct or struct pointer")

	ErrQueryNotQueryBuilder = errors.New("query does not implement QueryBuilder interface")
	ErrQueryModelNotSet     = errors.New("query must call Model() before applying data permission")
)
View Source
var (
	PrincipalSystem = &Principal{
		Type: PrincipalTypeSystem,
		ID:   orm.OperatorSystem,
		Name: "系统",
	}
	PrincipalAnonymous = NewUser(orm.OperatorAnonymous, "匿名")
)

Functions

func PublishRolePermissionsChangedEvent

func PublishRolePermissionsChangedEvent(publisher event.Publisher, roles ...string)

PublishRolePermissionsChangedEvent publishes a role permissions changed event via the provided publisher. If no roles are specified, subscribers should interpret the event as affecting all roles.

func SetExternalAppDetailsType

func SetExternalAppDetailsType[T any]()

func SetUserDetailsType

func SetUserDetailsType[T any]()

func SubscribeLoginEvent

func SubscribeLoginEvent(subscriber event.Subscriber, handler func(context.Context, *LoginEvent)) event.UnsubscribeFunc

SubscribeLoginEvent subscribes to login events. Returns an unsubscribe function that can be called to remove the subscription.

Types

type AllDataScope

type AllDataScope struct{}

AllDataScope grants access to all data without any restrictions. This is typically used for system administrators or users with full data access.

func (*AllDataScope) Apply

func (*AllDataScope) Key

func (*AllDataScope) Key() string

func (*AllDataScope) Priority

func (*AllDataScope) Priority() int

func (*AllDataScope) Supports

func (*AllDataScope) Supports(*Principal, *orm.Table) bool

type AuthManager

type AuthManager interface {
	// Authenticate finds a suitable authenticator and validates the credentials.
	Authenticate(ctx context.Context, authentication Authentication) (*Principal, error)
}

AuthManager orchestrates authentication by delegating to registered Authenticators. It iterates through available authenticators to find one that supports the authentication kind and can successfully validate the credentials.

type AuthTokens

type AuthTokens struct {
	AccessToken  string `json:"accessToken"`
	RefreshToken string `json:"refreshToken"`
}

AuthTokens holds the access and refresh token pair issued after successful authentication.

type Authentication

type Authentication struct {
	Type        string `json:"type"`
	Principal   string `json:"principal"`
	Credentials any    `json:"credentials"`
}

Authentication carries the client-supplied authentication payload.

type Authenticator

type Authenticator interface {
	// Supports returns true if this authenticator can handle the given authentication type.
	Supports(authType string) bool
	// Authenticate validates the credentials and returns the authenticated Principal.
	Authenticate(ctx context.Context, authentication Authentication) (*Principal, error)
}

Authenticator validates credentials and returns a Principal on success. Multiple authenticators can be registered to support different authentication methods (e.g., JWT, password, OAuth). Each authenticator declares which authentication kinds it supports via the Supports method.

type CachedRolePermissionsLoader

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

CachedRolePermissionsLoader is a decorator that adds caching to a RolePermissionsLoader. It uses the cache system and event bus for automatic cache invalidation.

func (*CachedRolePermissionsLoader) LoadPermissions

func (c *CachedRolePermissionsLoader) LoadPermissions(ctx context.Context, role string) (map[string]DataScope, error)

type ChallengeProvider

type ChallengeProvider interface {
	// Type returns the unique challenge type identifier (e.g. "totp", "select_department").
	Type() string
	// Order returns the evaluation priority. Lower values are evaluated first.
	Order() int
	// Evaluate checks whether this challenge applies to the given principal.
	// Return nil to indicate the challenge is not needed.
	Evaluate(ctx context.Context, principal *Principal) (*LoginChallenge, error)
	// Resolve validates the user's response and returns an optionally updated Principal.
	Resolve(ctx context.Context, principal *Principal, response any) (*Principal, error)
}

ChallengeProvider evaluates and resolves a login challenge. Register implementations via vef.ProvideChallengeProvider to inject additional steps into the login flow (e.g., 2FA, department selection).

Providers are evaluated sequentially in Order() ascending order. Each challenge is presented and resolved one at a time before the next provider is evaluated.

type ChallengeState

type ChallengeState struct {
	Principal *Principal
	Pending   []string
	Resolved  []string
}

ChallengeState holds the state tracked by a challenge token.

type ChallengeTokenStore

type ChallengeTokenStore interface {
	// Generate creates a challenge token encoding the principal and challenge state.
	Generate(principal *Principal, pending, resolved []string) (string, error)
	// Parse retrieves the challenge state from a token.
	Parse(token string) (*ChallengeState, error)
}

ChallengeTokenStore manages the lifecycle of challenge tokens. Challenge tokens carry the intermediate state between login steps, allowing the login flow to pause for user input (e.g., 2FA code, department selection). The default implementation uses JWT; alternatives (e.g., Redis) can be swapped via DI.

func NewJWTChallengeTokenStore

func NewJWTChallengeTokenStore(jwt *JWT) ChallengeTokenStore

NewJWTChallengeTokenStore creates a new JWT-based challenge token store.

func NewMemoryChallengeTokenStore

func NewMemoryChallengeTokenStore() ChallengeTokenStore

NewMemoryChallengeTokenStore creates a new memory-backed challenge token store.

func NewRedisChallengeTokenStore

func NewRedisChallengeTokenStore(client *redis.Client) ChallengeTokenStore

NewRedisChallengeTokenStore creates a new Redis-backed challenge token store.

type DataPermissionApplier

type DataPermissionApplier interface {
	// Apply adds data permission filters to the query based on the current context.
	Apply(query orm.SelectQuery) error
}

DataPermissionApplier applies data permission filters to database queries. Wraps the resolution and application of DataScope into a single operation.

func NewRequestScopedDataPermApplier

func NewRequestScopedDataPermApplier(
	principal *Principal,
	dataScope DataScope,
	logger log.Logger,
) DataPermissionApplier

NewRequestScopedDataPermApplier creates a new request-scoped data permission applier. This function is typically called by the data permission middleware for each request.

type DataPermissionResolver

type DataPermissionResolver interface {
	// ResolveDataScope returns the DataScope that should be applied for the permission.
	ResolveDataScope(ctx context.Context, principal *Principal, permToken string) (DataScope, error)
}

DataPermissionResolver determines the applicable DataScope for a permission. Used to translate permission tokens into concrete data filtering rules.

type DataScope

type DataScope interface {
	// Key returns a unique identifier for this data scope type.
	Key() string
	// Priority determines the order when multiple scopes apply (lower = higher priority).
	Priority() int
	// Supports returns true if this scope applies to the given Principal and table.
	Supports(principal *Principal, table *orm.Table) bool
	// Apply modifies the query to enforce the data scope restrictions.
	Apply(principal *Principal, query orm.SelectQuery) error
}

DataScope defines row-level data access restrictions. Implementations filter query results based on the Principal's permissions, enabling multi-tenant data isolation or hierarchical data access control.

func NewAllDataScope

func NewAllDataScope() DataScope

NewAllDataScope creates a new AllDataScope instance.

func NewSelfDataScope

func NewSelfDataScope(createdByColumn string) DataScope

NewSelfDataScope creates a new SelfDataScope instance. The createdByColumn parameter specifies the database column name for the creator. If empty, it defaults to "created_by".

type DeliveredCodeSender

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

DeliveredCodeSender orchestrates OTPCodeStore and OTPCodeDelivery to implement OTPCodeSender. Flow: generate code -> store it -> deliver through the channel.

func NewDeliveredCodeSender

func NewDeliveredCodeSender(store OTPCodeStore, delivery OTPCodeDelivery) *DeliveredCodeSender

NewDeliveredCodeSender creates a DeliveredCodeSender that generates codes via store and sends them via delivery.

func (*DeliveredCodeSender) Send

func (s *DeliveredCodeSender) Send(ctx context.Context, principal *Principal) error

type DeliveredCodeVerifier

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

DeliveredCodeVerifier delegates to OTPCodeStore to verify codes, implementing OTPCodeVerifier.

func NewDeliveredCodeVerifier

func NewDeliveredCodeVerifier(store OTPCodeStore) *DeliveredCodeVerifier

NewDeliveredCodeVerifier creates a DeliveredCodeVerifier backed by the given store.

func (*DeliveredCodeVerifier) Verify

func (v *DeliveredCodeVerifier) Verify(ctx context.Context, principal *Principal, code string) (bool, error)

type DepartmentLoader

type DepartmentLoader interface {
	// LoadDepartments returns the departments available to the user.
	// Return nil or an empty slice to skip the challenge.
	LoadDepartments(ctx context.Context, principal *Principal) ([]DepartmentOption, error)
}

DepartmentLoader loads the list of departments available to a user.

type DepartmentOption

type DepartmentOption struct {
	ID   string `json:"id"`
	Name string `json:"name"`
}

DepartmentOption represents a selectable department.

type DepartmentSelectionChallengeData

type DepartmentSelectionChallengeData struct {
	Departments []DepartmentOption `json:"departments"`
	Meta        map[string]any     `json:"meta,omitempty"`
}

DepartmentSelectionChallengeData describes the metadata for a department selection challenge.

type DepartmentSelectionChallengeProvider

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

DepartmentSelectionChallengeProvider orchestrates department selection evaluation and resolution. It implements the ChallengeProvider interface.

func NewDepartmentSelectionChallengeProvider

func NewDepartmentSelectionChallengeProvider(loader DepartmentLoader, selector DepartmentSelector) *DepartmentSelectionChallengeProvider

NewDepartmentSelectionChallengeProvider creates a department selection challenge provider. Default type "department_selection", order 500. Panics if loader or selector is nil.

func (*DepartmentSelectionChallengeProvider) Evaluate

func (*DepartmentSelectionChallengeProvider) Order

func (*DepartmentSelectionChallengeProvider) Resolve

func (p *DepartmentSelectionChallengeProvider) Resolve(ctx context.Context, principal *Principal, response any) (*Principal, error)

func (*DepartmentSelectionChallengeProvider) Type

type DepartmentSelector

type DepartmentSelector interface {
	// SelectDepartment validates the department choice and returns an enriched principal.
	SelectDepartment(ctx context.Context, principal *Principal, departmentID string) (*Principal, error)
}

DepartmentSelector validates the user's department selection and enriches the principal.

type ExternalAppConfig

type ExternalAppConfig struct {
	Enabled     bool   `json:"enabled"`
	IPWhitelist string `json:"ipWhitelist"`
}

ExternalAppConfig holds configuration for an external application.

type ExternalAppLoader

type ExternalAppLoader interface {
	// LoadByID retrieves an external app by its ID, returning the Principal,
	// secret key for signature verification, and any error.
	LoadByID(ctx context.Context, id string) (*Principal, string, error)
}

ExternalAppLoader retrieves external application credentials for API authentication. Used by OpenAPI authenticator to validate app-based signature authentication.

type Gender

type Gender string
const (
	GenderMale    Gender = "male"
	GenderFemale  Gender = "female"
	GenderUnknown Gender = "unknown"
)

type IPWhitelistValidator

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

IPWhitelistValidator validates IP addresses against a whitelist. It supports both individual IP addresses and CIDR notation.

func NewIPWhitelistValidator

func NewIPWhitelistValidator(whitelist string) *IPWhitelistValidator

NewIPWhitelistValidator creates a new IP whitelist validator from a comma-separated string. Supports individual IP addresses (e.g., "192.168.1.1") and CIDR notation (e.g., "192.168.1.0/24"). An empty whitelist means all IPs are allowed.

func (*IPWhitelistValidator) IsAllowed

func (v *IPWhitelistValidator) IsAllowed(ipStr string) bool

IsAllowed checks if the given IP address is in the whitelist.

func (*IPWhitelistValidator) IsEmpty

func (v *IPWhitelistValidator) IsEmpty() bool

IsEmpty returns true if the whitelist is empty (no restrictions).

type JWT

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

JWT provides low-level JWT token operations. It handles token generation, parsing, and validation without business logic.

func NewJWT

func NewJWT(config *JWTConfig) (*JWT, error)

NewJWT creates a new JWT instance with the given configuration. Secret expects a hex-encoded string; invalid hex will cause an error. Audience will be defaulted when empty.

func (*JWT) Generate

func (j *JWT) Generate(claimsBuilder *JWTClaimsBuilder, expires, notBefore time.Duration) (string, error)

Generate creates a JWT token with the given claims and expires. The expiration is computed as now + expires; iat and nbf are set to now.

func (*JWT) Parse

func (j *JWT) Parse(tokenString string) (*JWTClaimsAccessor, error)

Parse parses and validates a JWT token. It returns a read-only claims accessor which performs safe conversions and never panics.

type JWTChallengeTokenStore

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

JWTChallengeTokenStore implements ChallengeTokenStore using stateless JWT tokens. Challenge state (principal, pending/resolved types) is encoded directly in the token, avoiding server-side session storage.

func (*JWTChallengeTokenStore) Generate

func (s *JWTChallengeTokenStore) Generate(principal *Principal, pending, resolved []string) (string, error)

func (*JWTChallengeTokenStore) Parse

func (s *JWTChallengeTokenStore) Parse(token string) (*ChallengeState, error)

type JWTClaimsAccessor

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

func NewJWTClaimsAccessor

func NewJWTClaimsAccessor(claims jwt.MapClaims) *JWTClaimsAccessor

NewJWTClaimsAccessor creates a new JWT claims accessor.

func (*JWTClaimsAccessor) Claim

func (a *JWTClaimsAccessor) Claim(key string) any

Claim returns the claim.

func (*JWTClaimsAccessor) Details

func (a *JWTClaimsAccessor) Details() any

Details returns the details claim.

func (*JWTClaimsAccessor) ID

func (a *JWTClaimsAccessor) ID() string

ID returns the JWT ID claim. Returns empty string if the claim is missing or not a string.

func (*JWTClaimsAccessor) Roles

func (a *JWTClaimsAccessor) Roles() []string

Roles returns the roles claim. Supports both []string and []any payloads; returns empty slice if absent.

func (*JWTClaimsAccessor) Subject

func (a *JWTClaimsAccessor) Subject() string

Subject returns the subject claim. Returns empty string if the claim is missing or not a string.

func (*JWTClaimsAccessor) Type

func (a *JWTClaimsAccessor) Type() string

Type returns the token type claim. Returns empty string if the claim is missing or not a string.

type JWTClaimsBuilder

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

JWTClaimsBuilder helps build JWT claims for different token types.

func NewJWTClaimsBuilder

func NewJWTClaimsBuilder() *JWTClaimsBuilder

NewJWTClaimsBuilder creates a new JWT claims builder.

func (*JWTClaimsBuilder) Claim

func (b *JWTClaimsBuilder) Claim(key string) (any, bool)

Claim returns a custom claim.

func (*JWTClaimsBuilder) Details

func (b *JWTClaimsBuilder) Details() (any, bool)

Details returns the details claim.

func (*JWTClaimsBuilder) ID

func (b *JWTClaimsBuilder) ID() (string, bool)

ID returns the JWT ID claim.

func (*JWTClaimsBuilder) Roles

func (b *JWTClaimsBuilder) Roles() ([]string, bool)

Roles returns the roles claim.

func (*JWTClaimsBuilder) Subject

func (b *JWTClaimsBuilder) Subject() (string, bool)

Subject returns the subject claim.

func (*JWTClaimsBuilder) Type

func (b *JWTClaimsBuilder) Type() (string, bool)

Type returns the token type claim.

func (*JWTClaimsBuilder) WithClaim

func (b *JWTClaimsBuilder) WithClaim(key string, value any) *JWTClaimsBuilder

func (*JWTClaimsBuilder) WithDetails

func (b *JWTClaimsBuilder) WithDetails(details any) *JWTClaimsBuilder

func (*JWTClaimsBuilder) WithID

func (b *JWTClaimsBuilder) WithID(id string) *JWTClaimsBuilder

func (*JWTClaimsBuilder) WithRoles

func (b *JWTClaimsBuilder) WithRoles(roles []string) *JWTClaimsBuilder

func (*JWTClaimsBuilder) WithSubject

func (b *JWTClaimsBuilder) WithSubject(subject string) *JWTClaimsBuilder

func (*JWTClaimsBuilder) WithType

func (b *JWTClaimsBuilder) WithType(typ string) *JWTClaimsBuilder

type JWTConfig

type JWTConfig struct {
	Secret   string `config:"secret"`   // Secret key for JWT signing
	Audience string `config:"audience"` // JWT audience
}

JWTConfig is the configuration for the JWT token.

type LoginChallenge

type LoginChallenge struct {
	Type     string `json:"type"`
	Data     any    `json:"data,omitempty"`
	Required bool   `json:"required"`
}

LoginChallenge describes a challenge the user must complete during login.

type LoginEvent

type LoginEvent struct {
	event.BaseEvent

	AuthType   string  `json:"authType"`
	UserID     *string `json:"userId"` // Populated on success
	Username   string  `json:"username"`
	LoginIP    string  `json:"loginIp"`
	UserAgent  string  `json:"userAgent"`
	TraceID    string  `json:"traceId"`
	IsOk       bool    `json:"isOk"`
	FailReason string  `json:"failReason"` // Populated on failure
	ErrorCode  int     `json:"errorCode"`
}

LoginEvent represents a user login event.

func NewLoginEvent

func NewLoginEvent(params LoginEventParams) *LoginEvent

NewLoginEvent creates a new login event with the given parameters.

type LoginEventParams

type LoginEventParams struct {
	AuthType   string
	UserID     *string
	Username   string
	LoginIP    string
	UserAgent  string
	TraceID    string
	IsOk       bool
	FailReason string
	ErrorCode  int
}

LoginEventParams contains parameters for creating a LoginEvent.

type LoginResult

type LoginResult struct {
	Tokens         *AuthTokens     `json:"tokens,omitempty"`
	ChallengeToken string          `json:"challengeToken,omitempty"`
	Challenge      *LoginChallenge `json:"challenge,omitempty"`
}

LoginResult represents the response of a login attempt. When a challenge is pending, Tokens is nil and ChallengeToken + Challenge are set. When all challenges are resolved (or none were needed), Tokens is set.

type MemoryChallengeTokenStore

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

MemoryChallengeTokenStore implements ChallengeTokenStore using an in-memory cache. Suitable for single-instance deployments or testing; for distributed setups use Redis-backed stores.

func (*MemoryChallengeTokenStore) Generate

func (s *MemoryChallengeTokenStore) Generate(principal *Principal, pending, resolved []string) (string, error)

func (*MemoryChallengeTokenStore) Parse

type MemoryNonceStore

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

MemoryNonceStore implements NonceStore using an in-memory cache. This implementation is suitable for development and single-instance deployments. For distributed systems, use RedisNonceStore instead.

func (*MemoryNonceStore) StoreIfAbsent

func (m *MemoryNonceStore) StoreIfAbsent(ctx context.Context, appID, nonce string, ttl time.Duration) (bool, error)

StoreIfAbsent atomically stores the nonce only when it does not exist.

type NonceStore

type NonceStore interface {
	// StoreIfAbsent atomically stores a nonce only when it does not already exist.
	// It returns true when the nonce was newly stored, false when the nonce already existed.
	// The TTL should be slightly longer than timestamp tolerance to ensure
	// nonces remain valid while their corresponding timestamps are accepted.
	StoreIfAbsent(ctx context.Context, appID, nonce string, ttl time.Duration) (bool, error)
}

NonceStore manages nonce lifecycle for replay attack prevention. Stores used nonces with TTL to detect and reject duplicate requests. Implementations must be thread-safe for concurrent access.

func NewMemoryNonceStore

func NewMemoryNonceStore() NonceStore

NewMemoryNonceStore creates a new in-memory nonce store.

func NewRedisNonceStore

func NewRedisNonceStore(client *redis.Client) NonceStore

NewRedisNonceStore creates a new Redis-backed nonce store.

type OTPChallengeData

type OTPChallengeData struct {
	Destination string         `json:"destination"`
	Meta        map[string]any `json:"meta,omitempty"`
}

OTPChallengeData describes metadata presented to the user for an OTP challenge.

type OTPChallengeProvider

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

OTPChallengeProvider orchestrates OTP evaluation, sending, and verification. It implements the ChallengeProvider interface.

func NewDeliveredChallengeProvider

func NewDeliveredChallengeProvider(challengeType string, order int, evaluator OTPEvaluator, store OTPCodeStore, delivery OTPCodeDelivery) *OTPChallengeProvider

NewDeliveredChallengeProvider creates an OTP challenge provider backed by OTPCodeStore and OTPCodeDelivery.

func NewEmailChallengeProvider

func NewEmailChallengeProvider(evaluator OTPEvaluator, store OTPCodeStore, delivery OTPCodeDelivery) *OTPChallengeProvider

NewEmailChallengeProvider creates an Email OTP challenge provider. Default type "email_otp", order 300.

func NewOTPChallengeProvider

func NewOTPChallengeProvider(config OTPChallengeProviderConfig) *OTPChallengeProvider

NewOTPChallengeProvider creates a generic OTP challenge provider. ChallengeType, Evaluator, and Verifier are required; panics if any is missing.

func NewSMSChallengeProvider

func NewSMSChallengeProvider(evaluator OTPEvaluator, store OTPCodeStore, delivery OTPCodeDelivery) *OTPChallengeProvider

NewSMSChallengeProvider creates an SMS OTP challenge provider. Default type "sms_otp", order 200.

func NewTOTPChallengeProvider

func NewTOTPChallengeProvider(loader TOTPSecretLoader, opts ...TOTPOption) *OTPChallengeProvider

NewTOTPChallengeProvider creates a TOTP challenge provider. Only TOTPSecretLoader needs to be implemented by the application. Default type "totp", order 100, destination "Authenticator App".

func (*OTPChallengeProvider) Evaluate

func (p *OTPChallengeProvider) Evaluate(ctx context.Context, principal *Principal) (*LoginChallenge, error)

func (*OTPChallengeProvider) Order

func (p *OTPChallengeProvider) Order() int

func (*OTPChallengeProvider) Resolve

func (p *OTPChallengeProvider) Resolve(ctx context.Context, principal *Principal, response any) (*Principal, error)

func (*OTPChallengeProvider) Type

func (p *OTPChallengeProvider) Type() string

type OTPChallengeProviderConfig

type OTPChallengeProviderConfig struct {
	ChallengeType  string          // Required, unique identifier (e.g. "totp", "sms_otp").
	ChallengeOrder int             // Evaluation priority; lower values are evaluated first.
	Evaluator      OTPEvaluator    // Required.
	Sender         OTPCodeSender   // Optional; nil skips the send step.
	Verifier       OTPCodeVerifier // Required.
}

OTPChallengeProviderConfig configures an OTPChallengeProvider.

type OTPCodeDelivery

type OTPCodeDelivery interface {
	// Deliver sends the given OTP code to the principal via the configured channel.
	Deliver(ctx context.Context, principal *Principal, code string) error
}

OTPCodeDelivery sends a generated OTP code to the user through a specific channel (SMS, email, etc.).

type OTPCodeSender

type OTPCodeSender interface {
	// Send triggers the delivery of an OTP code to the given principal.
	Send(ctx context.Context, principal *Principal) error
}

OTPCodeSender triggers OTP code delivery. For TOTP this is nil (codes are generated by an authenticator app); for SMS/Email this sends the code through the chosen channel.

type OTPCodeStore

type OTPCodeStore interface {
	// Generate creates a new OTP code, stores it, and returns the generated code.
	Generate(ctx context.Context, principal *Principal) (string, error)
	// Verify checks whether the submitted code matches the stored code.
	Verify(ctx context.Context, principal *Principal, code string) (bool, error)
}

OTPCodeStore manages the lifecycle of server-generated OTP codes. Used for SMS/Email flows where the server generates, stores, and verifies codes.

type OTPCodeVerifier

type OTPCodeVerifier interface {
	// Verify checks whether the submitted code is valid for the given principal.
	Verify(ctx context.Context, principal *Principal, code string) (bool, error)
}

OTPCodeVerifier validates a user-submitted OTP code.

type OTPEvaluator

type OTPEvaluator interface {
	// Evaluate checks the principal and returns challenge data if OTP is required.
	// Return nil to skip the challenge; non-nil triggers a challenge.
	Evaluate(ctx context.Context, principal *Principal) (*OTPChallengeData, error)
}

OTPEvaluator determines whether a user needs OTP verification.

type PasswordChangeChallengeData

type PasswordChangeChallengeData struct {
	Reason string         `json:"reason"`
	Meta   map[string]any `json:"meta,omitempty"`
}

PasswordChangeChallengeData describes the metadata for a forced password change challenge.

type PasswordChangeChallengeProvider

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

PasswordChangeChallengeProvider orchestrates forced password change evaluation and resolution. It implements the ChallengeProvider interface.

func NewPasswordChangeChallengeProvider

func NewPasswordChangeChallengeProvider(checker PasswordChangeChecker, changer PasswordChanger) *PasswordChangeChallengeProvider

NewPasswordChangeChallengeProvider creates a forced password change challenge provider. Default type "password_change", order 400. Panics if checker or changer is nil.

func (*PasswordChangeChallengeProvider) Evaluate

func (*PasswordChangeChallengeProvider) Order

func (*PasswordChangeChallengeProvider) Resolve

func (p *PasswordChangeChallengeProvider) Resolve(ctx context.Context, principal *Principal, response any) (*Principal, error)

func (*PasswordChangeChallengeProvider) Type

type PasswordChangeChecker

type PasswordChangeChecker interface {
	// Check returns challenge data (including reason) if a password change is required,
	// or nil if no change is needed.
	Check(ctx context.Context, principal *Principal) (*PasswordChangeChallengeData, error)
}

PasswordChangeChecker determines whether a user must change their password.

type PasswordChanger

type PasswordChanger interface {
	// ChangePassword validates password strength and persists the new password.
	ChangePassword(ctx context.Context, principal *Principal, newPassword string) error
}

PasswordChanger validates and persists a new password.

type PasswordDecryptor

type PasswordDecryptor interface {
	// Decrypt transforms an encrypted password back to plaintext for verification.
	Decrypt(encryptedPassword string) (string, error)
}

PasswordDecryptor decrypts client-side encrypted passwords before verification. Used when passwords are encrypted during transmission for additional security.

type PermissionChecker

type PermissionChecker interface {
	// HasPermission returns true if the Principal has the specified permission token.
	HasPermission(ctx context.Context, principal *Principal, permToken string) (bool, error)
}

PermissionChecker verifies if a Principal has a specific permission. Used by authorization middleware to enforce access control on API endpoints.

type Principal

type Principal struct {
	// Type is the type of the principal.
	Type PrincipalType `json:"type"`
	// ID is the id of the user.
	ID string `json:"id"`
	// Name is the name of the user.
	Name string `json:"name"`
	// Roles is the roles of the user.
	Roles []string `json:"roles"`
	// Details is the details of the user.
	Details any `json:"details"`
}

Principal is the principal of the user.

func NewExternalApp

func NewExternalApp(id, name string, roles ...string) *Principal

NewExternalApp is the function to create a new external app principal.

func NewUser

func NewUser(id, name string, roles ...string) *Principal

NewUser is the function to create a new user principal.

func (*Principal) AttemptUnmarshalDetails

func (p *Principal) AttemptUnmarshalDetails(details any)

AttemptUnmarshalDetails attempts to unmarshal the details into the principal.

func (*Principal) UnmarshalJSON

func (p *Principal) UnmarshalJSON(data []byte) error

UnmarshalJSON implements custom JSON unmarshaling for Principal. This allows the Details field to be properly deserialized based on the Type field.

func (*Principal) WithRoles

func (p *Principal) WithRoles(roles ...string) *Principal

WithRoles adds roles to the principal.

type PrincipalType

type PrincipalType string

PrincipalType is the type of the principal.

const (
	// PrincipalTypeUser is the type of the user.
	PrincipalTypeUser PrincipalType = "user"
	// PrincipalTypeExternalApp is the type of the external app.
	PrincipalTypeExternalApp PrincipalType = "external_app"
	// PrincipalTypeSystem is the type of the system.
	PrincipalTypeSystem PrincipalType = orm.OperatorSystem
)

type RedisChallengeTokenStore

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

RedisChallengeTokenStore implements ChallengeTokenStore using Redis for distributed deployments.

func (*RedisChallengeTokenStore) Generate

func (s *RedisChallengeTokenStore) Generate(principal *Principal, pending, resolved []string) (string, error)

func (*RedisChallengeTokenStore) Parse

type RedisNonceStore

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

RedisNonceStore implements NonceStore using Redis for distributed deployments.

func (*RedisNonceStore) StoreIfAbsent

func (s *RedisNonceStore) StoreIfAbsent(ctx context.Context, appID, nonce string, ttl time.Duration) (bool, error)

StoreIfAbsent atomically stores the nonce only when it does not exist.

type RequestScopedDataPermApplier

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

RequestScopedDataPermApplier is the default implementation of DataPermissionApplier. It applies data permission filtering using a single DataScope instance.

IMPORTANT: This struct is request-scoped and should NOT be stored beyond request lifecycle.

func (*RequestScopedDataPermApplier) Apply

Apply implements security.DataPermissionApplier.Apply.

type RolePermissionsChangedEvent

type RolePermissionsChangedEvent struct {
	event.BaseEvent

	Roles []string `json:"roles"` // Affected role names (empty means all roles)
}

RolePermissionsChangedEvent is published when role permissions are modified.

type RolePermissionsLoader

type RolePermissionsLoader interface {
	// LoadPermissions returns a map of permission tokens to their DataScope for a role.
	LoadPermissions(ctx context.Context, role string) (map[string]DataScope, error)
}

RolePermissionsLoader retrieves permissions associated with a role. Used by RBAC implementations to build the permission set for authorization checks.

func NewCachedRolePermissionsLoader

func NewCachedRolePermissionsLoader(
	loader RolePermissionsLoader,
	eventBus event.Subscriber,
) RolePermissionsLoader

NewCachedRolePermissionsLoader creates a new cached role permissions loader. It automatically subscribes to role permissions change events to invalidate cache.

type SelfDataScope

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

SelfDataScope restricts access to data created by the user themselves. This is commonly used for personal data access where users can only see their own records.

func (*SelfDataScope) Apply

func (s *SelfDataScope) Apply(principal *Principal, query orm.SelectQuery) error

func (*SelfDataScope) Key

func (*SelfDataScope) Key() string

func (*SelfDataScope) Priority

func (*SelfDataScope) Priority() int

func (*SelfDataScope) Supports

func (s *SelfDataScope) Supports(_ *Principal, table *orm.Table) bool

type Signature

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

Signature provides HMAC-based signature generation and verification. It handles timestamp validation and supports optional data hash for integrity.

func NewSignature

func NewSignature(secret string, opts ...SignatureOption) (*Signature, error)

NewSignature creates a new Signature instance. The secret parameter is required and expects a hex-encoded string.

func (*Signature) Sign

func (s *Signature) Sign(appID string) (*SignatureResult, error)

Sign generates a signature for the given appID. Returns a SignatureResult containing all signature components.

func (*Signature) Verify

func (s *Signature) Verify(ctx context.Context, appID string, timestamp int64, nonce, signature string) error

Verify validates the signature against the provided parameters. Returns nil if valid, or an error describing the validation failure.

func (*Signature) VerifyWithSecret

func (s *Signature) VerifyWithSecret(ctx context.Context, secret, appID string, timestamp int64, nonce, signature string) error

VerifyWithSecret validates the signature using an externally provided secret. This is useful when the secret is loaded dynamically per-request (e.g., from ExternalAppLoader). The secret parameter expects a hex-encoded string.

type SignatureAlgorithm

type SignatureAlgorithm string

SignatureAlgorithm represents the HMAC algorithm used for signing.

const (
	SignatureAlgHmacSHA256 SignatureAlgorithm = "HMAC-SHA256"
	SignatureAlgHmacSHA512 SignatureAlgorithm = "HMAC-SHA512"
	SignatureAlgHmacSM3    SignatureAlgorithm = "HMAC-SM3"
)

type SignatureCredentials

type SignatureCredentials struct {
	// Timestamp is the Unix timestamp (seconds) when the request was created.
	Timestamp int64

	// Nonce is a random string to prevent replay attacks.
	Nonce string

	// Signature is the HMAC signature in hex encoding.
	Signature string
}

SignatureCredentials represents the credentials extracted from HTTP headers for signature-based authentication.

type SignatureOption

type SignatureOption func(*Signature)

SignatureOption configures a Signature instance.

func WithAlgorithm

func WithAlgorithm(algorithm SignatureAlgorithm) SignatureOption

WithAlgorithm sets the HMAC algorithm. Defaults to HMAC-SHA256.

func WithNonceStore

func WithNonceStore(store NonceStore) SignatureOption

WithNonceStore sets the nonce store for replay attack prevention. If not set, nonce validation is skipped.

func WithTimestampTolerance

func WithTimestampTolerance(tolerance time.Duration) SignatureOption

WithTimestampTolerance sets the maximum allowed time difference. Defaults to 5 minutes.

type SignatureResult

type SignatureResult struct {
	AppID     string
	Timestamp int64
	Nonce     string
	Signature string
}

SignatureResult contains the result of a signature operation.

type TOTPEvaluator

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

TOTPEvaluator checks whether a user needs TOTP verification, implementing OTPEvaluator.

func NewTOTPEvaluator

func NewTOTPEvaluator(loader TOTPSecretLoader, opts ...TOTPOption) *TOTPEvaluator

NewTOTPEvaluator creates a TOTPEvaluator with the given loader and optional configuration.

func (*TOTPEvaluator) Evaluate

func (e *TOTPEvaluator) Evaluate(ctx context.Context, principal *Principal) (*OTPChallengeData, error)

type TOTPOption

type TOTPOption func(*TOTPEvaluator)

TOTPOption configures optional parameters for TOTPEvaluator.

func WithTOTPDestination

func WithTOTPDestination(destination string) TOTPOption

WithTOTPDestination sets the destination description shown to the user for the TOTP challenge. Defaults to TOTPDefaultDestination ("Authenticator App").

type TOTPSecretLoader

type TOTPSecretLoader interface {
	// LoadSecret returns the base32-encoded TOTP secret for the given principal.
	// An empty string means the user has not configured TOTP.
	LoadSecret(ctx context.Context, principal *Principal) (string, error)
}

TOTPSecretLoader loads a user's TOTP secret.

type TOTPVerifier

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

TOTPVerifier validates TOTP codes using standard parameters (SHA1, 6 digits, 30s period, skew=1). It implements OTPCodeVerifier.

func NewTOTPVerifier

func NewTOTPVerifier(loader TOTPSecretLoader) *TOTPVerifier

NewTOTPVerifier creates a TOTPVerifier with the given secret loader.

func (*TOTPVerifier) Verify

func (v *TOTPVerifier) Verify(ctx context.Context, principal *Principal, code string) (bool, error)

type TokenGenerator

type TokenGenerator interface {
	// Generate creates a new token pair for the given Principal.
	Generate(principal *Principal) (*AuthTokens, error)
}

TokenGenerator creates access and refresh tokens for an authenticated Principal. Used after successful authentication to issue JWT tokens or similar credentials.

type UserInfo

type UserInfo struct {
	ID         string      `json:"id"`
	Name       string      `json:"name"`
	Gender     Gender      `json:"gender"`
	Avatar     null.String `json:"avatar"`
	PermTokens []string    `json:"permTokens"`
	Menus      []UserMenu  `json:"menus"`
	Details    any         `json:"details,omitempty"`
}

type UserInfoLoader

type UserInfoLoader interface {
	// LoadUserInfo retrieves detailed user information based on the Principal and parameters.
	LoadUserInfo(ctx context.Context, principal *Principal, params map[string]any) (*UserInfo, error)
}

UserInfoLoader retrieves extended user information for the current session. Used to populate user profile data, preferences, or other session-specific details.

type UserLoader

type UserLoader interface {
	// LoadByUsername retrieves a user by username, returning the Principal,
	// hashed password, and any error. Used for password-based authentication.
	LoadByUsername(ctx context.Context, username string) (*Principal, string, error)
	// LoadByID retrieves a user by their unique identifier.
	// Used for token refresh and session validation.
	LoadByID(ctx context.Context, id string) (*Principal, error)
}

UserLoader retrieves user information for authentication and authorization. Implementations typically query a database or external identity provider.

type UserMenu

type UserMenu struct {
	Type     UserMenuType   `json:"type"`
	Path     string         `json:"path"`
	Name     string         `json:"name"`
	Icon     null.String    `json:"icon"`
	Meta     map[string]any `json:"metadata,omitempty"`
	Children []UserMenu     `json:"children,omitempty"`
}

type UserMenuType

type UserMenuType string
const (
	UserMenuTypeDirectory UserMenuType = "directory"
	UserMenuTypeMenu      UserMenuType = "menu"
	UserMenuTypeView      UserMenuType = "view"
	UserMenuTypeDashboard UserMenuType = "dashboard"
	UserMenuTypeReport    UserMenuType = "report"
)

Jump to

Keyboard shortcuts

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