Files
Agent-System-Backend/internal/profiles/profiles.go
T
weichen 9a545b1e69 Add admin-issued accounts with forced first-login password change
Why: the user ruled out self-registration (2026-09-30): accounts are
created only by admins who set an initial password; first login (and
first login after any admin reset) must force a password change.

Evidence:
- migration v10 auth_accounts (users password/must_change/is_admin +
  sessions table; upgrade test walks v1<->v10 both directions).
- argon2id hash/verify with PHC encoding; store tests cover hash
  roundtrip, duplicate-owner conflict, must-change lifecycle, session
  keep/except semantics.
- e2e TestAdminAccountFlow: static login page reachable without auth,
  wrong password 401, login sets HttpOnly cookie + mustChangePassword,
  admin creates user (409 on duplicate), non-admin create 403, forced
  change clears flag and old password dies, admin reset revokes sessions
  and re-arms forced change, API-key channel untouched, 5-fail backoff
  429, logout kills session. Full go test ./... green.

Impact: middleware now accepts Bearer key OR session cookie and exempts
static + /api/auth/login (also fixes the latent case where the login
page itself could 401). Embedded no-auth mode unchanged.

Refs: proposal 2026-09-30-ui-redesign-and-auth.md section 5 (admin-issued
variant, user decision 2026-09-30)
2026-09-30 11:15:22 +00:00

81 lines
2.7 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Package profiles 管理智能体画像(4.7):数据化、可导入导出;内置默认画像(T0 基线)。
package profiles
import (
"reflect"
"agent-system-backend/internal/store"
core "agent-system-core"
)
// OwnerDefault 与 store 保持一致的默认租户(内置画像共享,owner=”)。
const OwnerDefault = store.OwnerDefault
const DefaultProfileID = "default"
// defaultSystemPrompt 默认画像不再携带常驻提示词:基座(身份/行为/谨慎/工具/风格)
// 与环境段由提示词库组装(prompts 库 base.*/env.*);文档任务输出要求已技能化
// (task.doc-minutes 等,按触发注入)。SystemPrompt 留空 = 完全走库。
const defaultSystemPrompt = ""
func DefaultProfile() core.AgentProfile {
maxTools := 40
maxTurns := 16
return core.AgentProfile{
ID: DefaultProfileID,
Name: "通用助手",
Description: "内置默认画像:全部工具可用,HTTP 工具执行前确认(T0 基线)。",
Builtin: true,
Source: "builtin",
SystemPrompt: defaultSystemPrompt,
Model: core.ModelRoute{},
Tools: core.ToolPolicy{Allow: []string{"*"}, Confirm: []string{"http."}},
Context: core.ContextPolicy{MaxHistoryMessages: 40, MaxToolResultChars: 20000},
HITL: core.HITLPolicy{AskOnAmbiguity: true},
Budget: core.RunBudget{MaxToolCalls: &maxTools, MaxTurns: &maxTurns},
}
}
type Service struct{ st *store.Store }
func New(st *store.Store) *Service { return &Service{st: st} }
// EnsureDefault 幂等写入内置画像;内置画像只读(Save 拒绝编辑),库中与内置定义
// 有差异的行只可能来自更早版本的内置定义,因此差异时升级为当前内置定义。
func (s *Service) EnsureDefault() error {
if cur, err := s.st.GetProfile(OwnerDefault, DefaultProfileID); err == nil {
if reflect.DeepEqual(cur, DefaultProfile()) {
return nil
}
}
return s.st.SaveProfile(DefaultProfile())
}
func (s *Service) List(owner string) ([]core.AgentProfile, error) { return s.st.ListProfiles(owner) }
func (s *Service) Get(owner, id string) (core.AgentProfile, error) {
p, err := s.st.GetProfile(owner, id)
if err == nil {
return p, nil
}
// 空或未知画像回落默认(T0 行为基线)。
return DefaultProfile(), nil
}
func (s *Service) Save(owner string, p core.AgentProfile) error {
if p.ID == DefaultProfileID {
return errReadOnly("profiles: builtin profile is read-only; duplicate and modify")
}
if p.Tools.Allow == nil {
p.Tools.Allow = []string{"*"}
}
p.Owner = owner
return s.st.SaveProfile(p)
}
func (s *Service) Delete(owner, id string) error { return s.st.DeleteProfile(owner, id) }
type errReadOnly string
func (e errReadOnly) Error() string { return string(e) }