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

3 months ago
//
// 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()
}