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:
+12
-10
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user