You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
OneAuth/oauth/user.go

401 lines
18 KiB
Go

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

//
// Copyright (C) 2024 veypi <i@veypi.com>
// 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()
}