mirror of
https://github.com/minio/minio.git
synced 2025-02-03 01:46:00 -05:00
Implement GetBucketACL - fixes #893
This commit is contained in:
parent
bf901d3b9a
commit
11048708bb
@ -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()
|
||||||
@ -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…
x
Reference in New Issue
Block a user