From c8713fd6502bf06b0bfcf2a43b0e7f983d8ccc4a Mon Sep 17 00:00:00 2001 From: Harshavardhana Date: Wed, 22 Apr 2015 18:19:20 -0700 Subject: [PATCH] Bring in full fledged acl support --- pkg/api/acl.go | 2 + pkg/api/api_bucket_handlers.go | 2 +- pkg/api/api_generic_handlers.go | 76 ++++++++++++++++++++------------- pkg/api/api_router.go | 2 +- pkg/api/api_test.go | 61 +++++++++++++------------- 5 files changed, 82 insertions(+), 61 deletions(-) diff --git a/pkg/api/acl.go b/pkg/api/acl.go index da21d36dc..2fef7e679 100644 --- a/pkg/api/acl.go +++ b/pkg/api/acl.go @@ -27,6 +27,8 @@ import ( // http://docs.aws.amazon.com/AmazonS3/latest/dev/acl-overview.html#setting-acls // Minio only supports three types for now i.e 'private, public-read, public-read-write' + +// ACLType - different acl types type ACLType int const ( diff --git a/pkg/api/api_bucket_handlers.go b/pkg/api/api_bucket_handlers.go index 0137bbbbd..e17031c55 100644 --- a/pkg/api/api_bucket_handlers.go +++ b/pkg/api/api_bucket_handlers.go @@ -128,7 +128,7 @@ func (server *minioAPI) putBucketHandler(w http.ResponseWriter, req *http.Reques vars := mux.Vars(req) bucket := vars["bucket"] - err := server.driver.CreateBucket(bucket) + err := server.driver.CreateBucket(bucket, getACLTypeString(aclType)) switch err.(type) { case nil: { diff --git a/pkg/api/api_generic_handlers.go b/pkg/api/api_generic_handlers.go index d60decf4e..a4c5f3d23 100644 --- a/pkg/api/api_generic_handlers.go +++ b/pkg/api/api_generic_handlers.go @@ -20,11 +20,14 @@ import ( "net/http" "strings" + "github.com/gorilla/mux" "github.com/minio-io/minio/pkg/api/config" + "github.com/minio-io/minio/pkg/storage/drivers" ) type vHandler struct { conf config.Config + driver drivers.Driver handler http.Handler } @@ -47,43 +50,56 @@ func stripAccessKey(r *http.Request) string { // 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{conf, h} +func validateHandler(conf config.Config, driver drivers.Driver, h http.Handler) http.Handler { + return vHandler{ + conf: conf, + driver: driver, + handler: h, + } } // Validate handler ServeHTTP() wrapper func (h vHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { accessKey := stripAccessKey(r) acceptsContentType := getContentType(r) - if accessKey != "" { - if err := h.conf.ReadConfig(); err != nil { - error := getErrorCode(InternalError) - errorResponse := getErrorResponse(error, "") - setCommonHeaders(w, getContentTypeString(acceptsContentType)) - w.WriteHeader(error.HTTPStatusCode) - w.Write(encodeErrorResponse(errorResponse, acceptsContentType)) - } else { - user, ok := h.conf.Users[accessKey] - if ok == false { - error := getErrorCode(AccessDenied) - errorResponse := getErrorResponse(error, "") - setCommonHeaders(w, getContentTypeString(acceptsContentType)) - w.WriteHeader(error.HTTPStatusCode) - w.Write(encodeErrorResponse(errorResponse, acceptsContentType)) - } else { - ok, _ = ValidateRequest(user, r) - if ok { - h.handler.ServeHTTP(w, r) - } else { - error := getErrorCode(AccessDenied) - errorResponse := getErrorResponse(error, "") - setCommonHeaders(w, getContentTypeString(acceptsContentType)) - w.WriteHeader(error.HTTPStatusCode) - w.Write(encodeErrorResponse(errorResponse, acceptsContentType)) - } - } + if acceptsContentType == unknownContentType { + writeErrorResponse(w, r, NotAcceptable, acceptsContentType, r.URL.Path) + return + } + // verify for if bucket is private or public + vars := mux.Vars(r) + bucket, ok := vars["bucket"] + if ok { + bucketMetadata, err := h.driver.GetBucketMetadata(bucket) + if err != nil { + writeErrorResponse(w, r, AccessDenied, acceptsContentType, r.URL.Path) + return } - } else { + if accessKey == "" && bucketMetadata.ACL.IsPrivate() { + writeErrorResponse(w, r, AccessDenied, acceptsContentType, r.URL.Path) + return + } + } + + switch true { + case accessKey != "": + if err := h.conf.ReadConfig(); err != nil { + writeErrorResponse(w, r, InternalError, acceptsContentType, r.URL.Path) + return + } + user, ok := h.conf.Users[accessKey] + if !ok { + writeErrorResponse(w, r, AccessDenied, acceptsContentType, r.URL.Path) + return + } + ok, _ = ValidateRequest(user, r) + if !ok { + writeErrorResponse(w, r, AccessDenied, acceptsContentType, r.URL.Path) + return + } + // Success + h.handler.ServeHTTP(w, r) + default: // Control reaches when no access key is found, ideally we would // like to throw back `403`. But for now with our tests lacking // this functionality it is better for us to be serving anonymous diff --git a/pkg/api/api_router.go b/pkg/api/api_router.go index 0e5813a52..d4b2d65d7 100644 --- a/pkg/api/api_router.go +++ b/pkg/api/api_router.go @@ -89,5 +89,5 @@ func HTTPHandler(domain string, driver drivers.Driver) http.Handler { log.Fatal(iodine.New(err, map[string]string{"domain": domain})) } - return validateHandler(conf, ignoreResourcesHandler(mux)) + return validateHandler(conf, api.driver, ignoreResourcesHandler(mux)) } diff --git a/pkg/api/api_test.go b/pkg/api/api_test.go index 7413c027f..474296c43 100644 --- a/pkg/api/api_test.go +++ b/pkg/api/api_test.go @@ -142,7 +142,7 @@ func (s *MySuite) TestEmptyObject(c *C) { Md5: "d41d8cd98f00b204e9800998ecf8427e", Size: 0, } - typedDriver.On("CreateBucket", "bucket").Return(nil).Once() + typedDriver.On("CreateBucket", "bucket", "private").Return(nil).Once() typedDriver.On("CreateObject", "bucket", "object", "", "", mock.Anything).Return(nil).Once() typedDriver.On("GetObjectMetadata", "bucket", "object", "").Return(metadata, nil).Once() typedDriver.On("GetObject", mock.Anything, "bucket", "object").Return(int64(0), nil).Once() @@ -152,7 +152,7 @@ func (s *MySuite) TestEmptyObject(c *C) { defer testServer.Close() buffer := bytes.NewBufferString("") - driver.CreateBucket("bucket") + driver.CreateBucket("bucket", "private") driver.CreateObject("bucket", "object", "", "", buffer) response, err := http.Get(testServer.URL + "/bucket/object") @@ -181,14 +181,14 @@ func (s *MySuite) TestBucket(c *C) { Name: "bucket", Created: time.Now(), } - typedDriver.On("CreateBucket", "bucket").Return(nil).Once() + typedDriver.On("CreateBucket", "bucket", "private").Return(nil).Once() typedDriver.On("GetBucketMetadata", "bucket").Return(metadata, nil).Twice() httpHandler := api.HTTPHandler("", driver) testServer := httptest.NewServer(httpHandler) defer testServer.Close() - driver.CreateBucket("bucket") + driver.CreateBucket("bucket", "private") response, err := http.Head(testServer.URL + "/bucket") c.Assert(err, IsNil) @@ -212,7 +212,7 @@ func (s *MySuite) TestObject(c *C) { Md5: "5eb63bbbe01eeed093cb22bb8f5acdc3", Size: 11, } - typedDriver.On("CreateBucket", "bucket").Return(nil).Once() + typedDriver.On("CreateBucket", "bucket", "private").Return(nil).Once() typedDriver.On("CreateObject", "bucket", "object", "", "", mock.Anything).Return(nil).Once() typedDriver.On("GetObjectMetadata", "bucket", "object", "").Return(metadata, nil).Twice() typedDriver.SetGetObjectWriter("bucket", "object", []byte("hello world")) @@ -223,7 +223,7 @@ func (s *MySuite) TestObject(c *C) { defer testServer.Close() buffer := bytes.NewBufferString("hello world") - driver.CreateBucket("bucket") + driver.CreateBucket("bucket", "private") driver.CreateObject("bucket", "object", "", "", buffer) response, err := http.Get(testServer.URL + "/bucket/object") @@ -280,8 +280,8 @@ func (s *MySuite) TestMultipleObjects(c *C) { buffer2 := bytes.NewBufferString("hello two") buffer3 := bytes.NewBufferString("hello three") - typedDriver.On("CreateBucket", "bucket").Return(nil).Once() - driver.CreateBucket("bucket") + typedDriver.On("CreateBucket", "bucket", "private").Return(nil).Once() + driver.CreateBucket("bucket", "private") typedDriver.On("CreateObject", "bucket", "object1", "", "", mock.Anything).Return(nil).Once() driver.CreateObject("bucket", "object1", "", "", buffer1) typedDriver.On("CreateObject", "bucket", "object2", "", "", mock.Anything).Return(nil).Once() @@ -397,8 +397,8 @@ func (s *MySuite) TestHeader(c *C) { testServer := httptest.NewServer(httpHandler) defer testServer.Close() - typedDriver.On("CreateBucket", "bucket").Return(nil).Once() - driver.CreateBucket("bucket") + typedDriver.On("CreateBucket", "bucket", "private").Return(nil).Once() + driver.CreateBucket("bucket", "private") typedDriver.On("GetObjectMetadata", "bucket", "object", "").Return(drivers.ObjectMetadata{}, drivers.ObjectNotFound{}).Once() response, err := http.Get(testServer.URL + "/bucket/object") @@ -450,7 +450,7 @@ func (s *MySuite) TestPutBucket(c *C) { c.Assert(len(buckets), Equals, 0) c.Assert(err, IsNil) - typedDriver.On("CreateBucket", "bucket").Return(nil).Once() + typedDriver.On("CreateBucket", "bucket", "private").Return(nil).Once() request, err := http.NewRequest("PUT", testServer.URL+"/bucket", bytes.NewBufferString("")) c.Assert(err, IsNil) request.Header.Add("x-amz-acl", "private") @@ -497,7 +497,7 @@ func (s *MySuite) TestPutObject(c *C) { date1 := time.Now().Add(-time.Second) // Put Bucket before - Put Object into a bucket - typedDriver.On("CreateBucket", "bucket").Return(nil).Once() + typedDriver.On("CreateBucket", "bucket", "private").Return(nil).Once() request, err := http.NewRequest("PUT", testServer.URL+"/bucket", bytes.NewBufferString("")) c.Assert(err, IsNil) request.Header.Add("x-amz-acl", "private") @@ -575,8 +575,9 @@ func (s *MySuite) TestListBuckets(c *C) { c.Assert(err, IsNil) c.Assert(len(listResponse.Buckets.Bucket), Equals, 0) - typedDriver.On("CreateBucket", "foo").Return(nil).Once() - driver.CreateBucket("foo") + typedDriver.On("CreateBucket", "foo", "private").Return(nil).Once() + err = driver.CreateBucket("foo", "private") + c.Assert(err, IsNil) bucketMetadata := []drivers.BucketMetadata{ {Name: "foo", Created: time.Now()}, @@ -592,8 +593,9 @@ func (s *MySuite) TestListBuckets(c *C) { c.Assert(len(listResponse.Buckets.Bucket), Equals, 1) c.Assert(listResponse.Buckets.Bucket[0].Name, Equals, "foo") - typedDriver.On("CreateBucket", "bar").Return(nil).Once() - driver.CreateBucket("bar") + typedDriver.On("CreateBucket", "bar", "private").Return(nil).Once() + err = driver.CreateBucket("bar", "private") + c.Assert(err, IsNil) bucketMetadata = []drivers.BucketMetadata{ {Name: "bar", Created: time.Now()}, @@ -689,8 +691,8 @@ func (s *MySuite) TestXMLNameNotInBucketListJson(c *C) { testServer := httptest.NewServer(httpHandler) defer testServer.Close() - typedDriver.On("CreateBucket", "foo").Return(nil).Once() - err := driver.CreateBucket("foo") + typedDriver.On("CreateBucket", "foo", "private").Return(nil).Once() + err := driver.CreateBucket("foo", "private") c.Assert(err, IsNil) typedDriver.On("ListBuckets").Return([]drivers.BucketMetadata{{Name: "foo", Created: time.Now()}}, nil) @@ -723,8 +725,8 @@ func (s *MySuite) TestXMLNameNotInObjectListJson(c *C) { testServer := httptest.NewServer(httpHandler) defer testServer.Close() - typedDriver.On("CreateBucket", "foo").Return(nil).Once() - err := driver.CreateBucket("foo") + typedDriver.On("CreateBucket", "foo", "private").Return(nil).Once() + err := driver.CreateBucket("foo", "private") c.Assert(err, IsNil) typedDriver.On("ListObjects", "foo", mock.Anything).Return([]drivers.ObjectMetadata{}, drivers.BucketResourcesMetadata{}, nil).Once() @@ -758,8 +760,8 @@ func (s *MySuite) TestContentTypePersists(c *C) { testServer := httptest.NewServer(httpHandler) defer testServer.Close() - typedDriver.On("CreateBucket", "bucket").Return(nil).Once() - err := driver.CreateBucket("bucket") + typedDriver.On("CreateBucket", "bucket", "private").Return(nil).Once() + err := driver.CreateBucket("bucket", "private") c.Assert(err, IsNil) client := http.Client{} @@ -849,10 +851,11 @@ func (s *MySuite) TestPartialContent(c *C) { Size: 11, } - typedDriver.On("CreateBucket", "foo").Return(nil).Once() - + typedDriver.On("CreateBucket", "foo", "private").Return(nil).Once() typedDriver.On("CreateObject", "foo", "bar", "", "", mock.Anything).Return(nil).Once() - driver.CreateBucket("foo") + err := driver.CreateBucket("foo", "private") + c.Assert(err, IsNil) + driver.CreateObject("foo", "bar", "", "", bytes.NewBufferString("hello world")) // prepare for GET on range request @@ -969,7 +972,7 @@ func (s *MySuite) TestPutBucketErrors(c *C) { defer testServer.Close() client := http.Client{} - typedDriver.On("CreateBucket", "foo").Return(drivers.BucketNameInvalid{}).Once() + typedDriver.On("CreateBucket", "foo", "private").Return(drivers.BucketNameInvalid{}).Once() request, err := http.NewRequest("PUT", testServer.URL+"/foo", bytes.NewBufferString("")) c.Assert(err, IsNil) request.Header.Add("x-amz-acl", "private") @@ -978,7 +981,7 @@ func (s *MySuite) TestPutBucketErrors(c *C) { c.Assert(err, IsNil) verifyError(c, response, "InvalidBucketName", "The specified bucket is not valid.", http.StatusBadRequest) - typedDriver.On("CreateBucket", "foo").Return(drivers.BucketExists{}).Once() + typedDriver.On("CreateBucket", "foo", "private").Return(drivers.BucketExists{}).Once() request, err = http.NewRequest("PUT", testServer.URL+"/foo", bytes.NewBufferString("")) c.Assert(err, IsNil) request.Header.Add("x-amz-acl", "private") @@ -987,7 +990,7 @@ func (s *MySuite) TestPutBucketErrors(c *C) { c.Assert(err, IsNil) verifyError(c, response, "BucketAlreadyExists", "The requested bucket name is not available.", http.StatusConflict) - typedDriver.On("CreateBucket", "foo").Return(drivers.BackendCorrupted{}).Once() + typedDriver.On("CreateBucket", "foo", "private").Return(drivers.BackendCorrupted{}).Once() request, err = http.NewRequest("PUT", testServer.URL+"/foo", bytes.NewBufferString("")) c.Assert(err, IsNil) request.Header.Add("x-amz-acl", "private") @@ -996,7 +999,7 @@ func (s *MySuite) TestPutBucketErrors(c *C) { c.Assert(err, IsNil) verifyError(c, response, "InternalError", "We encountered an internal error, please try again.", http.StatusInternalServerError) - typedDriver.On("CreateBucket", "foo").Return(nil).Once() + typedDriver.On("CreateBucket", "foo", "unknown").Return(nil).Once() request, err = http.NewRequest("PUT", testServer.URL+"/foo", bytes.NewBufferString("")) c.Assert(err, IsNil) request.Header.Add("x-amz-acl", "unknown")