From 821599ffcb8ec4b0e948e122bd94db831792b5cb Mon Sep 17 00:00:00 2001 From: agent-exec-transport Date: Mon, 19 Oct 2026 12:36:49 +0000 Subject: [PATCH] agent-exec transport 7e8283631fb518cee0db0eba1be4c918/79c99324c3c7 --- pkg/extension/link/link.go | 20 ++++++++- pkg/extension/link/link_test.go | 75 +++++++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+), 1 deletion(-) diff --git a/pkg/extension/link/link.go b/pkg/extension/link/link.go index e8728dc..ec9363b 100644 --- a/pkg/extension/link/link.go +++ b/pkg/extension/link/link.go @@ -6,15 +6,33 @@ import ( "os" "agent-system-backend/internal/app" + "agent-system-backend/internal/config" "agent-system-backend/pkg/extension" ) func init() { 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 把产品启动参数映射到基座。执行环境三项产品旗标留空时读环境变量, // 与 cmd/agent-system 的 config 键一致,这样 svc 注入环境即可,不必改产品 main。 func appOptions(opt extension.Options) app.Options { diff --git a/pkg/extension/link/link_test.go b/pkg/extension/link/link_test.go index fd54b0b..a7dce0d 100644 --- a/pkg/extension/link/link_test.go +++ b/pkg/extension/link/link_test.go @@ -1,11 +1,86 @@ package link import ( + "errors" + "os" + "path/filepath" "testing" + "agent-system-backend/internal/config" "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) { t.Setenv("AGENT_SYSTEM_EXEC_DEPS_DIR", "/from/env/deps") t.Setenv("AGENT_SYSTEM_EXEC_WORKSPACE", "overlay") -- 2.54.0