admin_test.go 2.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. // Tests for the M13a W5 JWT gate in deliverd-telegram.
  2. // Mirrors cmd/deliverd-fcm/admin_test.go but for the
  3. // telegram channel. The actual DLQ handlers need a real
  4. // Postgres pool; those paths are covered by the M8 smoke
  5. // and the m13a E2E.
  6. package main
  7. import (
  8. "crypto/hmac"
  9. "crypto/sha256"
  10. "encoding/base64"
  11. "encoding/json"
  12. "io"
  13. "log/slog"
  14. "net/http"
  15. "net/http/httptest"
  16. "os"
  17. "testing"
  18. "time"
  19. )
  20. const testSecret = "test-secret-with-32-bytes-min-len-abc"
  21. func mintTestJWT(t *testing.T, secret, role string) string {
  22. t.Helper()
  23. hb, _ := json.Marshal(map[string]string{"alg": "HS256", "typ": "JWT"})
  24. body, _ := json.Marshal(map[string]any{
  25. "sub": "u-test",
  26. "tid": "t-test",
  27. "role": role,
  28. "typ": "access",
  29. "iss": "broad-announce",
  30. "exp": time.Now().Add(15 * time.Minute).Unix(),
  31. "iat": time.Now().Unix(),
  32. })
  33. enc := base64.RawURLEncoding.EncodeToString(hb) + "." + base64.RawURLEncoding.EncodeToString(body)
  34. mac := hmac.New(sha256.New, []byte(secret))
  35. mac.Write([]byte(enc))
  36. return enc + "." + base64.RawURLEncoding.EncodeToString(mac.Sum(nil))
  37. }
  38. func discardLogger() *slog.Logger {
  39. return slog.New(slog.NewTextHandler(io.Discard, nil))
  40. }
  41. func TestWireAdminRoutes_NoSecret_Disabled(t *testing.T) {
  42. os.Unsetenv("BA_AUTHD_JWT_SECRET")
  43. mux := http.NewServeMux()
  44. wireAdminRoutes(mux, nil, discardLogger())
  45. for _, tc := range []struct{ method, path string }{
  46. {"GET", "/v1/admin/dlq"},
  47. {"GET", "/v1/admin/dlq/1"},
  48. } {
  49. rr := httptest.NewRecorder()
  50. mux.ServeHTTP(rr, httptest.NewRequest(tc.method, tc.path, nil))
  51. if rr.Code != http.StatusNotFound {
  52. t.Errorf("%s %s with no secret: got %d, want 404", tc.method, tc.path, rr.Code)
  53. }
  54. }
  55. }
  56. func TestWireAdminRoutes_WithSecret_Gated(t *testing.T) {
  57. t.Setenv("BA_AUTHD_JWT_SECRET", testSecret)
  58. t.Setenv("BA_AUTHD_ISSUER", "broad-announce")
  59. mux := http.NewServeMux()
  60. wireAdminRoutes(mux, nil, discardLogger())
  61. for _, tc := range []struct{ method, path string }{
  62. {"GET", "/v1/admin/dlq"},
  63. {"GET", "/v1/admin/dlq/1"},
  64. } {
  65. rr := httptest.NewRecorder()
  66. mux.ServeHTTP(rr, httptest.NewRequest(tc.method, tc.path, nil))
  67. if rr.Code != http.StatusUnauthorized {
  68. t.Errorf("%s %s with secret+no-token: status = %d, want 401", tc.method, tc.path, rr.Code)
  69. }
  70. }
  71. }
  72. func TestAdminDLQ_AnyAuthenticatedUser_CanList(t *testing.T) {
  73. t.Setenv("BA_AUTHD_JWT_SECRET", testSecret)
  74. mux := http.NewServeMux()
  75. wireAdminRoutes(mux, nil, discardLogger())
  76. for _, role := range []string{"viewer", "tenant_admin", "super_admin"} {
  77. t.Run(role, func(t *testing.T) {
  78. tok := mintTestJWT(t, testSecret, role)
  79. req := httptest.NewRequest("GET", "/v1/admin/dlq", nil)
  80. req.Header.Set("Authorization", "Bearer "+tok)
  81. rr := httptest.NewRecorder()
  82. func() {
  83. defer func() { _ = recover() }()
  84. mux.ServeHTTP(rr, req)
  85. }()
  86. if rr.Code == http.StatusUnauthorized || rr.Code == http.StatusForbidden {
  87. t.Errorf("%s: status = %d, want gate passed (500/panic expected)", role, rr.Code)
  88. }
  89. })
  90. }
  91. }