Files
knowledge-governance/cmd/api/bootstrap.go
T
zheromiku fbe80ad784 feat(auth): accounts, sessions, RBAC and server-stamped actors
上线账号权限管理(后端):
- 迁移 000032:users + user_sessions(token 只存 sha256,服务端可即时撤销)
- bcrypt 密码、8 次失败锁 15 分钟、统一 401 错误防用户名枚举
- 三角色 viewer/engineer/admin;HttpOnly+SameSite=Lax 会话 cookie,滑动续期
- 强制改密锁定:must_change_password 会话只能走 auth 端点;首登 dobotai
- bootstrap:users 表为空时创建 admin(初始密码 dobotai,强制改密)
- 生产双闸授权/回滚/失败重试收敛为 admin 角色;AuthorizedBy 用会话身份
- 全部业务写端点删除手填 actor 字段(operator_ref/reviewer_ref/created_by/
  sealed_by/confirmed_by),服务端从会话注入;auth 关闭的开发 socket 回退 system
- 成员管理 API:列表/创建/改角色/启停/重置密码,最后一名 active admin 保护
- RBAC 矩阵纯函数契约测试 + 中间件行为测试(登录/锁定/角色/审计戳)
- 附带修复:GET /api/v1/users 曾被 GET=viewer 规则误放行(测试抓到)
2026-09-30 10:54:07 +08:00

36 lines
1.0 KiB
Go

package main
import (
"context"
"errors"
"fmt"
"log"
"time"
postgresstore "knowledge-governance/internal/platform/postgres"
)
// bootstrapInitialAdmin creates the fixed day-one administrator when the user
// table is still empty. The initial password is a known constant the operator
// hands to the owner in person; must_change_password forces a private change
// on first login before any other screen is reachable. Once any user exists
// this is a no-op, so accounts are managed through the UI afterwards.
func bootstrapInitialAdmin(ctx context.Context, store *postgresstore.Store, now func() time.Time) error {
count, err := store.CountAuthUsers(ctx)
if err != nil {
return err
}
if count > 0 {
return nil
}
_, err = store.CreateInitialAdmin(ctx, "admin", "dobotai", now().UTC())
if errors.Is(err, postgresstore.ErrAuthDuplicateUsername) {
return nil
}
if err != nil {
return fmt.Errorf("bootstrap initial admin: %w", err)
}
log.Printf("bootstrap: created initial admin account (first login must change the password)")
return nil
}