From 51e80eaa6d5da34849637a43d1fd4ee4f9ae34d8 Mon Sep 17 00:00:00 2001 From: Harshavardhana Date: Mon, 23 Feb 2015 02:11:27 -0800 Subject: [PATCH] Add domain and subdomain support for MinioAPI This change brings in domain and subdomain support - ./minio --domain "yourminiodomain.com" This change brings in a much needed feature by keeping bucketnames as part of your 'DNS' name. All your existing applications can be migrated off from s3 to Minio without little to no modifications. NOTE: Setting up DNS for your `buckets` is out of scope of this feature --- main.go | 8 ++ pkg/api/minioapi/api_test.go | 24 +++--- pkg/api/minioapi/bucket_handlers.go | 105 +------------------------- pkg/api/minioapi/headers.go | 5 ++ pkg/api/minioapi/muxer.go | 56 ++++++++++---- pkg/api/minioapi/object_handlers.go | 6 ++ pkg/api/minioapi/policy_handlers.go | 112 ++++++++++++++++++++++++++++ pkg/httpserver/httpserver.go | 2 +- pkg/server/server.go | 3 +- pkg/storage/fs/fs.go | 16 ++-- pkg/storage/storage.go | 3 +- pkg/utils/crypto/signers/signers.go | 25 ++++++- pkg/utils/policy/policy.go | 13 +++- 13 files changed, 233 insertions(+), 145 deletions(-) create mode 100644 pkg/api/minioapi/policy_handlers.go diff --git a/main.go b/main.go index 686b01378..250e59909 100644 --- a/main.go +++ b/main.go @@ -41,6 +41,7 @@ func getStorageType(input string) server.StorageType { func runCmd(c *cli.Context) { storageTypeStr := c.String("storage-type") + domain := c.String("domain") apiaddress := c.String("api-address") webaddress := c.String("web-address") certFile := c.String("cert") @@ -52,6 +53,7 @@ func runCmd(c *cli.Context) { storageType := getStorageType(storageTypeStr) var serverConfigs []server.ServerConfig apiServerConfig := server.ServerConfig{ + Domain: domain, Address: apiaddress, Tls: tls, CertFile: certFile, @@ -61,6 +63,7 @@ func runCmd(c *cli.Context) { }, } webUiServerConfig := server.ServerConfig{ + Domain: domain, Address: webaddress, Tls: false, CertFile: "", @@ -79,6 +82,11 @@ func main() { app.Name = "minio" app.Usage = "" app.Flags = []cli.Flag{ + cli.StringFlag{ + Name: "domain,d", + Value: "", + Usage: "address for incoming API requests", + }, cli.StringFlag{ Name: "api-address,a", Value: ":9000", diff --git a/pkg/api/minioapi/api_test.go b/pkg/api/minioapi/api_test.go index cfa53a388..b4247f174 100644 --- a/pkg/api/minioapi/api_test.go +++ b/pkg/api/minioapi/api_test.go @@ -40,7 +40,7 @@ var _ = Suite(&MySuite{}) func (s *MySuite) TestNonExistantObject(c *C) { _, _, storage := inmemory.Start() - httpHandler := HttpHandler(storage) + httpHandler := HttpHandler("", storage) testServer := httptest.NewServer(httpHandler) defer testServer.Close() @@ -52,7 +52,7 @@ func (s *MySuite) TestNonExistantObject(c *C) { func (s *MySuite) TestEmptyObject(c *C) { _, _, storage := inmemory.Start() - httpHandler := HttpHandler(storage) + httpHandler := HttpHandler("", storage) testServer := httptest.NewServer(httpHandler) defer testServer.Close() @@ -77,7 +77,7 @@ func (s *MySuite) TestEmptyObject(c *C) { func (s *MySuite) TestObject(c *C) { _, _, storage := inmemory.Start() - httpHandler := HttpHandler(storage) + httpHandler := HttpHandler("", storage) testServer := httptest.NewServer(httpHandler) defer testServer.Close() @@ -100,7 +100,7 @@ func (s *MySuite) TestObject(c *C) { func (s *MySuite) TestMultipleObjects(c *C) { _, _, storage := inmemory.Start() - httpHandler := HttpHandler(storage) + httpHandler := HttpHandler("", storage) testServer := httptest.NewServer(httpHandler) defer testServer.Close() @@ -180,7 +180,7 @@ func (s *MySuite) TestMultipleObjects(c *C) { func (s *MySuite) TestNotImplemented(c *C) { _, _, storage := inmemory.Start() - httpHandler := HttpHandler(storage) + httpHandler := HttpHandler("", storage) testServer := httptest.NewServer(httpHandler) defer testServer.Close() @@ -191,7 +191,7 @@ func (s *MySuite) TestNotImplemented(c *C) { func (s *MySuite) TestHeader(c *C) { _, _, storage := inmemory.Start() - httpHandler := HttpHandler(storage) + httpHandler := HttpHandler("", storage) testServer := httptest.NewServer(httpHandler) defer testServer.Close() @@ -214,7 +214,7 @@ func (s *MySuite) TestHeader(c *C) { func (s *MySuite) TestPutBucket(c *C) { _, _, storage := inmemory.Start() - httpHandler := HttpHandler(storage) + httpHandler := HttpHandler("", storage) testServer := httptest.NewServer(httpHandler) defer testServer.Close() @@ -239,7 +239,7 @@ func (s *MySuite) TestPutBucket(c *C) { func (s *MySuite) TestPutObject(c *C) { _, _, storage := inmemory.Start() - httpHandler := HttpHandler(storage) + httpHandler := HttpHandler("", storage) testServer := httptest.NewServer(httpHandler) defer testServer.Close() @@ -289,7 +289,7 @@ func (s *MySuite) TestPutObject(c *C) { func (s *MySuite) TestListBuckets(c *C) { _, _, storage := inmemory.Start() - httpHandler := HttpHandler(storage) + httpHandler := HttpHandler("", storage) testServer := httptest.NewServer(httpHandler) defer testServer.Close() @@ -368,7 +368,7 @@ func verifyHeaders(c *C, header http.Header, date time.Time, size int, contentTy func (s *MySuite) TestXMLNameNotInBucketListJson(c *C) { _, _, storage := inmemory.Start() - httpHandler := HttpHandler(storage) + httpHandler := HttpHandler("", storage) testServer := httptest.NewServer(httpHandler) defer testServer.Close() @@ -393,7 +393,7 @@ func (s *MySuite) TestXMLNameNotInBucketListJson(c *C) { func (s *MySuite) TestXMLNameNotInObjectListJson(c *C) { _, _, storage := inmemory.Start() - httpHandler := HttpHandler(storage) + httpHandler := HttpHandler("", storage) testServer := httptest.NewServer(httpHandler) defer testServer.Close() @@ -418,7 +418,7 @@ func (s *MySuite) TestXMLNameNotInObjectListJson(c *C) { func (s *MySuite) TestContentTypePersists(c *C) { _, _, storage := inmemory.Start() - httpHandler := HttpHandler(storage) + httpHandler := HttpHandler("", storage) testServer := httptest.NewServer(httpHandler) defer testServer.Close() diff --git a/pkg/api/minioapi/bucket_handlers.go b/pkg/api/minioapi/bucket_handlers.go index e1680c59b..00a625eae 100644 --- a/pkg/api/minioapi/bucket_handlers.go +++ b/pkg/api/minioapi/bucket_handlers.go @@ -1,116 +1,13 @@ package minioapi import ( - "encoding/json" "log" "net/http" "github.com/gorilla/mux" mstorage "github.com/minio-io/minio/pkg/storage" - "github.com/minio-io/minio/pkg/utils/policy" ) -func (server *minioApi) putBucketPolicyHandler(w http.ResponseWriter, req *http.Request) { - vars := mux.Vars(req) - bucket := vars["bucket"] - acceptsContentType := getContentType(req) - - policy, ok := policy.Parsepolicy(req.Body) - if ok == false { - error := errorCodeError(InvalidPolicyDocument) - errorResponse := getErrorResponse(error, bucket) - w.WriteHeader(error.HttpStatusCode) - w.Write(writeErrorResponse(w, errorResponse, acceptsContentType)) - return - } - - err := server.storage.StoreBucketPolicy(bucket, policy) - switch err := err.(type) { - case nil: - { - w.WriteHeader(http.StatusNoContent) - writeCommonHeaders(w, getContentString(acceptsContentType)) - w.Header().Set("Connection", "keep-alive") - } - case mstorage.BucketNameInvalid: - { - error := errorCodeError(InvalidBucketName) - errorResponse := getErrorResponse(error, bucket) - w.WriteHeader(error.HttpStatusCode) - w.Write(writeErrorResponse(w, errorResponse, acceptsContentType)) - } - case mstorage.BucketNotFound: - { - error := errorCodeError(NoSuchBucket) - errorResponse := getErrorResponse(error, bucket) - w.WriteHeader(error.HttpStatusCode) - w.Write(writeErrorResponse(w, errorResponse, acceptsContentType)) - } - case mstorage.BackendCorrupted: - case mstorage.ImplementationError: - { - log.Println(err) - error := errorCodeError(InternalError) - errorResponse := getErrorResponse(error, bucket) - w.WriteHeader(error.HttpStatusCode) - w.Write(writeErrorResponse(w, errorResponse, acceptsContentType)) - } - } -} - -func (server *minioApi) getBucketPolicyHandler(w http.ResponseWriter, req *http.Request) { - vars := mux.Vars(req) - bucket := vars["bucket"] - acceptsContentType := getContentType(req) - - p, err := server.storage.GetBucketPolicy(bucket) - switch err := err.(type) { - case nil: - { - responsePolicy, ret := json.Marshal(p) - if ret != nil { - error := errorCodeError(InternalError) - errorResponse := getErrorResponse(error, bucket) - w.WriteHeader(error.HttpStatusCode) - w.Write(writeErrorResponse(w, errorResponse, acceptsContentType)) - } - writeCommonHeaders(w, getContentString(acceptsContentType)) - w.Header().Set("Connection", "keep-alive") - w.Write(responsePolicy) - } - case mstorage.BucketNameInvalid: - { - error := errorCodeError(InvalidBucketName) - errorResponse := getErrorResponse(error, bucket) - w.WriteHeader(error.HttpStatusCode) - w.Write(writeErrorResponse(w, errorResponse, acceptsContentType)) - } - case mstorage.BucketNotFound: - { - error := errorCodeError(NoSuchBucket) - errorResponse := getErrorResponse(error, bucket) - w.WriteHeader(error.HttpStatusCode) - w.Write(writeErrorResponse(w, errorResponse, acceptsContentType)) - } - case mstorage.BucketPolicyNotFound: - { - error := errorCodeError(NoSuchBucketPolicy) - errorResponse := getErrorResponse(error, bucket) - w.WriteHeader(error.HttpStatusCode) - w.Write(writeErrorResponse(w, errorResponse, acceptsContentType)) - } - case mstorage.BackendCorrupted: - case mstorage.ImplementationError: - { - log.Println(err) - error := errorCodeError(InternalError) - errorResponse := getErrorResponse(error, bucket) - w.WriteHeader(error.HttpStatusCode) - w.Write(writeErrorResponse(w, errorResponse, acceptsContentType)) - } - } -} - func (server *minioApi) listObjectsHandler(w http.ResponseWriter, req *http.Request) { vars := mux.Vars(req) bucket := vars["bucket"] @@ -224,7 +121,7 @@ func (server *minioApi) putBucketHandler(w http.ResponseWriter, req *http.Reques } case mstorage.ImplementationError: { - // Embed errors log on serve side + // Embed errors log on server side log.Println(err) error := errorCodeError(InternalError) errorResponse := getErrorResponse(error, bucket) diff --git a/pkg/api/minioapi/headers.go b/pkg/api/minioapi/headers.go index 1d28098b7..10286f2b9 100644 --- a/pkg/api/minioapi/headers.go +++ b/pkg/api/minioapi/headers.go @@ -27,6 +27,11 @@ import ( mstorage "github.com/minio-io/minio/pkg/storage" ) +// No encoder interface exists, so we create one. +type encoder interface { + Encode(v interface{}) error +} + // Write Common Header helpers func writeCommonHeaders(w http.ResponseWriter, acceptsType string) { w.Header().Set("Server", "Minio") diff --git a/pkg/api/minioapi/muxer.go b/pkg/api/minioapi/muxer.go index a64645357..9c43d31a4 100644 --- a/pkg/api/minioapi/muxer.go +++ b/pkg/api/minioapi/muxer.go @@ -20,7 +20,7 @@ import ( "log" "net/http" - "github.com/gorilla/mux" + x "github.com/gorilla/mux" mstorage "github.com/minio-io/minio/pkg/storage" "github.com/minio-io/minio/pkg/utils/config" ) @@ -30,30 +30,60 @@ const ( ) type minioApi struct { + domain string storage mstorage.Storage } -// No encoder interface exists, so we create one. -type encoder interface { - Encode(v interface{}) error +func pathMux(api minioApi, mux *x.Router) *x.Router { + mux.HandleFunc("/", api.listBucketsHandler).Methods("GET") + mux.HandleFunc("/{bucket}", api.listObjectsHandler).Methods("GET") + mux.HandleFunc("/{bucket}", api.putBucketHandler).Methods("PUT") + mux.HandleFunc("/{bucket}/{object:.*}", api.getObjectHandler).Methods("GET") + mux.HandleFunc("/{bucket}/{object:.*}", api.headObjectHandler).Methods("HEAD") + mux.HandleFunc("/{bucket}/{object:.*}", api.putObjectHandler).Methods("PUT") + + return mux +} + +func domainMux(api minioApi, mux *x.Router) *x.Router { + mux.HandleFunc("/", + api.listObjectsHandler).Host("{bucket}" + "." + api.domain).Methods("GET") + mux.HandleFunc("/{object:.*}", + api.getObjectHandler).Host("{bucket}" + "." + api.domain).Methods("GET") + mux.HandleFunc("/{object:.*}", + api.headObjectHandler).Host("{bucket}" + "." + api.domain).Methods("HEAD") + mux.HandleFunc("/{object:.*}", + api.putObjectHandler).Host("{bucket}" + "." + api.domain).Methods("PUT") + mux.HandleFunc("/", api.listBucketsHandler).Methods("GET") + mux.HandleFunc("/{bucket}", api.putBucketHandler).Methods("PUT") + + return mux } -func HttpHandler(storage mstorage.Storage) http.Handler { - mux := mux.NewRouter() +func getMux(api minioApi, mux *x.Router) *x.Router { + switch true { + case api.domain == "": + return pathMux(api, mux) + case api.domain != "": + s := mux.Host(api.domain).Subrouter() + return domainMux(api, s) + } + return nil +} + +func HttpHandler(domain string, storage mstorage.Storage) http.Handler { + var mux *x.Router var api = minioApi{} api.storage = storage + api.domain = domain + + r := x.NewRouter() + mux = getMux(api, r) var conf = config.Config{} if err := conf.SetupConfig(); err != nil { log.Fatal(err) } - mux.HandleFunc("/", api.listBucketsHandler).Methods("GET") - mux.HandleFunc("/{bucket}", api.listObjectsHandler).Methods("GET") - mux.HandleFunc("/{bucket}", api.putBucketHandler).Methods("PUT") - mux.HandleFunc("/{bucket}/{object:.*}", api.getObjectHandler).Methods("GET") - mux.HandleFunc("/{bucket}/{object:.*}", api.headObjectHandler).Methods("HEAD") - mux.HandleFunc("/{bucket}/{object:.*}", api.putObjectHandler).Methods("PUT") - return validateHandler(conf, ignoreResourcesHandler(mux)) } diff --git a/pkg/api/minioapi/object_handlers.go b/pkg/api/minioapi/object_handlers.go index db0fbd559..9982fc54c 100644 --- a/pkg/api/minioapi/object_handlers.go +++ b/pkg/api/minioapi/object_handlers.go @@ -103,6 +103,12 @@ func (server *minioApi) putObjectHandler(w http.ResponseWriter, req *http.Reques bucket = vars["bucket"] object = vars["object"] + resources := getBucketResources(req.URL.Query()) + if resources.policy == true && object == "" { + server.putBucketPolicyHandler(w, req) + return + } + err := server.storage.StoreObject(bucket, object, "", req.Body) switch err := err.(type) { case nil: diff --git a/pkg/api/minioapi/policy_handlers.go b/pkg/api/minioapi/policy_handlers.go new file mode 100644 index 000000000..b3773dcf0 --- /dev/null +++ b/pkg/api/minioapi/policy_handlers.go @@ -0,0 +1,112 @@ +package minioapi + +import ( + "encoding/json" + "log" + "net/http" + + "github.com/gorilla/mux" + mstorage "github.com/minio-io/minio/pkg/storage" + "github.com/minio-io/minio/pkg/utils/policy" +) + +func (server *minioApi) putBucketPolicyHandler(w http.ResponseWriter, req *http.Request) { + vars := mux.Vars(req) + bucket := vars["bucket"] + acceptsContentType := getContentType(req) + + policy, ok := policy.Parsepolicy(req.Body) + if ok == false { + error := errorCodeError(InvalidPolicyDocument) + errorResponse := getErrorResponse(error, bucket) + w.WriteHeader(error.HttpStatusCode) + w.Write(writeErrorResponse(w, errorResponse, acceptsContentType)) + return + } + + err := server.storage.StoreBucketPolicy(bucket, policy) + switch err := err.(type) { + case nil: + { + w.WriteHeader(http.StatusNoContent) + writeCommonHeaders(w, getContentString(acceptsContentType)) + w.Header().Set("Connection", "keep-alive") + } + case mstorage.BucketNameInvalid: + { + error := errorCodeError(InvalidBucketName) + errorResponse := getErrorResponse(error, bucket) + w.WriteHeader(error.HttpStatusCode) + w.Write(writeErrorResponse(w, errorResponse, acceptsContentType)) + } + case mstorage.BucketNotFound: + { + error := errorCodeError(NoSuchBucket) + errorResponse := getErrorResponse(error, bucket) + w.WriteHeader(error.HttpStatusCode) + w.Write(writeErrorResponse(w, errorResponse, acceptsContentType)) + } + case mstorage.BackendCorrupted: + case mstorage.ImplementationError: + { + log.Println(err) + error := errorCodeError(InternalError) + errorResponse := getErrorResponse(error, bucket) + w.WriteHeader(error.HttpStatusCode) + w.Write(writeErrorResponse(w, errorResponse, acceptsContentType)) + } + } +} + +func (server *minioApi) getBucketPolicyHandler(w http.ResponseWriter, req *http.Request) { + vars := mux.Vars(req) + bucket := vars["bucket"] + acceptsContentType := getContentType(req) + + p, err := server.storage.GetBucketPolicy(bucket) + switch err := err.(type) { + case nil: + { + responsePolicy, ret := json.Marshal(p) + if ret != nil { + error := errorCodeError(InternalError) + errorResponse := getErrorResponse(error, bucket) + w.WriteHeader(error.HttpStatusCode) + w.Write(writeErrorResponse(w, errorResponse, acceptsContentType)) + } + writeCommonHeaders(w, getContentString(acceptsContentType)) + w.Header().Set("Connection", "keep-alive") + w.Write(responsePolicy) + } + case mstorage.BucketNameInvalid: + { + error := errorCodeError(InvalidBucketName) + errorResponse := getErrorResponse(error, bucket) + w.WriteHeader(error.HttpStatusCode) + w.Write(writeErrorResponse(w, errorResponse, acceptsContentType)) + } + case mstorage.BucketNotFound: + { + error := errorCodeError(NoSuchBucket) + errorResponse := getErrorResponse(error, bucket) + w.WriteHeader(error.HttpStatusCode) + w.Write(writeErrorResponse(w, errorResponse, acceptsContentType)) + } + case mstorage.BucketPolicyNotFound: + { + error := errorCodeError(NoSuchBucketPolicy) + errorResponse := getErrorResponse(error, bucket) + w.WriteHeader(error.HttpStatusCode) + w.Write(writeErrorResponse(w, errorResponse, acceptsContentType)) + } + case mstorage.BackendCorrupted: + case mstorage.ImplementationError: + { + log.Println(err) + error := errorCodeError(InternalError) + errorResponse := getErrorResponse(error, bucket) + w.WriteHeader(error.HttpStatusCode) + w.Write(writeErrorResponse(w, errorResponse, acceptsContentType)) + } + } +} diff --git a/pkg/httpserver/httpserver.go b/pkg/httpserver/httpserver.go index c1b4253b8..5f8e6f7e5 100644 --- a/pkg/httpserver/httpserver.go +++ b/pkg/httpserver/httpserver.go @@ -27,7 +27,7 @@ type HttpServerConfig struct { TLS bool CertFile string KeyFile string - Websocket bool + Websocket bool // implement it - TODO } type HttpServer struct{} diff --git a/pkg/server/server.go b/pkg/server/server.go index 3aad8bf90..57e6a5d46 100644 --- a/pkg/server/server.go +++ b/pkg/server/server.go @@ -31,6 +31,7 @@ import ( ) type ServerConfig struct { + Domain string Address string Tls bool CertFile string @@ -78,7 +79,7 @@ func getHttpChannels(configs []ServerConfig) (ctrlChans []chan<- string, statusC ctrlChans, statusChans, storage = getStorageChannels(k.StorageType) // start minio api in a web server, pass storage driver into it - ctrlChan, statusChan, _ = httpserver.Start(minioapi.HttpHandler(storage), httpConfig) + ctrlChan, statusChan, _ = httpserver.Start(minioapi.HttpHandler(config.Domain, storage), httpConfig) ctrlChans = append(ctrlChans, ctrlChan) statusChans = append(statusChans, statusChan) diff --git a/pkg/storage/fs/fs.go b/pkg/storage/fs/fs.go index 3d0c08dba..3109ec855 100644 --- a/pkg/storage/fs/fs.go +++ b/pkg/storage/fs/fs.go @@ -127,14 +127,15 @@ func (storage *storage) GetBucketPolicy(bucket string) (interface{}, error) { // get policy path bucketPolicy := path.Join(storage.root, bucket+"_policy.json") filestat, err := os.Stat(bucketPolicy) - if filestat.IsDir() { - return policy.BucketPolicy{}, mstorage.BackendCorrupted{Path: bucketPolicy} - } if os.IsNotExist(err) { return policy.BucketPolicy{}, mstorage.BucketPolicyNotFound{Bucket: bucket} } + if filestat.IsDir() { + return policy.BucketPolicy{}, mstorage.BackendCorrupted{Path: bucketPolicy} + } + file, err := os.OpenFile(bucketPolicy, os.O_RDONLY, 0666) defer file.Close() if err != nil { @@ -170,10 +171,13 @@ func (storage *storage) StoreBucketPolicy(bucket string, policy interface{}) err // get policy path bucketPolicy := path.Join(storage.root, bucket+"_policy.json") - filestat, _ := os.Stat(bucketPolicy) - if filestat.IsDir() { - return mstorage.BackendCorrupted{Path: bucketPolicy} + filestat, ret := os.Stat(bucketPolicy) + if !os.IsNotExist(ret) { + if filestat.IsDir() { + return mstorage.BackendCorrupted{Path: bucketPolicy} + } } + file, err := os.OpenFile(bucketPolicy, os.O_WRONLY|os.O_CREATE, 0600) defer file.Close() if err != nil { diff --git a/pkg/storage/storage.go b/pkg/storage/storage.go index 3ffaf32ef..0d69defde 100644 --- a/pkg/storage/storage.go +++ b/pkg/storage/storage.go @@ -61,7 +61,8 @@ func IsValidBucket(bucket string) bool { if match, _ := regexp.MatchString("\\.\\.", bucket); match == true { return false } - match, _ := regexp.MatchString("^[a-zA-Z][a-zA-Z0-9\\.\\-]+[a-zA-Z0-9]$", bucket) + // We don't support buckets with '.' in them + match, _ := regexp.MatchString("^[a-zA-Z][a-zA-Z0-9\\-]+[a-zA-Z0-9]$", bucket) return match } diff --git a/pkg/utils/crypto/signers/signers.go b/pkg/utils/crypto/signers/signers.go index 0d9e98b86..2848fd8f2 100644 --- a/pkg/utils/crypto/signers/signers.go +++ b/pkg/utils/crypto/signers/signers.go @@ -67,9 +67,9 @@ func ValidateRequest(user config.User, req *http.Request) (bool, error) { encoder.Close() // DEBUG - //fmt.Println("Request header sent: ", req.Header.Get("Authorization")) - //fmt.Println("Header calculated: ", authHeader.String()) - //fmt.Printf("%q : %x", ss, ss) + // fmt.Println("Request header sent: ", req.Header.Get("Authorization")) + // fmt.Println("Header calculated: ", authHeader.String()) + // fmt.Printf("%q : %x", ss, ss) if req.Header.Get("Authorization") != authHeader.String() { return false, fmt.Errorf("Authorization header mismatch") } @@ -155,6 +155,11 @@ var subResList = []string{"acl", "lifecycle", "location", "logging", "notificati // + // [ sub-resource, if present. For example "?acl", "?location", "?logging", or "?torrent"]; func writeCanonicalizedResource(buf *bytes.Buffer, req *http.Request) { + bucket := bucketFromHostname(req) + if bucket != "" { + buf.WriteByte('/') + buf.WriteString(bucket) + } buf.WriteString(req.URL.Path) if req.URL.RawQuery != "" { n := 0 @@ -176,3 +181,17 @@ func writeCanonicalizedResource(buf *bytes.Buffer, req *http.Request) { } } } + +func bucketFromHostname(req *http.Request) string { + host := req.Host + if host == "" { + host = req.URL.Host + } + + host = strings.TrimSpace(host) + hostParts := strings.Split(host, ".") + if len(hostParts) > 1 { + return hostParts[0] + } + return "" +} diff --git a/pkg/utils/policy/policy.go b/pkg/utils/policy/policy.go index 7ebe7a272..0ffd1486c 100644 --- a/pkg/utils/policy/policy.go +++ b/pkg/utils/policy/policy.go @@ -31,8 +31,8 @@ const ( // TODO support canonical user const ( - AwsPrincipal = "arn:aws:iam::Account-ID:user/" - MinioPrincipal = "minio::Account-ID:user/" + AwsPrincipal = "arn:aws:iam::" + MinioPrincipal = "minio::" ) var SupportedActionMap = map[string]bool{ @@ -55,10 +55,13 @@ var SupportedEffectMap = map[string]bool{ func isValidAction(action []string) bool { var ok bool = false for _, a := range action { - if SupportedActionMap[a] { - ok = true + if !SupportedActionMap[a] { + goto error } } + ok = true + +error: return ok } @@ -104,6 +107,7 @@ func isValidPrincipal(principal string) bool { if len(username) == 0 { ok = false } + case strings.HasPrefix(principal, MinioPrincipal): username := strings.SplitAfter(principal, MinioPrincipal)[1] ok = true @@ -160,6 +164,7 @@ func Parsepolicy(data io.Reader) (BucketPolicy, bool) { if len(statement.Resource) == 0 { goto error } + if !isValidResource(statement.Resource) { goto error }