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.
|
// unsignedImagePath is an image URL that carries no signature.
|
||||||
const unsignedImagePath = "/v1/image/cdn.example.com/cat.jpg/100x100.jpeg"
|
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
|
// TestMaintenanceModeRefusesImageRequests verifies that while maintenance
|
||||||
// mode is on, both image routes answer 503 Service Unavailable with a
|
// mode is on, both image routes answer 503 Service Unavailable with a
|
||||||
// Retry-After header and the JSON error body the image handlers send.
|
// 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.MethodGet, unsignedImagePath},
|
||||||
{http.MethodHead, unsignedImagePath},
|
{http.MethodHead, unsignedImagePath},
|
||||||
{http.MethodGet, "/v1/e/token/cat.jpg"},
|
{http.MethodGet, encryptedImagePath},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range requests {
|
for _, tc := range requests {
|
||||||
@@ -95,7 +99,7 @@ func TestImageRequestsServedWithoutMaintenanceMode(t *testing.T) {
|
|||||||
}{
|
}{
|
||||||
{http.MethodGet, unsignedImagePath, http.StatusUnauthorized},
|
{http.MethodGet, unsignedImagePath, http.StatusUnauthorized},
|
||||||
{http.MethodHead, 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 {
|
for _, tc := range requests {
|
||||||
|
|||||||
Reference in New Issue
Block a user