fix(auth): surface BootstrapAdminFromConfig UpdateUserRole failures

Do not ignore role promotion errors when ensuring the configured admin
account holds role-admin; cover success and failure with unit tests.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
weichen
2026-09-13 11:37:03 +00:00
co-authored by Cursor
parent cf28e53308
commit f189ce9ff9
2 changed files with 106 additions and 10 deletions
+12 -10
View File
@@ -4,8 +4,8 @@ import (
"context"
"fmt"
"github.com/jackc/pgx/v5/pgxpool"
"chat-agent-system/internal/config"
"github.com/jackc/pgx/v5/pgxpool"
)
// BootstrapAdminFromConfig 确保配置的管理员账户在 PG 中存在且具备 role-admin。
@@ -25,7 +25,9 @@ func BootstrapAdminFromConfig(ctx context.Context, store AuthStore, cfg *config.
existing, err := store.GetUserByUsername(ctx, adminUser)
if err == nil && existing != nil {
if existing.RoleID != "role-admin" {
_, _ = store.UpdateUserRole(ctx, existing.ID, "role-admin", "系统管理员")
if _, roleErr := store.UpdateUserRole(ctx, existing.ID, "role-admin", "系统管理员"); roleErr != nil {
return fmt.Errorf("bootstrap admin UpdateUserRole: %w", roleErr)
}
}
if existing.PasswordHash == "" {
hash, hashErr := HashPassword(adminPass)
@@ -86,14 +88,14 @@ func BootstrapDevSeedUsers(ctx context.Context, store AuthStore, cfg *config.Con
continue
}
_, err := store.CreateUser(ctx, &UserAccount{
Username: su.username,
Name: su.name,
PasswordHash: passHash,
Status: StatusActive,
RoleID: su.roleID,
Position: su.position,
Department: su.dept,
EmployeeID: su.empID,
Username: su.username,
Name: su.name,
PasswordHash: passHash,
Status: StatusActive,
RoleID: su.roleID,
Position: su.position,
Department: su.dept,
EmployeeID: su.empID,
})
if err != nil {
return fmt.Errorf("seed user %s: %w", su.username, err)
+94
View File
@@ -0,0 +1,94 @@
package auth_test
import (
"context"
"errors"
"strings"
"testing"
"chat-agent-system/internal/auth"
"chat-agent-system/internal/config"
)
// failRoleStore wraps MemoryStore and forces UpdateUserRole to fail.
type failRoleStore struct {
*auth.MemoryStore
failRole bool
}
func (s *failRoleStore) UpdateUserRole(ctx context.Context, id int64, roleID, position string) (*auth.UserAccount, error) {
if s.failRole {
return nil, errors.New("forced UpdateUserRole failure")
}
return s.MemoryStore.UpdateUserRole(ctx, id, roleID, position)
}
func TestBootstrapAdminFromConfig_PromotesExistingUserToAdmin(t *testing.T) {
cfg := &config.Config{
AdminUsername: "opsadmin",
AdminPassword: "bootstrap-pass-999",
Env: "dev",
}
base := auth.NewMemoryStore(&config.Config{Env: "dev", AdminUsername: "other", AdminPassword: "x"})
ctx := context.Background()
created, err := base.CreateUser(ctx, &auth.UserAccount{
Username: "opsadmin",
Name: "临时账号",
PasswordHash: "hash",
Status: auth.StatusActive,
RoleID: "role-guest",
Position: "访客/待指派",
EmployeeID: "TMP-1",
})
if err != nil {
t.Fatalf("CreateUser: %v", err)
}
if created.RoleID == "role-admin" {
t.Fatal("precondition: seed must not already be role-admin")
}
if err := auth.BootstrapAdminFromConfig(ctx, base, cfg); err != nil {
t.Fatalf("BootstrapAdminFromConfig: %v", err)
}
got, err := base.GetUserByUsername(ctx, "opsadmin")
if err != nil {
t.Fatal(err)
}
if got.RoleID != "role-admin" {
t.Fatalf("want role-admin after bootstrap, got %s", got.RoleID)
}
if got.Position != "系统管理员" {
t.Fatalf("want 系统管理员 position, got %s", got.Position)
}
}
func TestBootstrapAdminFromConfig_UpdateUserRoleErrorNotSwallowed(t *testing.T) {
cfg := &config.Config{
AdminUsername: "opsadmin2",
AdminPassword: "bootstrap-pass-999",
Env: "dev",
}
base := auth.NewMemoryStore(&config.Config{Env: "dev", AdminUsername: "other", AdminPassword: "x"})
ctx := context.Background()
_, err := base.CreateUser(ctx, &auth.UserAccount{
Username: "opsadmin2",
Name: "临时账号",
PasswordHash: "hash",
Status: auth.StatusActive,
RoleID: "role-guest",
Position: "访客/待指派",
EmployeeID: "TMP-2",
})
if err != nil {
t.Fatalf("CreateUser: %v", err)
}
wrapped := &failRoleStore{MemoryStore: base, failRole: true}
err = auth.BootstrapAdminFromConfig(ctx, wrapped, cfg)
if err == nil {
t.Fatal("expected BootstrapAdminFromConfig to surface UpdateUserRole error")
}
if !strings.Contains(err.Error(), "UpdateUserRole") {
t.Fatalf("error should mention UpdateUserRole, got %v", err)
}
}