// // Copyright (C) 2024 veypi // 2025-07-24 15:27:31 // Distributed under terms of the MIT license. // package oauth import ( "time" "github.com/veypi/OneAuth/models" "gorm.io/gorm" ) // User 用户表 type User struct { models.BaseModel Username string `json:"username" gorm:"uniqueIndex;not null;size:50;comment:用户名""` Email string `json:"email" gorm:"uniqueIndex;size:100;comment:邮箱地址"` Phone string `json:"phone" gorm:"uniqueIndex;size:20;comment:手机号码"` PasswordHash string `json:"-" gorm:"not null;size:255;comment:密码哈希"` Nickname string `json:"nickname" gorm:"size:50;comment:昵称"` Avatar string `json:"avatar" gorm:"size:255;comment:头像URL"` IsActive bool `json:"is_active" gorm:"default:true;comment:是否激活"` IsSuperuser bool `json:"is_superuser" gorm:"default:false;comment:是否为超级用户"` LastLoginAt *time.Time `json:"last_login_at" gorm:"comment:最后登录时间"` EmailVerified bool `json:"email_verified" gorm:"default:false;comment:邮箱是否已验证"` PhoneVerified bool `json:"phone_verified" gorm:"default:false;comment:手机是否已验证"` TwoFactorAuth bool `json:"two_factor_auth" gorm:"default:false;comment:是否启用双因素认证"` Locale string `json:"locale" gorm:"size:10;default:zh-CN;comment:语言偏好"` Timezone string `json:"timezone" gorm:"size:50;default:Asia/Shanghai;comment:时区"` Bio string `json:"bio" gorm:"type:text;comment:个人简介"` // 关联关系 Roles []Role `json:"roles" gorm:"many2many:user_roles;"` UserRoles []UserRole `json:"-"` OAuthAccounts []OAuthAccount `json:"oauth_accounts"` Tokens []UserToken `json:"-"` MetaData []byte `json:"meta_data" gorm:"type:jsonb;comment:用户元数据"` // 存储用户自定义的元数据 } // Role 角色表 type Role struct { models.BaseModel Name string `json:"name" gorm:"uniqueIndex;not null;size:50;comment:角色名称" validate:"required"` DisplayName string `json:"display_name" gorm:"size:100;comment:显示名称"` Description string `json:"description" gorm:"type:text;comment:角色描述"` IsSystem bool `json:"is_system" gorm:"default:false;comment:是否为系统角色"` IsActive bool `json:"is_active" gorm:"default:true;comment:是否激活"` // 关联关系 Users []User `json:"-" gorm:"many2many:user_roles;"` UserRoles []UserRole `json:"-"` Permissions []Permission `json:"permissions" gorm:"many2many:role_permissions;"` } // Permission 权限表 type Permission struct { models.BaseModel Name string `json:"name" gorm:"uniqueIndex;not null;size:100;comment:权限名称" validate:"required"` DisplayName string `json:"display_name" gorm:"size:100;comment:显示名称"` Description string `json:"description" gorm:"type:text;comment:权限描述"` Resource string `json:"resource" gorm:"not null;size:50;comment:资源名称" validate:"required"` Action string `json:"action" gorm:"not null;size:50;comment:操作类型" validate:"required"` IsSystem bool `json:"is_system" gorm:"default:false;comment:是否为系统权限"` // 关联关系 Roles []Role `json:"-" gorm:"many2many:role_permissions;"` } // UserRole 用户角色关联表 type UserRole struct { UserID string `json:"user_id" gorm:"primaryKey;type:varchar(32);comment:用户ID"` RoleID string `json:"role_id" gorm:"primaryKey;type:varchar(32);comment:角色ID"` GrantedBy string `json:"granted_by" gorm:"type:varchar(32);comment:授权人ID"` GrantedAt time.Time `json:"granted_at" gorm:"autoCreateTime;comment:授权时间"` ExpiresAt *time.Time `json:"expires_at" gorm:"comment:过期时间"` // 关联关系 User *User `json:"user" gorm:"foreignKey:UserID"` Role *Role `json:"role" gorm:"foreignKey:RoleID"` GrantedByUser *User `json:"granted_by_user" gorm:"foreignKey:GrantedBy"` } // RolePermission 角色权限关联表 type RolePermission struct { RoleID string `json:"role_id" gorm:"primaryKey;type:varchar(32);comment:角色ID"` PermissionID string `json:"permission_id" gorm:"primaryKey;type:varchar(32);comment:权限ID"` GrantedBy string `json:"granted_by" gorm:"type:varchar(32);comment:授权人ID"` GrantedAt time.Time `json:"granted_at" gorm:"autoCreateTime;comment:授权时间"` // 关联关系 Role *Role `json:"role" gorm:"foreignKey:RoleID"` Permission *Permission `json:"permission" gorm:"foreignKey:PermissionID"` GrantedByUser *User `json:"granted_by_user" gorm:"foreignKey:GrantedBy"` } // UserLoginLog 用户登录日志表 type UserLoginLog struct { models.BaseModel UserID string `json:"user_id" gorm:"not null;type:varchar(32);index;comment:用户ID"` IPAddress string `json:"ip_address" gorm:"size:45;comment:IP地址"` UserAgent string `json:"user_agent" gorm:"type:text;comment:用户代理"` LoginAt time.Time `json:"login_at" gorm:"autoCreateTime;comment:登录时间"` Success bool `json:"success" gorm:"default:true;comment:是否成功"` FailReason string `json:"fail_reason" gorm:"size:255;comment:失败原因"` Location string `json:"location" gorm:"size:100;comment:地理位置"` // 地理位置 // 关联关系 User User `json:"user" gorm:"foreignKey:UserID"` } // GORM Hooks func (u *User) BeforeCreate(tx *gorm.DB) error { if err := u.BaseModel.BeforeCreate(tx); err != nil { return err } if u.Locale == "" { u.Locale = "zh-CN" } if u.Timezone == "" { u.Timezone = "Asia/Shanghai" } return nil } // 用户方法 func (u *User) HasRole(roleName string) bool { for _, role := range u.Roles { if role.Name == roleName { return true } } return false } func (u *User) HasPermission(resource, action string) bool { for _, role := range u.Roles { for _, permission := range role.Permissions { if permission.Resource == resource && permission.Action == action { return true } } } return false } func (u *User) GetPermissions() []Permission { var permissions []Permission permissionMap := make(map[string]bool) for _, role := range u.Roles { for _, permission := range role.Permissions { if !permissionMap[permission.ID] { permissions = append(permissions, permission) permissionMap[permission.ID] = true } } } return permissions } // 角色方法 func (r *Role) HasPermission(resource, action string) bool { for _, permission := range r.Permissions { if permission.Resource == resource && permission.Action == action { return true } } return false } // ===== OAuth 服务器相关模型 ===== // OAuthClient OAuth客户端表 type OAuthClient struct { models.BaseModel ClientID string `json:"client_id" gorm:"uniqueIndex;not null;size:255;comment:客户端ID"` ClientSecret string `json:"-" gorm:"not null;size:255;comment:客户端密钥"` ClientName string `json:"client_name" gorm:"not null;size:255;comment:客户端名称"` ClientURI string `json:"client_uri" gorm:"size:500;comment:客户端主页"` LogoURI string `json:"logo_uri" gorm:"size:500;comment:客户端Logo"` TermsOfServiceURI string `json:"tos_uri" gorm:"size:500;comment:服务条款URL"` PolicyURI string `json:"policy_uri" gorm:"size:500;comment:隐私政策URL"` RedirectURIs string `json:"redirect_uris" gorm:"type:text;comment:重定向URI列表,JSON格式"` ResponseTypes string `json:"response_types" gorm:"size:255;default:'code';comment:响应类型"` GrantTypes string `json:"grant_types" gorm:"size:255;default:'authorization_code,refresh_token';comment:授权类型"` Scope string `json:"scope" gorm:"type:text;comment:授权范围"` Contacts string `json:"contacts" gorm:"type:text;comment:联系人邮箱,JSON格式"` IsPublic bool `json:"is_public" gorm:"default:false;comment:是否为公开客户端"` IsActive bool `json:"is_active" gorm:"default:true;comment:是否激活"` OwnerID string `json:"owner_id" gorm:"type:varchar(32);comment:客户端拥有者ID"` // 关联关系 Owner *User `json:"owner" gorm:"foreignKey:OwnerID"` AuthorizationCodes []OAuthAuthorizationCode `json:"-"` AccessTokens []OAuthAccessToken `json:"-"` RefreshTokens []OAuthRefreshToken `json:"-"` } // OAuthAuthorizationCode 授权码表 type OAuthAuthorizationCode struct { models.BaseModel Code string `json:"code" gorm:"uniqueIndex;not null;size:255;comment:授权码"` ClientID string `json:"client_id" gorm:"not null;type:varchar(32);index;comment:客户端ID"` UserID string `json:"user_id" gorm:"not null;type:varchar(32);index;comment:用户ID"` RedirectURI string `json:"redirect_uri" gorm:"not null;size:500;comment:重定向URI"` Scope string `json:"scope" gorm:"type:text;comment:授权范围"` CodeChallenge string `json:"code_challenge" gorm:"size:255;comment:PKCE代码挑战"` CodeChallengeMethod string `json:"code_challenge_method" gorm:"size:50;comment:PKCE挑战方法"` ExpiresAt time.Time `json:"expires_at" gorm:"not null;comment:过期时间"` Used bool `json:"used" gorm:"default:false;comment:是否已使用"` // 关联关系 Client *OAuthClient `json:"client" gorm:"foreignKey:ClientID"` User *User `json:"user" gorm:"foreignKey:UserID"` } // OAuthAccessToken 访问令牌表 type OAuthAccessToken struct { models.BaseModel Token string `json:"token" gorm:"uniqueIndex;not null;size:500;comment:访问令牌"` ClientID string `json:"client_id" gorm:"not null;type:varchar(32);index;comment:客户端ID"` UserID string `json:"user_id" gorm:"not null;type:varchar(32);index;comment:用户ID"` Scope string `json:"scope" gorm:"type:text;comment:授权范围"` ExpiresAt time.Time `json:"expires_at" gorm:"not null;comment:过期时间"` Revoked bool `json:"revoked" gorm:"default:false;comment:是否已撤销"` // 关联关系 Client *OAuthClient `json:"client" gorm:"foreignKey:ClientID"` User *User `json:"user" gorm:"foreignKey:UserID"` RefreshToken *OAuthRefreshToken `json:"refresh_token"` } // OAuthRefreshToken 刷新令牌表 type OAuthRefreshToken struct { models.BaseModel Token string `json:"token" gorm:"uniqueIndex;not null;size:500;comment:刷新令牌"` AccessTokenID string `json:"access_token_id" gorm:"type:varchar(32);uniqueIndex;comment:访问令牌ID"` ClientID string `json:"client_id" gorm:"not null;type:varchar(32);index;comment:客户端ID"` UserID string `json:"user_id" gorm:"not null;type:varchar(32);index;comment:用户ID"` Scope string `json:"scope" gorm:"type:text;comment:授权范围"` ExpiresAt time.Time `json:"expires_at" gorm:"not null;comment:过期时间"` Revoked bool `json:"revoked" gorm:"default:false;comment:是否已撤销"` // 关联关系 Client *OAuthClient `json:"client" gorm:"foreignKey:ClientID"` User *User `json:"user" gorm:"foreignKey:UserID"` AccessToken *OAuthAccessToken `json:"access_token" gorm:"foreignKey:AccessTokenID"` } // OAuthScope OAuth授权范围表 type OAuthScope struct { models.BaseModel Name string `json:"name" gorm:"uniqueIndex;not null;size:100;comment:范围名称"` DisplayName string `json:"display_name" gorm:"size:100;comment:显示名称"` Description string `json:"description" gorm:"type:text;comment:范围描述"` IsDefault bool `json:"is_default" gorm:"default:false;comment:是否为默认范围"` IsSystem bool `json:"is_system" gorm:"default:false;comment:是否为系统范围"` } // OAuthClientScope 客户端授权范围关联表 type OAuthClientScope struct { ClientID string `json:"client_id" gorm:"primaryKey;type:varchar(32);comment:客户端ID"` ScopeID string `json:"scope_id" gorm:"primaryKey;type:varchar(32);comment:范围ID"` // 关联关系 Client *OAuthClient `json:"client" gorm:"foreignKey:ClientID"` Scope *OAuthScope `json:"scope" gorm:"foreignKey:ScopeID"` } // OAuthProvider 第三方OAuth提供商表(用于OAuth客户端模式) type OAuthProvider struct { models.BaseModel Name string `json:"name" gorm:"uniqueIndex;not null;size:100;comment:提供商名称"` DisplayName string `json:"display_name" gorm:"size:100;comment:显示名称"` ClientID string `json:"client_id" gorm:"not null;size:255;comment:客户端ID"` ClientSecret string `json:"-" gorm:"not null;size:255;comment:客户端密钥"` AuthURL string `json:"auth_url" gorm:"not null;size:500;comment:授权URL"` TokenURL string `json:"token_url" gorm:"not null;size:500;comment:令牌URL"` UserInfoURL string `json:"user_info_url" gorm:"size:500;comment:用户信息URL"` Scope string `json:"scope" gorm:"type:text;comment:默认授权范围"` IsActive bool `json:"is_active" gorm:"default:true;comment:是否激活"` // 关联关系 OAuthAccounts []OAuthAccount `json:"oauth_accounts"` } // OAuthAccount 用户OAuth账户表(第三方登录) type OAuthAccount struct { models.BaseModel UserID string `json:"user_id" gorm:"not null;type:varchar(32);index;comment:用户ID"` ProviderID string `json:"provider_id" gorm:"not null;type:varchar(32);index;comment:提供商ID"` ProviderUserID string `json:"provider_user_id" gorm:"not null;size:255;comment:提供商用户ID"` Email string `json:"email" gorm:"size:255;comment:邮箱"` Username string `json:"username" gorm:"size:255;comment:用户名"` Nickname string `json:"nickname" gorm:"size:255;comment:昵称"` Avatar string `json:"avatar" gorm:"size:500;comment:头像URL"` AccessToken string `json:"-" gorm:"type:text;comment:访问令牌"` RefreshToken string `json:"-" gorm:"type:text;comment:刷新令牌"` ExpiresAt *time.Time `json:"expires_at" gorm:"comment:令牌过期时间"` // 关联关系 User *User `json:"user" gorm:"foreignKey:UserID"` Provider *OAuthProvider `json:"provider" gorm:"foreignKey:ProviderID"` } // UserToken 用户令牌表(API令牌等) type UserToken struct { models.BaseModel UserID string `json:"user_id" gorm:"not null;type:varchar(32);index;comment:用户ID"` TokenType string `json:"token_type" gorm:"not null;size:50;comment:令牌类型"` // api, session, etc. Token string `json:"token" gorm:"uniqueIndex;not null;size:500;comment:令牌值"` Name string `json:"name" gorm:"size:100;comment:令牌名称"` Description string `json:"description" gorm:"type:text;comment:令牌描述"` Scope string `json:"scope" gorm:"type:text;comment:授权范围"` ExpiresAt *time.Time `json:"expires_at" gorm:"comment:过期时间"` LastUsedAt *time.Time `json:"last_used_at" gorm:"comment:最后使用时间"` IsActive bool `json:"is_active" gorm:"default:true;comment:是否激活"` // 关联关系 User *User `json:"user" gorm:"foreignKey:UserID"` } // UserSession 用户会话表 type UserSession struct { models.BaseModel UserID string `json:"user_id" gorm:"not null;type:varchar(32);index;comment:用户ID"` SessionID string `json:"session_id" gorm:"uniqueIndex;not null;size:255;comment:会话ID"` IPAddress string `json:"ip_address" gorm:"size:45;comment:IP地址"` UserAgent string `json:"user_agent" gorm:"type:text;comment:用户代理"` ExpiresAt time.Time `json:"expires_at" gorm:"not null;comment:过期时间"` IsActive bool `json:"is_active" gorm:"default:true;comment:是否激活"` LastActivity time.Time `json:"last_activity" gorm:"comment:最后活动时间"` // 关联关系 User *User `json:"user" gorm:"foreignKey:UserID"` } // OAuthUserConsent 用户授权同意表 type OAuthUserConsent struct { models.BaseModel UserID string `json:"user_id" gorm:"not null;type:varchar(32);index;comment:用户ID"` ClientID string `json:"client_id" gorm:"not null;type:varchar(32);index;comment:客户端ID"` Scope string `json:"scope" gorm:"type:text;comment:授权范围"` ConsentAt time.Time `json:"consent_at" gorm:"autoCreateTime;comment:同意时间"` ExpiresAt *time.Time `json:"expires_at" gorm:"comment:同意过期时间"` // 关联关系 User *User `json:"user" gorm:"foreignKey:UserID"` Client *OAuthClient `json:"client" gorm:"foreignKey:ClientID"` } // ===== OAuth 模型方法 ===== // OAuthClient 方法 func (c *OAuthClient) IsRedirectURIValid(uri string) bool { // 这里应该解析 RedirectURIs JSON 并验证 // 简化实现,实际使用时需要完善 return true } func (c *OAuthClient) HasScope(scope string) bool { // 这里应该解析 Scope 并检查 // 简化实现,实际使用时需要完善 return true } // OAuthAuthorizationCode 方法 func (code *OAuthAuthorizationCode) IsExpired() bool { return time.Now().After(code.ExpiresAt) } // OAuthAccessToken 方法 func (token *OAuthAccessToken) IsExpired() bool { return time.Now().After(token.ExpiresAt) } func (token *OAuthAccessToken) IsValid() bool { return !token.Revoked && !token.IsExpired() } // OAuthRefreshToken 方法 func (token *OAuthRefreshToken) IsExpired() bool { return time.Now().After(token.ExpiresAt) } func (token *OAuthRefreshToken) IsValid() bool { return !token.Revoked && !token.IsExpired() } // UserSession 方法 func (s *UserSession) IsExpired() bool { return time.Now().After(s.ExpiresAt) } func (s *UserSession) IsValid() bool { return s.IsActive && !s.IsExpired() }