Load the super-admin config in the product composition root #218
@@ -6,15 +6,33 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
|
|
||||||
"agent-system-backend/internal/app"
|
"agent-system-backend/internal/app"
|
||||||
|
"agent-system-backend/internal/config"
|
||||||
"agent-system-backend/pkg/extension"
|
"agent-system-backend/pkg/extension"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
extension.SetRunner(func(opt extension.Options) error {
|
extension.SetRunner(func(opt extension.Options) error {
|
||||||
return app.Run(appOptions(opt))
|
appOpt, err := runOptions(opt)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return app.Run(appOpt)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// runOptions 在 appOptions 基础上补齐超级管理员凭据,与 cmd/agent-system 同源:
|
||||||
|
// 进程环境优先,未设置的键从 AGENT_SYSTEM_ENV_FILE 指向的受保护 dotenv 补齐。
|
||||||
|
func runOptions(opt extension.Options) (app.Options, error) {
|
||||||
|
appOpt := appOptions(opt)
|
||||||
|
authEnabled := config.Resolved{Config: config.Config{AuthMode: opt.AuthMode, StorageDSN: opt.StorageDSN}}.AuthEnabled()
|
||||||
|
superAdmin, err := config.LoadSuperAdmin(os.LookupEnv, "", authEnabled)
|
||||||
|
if err != nil {
|
||||||
|
return app.Options{}, err
|
||||||
|
}
|
||||||
|
appOpt.SuperAdmin = superAdmin
|
||||||
|
return appOpt, nil
|
||||||
|
}
|
||||||
|
|
||||||
// appOptions 把产品启动参数映射到基座。执行环境三项产品旗标留空时读环境变量,
|
// appOptions 把产品启动参数映射到基座。执行环境三项产品旗标留空时读环境变量,
|
||||||
// 与 cmd/agent-system 的 config 键一致,这样 svc 注入环境即可,不必改产品 main。
|
// 与 cmd/agent-system 的 config 键一致,这样 svc 注入环境即可,不必改产品 main。
|
||||||
func appOptions(opt extension.Options) app.Options {
|
func appOptions(opt extension.Options) app.Options {
|
||||||
|
|||||||
@@ -1,11 +1,86 @@
|
|||||||
package link
|
package link
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"agent-system-backend/internal/config"
|
||||||
"agent-system-backend/pkg/extension"
|
"agent-system-backend/pkg/extension"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const fakeSuperAdminPassword = "Fake-Test-Passw0rd-Only"
|
||||||
|
|
||||||
|
func clearSuperAdminEnv(t *testing.T) {
|
||||||
|
t.Helper()
|
||||||
|
for _, key := range []string{config.EnvSuperAdminOwner, config.EnvSuperAdminPassword, config.EnvFileKey} {
|
||||||
|
t.Setenv(key, "")
|
||||||
|
os.Unsetenv(key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeEnvFile(t *testing.T, mode os.FileMode) string {
|
||||||
|
t.Helper()
|
||||||
|
path := filepath.Join(t.TempDir(), "super-admin.env")
|
||||||
|
content := "SUPER_ADMIN_OWNER=fake-file-owner\nSUPER_ADMIN_PASSWORD=" + fakeSuperAdminPassword + "\n"
|
||||||
|
if err := os.WriteFile(path, []byte(content), 0o600); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := os.Chmod(path, mode); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRunOptionsLoadsSuperAdminFromEnv(t *testing.T) {
|
||||||
|
clearSuperAdminEnv(t)
|
||||||
|
t.Setenv(config.EnvSuperAdminOwner, "fake-env-owner")
|
||||||
|
t.Setenv(config.EnvSuperAdminPassword, fakeSuperAdminPassword)
|
||||||
|
got, err := runOptions(extension.Options{AuthMode: "on", RequireProjectContext: true})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("runOptions: %v", err)
|
||||||
|
}
|
||||||
|
if got.SuperAdmin.Owner != "fake-env-owner" || got.SuperAdmin.Password() == "" {
|
||||||
|
t.Fatalf("super admin not loaded from env: %v", got.SuperAdmin)
|
||||||
|
}
|
||||||
|
if !got.RequireProjectContext || got.AuthMode != "on" {
|
||||||
|
t.Fatalf("appOptions mapping lost: auth=%q requireProject=%t", got.AuthMode, got.RequireProjectContext)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRunOptionsLoadsSuperAdminFromEnvFile(t *testing.T) {
|
||||||
|
clearSuperAdminEnv(t)
|
||||||
|
t.Setenv(config.EnvFileKey, writeEnvFile(t, 0o600))
|
||||||
|
got, err := runOptions(extension.Options{AuthMode: "on"})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("runOptions: %v", err)
|
||||||
|
}
|
||||||
|
if got.SuperAdmin.Owner != "fake-file-owner" || got.SuperAdmin.Password() == "" {
|
||||||
|
t.Fatalf("super admin not loaded from env file: %v", got.SuperAdmin)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRunOptionsSkipsSuperAdminWithoutAuthentication(t *testing.T) {
|
||||||
|
clearSuperAdminEnv(t)
|
||||||
|
got, err := runOptions(extension.Options{AuthMode: "off"})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("runOptions without auth: %v", err)
|
||||||
|
}
|
||||||
|
if got.SuperAdmin.Owner != "" || got.SuperAdmin.Password() != "" {
|
||||||
|
t.Fatalf("super admin should be empty without auth: %v", got.SuperAdmin)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRunOptionsRejectsExposedEnvFile(t *testing.T) {
|
||||||
|
clearSuperAdminEnv(t)
|
||||||
|
t.Setenv(config.EnvFileKey, writeEnvFile(t, 0o640))
|
||||||
|
_, err := runOptions(extension.Options{AuthMode: "on"})
|
||||||
|
if !errors.Is(err, config.ErrSuperAdminConfig) {
|
||||||
|
t.Fatalf("exposed env file accepted: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestAppOptionsPrefersExplicitExecPaths(t *testing.T) {
|
func TestAppOptionsPrefersExplicitExecPaths(t *testing.T) {
|
||||||
t.Setenv("AGENT_SYSTEM_EXEC_DEPS_DIR", "/from/env/deps")
|
t.Setenv("AGENT_SYSTEM_EXEC_DEPS_DIR", "/from/env/deps")
|
||||||
t.Setenv("AGENT_SYSTEM_EXEC_WORKSPACE", "overlay")
|
t.Setenv("AGENT_SYSTEM_EXEC_WORKSPACE", "overlay")
|
||||||
|
|||||||
Reference in New Issue
Block a user