Build signed object URLs as path-only (/api/objects/...) instead of prefixing APP_BASE_URL, so media loads from whatever host or reverse proxy served the app. Remove the now-unused base URL from the signer and drop the APP_BASE_URL / Server.BaseURL config.
83 lines
2.0 KiB
Go
83 lines
2.0 KiB
Go
package storage
|
|
|
|
import (
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"net/url"
|
|
"strconv"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func expiryOf(t *testing.T, raw string) int64 {
|
|
t.Helper()
|
|
u, err := url.Parse(raw)
|
|
if err != nil {
|
|
t.Fatalf("parse url: %v", err)
|
|
}
|
|
e, err := strconv.ParseInt(u.Query().Get("e"), 10, 64)
|
|
if err != nil {
|
|
t.Fatalf("parse expiry: %v", err)
|
|
}
|
|
return e
|
|
}
|
|
|
|
func TestSignerURLStableWithinWindow(t *testing.T) {
|
|
s, err := NewSigner("secret", time.Hour, time.Hour)
|
|
if err != nil {
|
|
t.Fatalf("new signer: %v", err)
|
|
}
|
|
first := s.URL("objects/a/thumb.webp")
|
|
second := s.URL("objects/a/thumb.webp")
|
|
if first != second {
|
|
t.Fatalf("URL changed within window:\n %q\n %q", first, second)
|
|
}
|
|
}
|
|
|
|
func TestSignerURLChangesAcrossWindow(t *testing.T) {
|
|
s, err := NewSigner("secret", time.Hour, 10*time.Minute)
|
|
if err != nil {
|
|
t.Fatalf("new signer: %v", err)
|
|
}
|
|
base := time.Now()
|
|
s.now = func() time.Time { return base }
|
|
first := expiryOf(t, s.URL("objects/a/thumb.webp"))
|
|
s.now = func() time.Time { return base.Add(s.window) }
|
|
second := expiryOf(t, s.URL("objects/a/thumb.webp"))
|
|
if first == second {
|
|
t.Fatalf("expiry did not advance across window: %d", first)
|
|
}
|
|
}
|
|
|
|
func TestSignerURLVerifies(t *testing.T) {
|
|
s, err := NewSigner("secret", time.Hour, 10*time.Minute)
|
|
if err != nil {
|
|
t.Fatalf("new signer: %v", err)
|
|
}
|
|
key := "objects/a/thumb.webp"
|
|
raw := s.URL(key)
|
|
req := httptest.NewRequest(http.MethodGet, raw, nil)
|
|
if err := s.VerifyRequest(key, req); err != nil {
|
|
t.Fatalf("verify issued URL: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestSignerURLIsOriginRelative(t *testing.T) {
|
|
s, err := NewSigner("secret", time.Hour, 10*time.Minute)
|
|
if err != nil {
|
|
t.Fatalf("new signer: %v", err)
|
|
}
|
|
raw := s.URL("objects/a/thumb.webp")
|
|
u, err := url.Parse(raw)
|
|
if err != nil {
|
|
t.Fatalf("parse url: %v", err)
|
|
}
|
|
if u.Scheme != "" || u.Host != "" || u.IsAbs() {
|
|
t.Fatalf("url is not origin-relative: %q", raw)
|
|
}
|
|
if !strings.HasPrefix(raw, "/api/objects/") {
|
|
t.Fatalf("url = %q, want /api/objects/ prefix", raw)
|
|
}
|
|
}
|