发布 0.1.0:通知渠道、告警规则、令牌版本与安全加固
CI / test (push) Successful in 30s
Release / release (push) Successful in 49s

This commit is contained in:
Wang Defa
2026-07-10 17:38:34 +08:00
parent 4af6a0ca92
commit dbba1f4905
78 changed files with 6898 additions and 551 deletions
+34 -16
View File
@@ -7,6 +7,7 @@ import (
"strings"
"golang.org/x/crypto/bcrypt"
"gorm.io/gorm"
"oci-portal/internal/model"
)
@@ -33,42 +34,55 @@ type UpdateCredentialsInput struct {
CurrentPassword string `json:"currentPassword" binding:"required"`
}
// UpdateCredentials 修改用户名 / 密码:当前密码必验;改名后旧 JWT 的
// sub 不再命中账号,前端应强制重新登录
func (s *AuthService) UpdateCredentials(ctx context.Context, username string, in UpdateCredentialsInput) error {
// UpdateCredentials 修改用户名 / 密码:当前密码必验;成功后令牌版本递增
// (全部旧 JWT 立即失效),返回最终用户名供调用方为操作者重签新令牌
func (s *AuthService) UpdateCredentials(ctx context.Context, username string, in UpdateCredentialsInput) (string, error) {
user, err := s.findUser(ctx, username)
if err != nil {
return err
return "", err
}
if bcrypt.CompareHashAndPassword([]byte(user.PasswordHash), []byte(in.CurrentPassword)) != nil {
return ErrCredentialConfirm
return "", ErrCredentialConfirm
}
newName := strings.TrimSpace(in.NewUsername)
if err := validateCredentialChange(user, newName, in.NewPassword); err != nil {
return err
return "", err
}
updates, finalName, err := s.credentialUpdates(ctx, user, newName, in.NewPassword)
if err != nil {
return "", err
}
// 同一条 UPDATE 里递增令牌版本,与凭据变更保持原子
updates["token_version"] = gorm.Expr("token_version + 1")
if err := s.db.WithContext(ctx).Model(&model.User{}).Where("id = ?", user.ID).Updates(updates).Error; err != nil {
return "", fmt.Errorf("update credentials: %w", err)
}
return finalName, nil
}
// credentialUpdates 组装凭据变更字段并返回最终用户名。
func (s *AuthService) credentialUpdates(ctx context.Context, user *model.User, newName, newPassword string) (map[string]any, string, error) {
updates := map[string]any{}
finalName := user.Username
if newName != "" && newName != user.Username {
taken, err := s.usernameTaken(ctx, newName, user.ID)
if err != nil {
return err
return nil, "", err
}
if taken {
return fmt.Errorf("用户名已被占用: %w", ErrCredentialInvalid)
return nil, "", fmt.Errorf("用户名已被占用: %w", ErrCredentialInvalid)
}
updates["username"] = newName
finalName = newName
}
if in.NewPassword != "" {
hash, err := bcrypt.GenerateFromPassword([]byte(in.NewPassword), bcrypt.DefaultCost)
if newPassword != "" {
hash, err := bcrypt.GenerateFromPassword([]byte(newPassword), bcrypt.DefaultCost)
if err != nil {
return fmt.Errorf("hash password: %w", err)
return nil, "", fmt.Errorf("hash password: %w", err)
}
updates["password_hash"] = string(hash)
}
if err := s.db.WithContext(ctx).Model(&model.User{}).Where("id = ?", user.ID).Updates(updates).Error; err != nil {
return fmt.Errorf("update credentials: %w", err)
}
return nil
return updates, finalName, nil
}
// validateCredentialChange 校验改名 / 改密输入;两者均无实际变更时报非法。
@@ -122,7 +136,11 @@ func (s *AuthService) SetPasswordLoginDisabled(ctx context.Context, username str
return ErrNeedIdentity
}
}
return s.settings.SetPasswordLoginDisabled(ctx, disabled)
if err := s.settings.SetPasswordLoginDisabled(ctx, disabled); err != nil {
return err
}
// 登录策略属敏感变更:递增令牌版本,已签发会话全部失效
return s.bumpTokenVersion(ctx, username)
}
func (s *AuthService) identityCount(ctx context.Context, userID uint) (int64, error) {