Send CORS headers only from the image routes (closes #98) #162
@@ -0,0 +1,64 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TestCORSOnlyOnImageRoutes verifies that the image routes answer with the
|
||||
// configured access_control_allow_origin, a preflight request included, and
|
||||
// that the login and URL generator pages send no Access-Control-Allow-Origin,
|
||||
// so no other site can read them. /metrics is left out: its middleware
|
||||
// registers with the process-wide Prometheus registry, which only one test
|
||||
// in this package can do.
|
||||
func TestCORSOnlyOnImageRoutes(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
const appOrigin = "https://app.example.com"
|
||||
|
||||
s := newTestServer(t)
|
||||
s.config.AccessControlAllowOrigin = appOrigin
|
||||
s.SetupRoutes()
|
||||
|
||||
requests := []struct {
|
||||
method string
|
||||
path string
|
||||
want string
|
||||
}{
|
||||
{http.MethodGet, unsignedImagePath, appOrigin},
|
||||
{http.MethodHead, unsignedImagePath, appOrigin},
|
||||
{http.MethodOptions, unsignedImagePath, appOrigin},
|
||||
{http.MethodGet, encryptedImagePath, appOrigin},
|
||||
{http.MethodGet, "/", ""},
|
||||
{http.MethodOptions, "/", ""},
|
||||
{http.MethodPost, "/generate", ""},
|
||||
{http.MethodGet, "/logout", ""},
|
||||
}
|
||||
|
||||
for _, tc := range requests {
|
||||
t.Run(tc.method+" "+tc.path, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
req := httptest.NewRequestWithContext(
|
||||
t.Context(), tc.method, tc.path, nil)
|
||||
req.Header.Set("Origin", appOrigin)
|
||||
|
||||
// An OPTIONS request naming the method it asks about is the
|
||||
// preflight a browser sends before some cross-origin requests.
|
||||
if tc.method == http.MethodOptions {
|
||||
req.Header.Set("Access-Control-Request-Method", http.MethodGet)
|
||||
}
|
||||
|
||||
rec := httptest.NewRecorder()
|
||||
s.ServeHTTP(rec, req)
|
||||
t.Logf("status %d", rec.Code)
|
||||
|
||||
got := rec.Header().Get("Access-Control-Allow-Origin")
|
||||
if got != tc.want {
|
||||
t.Errorf("Access-Control-Allow-Origin = %q, want %q",
|
||||
got, tc.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,10 @@ import (
|
||||
// unsignedImagePath is an image URL that carries no signature.
|
||||
const unsignedImagePath = "/v1/image/cdn.example.com/cat.jpg/100x100.jpeg"
|
||||
|
||||
// encryptedImagePath is an encrypted image URL whose token cannot be
|
||||
// decrypted.
|
||||
const encryptedImagePath = "/v1/e/token/cat.jpg"
|
||||
|
||||
// TestMaintenanceModeRefusesImageRequests verifies that while maintenance
|
||||
// mode is on, both image routes answer 503 Service Unavailable with a
|
||||
// Retry-After header and the JSON error body the image handlers send.
|
||||
@@ -28,7 +32,7 @@ func TestMaintenanceModeRefusesImageRequests(t *testing.T) {
|
||||
}{
|
||||
{http.MethodGet, unsignedImagePath},
|
||||
{http.MethodHead, unsignedImagePath},
|
||||
{http.MethodGet, "/v1/e/token/cat.jpg"},
|
||||
{http.MethodGet, encryptedImagePath},
|
||||
}
|
||||
|
||||
for _, tc := range requests {
|
||||
@@ -95,7 +99,7 @@ func TestImageRequestsServedWithoutMaintenanceMode(t *testing.T) {
|
||||
}{
|
||||
{http.MethodGet, unsignedImagePath, http.StatusUnauthorized},
|
||||
{http.MethodHead, unsignedImagePath, http.StatusUnauthorized},
|
||||
{http.MethodGet, "/v1/e/token/cat.jpg", http.StatusBadRequest},
|
||||
{http.MethodGet, encryptedImagePath, http.StatusBadRequest},
|
||||
}
|
||||
|
||||
for _, tc := range requests {
|
||||
|
||||
Reference in New Issue
Block a user