feat: better admin permissions (#5755)
* feat: add casbin admin permissions * feat: improve audit logging to associate logs with actual operators and target users * feat: enhance admin permissions and UI interactions for sensitive actions * Refactor authz RBAC and tighten channel permissions * Split channel authz field policy * Address channel authz review findings
This commit is contained in:
+61
-42
@@ -22,37 +22,38 @@ const UserNameMaxLength = 20
|
||||
// User if you add sensitive fields, don't forget to clean them in setupLogin function.
|
||||
// Otherwise, the sensitive information will be saved on local storage in plain text!
|
||||
type User struct {
|
||||
Id int `json:"id"`
|
||||
Username string `json:"username" gorm:"unique;index" validate:"max=20"`
|
||||
Password string `json:"password" gorm:"not null;" validate:"min=8,max=20"`
|
||||
OriginalPassword string `json:"original_password" gorm:"-:all"` // this field is only for Password change verification, don't save it to database!
|
||||
DisplayName string `json:"display_name" gorm:"index" validate:"max=20"`
|
||||
Role int `json:"role" gorm:"type:int;default:1"` // admin, common
|
||||
Status int `json:"status" gorm:"type:int;default:1"` // enabled, disabled
|
||||
Email string `json:"email" gorm:"index" validate:"max=50"`
|
||||
GitHubId string `json:"github_id" gorm:"column:github_id;index"`
|
||||
DiscordId string `json:"discord_id" gorm:"column:discord_id;index"`
|
||||
OidcId string `json:"oidc_id" gorm:"column:oidc_id;index"`
|
||||
WeChatId string `json:"wechat_id" gorm:"column:wechat_id;index"`
|
||||
TelegramId string `json:"telegram_id" gorm:"column:telegram_id;index"`
|
||||
VerificationCode string `json:"verification_code" gorm:"-:all"` // this field is only for Email verification, don't save it to database!
|
||||
AccessToken *string `json:"-" gorm:"type:char(32);column:access_token;uniqueIndex"` // this token is for system management
|
||||
Quota int `json:"quota" gorm:"type:int;default:0"`
|
||||
UsedQuota int `json:"used_quota" gorm:"type:int;default:0;column:used_quota"` // used quota
|
||||
RequestCount int `json:"request_count" gorm:"type:int;default:0;"` // request number
|
||||
Group string `json:"group" gorm:"type:varchar(64);default:'default'"`
|
||||
AffCode string `json:"aff_code" gorm:"type:varchar(32);column:aff_code;uniqueIndex"`
|
||||
AffCount int `json:"aff_count" gorm:"type:int;default:0;column:aff_count"`
|
||||
AffQuota int `json:"aff_quota" gorm:"type:int;default:0;column:aff_quota"` // 邀请剩余额度
|
||||
AffHistoryQuota int `json:"aff_history_quota" gorm:"type:int;default:0;column:aff_history"` // 邀请历史额度
|
||||
InviterId int `json:"inviter_id" gorm:"type:int;column:inviter_id;index"`
|
||||
DeletedAt gorm.DeletedAt `gorm:"index"`
|
||||
LinuxDOId string `json:"linux_do_id" gorm:"column:linux_do_id;index"`
|
||||
Setting string `json:"setting" gorm:"type:text;column:setting"`
|
||||
Remark string `json:"remark,omitempty" gorm:"type:varchar(255)" validate:"max=255"`
|
||||
StripeCustomer string `json:"stripe_customer" gorm:"type:varchar(64);column:stripe_customer;index"`
|
||||
CreatedAt int64 `json:"created_at" gorm:"autoCreateTime;column:created_at"`
|
||||
LastLoginAt int64 `json:"last_login_at" gorm:"default:0;column:last_login_at"`
|
||||
Id int `json:"id"`
|
||||
Username string `json:"username" gorm:"unique;index" validate:"max=20"`
|
||||
Password string `json:"password" gorm:"not null;" validate:"min=8,max=20"`
|
||||
OriginalPassword string `json:"original_password" gorm:"-:all"` // this field is only for Password change verification, don't save it to database!
|
||||
DisplayName string `json:"display_name" gorm:"index" validate:"max=20"`
|
||||
Role int `json:"role" gorm:"type:int;default:1"` // admin, common
|
||||
Status int `json:"status" gorm:"type:int;default:1"` // enabled, disabled
|
||||
Email string `json:"email" gorm:"index" validate:"max=50"`
|
||||
GitHubId string `json:"github_id" gorm:"column:github_id;index"`
|
||||
DiscordId string `json:"discord_id" gorm:"column:discord_id;index"`
|
||||
OidcId string `json:"oidc_id" gorm:"column:oidc_id;index"`
|
||||
WeChatId string `json:"wechat_id" gorm:"column:wechat_id;index"`
|
||||
TelegramId string `json:"telegram_id" gorm:"column:telegram_id;index"`
|
||||
VerificationCode string `json:"verification_code" gorm:"-:all"` // this field is only for Email verification, don't save it to database!
|
||||
AccessToken *string `json:"-" gorm:"type:char(32);column:access_token;uniqueIndex"` // this token is for system management
|
||||
Quota int `json:"quota" gorm:"type:int;default:0"`
|
||||
UsedQuota int `json:"used_quota" gorm:"type:int;default:0;column:used_quota"` // used quota
|
||||
RequestCount int `json:"request_count" gorm:"type:int;default:0;"` // request number
|
||||
Group string `json:"group" gorm:"type:varchar(64);default:'default'"`
|
||||
AffCode string `json:"aff_code" gorm:"type:varchar(32);column:aff_code;uniqueIndex"`
|
||||
AffCount int `json:"aff_count" gorm:"type:int;default:0;column:aff_count"`
|
||||
AffQuota int `json:"aff_quota" gorm:"type:int;default:0;column:aff_quota"` // 邀请剩余额度
|
||||
AffHistoryQuota int `json:"aff_history_quota" gorm:"type:int;default:0;column:aff_history"` // 邀请历史额度
|
||||
InviterId int `json:"inviter_id" gorm:"type:int;column:inviter_id;index"`
|
||||
DeletedAt gorm.DeletedAt `gorm:"index"`
|
||||
LinuxDOId string `json:"linux_do_id" gorm:"column:linux_do_id;index"`
|
||||
Setting string `json:"setting" gorm:"type:text;column:setting"`
|
||||
Remark string `json:"remark,omitempty" gorm:"type:varchar(255)" validate:"max=255"`
|
||||
StripeCustomer string `json:"stripe_customer" gorm:"type:varchar(64);column:stripe_customer;index"`
|
||||
CreatedAt int64 `json:"created_at" gorm:"autoCreateTime;column:created_at"`
|
||||
LastLoginAt int64 `json:"last_login_at" gorm:"default:0;column:last_login_at"`
|
||||
AdminPermissions map[string]map[string]bool `json:"admin_permissions,omitempty" gorm:"-:all"`
|
||||
}
|
||||
|
||||
func (user *User) ToBaseUser() *UserBase {
|
||||
@@ -408,6 +409,11 @@ func (user *User) Insert(inviterId int) error {
|
||||
return result.Error
|
||||
}
|
||||
|
||||
user.finishInsert(inviterId)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (user *User) finishInsert(inviterId int) {
|
||||
// 用户创建成功后,根据角色初始化边栏配置
|
||||
// 需要重新获取用户以确保有正确的ID和Role
|
||||
var createdUser User
|
||||
@@ -437,7 +443,10 @@ func (user *User) Insert(inviterId int) error {
|
||||
_ = inviteUser(inviterId)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (user *User) FinishInsert(inviterId int) {
|
||||
user.finishInsert(inviterId)
|
||||
}
|
||||
|
||||
// InsertWithTx inserts a new user within an existing transaction.
|
||||
@@ -500,6 +509,13 @@ func (user *User) FinalizeOAuthUserCreation(inviterId int) {
|
||||
}
|
||||
|
||||
func (user *User) Update(updatePassword bool) error {
|
||||
if err := user.UpdateWithTx(DB, updatePassword); err != nil {
|
||||
return err
|
||||
}
|
||||
return updateUserCache(*user)
|
||||
}
|
||||
|
||||
func (user *User) UpdateWithTx(tx *gorm.DB, updatePassword bool) error {
|
||||
var err error
|
||||
if updatePassword {
|
||||
user.Password, err = common.Password2Hash(user.Password)
|
||||
@@ -508,16 +524,21 @@ func (user *User) Update(updatePassword bool) error {
|
||||
}
|
||||
}
|
||||
newUser := *user
|
||||
DB.First(&user, user.Id)
|
||||
if err = DB.Model(user).Updates(newUser).Error; err != nil {
|
||||
tx.First(&user, user.Id)
|
||||
if err = tx.Model(user).Updates(newUser).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Update cache
|
||||
return updateUserCache(*user)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (user *User) Edit(updatePassword bool) error {
|
||||
if err := user.EditWithTx(DB, updatePassword); err != nil {
|
||||
return err
|
||||
}
|
||||
return updateUserCache(*user)
|
||||
}
|
||||
|
||||
func (user *User) EditWithTx(tx *gorm.DB, updatePassword bool) error {
|
||||
var err error
|
||||
if updatePassword {
|
||||
user.Password, err = common.Password2Hash(user.Password)
|
||||
@@ -537,13 +558,11 @@ func (user *User) Edit(updatePassword bool) error {
|
||||
updates["password"] = newUser.Password
|
||||
}
|
||||
|
||||
DB.First(&user, user.Id)
|
||||
if err = DB.Model(user).Updates(updates).Error; err != nil {
|
||||
tx.First(&user, user.Id)
|
||||
if err = tx.Model(user).Updates(updates).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Update cache
|
||||
return updateUserCache(*user)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (user *User) ClearBinding(bindingType string) error {
|
||||
|
||||
Reference in New Issue
Block a user