- pier: TestSessionReserve (A-WR-03), TestLockScope (A-WR-07), TestScopeWidening (A-WR-04), TestStatelessFanOut (A-WR-05, A-WR-06), TestPersonalTokenBinding (A-WR-09), TestDispatchHardening (A-WR-01, A-WR-02, A-WR-08, A-IN-04, A-IN-05, A-IN-08), TestCatalogPathFields (A-WR-02, A-IN-07), TestPromptWriteCeiling (C-IN-01), TestTokenScopedRevoke (B-WR-04, C-IN-02), TestResolveClientAddress (B-IN-04); test seam WrapStore - pact TestFrontendTokenScopes, towel TestClientIP, cabana TestTokenScopeNameParity - cabana TestAccountContributionURL (B-WR-03), TestAccountContributionsIsolation (B-WR-06, B-IN-03), TestRecordActionPromptLocalized (B-IN-02), and the Postgres TestAccountTokenRevokeEvent (B-WR-04) and TestResolveBackendTokenRemote (B-IN-04) - vitest: hotkeysEditing.test.ts (B-WR-01, B-WR-02) and the extended AccountContributions.test.ts (B-WR-03, B-WR-06, B-IN-01) - check-phase19.7.sh requires every name (GO_TESTS, GO_TAGGED_TESTS, DOCKER_TESTS, ADMIN_FILES) and scans the new files (PHASE_TEST_FILES)
79 lines
3.0 KiB
Go
79 lines
3.0 KiB
Go
package cabana_test
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"net/http"
|
|
"strings"
|
|
"testing"
|
|
|
|
"git.golem15.com/golem15/summercms/modules/backpack"
|
|
"git.golem15.com/golem15/summercms/modules/cabana"
|
|
"git.golem15.com/golem15/summercms/modules/towel"
|
|
)
|
|
|
|
// TestResolveBackendTokenRemote pins plan 07's client address on the MCP
|
|
// token audit line (B-IN-04): ResolveBackendToken logs the address the
|
|
// caller put on the context with towel.WithClientIP as remote, on a failed
|
|
// and on a successful resolve, and never the token.
|
|
func TestResolveBackendTokenRemote(t *testing.T) {
|
|
const bogus = "g15_0000000000000000000000000000000000000000"
|
|
|
|
t.Run("a failed resolve without the admin logs remote", func(t *testing.T) {
|
|
logs := captureProcessLog(t)
|
|
ctx := towel.WithClientIP(context.Background(), "198.51.100.4")
|
|
if _, _, _, err := cabana.ResolveBackendToken(ctx, backpack.New(nil), bogus); !errors.Is(err, cabana.ErrUnauthenticated) {
|
|
t.Fatalf("err = %v", err)
|
|
}
|
|
logged := logs.String()
|
|
if !strings.Contains(logged, "admin.auth") || !strings.Contains(logged, "outcome=failed") || !strings.Contains(logged, "remote=198.51.100.4") {
|
|
t.Fatalf("audit line = %s", logged)
|
|
}
|
|
})
|
|
|
|
// The Postgres part runs on a Docker host (the gate's docker stage);
|
|
// under -short it is not created, so no subtest is reported as skipped.
|
|
if testing.Short() {
|
|
return
|
|
}
|
|
t.Run("failed and successful resolves log remote", func(t *testing.T) {
|
|
env := newScreensEnv(t)
|
|
admin, jwt := env.admin(t, env.unique("remote"), true, "")
|
|
out := env.expect(t, http.StatusCreated, http.MethodPost, "/account/tokens", jwt, map[string]any{
|
|
"name": env.unique("agent"), "expires": "90d", "scopes": []string{"*"},
|
|
})
|
|
raw, _ := json.Marshal(out["data"])
|
|
var created cabana.AdminAPITokenCreated
|
|
if err := json.Unmarshal(raw, &created); err != nil || created.Token == "" {
|
|
t.Fatalf("created token = %s (%v)", raw, err)
|
|
}
|
|
logs := captureProcessLog(t)
|
|
ctx := towel.WithClientIP(context.Background(), "203.0.113.9")
|
|
if _, _, _, err := cabana.ResolveBackendToken(ctx, env.app, bogus); !errors.Is(err, cabana.ErrUnauthenticated) {
|
|
t.Fatalf("unknown token err = %v", err)
|
|
}
|
|
principal, row, _, err := cabana.ResolveBackendToken(ctx, env.app, created.Token)
|
|
if err != nil || principal == nil || principal.ID != admin.ID || row == nil || row.ID != created.ID {
|
|
t.Fatalf("resolve = %+v, %+v, %v", principal, row, err)
|
|
}
|
|
var failed, success bool
|
|
for _, line := range strings.Split(logs.String(), "\n") {
|
|
if !strings.Contains(line, "admin.auth") {
|
|
continue
|
|
}
|
|
if strings.Contains(line, created.Token) || strings.Contains(line, bogus) {
|
|
t.Fatalf("the audit line carries a token: %s", line)
|
|
}
|
|
if !strings.Contains(line, "remote=203.0.113.9") {
|
|
t.Fatalf("audit line without the client address: %s", line)
|
|
}
|
|
failed = failed || strings.Contains(line, "outcome=failed")
|
|
success = success || strings.Contains(line, "outcome=success")
|
|
}
|
|
if !failed || !success {
|
|
t.Fatalf("audit lines (failed %v, success %v): %s", failed, success, logs.String())
|
|
}
|
|
})
|
|
}
|