Implement GetBucketACL - fixes #893

master
Harshavardhana 9 years ago
parent bf901d3b9a
commit 11048708bb
  1. 6
      pkg/tasker/commands.go
  2. 35
      pkg/tasker/task.go
  3. 23
      pkg/tasker/taskctl.go
  4. 45
      server-api-bucket-handlers.go
  5. 21
      server-api-definitions.go
  6. 46
      server-api-response.go
  7. 6
      server-api-signature-handler.go
  8. 1
      server-router.go

@ -23,7 +23,8 @@ type Command uint8
const ( const (
// CmdNOOP does nothing. It is a default placeholder. Uninitialized variable of this type will point to NOOP command by default. // CmdNOOP does nothing. It is a default placeholder. Uninitialized variable of this type will point to NOOP command by default.
CmdNOOP Command = iota CmdNOOP Command = iota
// CmdSignalEnd gracefully ends current task. Never ending tasks (loop over) or Batched jobs will not take the next iteration, but may finish the current state to completion. // CmdSignalEnd gracefully ends current task. Never ending tasks (loop over) or Batched jobs will not take the next iteration,
// but may finish the current state to completion.
CmdSignalEnd CmdSignalEnd
// CmdSignalAbort ends the current task at hand immediately. It may still cleanup dangling issues quickly. // CmdSignalAbort ends the current task at hand immediately. It may still cleanup dangling issues quickly.
CmdSignalAbort CmdSignalAbort
@ -37,6 +38,7 @@ const (
CmdPriorityMedium CmdPriorityMedium
// CmdPriorityHigh is optimized for speed. This option is ideal for short lived tasks (like meta-data related) that are latency sensitive. Use this option wisely. // CmdPriorityHigh is optimized for speed. This option is ideal for short lived tasks (like meta-data related) that are latency sensitive. Use this option wisely.
CmdPriorityHigh CmdPriorityHigh
// CmdPrioritySuper is an exclusive priority. All tasks with priority lower than Super (including High) are paused temporarily until this task completes. Anytime you consider using this priority level, please seek for approval. // CmdPrioritySuper is an exclusive priority. All tasks with priority lower than Super (including High) are paused
// temporarily until this task completes. Anytime you consider using this priority level, please seek for approval.
CmdPrioritySuper CmdPrioritySuper
) )

@ -21,20 +21,18 @@ import (
"sync" "sync"
) )
/* NOTE: // NOTE: Task is a private entity. It is created and managed by TaskCtl
Task is a private entity. It is created and managed by TaskCtl // entirely. Only TaskCtl and Handle objects are exposed outside.
entirely. Only TaskCtl and Handle objects are exposed outside.
*/
/* taskRef is a unique reference ID to a task. It is assigned by the // taskRef is a unique reference ID to a task. It is assigned by the
TaskCtl during the creation of a task. All tasfRef variables are // TaskCtl during the creation of a task. All tasfRef variables are
named "this". */ // named "this".
type taskRef *list.Element type taskRef *list.Element
/* Task is an abstract concept built on top of Go routines and // Task is an abstract concept built on top of Go routines and
channels. Tasks themselves are expected to co-operate and comply with // channels. Tasks themselves are expected to co-operate and comply with
the TaskCtl commands. // the TaskCtl commands.
*/
type task struct { type task struct {
mutex *sync.Mutex mutex *sync.Mutex
@ -46,14 +44,13 @@ type task struct {
closeCh chan taskRef // Channel to notify the TaskCtl about ending this task. closeCh chan taskRef // Channel to notify the TaskCtl about ending this task.
} }
/* NewTask creates a new task structure and returns a handle to // NewTask creates a new task structure and returns a handle to
it. Only the task controller has access to the task structure. The // it. Only the task controller has access to the task structure. The
caller routine only receives a handle to its task structure. Task // caller routine only receives a handle to its task structure. Task
handle is like a reference to task self. Caller is expected to listen // handle is like a reference to task self. Caller is expected to listen
for commands from the task controller and comply with it // for commands from the task controller and comply with it co-operatively.
co-operatively. // this: Task reference is unique identifier assigned by the TaskCtl.
this: Task reference is unique identifier assigned by the TaskCtl. // name: Free form name of the task. Eg. "Late Night Disk Scrubber".
name: Free form name of the task. Eg. "Late Night Disk Scrubber". */
func newTask(name string) task { func newTask(name string) task {
return task{ return task{
// this: Is set by the TaskCtl's NewTask function. // this: Is set by the TaskCtl's NewTask function.

@ -29,7 +29,8 @@ type TaskCtl struct {
tasks *list.List tasks *list.List
} }
// New creates a new TaskCtl to create and control a collection of tasks. Single application can create multiple task controllers to manage different set of tasks separately. // New creates a new TaskCtl to create and control a collection of tasks.
// Single application can create multiple task controllers to manage different set of tasks separately.
func New(name string) *TaskCtl { func New(name string) *TaskCtl {
return &TaskCtl{ return &TaskCtl{
mutex: &sync.Mutex{}, mutex: &sync.Mutex{},
@ -37,7 +38,10 @@ func New(name string) *TaskCtl {
} }
} }
// NewTask creates a new task structure and returns a handle to it. Only the task controller has access to the task structure. The caller routine only receives a handle to its task structure. Task handle is like a reference to task self. Caller is expected to listen for commands from the task controller and comply with it co-operatively. // NewTask creates a new task structure and returns a handle to it. Only the task controller
// has access to the task structure. The caller routine only receives a handle to its task structure.
// Task handle is like a reference to task self. Caller is expected to listen for commands from
// the task controller and comply with it co-operatively.
func (tc *TaskCtl) NewTask(name string) Handle { func (tc *TaskCtl) NewTask(name string) Handle {
tc.mutex.Lock() tc.mutex.Lock()
defer tc.mutex.Unlock() defer tc.mutex.Unlock()
@ -45,7 +49,7 @@ func (tc *TaskCtl) NewTask(name string) Handle {
// Create a new task. // Create a new task.
tsk := newTask(name) tsk := newTask(name)
//Register this task in the TaskCtl's tasklist and save the reference. // Register this task in the TaskCtl's tasklist and save the reference.
tsk.this = tc.tasks.PushBack(tsk) tsk.this = tc.tasks.PushBack(tsk)
// Free task from the tasklist upon close call. // Free task from the tasklist upon close call.
@ -79,7 +83,10 @@ func (tc *TaskCtl) Shutdown() {
wg.Add(1) wg.Add(1)
thisTask := e.Value.(task) // Make a local copy for go routine. thisTask := e.Value.(task) // Make a local copy for go routine.
// End tasks in background. Flow of events from here is as follows: thisTask.handle.Close() -> tc.NewTask() -> this.task.close(). // End tasks in background. Flow of events from here is as follows: thisTask.handle.Close() -> tc.NewTask() -> this.task.close().
go func() { thisTask.getHandle().Close(); wg.Done() }() go func() {
thisTask.getHandle().Close()
wg.Done()
}()
} }
wg.Wait() // Wait for all tasks to end gracefully. wg.Wait() // Wait for all tasks to end gracefully.
@ -105,10 +112,10 @@ func (tc *TaskCtl) Suspend() bool {
locTask := e.Value.(task) // Make a local copy for go routine. locTask := e.Value.(task) // Make a local copy for go routine.
locI := i // local i locI := i // local i
// Suspend a task in background. // Suspend a task in background.
go func() { go func(locI int) {
defer wg.Done() defer wg.Done()
statusAll[locI] = locTask.command(CmdSignalSuspend) statusAll[locI] = locTask.command(CmdSignalSuspend)
}() }(locI)
i++ i++
} }
@ -139,10 +146,10 @@ func (tc *TaskCtl) Resume() bool {
locTask := e.Value.(task) // Make a local copy for go routine. locTask := e.Value.(task) // Make a local copy for go routine.
locI := i // local i locI := i // local i
// Resume a task in background. // Resume a task in background.
go func() { go func(locI int) {
defer wg.Done() defer wg.Done()
statusAll[locI] = locTask.command(CmdSignalResume) statusAll[locI] = locTask.command(CmdSignalResume)
}() }(locI)
i++ i++
} }
wg.Wait() // Wait for all tasks to resume. wg.Wait() // Wait for all tasks to resume.

@ -403,8 +403,6 @@ func (api API) PutBucketACLHandler(w http.ResponseWriter, req *http.Request) {
if err != nil { if err != nil {
errorIf(err.Trace(), "PutBucketACL failed.", nil) errorIf(err.Trace(), "PutBucketACL failed.", nil)
switch err.ToGoError().(type) { switch err.ToGoError().(type) {
case signv4.DoesNotMatch:
writeErrorResponse(w, req, SignatureDoesNotMatch, req.URL.Path)
case donut.BucketNameInvalid: case donut.BucketNameInvalid:
writeErrorResponse(w, req, InvalidBucketName, req.URL.Path) writeErrorResponse(w, req, InvalidBucketName, req.URL.Path)
case donut.BucketNotFound: case donut.BucketNotFound:
@ -417,6 +415,47 @@ func (api API) PutBucketACLHandler(w http.ResponseWriter, req *http.Request) {
writeSuccessResponse(w) writeSuccessResponse(w)
} }
// GetBucketACLHandler - GET ACL on a Bucket
// ----------
// This operation uses acl subresource to the return the ``acl``
// of a bucket. One must have permission to access the bucket to
// know its ``acl``. This operation willl return response of 404
// if bucket not found and 403 for invalid credentials.
func (api API) GetBucketACLHandler(w http.ResponseWriter, req *http.Request) {
// Ticket master block
{
op := APIOperation{}
op.ProceedCh = make(chan struct{})
api.OP <- op
// block until Ticket master gives us a go
<-op.ProceedCh
}
vars := mux.Vars(req)
bucket := vars["bucket"]
bucketMetadata, err := api.Donut.GetBucketMetadata(bucket)
if err != nil {
errorIf(err.Trace(), "GetBucketMetadata failed.", nil)
switch err.ToGoError().(type) {
case donut.BucketNotFound:
writeErrorResponse(w, req, NoSuchBucket, req.URL.Path)
case donut.BucketNameInvalid:
writeErrorResponse(w, req, InvalidBucketName, req.URL.Path)
default:
writeErrorResponse(w, req, InternalError, req.URL.Path)
}
return
}
// generate response
response := generateAccessControlPolicyResponse(bucketMetadata.ACL)
encodedSuccessResponse := encodeSuccessResponse(response)
// write headers
setCommonHeaders(w, len(encodedSuccessResponse))
// write body
w.Write(encodedSuccessResponse)
}
// HeadBucketHandler - HEAD Bucket // HeadBucketHandler - HEAD Bucket
// ---------- // ----------
// This operation is useful to determine if a bucket exists. // This operation is useful to determine if a bucket exists.
@ -440,8 +479,6 @@ func (api API) HeadBucketHandler(w http.ResponseWriter, req *http.Request) {
if err != nil { if err != nil {
errorIf(err.Trace(), "GetBucketMetadata failed.", nil) errorIf(err.Trace(), "GetBucketMetadata failed.", nil)
switch err.ToGoError().(type) { switch err.ToGoError().(type) {
case signv4.DoesNotMatch:
writeErrorResponse(w, req, SignatureDoesNotMatch, req.URL.Path)
case donut.BucketNotFound: case donut.BucketNotFound:
writeErrorResponse(w, req, NoSuchBucket, req.URL.Path) writeErrorResponse(w, req, NoSuchBucket, req.URL.Path)
case donut.BucketNameInvalid: case donut.BucketNameInvalid:

@ -23,6 +23,26 @@ const (
maxObjectList = 1000 maxObjectList = 1000
) )
// AccessControlPolicyResponse - format for get bucket acl response
type AccessControlPolicyResponse struct {
AccessControlList struct {
Grant []Grant
}
Owner Owner
}
// Grant container for grantee and permission
type Grant struct {
Grantee struct {
ID string
DisplayName string
EmailAddress string
Type string
URI string
}
Permission string
}
// ListObjectsResponse - format for list objects response // ListObjectsResponse - format for list objects response
type ListObjectsResponse struct { type ListObjectsResponse struct {
XMLName xml.Name `xml:"http://s3.amazonaws.com/doc/2006-03-01/ ListBucketResult" json:"-"` XMLName xml.Name `xml:"http://s3.amazonaws.com/doc/2006-03-01/ ListBucketResult" json:"-"`
@ -182,6 +202,7 @@ var notimplementedBucketResourceNames = map[string]bool{
"location": true, "location": true,
"logging": true, "logging": true,
"notification": true, "notification": true,
"replication": true,
"tagging": true, "tagging": true,
"versions": true, "versions": true,
"requestPayment": true, "requestPayment": true,

@ -54,14 +54,44 @@ func generateListBucketsResponse(buckets []donut.BucketMetadata) ListBucketsResp
return data return data
} }
// takes a set of objects and prepares the objects for serialization // generates an AccessControlPolicy response for the said ACL.
// input: func generateAccessControlPolicyResponse(acl donut.BucketACL) AccessControlPolicyResponse {
// bucket name accessCtrlPolicyResponse := AccessControlPolicyResponse{}
// array of object metadata accessCtrlPolicyResponse.Owner = Owner{
// results truncated flag ID: "minio",
// DisplayName: "minio",
// output: }
// populated struct that can be serialized to match xml and json api spec output defaultGrant := Grant{}
defaultGrant.Grantee.ID = "minio"
defaultGrant.Grantee.DisplayName = "minio"
defaultGrant.Permission = "FULL_CONTROL"
accessCtrlPolicyResponse.AccessControlList.Grant = append(accessCtrlPolicyResponse.AccessControlList.Grant, defaultGrant)
switch {
case acl.IsPublicRead():
publicReadGrant := Grant{}
publicReadGrant.Grantee.ID = "minio"
publicReadGrant.Grantee.DisplayName = "minio"
publicReadGrant.Grantee.URI = "http://acs.amazonaws.com/groups/global/AllUsers"
publicReadGrant.Permission = "READ"
accessCtrlPolicyResponse.AccessControlList.Grant = append(accessCtrlPolicyResponse.AccessControlList.Grant, publicReadGrant)
case acl.IsPublicReadWrite():
publicReadGrant := Grant{}
publicReadGrant.Grantee.ID = "minio"
publicReadGrant.Grantee.DisplayName = "minio"
publicReadGrant.Grantee.URI = "http://acs.amazonaws.com/groups/global/AllUsers"
publicReadGrant.Permission = "READ"
publicReadWriteGrant := Grant{}
publicReadWriteGrant.Grantee.ID = "minio"
publicReadWriteGrant.Grantee.DisplayName = "minio"
publicReadWriteGrant.Grantee.URI = "http://acs.amazonaws.com/groups/global/AllUsers"
publicReadWriteGrant.Permission = "WRITE"
accessCtrlPolicyResponse.AccessControlList.Grant = append(accessCtrlPolicyResponse.AccessControlList.Grant, publicReadGrant)
accessCtrlPolicyResponse.AccessControlList.Grant = append(accessCtrlPolicyResponse.AccessControlList.Grant, publicReadWriteGrant)
}
return accessCtrlPolicyResponse
}
// generates an ListObjects response for the said bucket with other enumerated options.
func generateListObjectsResponse(bucket string, objects []donut.ObjectMetadata, bucketResources donut.BucketResourcesMetadata) ListObjectsResponse { func generateListObjectsResponse(bucket string, objects []donut.ObjectMetadata, bucketResources donut.BucketResourcesMetadata) ListObjectsResponse {
var contents []*Object var contents []*Object
var prefixes []*CommonPrefix var prefixes []*CommonPrefix

@ -51,9 +51,9 @@ func isRequestPresignedSignatureV4(req *http.Request) bool {
func (s signatureHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { func (s signatureHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
var signature *signv4.Signature var signature *signv4.Signature
if isRequestSignatureV4(r) { if isRequestSignatureV4(r) {
// If the request is not a PUT method handle the verification here. // For PUT and POST requests with payload, send the call upwards for verification.
// For PUT and POST requests with payload, send the call upwards for verification // Or PUT and POST requests without payload, verify here.
if r.Method != "PUT" && r.Method != "POST" { if (r.Body == nil && (r.Method == "PUT" || r.Method == "POST")) || (r.Method != "PUT" && r.Method != "POST") {
// Init signature V4 verification // Init signature V4 verification
var err *probe.Error var err *probe.Error
signature, err = initSignatureV4(r) signature, err = initSignatureV4(r)

@ -28,6 +28,7 @@ import (
// registerAPI - register all the object API handlers to their respective paths // registerAPI - register all the object API handlers to their respective paths
func registerAPI(mux *router.Router, a API) { func registerAPI(mux *router.Router, a API) {
mux.HandleFunc("/", a.ListBucketsHandler).Methods("GET") mux.HandleFunc("/", a.ListBucketsHandler).Methods("GET")
mux.HandleFunc("/{bucket}", a.GetBucketACLHandler).Queries("acl", "").Methods("GET")
mux.HandleFunc("/{bucket}", a.ListObjectsHandler).Methods("GET") mux.HandleFunc("/{bucket}", a.ListObjectsHandler).Methods("GET")
mux.HandleFunc("/{bucket}", a.PutBucketACLHandler).Queries("acl", "").Methods("PUT") mux.HandleFunc("/{bucket}", a.PutBucketACLHandler).Queries("acl", "").Methods("PUT")
mux.HandleFunc("/{bucket}", a.PutBucketHandler).Methods("PUT") mux.HandleFunc("/{bucket}", a.PutBucketHandler).Methods("PUT")

Loading…
Cancel
Save