+39
-18
@@ -329,27 +329,48 @@ func (o *OAuthService) Identities(ctx context.Context, username string) ([]model
|
||||
}
|
||||
|
||||
// Unbind 解绑外部身份(校验归属);密码登录被禁用时不允许解绑最后一个身份,防自锁。
|
||||
// 检查与删除在同一事务内并锁定用户行,防与禁用密码登录并发绕过「至少一种登录方式」。
|
||||
func (o *OAuthService) Unbind(ctx context.Context, username string, id uint) error {
|
||||
user, err := o.auth.findUser(ctx, username)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
n, err := o.auth.identityCount(ctx, user.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if n <= 1 {
|
||||
if off, err := o.auth.PasswordLoginDisabled(ctx); err == nil && off {
|
||||
return ErrLastIdentity
|
||||
err := o.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
user, err := lockUserForAuthChange(tx, username)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
res := o.db.WithContext(ctx).Where("id = ? AND user_id = ?", id, user.ID).Delete(&model.UserIdentity{})
|
||||
if res.Error != nil {
|
||||
return fmt.Errorf("unbind identity: %w", res.Error)
|
||||
}
|
||||
if res.RowsAffected == 0 {
|
||||
return gorm.ErrRecordNotFound
|
||||
if err := ensureNotLastLogin(tx, user.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
res := tx.Where("id = ? AND user_id = ?", id, user.ID).Delete(&model.UserIdentity{})
|
||||
if res.Error != nil {
|
||||
return fmt.Errorf("unbind identity: %w", res.Error)
|
||||
}
|
||||
if res.RowsAffected == 0 {
|
||||
return gorm.ErrRecordNotFound
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// 解绑属敏感变更:递增令牌版本,已签发会话全部失效
|
||||
return o.auth.bumpTokenVersion(ctx, username)
|
||||
}
|
||||
|
||||
// ensureNotLastLogin 事务内校验不变量:仅剩一个身份且密码登录已禁用时拒绝解绑;
|
||||
// 开关读取失败按失败关闭处理(返回错误),不允许失败放行造成自锁。
|
||||
func ensureNotLastLogin(tx *gorm.DB, userID uint) error {
|
||||
n, err := identityCountTx(tx, userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if n > 1 {
|
||||
return nil
|
||||
}
|
||||
off, err := settingValueTx(tx, settingSecPasswordLoginOff)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if off == "1" {
|
||||
return ErrLastIdentity
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user