mirror of
https://github.com/pgsty/minio.git
synced 2026-10-31 12:52:27 +03:00
fix: skip bucket CORS lookup without an origin
Bypass per-bucket metadata work for non-CORS traffic, including admin and Console routes. Keep operational metadata errors fail-closed and pin the existing global fallback for missing buckets. Signed-off-by: Feng Ruohang <rh@vonng.com>
This commit is contained in:
@@ -785,6 +785,10 @@ func corsHandler(handler http.Handler) http.Handler {
|
|||||||
}
|
}
|
||||||
globalCors := cors.New(opts).Handler(handler)
|
globalCors := cors.New(opts).Handler(handler)
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if r.Header.Get("Origin") == "" {
|
||||||
|
handler.ServeHTTP(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
if bucket, _ := request2BucketObjectName(r); bucket != "" && globalBucketMetadataSys != nil {
|
if bucket, _ := request2BucketObjectName(r); bucket != "" && globalBucketMetadataSys != nil {
|
||||||
cfg, _, err := globalBucketMetadataSys.GetCorsConfig(bucket)
|
cfg, _, err := globalBucketMetadataSys.GetCorsConfig(bucket)
|
||||||
if err == nil && cfg != nil {
|
if err == nil && cfg != nil {
|
||||||
|
|||||||
@@ -18,15 +18,27 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync/atomic"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/minio/minio/internal/auth"
|
"github.com/minio/minio/internal/auth"
|
||||||
"github.com/minio/minio/internal/bucket/cors"
|
"github.com/minio/minio/internal/bucket/cors"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type corsLookupCountingObjectLayer struct {
|
||||||
|
ObjectLayer
|
||||||
|
getObjectNInfoCalls atomic.Int64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *corsLookupCountingObjectLayer) GetObjectNInfo(ctx context.Context, bucket, object string, rs *HTTPRangeSpec, h http.Header, opts ObjectOptions) (*GetObjectReader, error) {
|
||||||
|
o.getObjectNInfoCalls.Add(1)
|
||||||
|
return o.ObjectLayer.GetObjectNInfo(ctx, bucket, object, rs, h, opts)
|
||||||
|
}
|
||||||
|
|
||||||
func TestPerBucketCorsPreflight(t *testing.T) {
|
func TestPerBucketCorsPreflight(t *testing.T) {
|
||||||
cfg := &cors.Config{CORSRules: []cors.Rule{{
|
cfg := &cors.Config{CORSRules: []cors.Rule{{
|
||||||
AllowedOrigins: []string{"http://example.com"},
|
AllowedOrigins: []string{"http://example.com"},
|
||||||
@@ -245,8 +257,13 @@ func TestPerBucketCorsOriginPatternResponse(t *testing.T) {
|
|||||||
|
|
||||||
func TestBucketCorsMetadataErrorFailsClosed(t *testing.T) {
|
func TestBucketCorsMetadataErrorFailsClosed(t *testing.T) {
|
||||||
oldObjectAPI := newObjectLayerFn()
|
oldObjectAPI := newObjectLayerFn()
|
||||||
|
oldMetadataSys := globalBucketMetadataSys
|
||||||
setObjectLayer(nil)
|
setObjectLayer(nil)
|
||||||
defer setObjectLayer(oldObjectAPI)
|
globalBucketMetadataSys = NewBucketMetadataSys()
|
||||||
|
defer func() {
|
||||||
|
setObjectLayer(oldObjectAPI)
|
||||||
|
globalBucketMetadataSys = oldMetadataSys
|
||||||
|
}()
|
||||||
|
|
||||||
wrapped := corsHandler(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
wrapped := corsHandler(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
||||||
w.WriteHeader(http.StatusNoContent)
|
w.WriteHeader(http.StatusNoContent)
|
||||||
@@ -273,6 +290,34 @@ func TestBucketCorsMetadataErrorFailsClosed(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestBucketCorsSkipsMetadataLookupWithoutOrigin(t *testing.T) {
|
||||||
|
ExecObjectLayerAPITest(ExecObjectLayerAPITestArgs{
|
||||||
|
t: t,
|
||||||
|
objAPITest: testBucketCorsSkipsMetadataLookupWithoutOrigin,
|
||||||
|
endpoints: []string{"GetObject"},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func testBucketCorsSkipsMetadataLookupWithoutOrigin(obj ObjectLayer, _ string, _ string, _ http.Handler, _ auth.Credentials, t *testing.T) {
|
||||||
|
oldObjectAPI := newObjectLayerFn()
|
||||||
|
counting := &corsLookupCountingObjectLayer{ObjectLayer: obj}
|
||||||
|
setObjectLayer(counting)
|
||||||
|
defer setObjectLayer(oldObjectAPI)
|
||||||
|
|
||||||
|
wrapped := corsHandler(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
||||||
|
w.WriteHeader(http.StatusNoContent)
|
||||||
|
}))
|
||||||
|
rec := httptest.NewRecorder()
|
||||||
|
wrapped.ServeHTTP(rec, httptest.NewRequest(http.MethodGet, "/api/v1/login", nil))
|
||||||
|
|
||||||
|
if rec.Code != http.StatusNoContent {
|
||||||
|
t.Fatalf("status = %d, want %d", rec.Code, http.StatusNoContent)
|
||||||
|
}
|
||||||
|
if got := counting.getObjectNInfoCalls.Load(); got != 0 {
|
||||||
|
t.Fatalf("request without Origin performed %d bucket metadata reads", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestBucketCorsNoConfigUsesGlobalFallback(t *testing.T) {
|
func TestBucketCorsNoConfigUsesGlobalFallback(t *testing.T) {
|
||||||
ExecObjectLayerAPITest(ExecObjectLayerAPITestArgs{
|
ExecObjectLayerAPITest(ExecObjectLayerAPITestArgs{
|
||||||
t: t,
|
t: t,
|
||||||
@@ -281,6 +326,34 @@ func TestBucketCorsNoConfigUsesGlobalFallback(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestBucketCorsMissingBucketUsesGlobalFallback(t *testing.T) {
|
||||||
|
ExecObjectLayerAPITest(ExecObjectLayerAPITestArgs{
|
||||||
|
t: t,
|
||||||
|
objAPITest: testBucketCorsMissingBucketUsesGlobalFallback,
|
||||||
|
endpoints: []string{"GetBucketCors"},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func testBucketCorsMissingBucketUsesGlobalFallback(_ ObjectLayer, _ string, bucket string, _ http.Handler, _ auth.Credentials, t *testing.T) {
|
||||||
|
wrapped := corsHandler(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
||||||
|
w.WriteHeader(http.StatusNotFound)
|
||||||
|
}))
|
||||||
|
rec := httptest.NewRecorder()
|
||||||
|
req := httptest.NewRequest(http.MethodGet, getGetObjectURL("", bucket+"-missing", "object"), nil)
|
||||||
|
req.Header.Set("Origin", "https://app.example.com")
|
||||||
|
wrapped.ServeHTTP(rec, req)
|
||||||
|
|
||||||
|
if rec.Code != http.StatusNotFound {
|
||||||
|
t.Fatalf("status = %d, want %d", rec.Code, http.StatusNotFound)
|
||||||
|
}
|
||||||
|
if got := rec.Header().Get("Access-Control-Allow-Origin"); got != "https://app.example.com" {
|
||||||
|
t.Fatalf("allow-origin = %q", got)
|
||||||
|
}
|
||||||
|
if got := rec.Header().Get("Access-Control-Allow-Credentials"); got != "true" {
|
||||||
|
t.Fatalf("allow-credentials = %q", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func testBucketCorsNoConfigUsesGlobalFallback(_ ObjectLayer, _ string, bucket string, _ http.Handler, _ auth.Credentials, t *testing.T) {
|
func testBucketCorsNoConfigUsesGlobalFallback(_ ObjectLayer, _ string, bucket string, _ http.Handler, _ auth.Credentials, t *testing.T) {
|
||||||
wrapped := corsHandler(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
wrapped := corsHandler(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
||||||
w.WriteHeader(http.StatusNoContent)
|
w.WriteHeader(http.StatusNoContent)
|
||||||
|
|||||||
Reference in New Issue
Block a user