|
|
|
@ -17,18 +17,24 @@ |
|
|
|
|
package api |
|
|
|
|
|
|
|
|
|
import ( |
|
|
|
|
"errors" |
|
|
|
|
"net/http" |
|
|
|
|
"strings" |
|
|
|
|
"time" |
|
|
|
|
|
|
|
|
|
"github.com/minio-io/minio/pkg/api/config" |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
type vHandler struct { |
|
|
|
|
type timeHandler struct { |
|
|
|
|
handler http.Handler |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
type validateHandler struct { |
|
|
|
|
conf config.Config |
|
|
|
|
handler http.Handler |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
type rHandler struct { |
|
|
|
|
type resourceHandler struct { |
|
|
|
|
handler http.Handler |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -45,17 +51,58 @@ func stripAccessKey(r *http.Request) string { |
|
|
|
|
return splits[0] |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func getDate(req *http.Request) (time.Time, error) { |
|
|
|
|
if req.Header.Get("x-amz-date") != "" { |
|
|
|
|
return time.Parse(http.TimeFormat, req.Header.Get("x-amz-date")) |
|
|
|
|
} |
|
|
|
|
if req.Header.Get("Date") != "" { |
|
|
|
|
return time.Parse(http.TimeFormat, req.Header.Get("Date")) |
|
|
|
|
} |
|
|
|
|
return time.Time{}, errors.New("invalid request") |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func timeValidityHandler(h http.Handler) http.Handler { |
|
|
|
|
return timeHandler{h} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (h timeHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { |
|
|
|
|
acceptsContentType := getContentType(r) |
|
|
|
|
if acceptsContentType == unknownContentType { |
|
|
|
|
writeErrorResponse(w, r, NotAcceptable, acceptsContentType, r.URL.Path) |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
// Verify if date headers are set, if not reject the request
|
|
|
|
|
if r.Header.Get("x-amz-date") == "" && r.Header.Get("Date") == "" { |
|
|
|
|
// there is no way to knowing if this is a valid request, could be a attack reject such clients
|
|
|
|
|
writeErrorResponse(w, r, RequestTimeTooSkewed, acceptsContentType, r.URL.Path) |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
date, err := getDate(r) |
|
|
|
|
if err != nil { |
|
|
|
|
// there is no way to knowing if this is a valid request, could be a attack reject such clients
|
|
|
|
|
writeErrorResponse(w, r, RequestTimeTooSkewed, acceptsContentType, r.URL.Path) |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
duration := time.Since(date) |
|
|
|
|
minutes := time.Duration(5) * time.Minute |
|
|
|
|
if duration.Minutes() > minutes.Minutes() { |
|
|
|
|
writeErrorResponse(w, r, RequestTimeTooSkewed, acceptsContentType, r.URL.Path) |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
h.handler.ServeHTTP(w, r) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Validate handler is wrapper handler used for API request validation with authorization header.
|
|
|
|
|
// Current authorization layer supports S3's standard HMAC based signature request.
|
|
|
|
|
func validateHandler(conf config.Config, h http.Handler) http.Handler { |
|
|
|
|
return vHandler{ |
|
|
|
|
func validateRequestHandler(conf config.Config, h http.Handler) http.Handler { |
|
|
|
|
return validateHandler{ |
|
|
|
|
conf: conf, |
|
|
|
|
handler: h, |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Validate handler ServeHTTP() wrapper
|
|
|
|
|
func (h vHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { |
|
|
|
|
func (h validateHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { |
|
|
|
|
acceptsContentType := getContentType(r) |
|
|
|
|
if acceptsContentType == unknownContentType { |
|
|
|
|
writeErrorResponse(w, r, NotAcceptable, acceptsContentType, r.URL.Path) |
|
|
|
@ -105,21 +152,25 @@ func (h vHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { |
|
|
|
|
// Since we do not support all the S3 queries, it is necessary for us to throw back a
|
|
|
|
|
// valid error message indicating such a feature to have been not implemented.
|
|
|
|
|
func ignoreResourcesHandler(h http.Handler) http.Handler { |
|
|
|
|
return rHandler{h} |
|
|
|
|
return resourceHandler{h} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Resource handler ServeHTTP() wrapper
|
|
|
|
|
func (h rHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { |
|
|
|
|
func (h resourceHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { |
|
|
|
|
acceptsContentType := getContentType(r) |
|
|
|
|
if acceptsContentType == unknownContentType { |
|
|
|
|
writeErrorResponse(w, r, NotAcceptable, acceptsContentType, r.URL.Path) |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
if ignoreUnImplementedObjectResources(r) || ignoreUnImplementedBucketResources(r) { |
|
|
|
|
error := getErrorCode(NotImplemented) |
|
|
|
|
errorResponse := getErrorResponse(error, "") |
|
|
|
|
setCommonHeaders(w, getContentTypeString(acceptsContentType)) |
|
|
|
|
w.WriteHeader(error.HTTPStatusCode) |
|
|
|
|
w.Write(encodeErrorResponse(errorResponse, acceptsContentType)) |
|
|
|
|
} else { |
|
|
|
|
h.handler.ServeHTTP(w, r) |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
h.handler.ServeHTTP(w, r) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
//// helpers
|
|
|
|
|