This commit is contained in:
2025-08-22 18:57:40 -04:00
parent d648a55c0c
commit df567983c1
20 changed files with 4519 additions and 8 deletions

View File

@ -188,6 +188,23 @@ type CreateStaticTokenResponse struct {
CreatedAt time.Time `json:"created_at"`
}
// CreateTokenRequest represents a request to create a token
type CreateTokenRequest struct {
AppID string `json:"app_id" validate:"required"`
Type TokenType `json:"type" validate:"required,oneof=static user"`
UserID string `json:"user_id,omitempty"` // Required for user tokens
Permissions []string `json:"permissions,omitempty"`
ExpiresAt *time.Time `json:"expires_at,omitempty"`
Metadata map[string]string `json:"metadata,omitempty"`
}
// CreateTokenResponse represents a response for creating a token
type CreateTokenResponse struct {
Token string `json:"token"`
ExpiresAt time.Time `json:"expires_at"`
TokenType TokenType `json:"token_type"`
}
// AuthContext represents the authentication context for a request
type AuthContext struct {
UserID string `json:"user_id"`
@ -196,3 +213,25 @@ type AuthContext struct {
Claims map[string]string `json:"claims"`
AppID string `json:"app_id"`
}
// TokenResponse represents the OAuth2 token response
type TokenResponse struct {
AccessToken string `json:"access_token"`
TokenType string `json:"token_type"`
ExpiresIn int `json:"expires_in"`
RefreshToken string `json:"refresh_token,omitempty"`
IDToken string `json:"id_token,omitempty"`
Scope string `json:"scope,omitempty"`
}
// UserInfo represents user information from the OAuth2/OIDC provider
type UserInfo struct {
Sub string `json:"sub"`
Email string `json:"email"`
EmailVerified bool `json:"email_verified"`
Name string `json:"name"`
GivenName string `json:"given_name"`
FamilyName string `json:"family_name"`
Picture string `json:"picture"`
PreferredUsername string `json:"preferred_username"`
}

153
internal/domain/session.go Normal file
View File

@ -0,0 +1,153 @@
package domain
import (
"time"
"github.com/google/uuid"
)
// SessionStatus represents the status of a user session
type SessionStatus string
const (
SessionStatusActive SessionStatus = "active"
SessionStatusExpired SessionStatus = "expired"
SessionStatusRevoked SessionStatus = "revoked"
SessionStatusSuspended SessionStatus = "suspended"
)
// SessionType represents the type of session
type SessionType string
const (
SessionTypeWeb SessionType = "web"
SessionTypeMobile SessionType = "mobile"
SessionTypeAPI SessionType = "api"
)
// UserSession represents a user session in the system
type UserSession struct {
ID uuid.UUID `json:"id" db:"id"`
UserID string `json:"user_id" validate:"required" db:"user_id"`
AppID string `json:"app_id" validate:"required" db:"app_id"`
SessionType SessionType `json:"session_type" validate:"required,oneof=web mobile api" db:"session_type"`
Status SessionStatus `json:"status" validate:"required,oneof=active expired revoked suspended" db:"status"`
AccessToken string `json:"-" db:"access_token"` // Hidden from JSON for security
RefreshToken string `json:"-" db:"refresh_token"` // Hidden from JSON for security
IDToken string `json:"-" db:"id_token"` // Hidden from JSON for security
IPAddress string `json:"ip_address" db:"ip_address"`
UserAgent string `json:"user_agent" db:"user_agent"`
LastActivity time.Time `json:"last_activity" db:"last_activity"`
ExpiresAt time.Time `json:"expires_at" db:"expires_at"`
CreatedAt time.Time `json:"created_at" db:"created_at"`
UpdatedAt time.Time `json:"updated_at" db:"updated_at"`
RevokedAt *time.Time `json:"revoked_at,omitempty" db:"revoked_at"`
RevokedBy *string `json:"revoked_by,omitempty" db:"revoked_by"`
Metadata SessionMetadata `json:"metadata" db:"metadata"`
}
// SessionMetadata contains additional session information
type SessionMetadata struct {
DeviceInfo string `json:"device_info,omitempty"`
Location string `json:"location,omitempty"`
LoginMethod string `json:"login_method,omitempty"`
TenantID string `json:"tenant_id,omitempty"`
Permissions []string `json:"permissions,omitempty"`
Claims map[string]string `json:"claims,omitempty"`
RefreshCount int `json:"refresh_count"`
LastRefresh *time.Time `json:"last_refresh,omitempty"`
}
// CreateSessionRequest represents a request to create a new session
type CreateSessionRequest struct {
UserID string `json:"user_id" validate:"required"`
AppID string `json:"app_id" validate:"required"`
SessionType SessionType `json:"session_type" validate:"required,oneof=web mobile api"`
IPAddress string `json:"ip_address" validate:"required,ip"`
UserAgent string `json:"user_agent" validate:"required"`
ExpiresAt time.Time `json:"expires_at" validate:"required"`
Permissions []string `json:"permissions,omitempty"`
Claims map[string]string `json:"claims,omitempty"`
TenantID string `json:"tenant_id,omitempty"`
}
// UpdateSessionRequest represents a request to update a session
type UpdateSessionRequest struct {
Status *SessionStatus `json:"status,omitempty" validate:"omitempty,oneof=active expired revoked suspended"`
LastActivity *time.Time `json:"last_activity,omitempty"`
ExpiresAt *time.Time `json:"expires_at,omitempty"`
IPAddress *string `json:"ip_address,omitempty" validate:"omitempty,ip"`
UserAgent *string `json:"user_agent,omitempty"`
}
// SessionListRequest represents a request to list sessions
type SessionListRequest struct {
UserID string `json:"user_id,omitempty"`
AppID string `json:"app_id,omitempty"`
Status *SessionStatus `json:"status,omitempty"`
SessionType *SessionType `json:"session_type,omitempty"`
TenantID string `json:"tenant_id,omitempty"`
Limit int `json:"limit" validate:"min=1,max=100"`
Offset int `json:"offset" validate:"min=0"`
}
// SessionListResponse represents a response for listing sessions
type SessionListResponse struct {
Sessions []*UserSession `json:"sessions"`
Total int `json:"total"`
Limit int `json:"limit"`
Offset int `json:"offset"`
}
// IsActive checks if the session is currently active
func (s *UserSession) IsActive() bool {
return s.Status == SessionStatusActive && time.Now().Before(s.ExpiresAt)
}
// IsExpired checks if the session has expired
func (s *UserSession) IsExpired() bool {
return time.Now().After(s.ExpiresAt) || s.Status == SessionStatusExpired
}
// IsRevoked checks if the session has been revoked
func (s *UserSession) IsRevoked() bool {
return s.Status == SessionStatusRevoked
}
// CanRefresh checks if the session can be refreshed
func (s *UserSession) CanRefresh() bool {
return s.IsActive() && s.RefreshToken != ""
}
// UpdateActivity updates the last activity timestamp
func (s *UserSession) UpdateActivity() {
s.LastActivity = time.Now()
s.UpdatedAt = time.Now()
}
// Revoke marks the session as revoked
func (s *UserSession) Revoke(revokedBy string) {
now := time.Now()
s.Status = SessionStatusRevoked
s.RevokedAt = &now
s.RevokedBy = &revokedBy
s.UpdatedAt = now
}
// Expire marks the session as expired
func (s *UserSession) Expire() {
s.Status = SessionStatusExpired
s.UpdatedAt = time.Now()
}
// Suspend marks the session as suspended
func (s *UserSession) Suspend() {
s.Status = SessionStatusSuspended
s.UpdatedAt = time.Now()
}
// Activate marks the session as active
func (s *UserSession) Activate() {
s.Status = SessionStatusActive
s.UpdatedAt = time.Now()
}

307
internal/domain/tenant.go Normal file
View File

@ -0,0 +1,307 @@
package domain
import (
"time"
"github.com/google/uuid"
)
// TenantStatus represents the status of a tenant
type TenantStatus string
const (
TenantStatusActive TenantStatus = "active"
TenantStatusSuspended TenantStatus = "suspended"
TenantStatusInactive TenantStatus = "inactive"
)
// Tenant represents a tenant in the multi-tenant system
type Tenant struct {
ID uuid.UUID `json:"id" db:"id"`
Name string `json:"name" validate:"required,min=1,max=255" db:"name"`
Slug string `json:"slug" validate:"required,min=1,max=100,alphanum" db:"slug"`
Status TenantStatus `json:"status" validate:"required,oneof=active suspended inactive" db:"status"`
Domain string `json:"domain,omitempty" validate:"omitempty,fqdn" db:"domain"`
Description string `json:"description,omitempty" validate:"max=1000" db:"description"`
Settings TenantSettings `json:"settings" db:"settings"`
CreatedAt time.Time `json:"created_at" db:"created_at"`
UpdatedAt time.Time `json:"updated_at" db:"updated_at"`
CreatedBy string `json:"created_by" db:"created_by"`
UpdatedBy string `json:"updated_by" db:"updated_by"`
}
// TenantSettings contains tenant-specific configuration
type TenantSettings struct {
// Authentication settings
AuthProvider string `json:"auth_provider,omitempty"` // oauth2, saml, header
SAMLSettings *SAMLSettings `json:"saml_settings,omitempty"`
OAuth2Settings *OAuth2Settings `json:"oauth2_settings,omitempty"`
// Session settings
SessionTimeout time.Duration `json:"session_timeout,omitempty"`
MaxConcurrentSessions int `json:"max_concurrent_sessions,omitempty"`
// Security settings
RequireMFA bool `json:"require_mfa"`
AllowedIPRanges []string `json:"allowed_ip_ranges,omitempty"`
PasswordPolicy *PasswordPolicy `json:"password_policy,omitempty"`
// Token settings
DefaultTokenDuration time.Duration `json:"default_token_duration,omitempty"`
MaxTokenDuration time.Duration `json:"max_token_duration,omitempty"`
// Feature flags
Features map[string]bool `json:"features,omitempty"`
// Custom attributes
CustomAttributes map[string]string `json:"custom_attributes,omitempty"`
}
// SAMLSettings contains SAML-specific configuration for a tenant
type SAMLSettings struct {
IDPMetadataURL string `json:"idp_metadata_url,omitempty"`
SPEntityID string `json:"sp_entity_id,omitempty"`
ACSURL string `json:"acs_url,omitempty"`
SPPrivateKey string `json:"sp_private_key,omitempty"`
SPCertificate string `json:"sp_certificate,omitempty"`
AttributeMapping map[string]string `json:"attribute_mapping,omitempty"`
}
// OAuth2Settings contains OAuth2-specific configuration for a tenant
type OAuth2Settings struct {
ProviderURL string `json:"provider_url,omitempty"`
ClientID string `json:"client_id,omitempty"`
ClientSecret string `json:"client_secret,omitempty"`
Scopes []string `json:"scopes,omitempty"`
AttributeMapping map[string]string `json:"attribute_mapping,omitempty"`
}
// PasswordPolicy defines password requirements for a tenant
type PasswordPolicy struct {
MinLength int `json:"min_length"`
RequireUppercase bool `json:"require_uppercase"`
RequireLowercase bool `json:"require_lowercase"`
RequireNumbers bool `json:"require_numbers"`
RequireSymbols bool `json:"require_symbols"`
MaxAge time.Duration `json:"max_age,omitempty"`
PreventReuse int `json:"prevent_reuse"` // Number of previous passwords to prevent reuse
}
// TenantUser represents a user within a specific tenant
type TenantUser struct {
ID uuid.UUID `json:"id" db:"id"`
TenantID uuid.UUID `json:"tenant_id" validate:"required" db:"tenant_id"`
UserID string `json:"user_id" validate:"required" db:"user_id"`
Email string `json:"email" validate:"required,email" db:"email"`
Name string `json:"name" validate:"required" db:"name"`
Roles []string `json:"roles" db:"roles"`
Permissions []string `json:"permissions" db:"permissions"`
Status UserStatus `json:"status" validate:"required,oneof=active inactive suspended" db:"status"`
Metadata map[string]string `json:"metadata,omitempty" db:"metadata"`
CreatedAt time.Time `json:"created_at" db:"created_at"`
UpdatedAt time.Time `json:"updated_at" db:"updated_at"`
LastLoginAt *time.Time `json:"last_login_at,omitempty" db:"last_login_at"`
}
// UserStatus represents the status of a user within a tenant
type UserStatus string
const (
UserStatusActive UserStatus = "active"
UserStatusInactive UserStatus = "inactive"
UserStatusSuspended UserStatus = "suspended"
)
// TenantRole represents a role within a tenant
type TenantRole struct {
ID uuid.UUID `json:"id" db:"id"`
TenantID uuid.UUID `json:"tenant_id" validate:"required" db:"tenant_id"`
Name string `json:"name" validate:"required,min=1,max=100" db:"name"`
Description string `json:"description,omitempty" validate:"max=500" db:"description"`
Permissions []string `json:"permissions" db:"permissions"`
IsSystem bool `json:"is_system" db:"is_system"` // System roles cannot be deleted
CreatedAt time.Time `json:"created_at" db:"created_at"`
UpdatedAt time.Time `json:"updated_at" db:"updated_at"`
CreatedBy string `json:"created_by" db:"created_by"`
UpdatedBy string `json:"updated_by" db:"updated_by"`
}
// CreateTenantRequest represents a request to create a new tenant
type CreateTenantRequest struct {
Name string `json:"name" validate:"required,min=1,max=255"`
Slug string `json:"slug" validate:"required,min=1,max=100,alphanum"`
Domain string `json:"domain,omitempty" validate:"omitempty,fqdn"`
Description string `json:"description,omitempty" validate:"max=1000"`
Settings TenantSettings `json:"settings,omitempty"`
}
// UpdateTenantRequest represents a request to update a tenant
type UpdateTenantRequest struct {
Name *string `json:"name,omitempty" validate:"omitempty,min=1,max=255"`
Status *TenantStatus `json:"status,omitempty" validate:"omitempty,oneof=active suspended inactive"`
Domain *string `json:"domain,omitempty" validate:"omitempty,fqdn"`
Description *string `json:"description,omitempty" validate:"omitempty,max=1000"`
Settings *TenantSettings `json:"settings,omitempty"`
}
// CreateTenantUserRequest represents a request to create a user in a tenant
type CreateTenantUserRequest struct {
TenantID uuid.UUID `json:"tenant_id" validate:"required"`
UserID string `json:"user_id" validate:"required"`
Email string `json:"email" validate:"required,email"`
Name string `json:"name" validate:"required"`
Roles []string `json:"roles,omitempty"`
Permissions []string `json:"permissions,omitempty"`
Metadata map[string]string `json:"metadata,omitempty"`
}
// UpdateTenantUserRequest represents a request to update a tenant user
type UpdateTenantUserRequest struct {
Email *string `json:"email,omitempty" validate:"omitempty,email"`
Name *string `json:"name,omitempty" validate:"omitempty,min=1"`
Roles []string `json:"roles,omitempty"`
Permissions []string `json:"permissions,omitempty"`
Status *UserStatus `json:"status,omitempty" validate:"omitempty,oneof=active inactive suspended"`
Metadata map[string]string `json:"metadata,omitempty"`
}
// CreateTenantRoleRequest represents a request to create a role in a tenant
type CreateTenantRoleRequest struct {
TenantID uuid.UUID `json:"tenant_id" validate:"required"`
Name string `json:"name" validate:"required,min=1,max=100"`
Description string `json:"description,omitempty" validate:"max=500"`
Permissions []string `json:"permissions,omitempty"`
}
// UpdateTenantRoleRequest represents a request to update a tenant role
type UpdateTenantRoleRequest struct {
Name *string `json:"name,omitempty" validate:"omitempty,min=1,max=100"`
Description *string `json:"description,omitempty" validate:"omitempty,max=500"`
Permissions []string `json:"permissions,omitempty"`
}
// TenantListRequest represents a request to list tenants
type TenantListRequest struct {
Status *TenantStatus `json:"status,omitempty"`
Domain string `json:"domain,omitempty"`
Limit int `json:"limit" validate:"min=1,max=100"`
Offset int `json:"offset" validate:"min=0"`
}
// TenantListResponse represents a response for listing tenants
type TenantListResponse struct {
Tenants []*Tenant `json:"tenants"`
Total int `json:"total"`
Limit int `json:"limit"`
Offset int `json:"offset"`
}
// IsActive checks if the tenant is active
func (t *Tenant) IsActive() bool {
return t.Status == TenantStatusActive
}
// IsSuspended checks if the tenant is suspended
func (t *Tenant) IsSuspended() bool {
return t.Status == TenantStatusSuspended
}
// HasFeature checks if a feature is enabled for the tenant
func (t *Tenant) HasFeature(feature string) bool {
if t.Settings.Features == nil {
return false
}
enabled, exists := t.Settings.Features[feature]
return exists && enabled
}
// GetAuthProvider returns the authentication provider for the tenant
func (t *Tenant) GetAuthProvider() string {
if t.Settings.AuthProvider != "" {
return t.Settings.AuthProvider
}
return "header" // default
}
// GetSessionTimeout returns the session timeout for the tenant
func (t *Tenant) GetSessionTimeout() time.Duration {
if t.Settings.SessionTimeout > 0 {
return t.Settings.SessionTimeout
}
return 8 * time.Hour // default
}
// GetMaxConcurrentSessions returns the maximum concurrent sessions for the tenant
func (t *Tenant) GetMaxConcurrentSessions() int {
if t.Settings.MaxConcurrentSessions > 0 {
return t.Settings.MaxConcurrentSessions
}
return 10 // default
}
// IsActive checks if the tenant user is active
func (tu *TenantUser) IsActive() bool {
return tu.Status == UserStatusActive
}
// IsSuspended checks if the tenant user is suspended
func (tu *TenantUser) IsSuspended() bool {
return tu.Status == UserStatusSuspended
}
// HasRole checks if the user has a specific role
func (tu *TenantUser) HasRole(role string) bool {
for _, r := range tu.Roles {
if r == role {
return true
}
}
return false
}
// HasPermission checks if the user has a specific permission
func (tu *TenantUser) HasPermission(permission string) bool {
for _, p := range tu.Permissions {
if p == permission {
return true
}
}
return false
}
// UpdateLastLogin updates the last login timestamp
func (tu *TenantUser) UpdateLastLogin() {
now := time.Now()
tu.LastLoginAt = &now
tu.UpdatedAt = now
}
// IsSystemRole checks if the role is a system role
func (tr *TenantRole) IsSystemRole() bool {
return tr.IsSystem
}
// HasPermission checks if the role has a specific permission
func (tr *TenantRole) HasPermission(permission string) bool {
for _, p := range tr.Permissions {
if p == permission {
return true
}
}
return false
}
// TenantContext represents the tenant context for a request
type TenantContext struct {
TenantID uuid.UUID `json:"tenant_id"`
TenantSlug string `json:"tenant_slug"`
UserID string `json:"user_id"`
Roles []string `json:"roles"`
Permissions []string `json:"permissions"`
}
// MultiTenantAuthContext extends AuthContext with tenant information
type MultiTenantAuthContext struct {
*AuthContext
TenantContext *TenantContext `json:"tenant_context,omitempty"`
}