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()) } }) }