mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-09-10 02:25:56 +00:00
86 lines
3.6 KiB
Go
86 lines
3.6 KiB
Go
package api
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/rcourtman/pulse-go-rewrite/internal/config"
|
|
)
|
|
|
|
// Issue #1644 follow-up: the auto-register path writes bound_hostname for host
|
|
// install tokens without bound_agent_id or a binding version. The first
|
|
// command-channel enrollment must take the clean first-use bind.
|
|
func TestIssue1644AutoRegisteredHostTokenBindsExecOnFirstUse(t *testing.T) {
|
|
stubAutoRegisterNetworkDeps(t)
|
|
|
|
dataPath := t.TempDir()
|
|
cfg := &config.Config{DataPath: dataPath, AuthUser: "admin", AuthPass: "hashed-password"}
|
|
handler := newTestConfigHandlers(t, cfg)
|
|
|
|
installReq := httptest.NewRequest(http.MethodPost, "/api/agent-install-command", strings.NewReader(`{"type":"host","name":"issue-1644-exec","enableCommands":true}`))
|
|
installReq.Host = "pulse.example:7655"
|
|
installRec := httptest.NewRecorder()
|
|
handler.HandleAgentInstallCommand(installRec, installReq)
|
|
if installRec.Code != http.StatusOK {
|
|
t.Fatalf("host install token mint status = %d, body=%s", installRec.Code, installRec.Body.String())
|
|
}
|
|
var install AgentInstallCommandResponse
|
|
if err := json.Unmarshal(installRec.Body.Bytes(), &install); err != nil {
|
|
t.Fatalf("decode host install token response: %v", err)
|
|
}
|
|
rawToken := strings.TrimSpace(install.Token)
|
|
if rawToken == "" {
|
|
t.Fatal("host install token mint omitted runtime token")
|
|
}
|
|
if !cfg.APITokens[0].HasScope(config.ScopeAgentExec) {
|
|
t.Fatalf("commands-enabled host install token is missing %s: %v", config.ScopeAgentExec, cfg.APITokens[0].Scopes)
|
|
}
|
|
|
|
registerRec := runAgentAutoRegister(t, handler, rawToken, AutoRegisterRequest{
|
|
Type: "pve", Host: "https://pve-exec.local:8006", TokenID: "pulse-monitor@pve!pulse-pve-exec",
|
|
TokenValue: "proxmox-secret", ServerName: "pve-exec", Source: "agent",
|
|
})
|
|
if registerRec.Code != http.StatusOK {
|
|
t.Fatalf("host-token pve registration status = %d, body=%s", registerRec.Code, registerRec.Body.String())
|
|
}
|
|
if got := cfg.APITokens[0].Metadata["bound_hostname"]; got != "pve-exec" {
|
|
t.Fatalf("auto-register bound hostname = %q, want %q", got, "pve-exec")
|
|
}
|
|
if got := strings.TrimSpace(cfg.APITokens[0].Metadata["bound_agent_id"]); got != "" {
|
|
t.Fatalf("auto-register wrote bound_agent_id = %q; the exec first-use path assumes it is empty", got)
|
|
}
|
|
|
|
decision := evaluateAgentExecBinding(&cfg.APITokens[0], "agent-machine-id", "pve-exec.lan")
|
|
if !decision.admit || !decision.firstBind {
|
|
t.Fatalf("auto-registered install token exec decision = %+v, want a clean first bind", decision)
|
|
}
|
|
if decision.legacyMigrate {
|
|
t.Fatal("auto-registered install token was admitted through the legacy migration branch")
|
|
}
|
|
|
|
router := &Router{config: cfg, persistence: config.NewConfigPersistence(dataPath)}
|
|
admission, ok := router.admitAgentExecToken(rawToken, "agent-machine-id", "pve-exec.lan")
|
|
if !ok {
|
|
t.Fatal("auto-registered host install token was rejected on the command channel")
|
|
}
|
|
if admission.AgentID != "agent-machine-id" {
|
|
t.Fatalf("admitted agent id = %q, want %q", admission.AgentID, "agent-machine-id")
|
|
}
|
|
|
|
config.Mu.RLock()
|
|
defer config.Mu.RUnlock()
|
|
bound := cfg.APITokens[0].Metadata
|
|
if got := bound["bound_agent_id"]; got != "agent-machine-id" {
|
|
t.Fatalf("bound_agent_id = %q, want %q", got, "agent-machine-id")
|
|
}
|
|
if got := bound[agentExecBindingVersionKey]; got != agentExecBindingVersion {
|
|
t.Fatalf("%s = %q, want %q", agentExecBindingVersionKey, got, agentExecBindingVersion)
|
|
}
|
|
if got := bound["bound_hostname"]; got != "pve-exec" {
|
|
t.Fatalf("bound_hostname after exec bind = %q, want the auto-registered %q", got, "pve-exec")
|
|
}
|
|
}
|