aboutsummaryrefslogtreecommitdiff
path: root/weed/s3api/cors/middleware.go
blob: c9cd0e19e894731499328cfa6f861a609f5f0891 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
package cors

import (
	"net/http"

	"github.com/seaweedfs/seaweedfs/weed/glog"
	"github.com/seaweedfs/seaweedfs/weed/s3api/s3_constants"
	"github.com/seaweedfs/seaweedfs/weed/s3api/s3err"
)

// BucketChecker interface for checking bucket existence
type BucketChecker interface {
	CheckBucket(r *http.Request, bucket string) s3err.ErrorCode
}

// CORSConfigGetter interface for getting CORS configuration
type CORSConfigGetter interface {
	GetCORSConfiguration(bucket string) (*CORSConfiguration, s3err.ErrorCode)
}

// Middleware handles CORS evaluation for all S3 API requests
type Middleware struct {
	bucketChecker    BucketChecker
	corsConfigGetter CORSConfigGetter
}

// NewMiddleware creates a new CORS middleware instance
func NewMiddleware(bucketChecker BucketChecker, corsConfigGetter CORSConfigGetter) *Middleware {
	return &Middleware{
		bucketChecker:    bucketChecker,
		corsConfigGetter: corsConfigGetter,
	}
}

// Handler returns the CORS middleware handler
func (m *Middleware) Handler(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// Parse CORS request
		corsReq := ParseRequest(r)

		// If not a CORS request, continue normally
		if corsReq.Origin == "" {
			next.ServeHTTP(w, r)
			return
		}

		// Extract bucket from request
		bucket, _ := s3_constants.GetBucketAndObject(r)
		if bucket == "" {
			next.ServeHTTP(w, r)
			return
		}

		// Check if bucket exists
		if err := m.bucketChecker.CheckBucket(r, bucket); err != s3err.ErrNone {
			// For non-existent buckets, let the normal handler deal with it
			next.ServeHTTP(w, r)
			return
		}

		// Load CORS configuration from cache
		config, errCode := m.corsConfigGetter.GetCORSConfiguration(bucket)
		if errCode != s3err.ErrNone || config == nil {
			// No CORS configuration, handle based on request type
			if corsReq.IsPreflightRequest {
				// Preflight request without CORS config should fail
				s3err.WriteErrorResponse(w, r, s3err.ErrAccessDenied)
				return
			}
			// Non-preflight request, continue normally
			next.ServeHTTP(w, r)
			return
		}

		// Evaluate CORS request
		corsResp, err := EvaluateRequest(config, corsReq)
		if err != nil {
			glog.V(3).Infof("CORS evaluation failed for bucket %s: %v", bucket, err)
			if corsReq.IsPreflightRequest {
				// Preflight request that doesn't match CORS rules should fail
				s3err.WriteErrorResponse(w, r, s3err.ErrAccessDenied)
				return
			}
			// Non-preflight request, continue normally but without CORS headers
			next.ServeHTTP(w, r)
			return
		}

		// Apply CORS headers
		ApplyHeaders(w, corsResp)

		// Handle preflight requests
		if corsReq.IsPreflightRequest {
			// Preflight request should return 200 OK with just CORS headers
			w.WriteHeader(http.StatusOK)
			return
		}

		// For actual requests, continue with normal processing
		next.ServeHTTP(w, r)
	})
}

// HandleOptionsRequest handles OPTIONS requests for CORS preflight
func (m *Middleware) HandleOptionsRequest(w http.ResponseWriter, r *http.Request) {
	// Parse CORS request
	corsReq := ParseRequest(r)

	// If not a CORS request, return OK
	if corsReq.Origin == "" {
		w.WriteHeader(http.StatusOK)
		return
	}

	// Extract bucket from request
	bucket, _ := s3_constants.GetBucketAndObject(r)
	if bucket == "" {
		w.WriteHeader(http.StatusOK)
		return
	}

	// Check if bucket exists
	if err := m.bucketChecker.CheckBucket(r, bucket); err != s3err.ErrNone {
		// For non-existent buckets, return OK (let other handlers deal with bucket existence)
		w.WriteHeader(http.StatusOK)
		return
	}

	// Load CORS configuration from cache
	config, errCode := m.corsConfigGetter.GetCORSConfiguration(bucket)
	if errCode != s3err.ErrNone || config == nil {
		// No CORS configuration for OPTIONS request should return access denied
		if corsReq.IsPreflightRequest {
			s3err.WriteErrorResponse(w, r, s3err.ErrAccessDenied)
			return
		}
		w.WriteHeader(http.StatusOK)
		return
	}

	// Evaluate CORS request
	corsResp, err := EvaluateRequest(config, corsReq)
	if err != nil {
		glog.V(3).Infof("CORS evaluation failed for bucket %s: %v", bucket, err)
		if corsReq.IsPreflightRequest {
			s3err.WriteErrorResponse(w, r, s3err.ErrAccessDenied)
			return
		}
		w.WriteHeader(http.StatusOK)
		return
	}

	// Apply CORS headers and return success
	ApplyHeaders(w, corsResp)
	w.WriteHeader(http.StatusOK)
}