예제 #1
0
func (api Minio) isValidOp(w http.ResponseWriter, req *http.Request, acceptsContentType contentType) bool {
	vars := mux.Vars(req)
	bucket := vars["bucket"]

	bucketMetadata, err := api.Donut.GetBucketMetadata(bucket, nil)
	if err == nil {
		if _, err := StripAccessKeyID(req.Header.Get("Authorization")); err != nil {
			if bucketMetadata.ACL.IsPrivate() {
				return true
				//uncomment this when we have webcli
				//writeErrorResponse(w, req, AccessDenied, acceptsContentType, req.URL.Path)
				//return false
			}
			if bucketMetadata.ACL.IsPublicRead() && req.Method == "PUT" {
				return true
				//uncomment this when we have webcli
				//writeErrorResponse(w, req, AccessDenied, acceptsContentType, req.URL.Path)
				//return false
			}
		}
		return true
	}
	switch err.ToGoError().(type) {
	case donut.BucketNotFound:
		writeErrorResponse(w, req, NoSuchBucket, acceptsContentType, req.URL.Path)
		return false
	case donut.BucketNameInvalid:
		writeErrorResponse(w, req, InvalidBucketName, acceptsContentType, req.URL.Path)
		return false
	default:
		log.Error.Println(err.Trace())
		writeErrorResponse(w, req, InternalError, acceptsContentType, req.URL.Path)
		return false
	}
}
예제 #2
0
// ListMultipartUploadsHandler - GET Bucket (List Multipart uploads)
// -------------------------
// This operation lists in-progress multipart uploads. An in-progress
// multipart upload is a multipart upload that has been initiated,
// using the Initiate Multipart Upload request, but has not yet been completed or aborted.
// This operation returns at most 1,000 multipart uploads in the response.
//
func (api Minio) ListMultipartUploadsHandler(w http.ResponseWriter, req *http.Request) {
	// Ticket master block
	{
		op := Operation{}
		op.ProceedCh = make(chan struct{})
		api.OP <- op
		// block until ticket master gives us a go
		<-op.ProceedCh
	}

	acceptsContentType := getContentType(req)
	if !api.isValidOp(w, req, acceptsContentType) {
		return
	}

	resources := getBucketMultipartResources(req.URL.Query())
	if resources.MaxUploads < 0 {
		writeErrorResponse(w, req, InvalidMaxUploads, acceptsContentType, req.URL.Path)
		return
	}
	if resources.MaxUploads == 0 {
		resources.MaxUploads = maxObjectList
	}

	vars := mux.Vars(req)
	bucket := vars["bucket"]

	var signature *donut.Signature
	if _, ok := req.Header["Authorization"]; ok {
		// Init signature V4 verification
		var err *probe.Error
		signature, err = InitSignatureV4(req)
		if err != nil {
			writeErrorResponse(w, req, InternalError, acceptsContentType, req.URL.Path)
			return
		}
	}

	resources, err := api.Donut.ListMultipartUploads(bucket, resources, signature)
	if err == nil {
		// generate response
		response := generateListMultipartUploadsResponse(bucket, resources)
		encodedSuccessResponse := encodeSuccessResponse(response, acceptsContentType)
		// write headers
		setCommonHeaders(w, getContentTypeString(acceptsContentType), len(encodedSuccessResponse))
		// write body
		w.Write(encodedSuccessResponse)
		return
	}
	switch err.ToGoError().(type) {
	case donut.SignatureDoesNotMatch:
		writeErrorResponse(w, req, SignatureDoesNotMatch, acceptsContentType, req.URL.Path)
	case donut.BucketNotFound:
		writeErrorResponse(w, req, NoSuchBucket, acceptsContentType, req.URL.Path)
	default:
		log.Error.Println(err.Trace())
		writeErrorResponse(w, req, InternalError, acceptsContentType, req.URL.Path)
	}
}
예제 #3
0
// NewMultipartUploadHandler - New multipart upload
func (api Minio) NewMultipartUploadHandler(w http.ResponseWriter, req *http.Request) {
	// Ticket master block
	{
		op := Operation{}
		op.ProceedCh = make(chan struct{})
		api.OP <- op
		// block until Ticket master gives us a go
		<-op.ProceedCh
	}

	acceptsContentType := getContentType(req)
	if !api.isValidOp(w, req, acceptsContentType) {
		return
	}

	if !isRequestUploads(req.URL.Query()) {
		writeErrorResponse(w, req, MethodNotAllowed, acceptsContentType, req.URL.Path)
		return
	}

	var object, bucket string
	vars := mux.Vars(req)
	bucket = vars["bucket"]
	object = vars["object"]

	var signature *donut.Signature
	if _, ok := req.Header["Authorization"]; ok {
		// Init signature V4 verification
		var err *probe.Error
		signature, err = InitSignatureV4(req)
		if err != nil {
			writeErrorResponse(w, req, InternalError, acceptsContentType, req.URL.Path)
			return
		}
	}

	uploadID, err := api.Donut.NewMultipartUpload(bucket, object, req.Header.Get("Content-Type"), signature)
	if err == nil {
		response := generateInitiateMultipartUploadResponse(bucket, object, uploadID)
		encodedSuccessResponse := encodeSuccessResponse(response, acceptsContentType)
		// write headers
		setCommonHeaders(w, getContentTypeString(acceptsContentType), len(encodedSuccessResponse))
		// write body
		w.Write(encodedSuccessResponse)
		return
	}
	switch err.ToGoError().(type) {
	case donut.SignatureDoesNotMatch:
		writeErrorResponse(w, req, SignatureDoesNotMatch, acceptsContentType, req.URL.Path)
	case donut.ObjectExists:
		writeErrorResponse(w, req, MethodNotAllowed, acceptsContentType, req.URL.Path)
	default:
		log.Error.Println(err.Trace())
		writeErrorResponse(w, req, InternalError, acceptsContentType, req.URL.Path)
	}
}
예제 #4
0
// HeadObjectHandler - HEAD Object
// -----------
// The HEAD operation retrieves metadata from an object without returning the object itself.
func (api Minio) HeadObjectHandler(w http.ResponseWriter, req *http.Request) {
	// ticket master block
	{
		op := Operation{}
		op.ProceedCh = make(chan struct{})
		api.OP <- op
		// block until Ticket master gives us a go
		<-op.ProceedCh
	}

	acceptsContentType := getContentType(req)
	if !api.isValidOp(w, req, acceptsContentType) {
		return
	}

	var object, bucket string
	vars := mux.Vars(req)
	bucket = vars["bucket"]
	object = vars["object"]

	var signature *donut.Signature
	if _, ok := req.Header["Authorization"]; ok {
		// Init signature V4 verification
		var err *probe.Error
		signature, err = InitSignatureV4(req)
		if err != nil {
			writeErrorResponse(w, req, InternalError, acceptsContentType, req.URL.Path)
			return
		}
	}

	metadata, err := api.Donut.GetObjectMetadata(bucket, object, signature)
	if err == nil {
		setObjectHeaders(w, metadata, nil)
		w.WriteHeader(http.StatusOK)
		return
	}
	switch err.ToGoError().(type) {
	case donut.SignatureDoesNotMatch:
		writeErrorResponse(w, req, SignatureDoesNotMatch, acceptsContentType, req.URL.Path)
	case donut.BucketNameInvalid:
		writeErrorResponse(w, req, InvalidBucketName, acceptsContentType, req.URL.Path)
	case donut.BucketNotFound:
		writeErrorResponse(w, req, NoSuchBucket, acceptsContentType, req.URL.Path)
	case donut.ObjectNotFound:
		writeErrorResponse(w, req, NoSuchKey, acceptsContentType, req.URL.Path)
	case donut.ObjectNameInvalid:
		writeErrorResponse(w, req, NoSuchKey, acceptsContentType, req.URL.Path)
	default:
		log.Error.Println(err.Trace())
		writeErrorResponse(w, req, InternalError, acceptsContentType, req.URL.Path)
	}
}
예제 #5
0
// PutBucketACLHandler - PUT Bucket ACL
// ----------
// This implementation of the PUT operation modifies the bucketACL for authenticated request
func (api Minio) PutBucketACLHandler(w http.ResponseWriter, req *http.Request) {
	// Ticket master block
	{
		op := Operation{}
		op.ProceedCh = make(chan struct{})
		api.OP <- op
		// block until Ticket master gives us a go
		<-op.ProceedCh
	}

	acceptsContentType := getContentType(req)

	// read from 'x-amz-acl'
	aclType := getACLType(req)
	if aclType == unsupportedACLType {
		writeErrorResponse(w, req, NotImplemented, acceptsContentType, req.URL.Path)
		return
	}

	vars := mux.Vars(req)
	bucket := vars["bucket"]

	var signature *donut.Signature
	if _, ok := req.Header["Authorization"]; ok {
		// Init signature V4 verification
		var err *probe.Error
		signature, err = InitSignatureV4(req)
		if err != nil {
			writeErrorResponse(w, req, InternalError, acceptsContentType, req.URL.Path)
			return
		}
	}

	err := api.Donut.SetBucketMetadata(bucket, map[string]string{"acl": getACLTypeString(aclType)}, signature)
	if err == nil {
		writeSuccessResponse(w, acceptsContentType)
		return
	}
	switch err.ToGoError().(type) {
	case donut.SignatureDoesNotMatch:
		writeErrorResponse(w, req, SignatureDoesNotMatch, acceptsContentType, req.URL.Path)
	case donut.BucketNameInvalid:
		writeErrorResponse(w, req, InvalidBucketName, acceptsContentType, req.URL.Path)
	case donut.BucketNotFound:
		writeErrorResponse(w, req, NoSuchBucket, acceptsContentType, req.URL.Path)
	default:
		log.Error.Println(err.Trace())
		writeErrorResponse(w, req, InternalError, acceptsContentType, req.URL.Path)
	}
}
예제 #6
0
// AbortMultipartUploadHandler - Abort multipart upload
func (api Minio) AbortMultipartUploadHandler(w http.ResponseWriter, req *http.Request) {
	// Ticket master block
	{
		op := Operation{}
		op.ProceedCh = make(chan struct{})
		api.OP <- op
		// block until Ticket master gives us a go
		<-op.ProceedCh
	}

	acceptsContentType := getContentType(req)
	if !api.isValidOp(w, req, acceptsContentType) {
		return
	}

	vars := mux.Vars(req)
	bucket := vars["bucket"]
	object := vars["object"]

	objectResourcesMetadata := getObjectResources(req.URL.Query())

	var signature *donut.Signature
	if _, ok := req.Header["Authorization"]; ok {
		// Init signature V4 verification
		var err *probe.Error
		signature, err = InitSignatureV4(req)
		if err != nil {
			writeErrorResponse(w, req, InternalError, acceptsContentType, req.URL.Path)
			return
		}
	}

	err := api.Donut.AbortMultipartUpload(bucket, object, objectResourcesMetadata.UploadID, signature)
	if err == nil {
		setCommonHeaders(w, getContentTypeString(acceptsContentType), 0)
		w.WriteHeader(http.StatusNoContent)
		return
	}
	switch err.ToGoError().(type) {
	case donut.SignatureDoesNotMatch:
		writeErrorResponse(w, req, SignatureDoesNotMatch, acceptsContentType, req.URL.Path)
	case donut.InvalidUploadID:
		writeErrorResponse(w, req, NoSuchUpload, acceptsContentType, req.URL.Path)
	default:
		log.Error.Println(err.Trace())
		writeErrorResponse(w, req, InternalError, acceptsContentType, req.URL.Path)
	}
}
예제 #7
0
// PutBucketHandler - PUT Bucket
// ----------
// This implementation of the PUT operation creates a new bucket for authenticated request
func (api Minio) PutBucketHandler(w http.ResponseWriter, req *http.Request) {
	// Ticket master block
	{
		op := Operation{}
		op.ProceedCh = make(chan struct{})
		api.OP <- op
		// block until Ticket master gives us a go
		<-op.ProceedCh
	}

	acceptsContentType := getContentType(req)
	// uncomment this when we have webcli
	// without access key credentials one cannot create a bucket
	// if _, err := StripAccessKeyID(req); err != nil {
	// 	writeErrorResponse(w, req, AccessDenied, acceptsContentType, req.URL.Path)
	//	return
	// }

	if isRequestBucketACL(req.URL.Query()) {
		api.PutBucketACLHandler(w, req)
		return
	}
	// read from 'x-amz-acl'
	aclType := getACLType(req)
	if aclType == unsupportedACLType {
		writeErrorResponse(w, req, NotImplemented, acceptsContentType, req.URL.Path)
		return
	}

	vars := mux.Vars(req)
	bucket := vars["bucket"]

	var signature *donut.Signature
	if _, ok := req.Header["Authorization"]; ok {
		// Init signature V4 verification
		var err *probe.Error
		signature, err = InitSignatureV4(req)
		if err != nil {
			writeErrorResponse(w, req, InternalError, acceptsContentType, req.URL.Path)
			return
		}
	}

	// if body of request is non-nil then check for validity of Content-Length
	if req.Body != nil {
		/// if Content-Length missing, deny the request
		size := req.Header.Get("Content-Length")
		if size == "" {
			writeErrorResponse(w, req, MissingContentLength, acceptsContentType, req.URL.Path)
			return
		}
	}

	err := api.Donut.MakeBucket(bucket, getACLTypeString(aclType), req.Body, signature)
	if err == nil {
		// Make sure to add Location information here only for bucket
		w.Header().Set("Location", "/"+bucket)
		writeSuccessResponse(w, acceptsContentType)
		return
	}
	switch err.ToGoError().(type) {
	case donut.SignatureDoesNotMatch:
		writeErrorResponse(w, req, SignatureDoesNotMatch, acceptsContentType, req.URL.Path)
	case donut.TooManyBuckets:
		writeErrorResponse(w, req, TooManyBuckets, acceptsContentType, req.URL.Path)
	case donut.BucketNameInvalid:
		writeErrorResponse(w, req, InvalidBucketName, acceptsContentType, req.URL.Path)
	case donut.BucketExists:
		writeErrorResponse(w, req, BucketAlreadyExists, acceptsContentType, req.URL.Path)
	default:
		log.Error.Println(err.Trace())
		writeErrorResponse(w, req, InternalError, acceptsContentType, req.URL.Path)
	}
}
예제 #8
0
// CompleteMultipartUploadHandler - Complete multipart upload
func (api Minio) CompleteMultipartUploadHandler(w http.ResponseWriter, req *http.Request) {
	// Ticket master block
	{
		op := Operation{}
		op.ProceedCh = make(chan struct{})
		api.OP <- op
		// block until Ticket master gives us a go
		<-op.ProceedCh
	}

	acceptsContentType := getContentType(req)
	if !api.isValidOp(w, req, acceptsContentType) {
		return
	}

	vars := mux.Vars(req)
	bucket := vars["bucket"]
	object := vars["object"]

	objectResourcesMetadata := getObjectResources(req.URL.Query())

	var signature *donut.Signature
	if _, ok := req.Header["Authorization"]; ok {
		// Init signature V4 verification
		var err *probe.Error
		signature, err = InitSignatureV4(req)
		if err != nil {
			writeErrorResponse(w, req, InternalError, acceptsContentType, req.URL.Path)
			return
		}
	}
	metadata, err := api.Donut.CompleteMultipartUpload(bucket, object, objectResourcesMetadata.UploadID, req.Body, signature)
	if err == nil {
		response := generateCompleteMultpartUploadResponse(bucket, object, "", metadata.MD5Sum)
		encodedSuccessResponse := encodeSuccessResponse(response, acceptsContentType)
		// write headers
		setCommonHeaders(w, getContentTypeString(acceptsContentType), len(encodedSuccessResponse))
		// write body
		w.Write(encodedSuccessResponse)
		return
	}
	switch err.ToGoError().(type) {
	case donut.InvalidUploadID:
		writeErrorResponse(w, req, NoSuchUpload, acceptsContentType, req.URL.Path)
	case donut.InvalidPart:
		writeErrorResponse(w, req, InvalidPart, acceptsContentType, req.URL.Path)
	case donut.InvalidPartOrder:
		writeErrorResponse(w, req, InvalidPartOrder, acceptsContentType, req.URL.Path)
	case donut.MissingDateHeader:
		writeErrorResponse(w, req, RequestTimeTooSkewed, acceptsContentType, req.URL.Path)
	case donut.SignatureDoesNotMatch:
		writeErrorResponse(w, req, SignatureDoesNotMatch, acceptsContentType, req.URL.Path)
	case donut.IncompleteBody:
		writeErrorResponse(w, req, IncompleteBody, acceptsContentType, req.URL.Path)
	case donut.MalformedXML:
		writeErrorResponse(w, req, MalformedXML, acceptsContentType, req.URL.Path)
	default:
		log.Error.Println(err.Trace())
		writeErrorResponse(w, req, InternalError, acceptsContentType, req.URL.Path)
	}
}
예제 #9
0
// PutObjectPartHandler - Upload part
func (api Minio) PutObjectPartHandler(w http.ResponseWriter, req *http.Request) {
	// Ticket master block
	{
		op := Operation{}
		op.ProceedCh = make(chan struct{})
		api.OP <- op
		// block until Ticket master gives us a go
		<-op.ProceedCh
	}

	acceptsContentType := getContentType(req)
	if !api.isValidOp(w, req, acceptsContentType) {
		return
	}

	// get Content-MD5 sent by client and verify if valid
	md5 := req.Header.Get("Content-MD5")
	if !isValidMD5(md5) {
		writeErrorResponse(w, req, InvalidDigest, acceptsContentType, req.URL.Path)
		return
	}

	/// if Content-Length missing, throw away
	size := req.Header.Get("Content-Length")
	if size == "" {
		writeErrorResponse(w, req, MissingContentLength, acceptsContentType, req.URL.Path)
		return
	}

	/// maximum Upload size for multipart objects in a single operation
	if isMaxObjectSize(size) {
		writeErrorResponse(w, req, EntityTooLarge, acceptsContentType, req.URL.Path)
		return
	}

	var sizeInt64 int64
	{
		var err error
		sizeInt64, err = strconv.ParseInt(size, 10, 64)
		if err != nil {
			writeErrorResponse(w, req, InvalidRequest, acceptsContentType, req.URL.Path)
			return
		}
	}

	vars := mux.Vars(req)
	bucket := vars["bucket"]
	object := vars["object"]

	uploadID := req.URL.Query().Get("uploadId")
	partIDString := req.URL.Query().Get("partNumber")

	var partID int
	{
		var err error
		partID, err = strconv.Atoi(partIDString)
		if err != nil {
			writeErrorResponse(w, req, InvalidPart, acceptsContentType, req.URL.Path)
		}
	}

	var signature *donut.Signature
	if _, ok := req.Header["Authorization"]; ok {
		// Init signature V4 verification
		var err *probe.Error
		signature, err = InitSignatureV4(req)
		if err != nil {
			writeErrorResponse(w, req, InternalError, acceptsContentType, req.URL.Path)
			return
		}
	}

	calculatedMD5, err := api.Donut.CreateObjectPart(bucket, object, uploadID, partID, "", md5, sizeInt64, req.Body, signature)
	if err == nil {
		w.Header().Set("ETag", calculatedMD5)
		writeSuccessResponse(w, acceptsContentType)
		return
	}
	switch err.ToGoError().(type) {
	case donut.InvalidUploadID:
		writeErrorResponse(w, req, NoSuchUpload, acceptsContentType, req.URL.Path)
	case donut.ObjectExists:
		writeErrorResponse(w, req, MethodNotAllowed, acceptsContentType, req.URL.Path)
	case donut.BadDigest:
		writeErrorResponse(w, req, BadDigest, acceptsContentType, req.URL.Path)
	case donut.SignatureDoesNotMatch:
		writeErrorResponse(w, req, SignatureDoesNotMatch, acceptsContentType, req.URL.Path)
	case donut.IncompleteBody:
		writeErrorResponse(w, req, IncompleteBody, acceptsContentType, req.URL.Path)
	case donut.EntityTooLarge:
		writeErrorResponse(w, req, EntityTooLarge, acceptsContentType, req.URL.Path)
	case donut.InvalidDigest:
		writeErrorResponse(w, req, InvalidDigest, acceptsContentType, req.URL.Path)
	default:
		log.Error.Println(err.Trace())
		writeErrorResponse(w, req, InternalError, acceptsContentType, req.URL.Path)
	}
}
예제 #10
0
// PutObjectHandler - PUT Object
// ----------
// This implementation of the PUT operation adds an object to a bucket.
func (api Minio) PutObjectHandler(w http.ResponseWriter, req *http.Request) {
	// Ticket master block
	{
		op := Operation{}
		op.ProceedCh = make(chan struct{})
		api.OP <- op
		// block until Ticket master gives us a go
		<-op.ProceedCh
	}

	acceptsContentType := getContentType(req)
	if !api.isValidOp(w, req, acceptsContentType) {
		return
	}

	var object, bucket string
	vars := mux.Vars(req)
	bucket = vars["bucket"]
	object = vars["object"]

	// get Content-MD5 sent by client and verify if valid
	md5 := req.Header.Get("Content-MD5")
	if !isValidMD5(md5) {
		writeErrorResponse(w, req, InvalidDigest, acceptsContentType, req.URL.Path)
		return
	}
	/// if Content-Length missing, deny the request
	size := req.Header.Get("Content-Length")
	if size == "" {
		writeErrorResponse(w, req, MissingContentLength, acceptsContentType, req.URL.Path)
		return
	}
	/// maximum Upload size for objects in a single operation
	if isMaxObjectSize(size) {
		writeErrorResponse(w, req, EntityTooLarge, acceptsContentType, req.URL.Path)
		return
	}
	/// minimum Upload size for objects in a single operation
	//
	// Surprisingly while Amazon in their document states that S3 objects have 1byte
	// as the minimum limit, they do not seem to enforce it one can successfully
	// create a 0byte file using a regular putObject() operation
	//
	// if isMinObjectSize(size) {
	//      writeErrorResponse(w, req, EntityTooSmall, acceptsContentType, req.URL.Path)
	//	return
	// }
	var sizeInt64 int64
	{
		var err error
		sizeInt64, err = strconv.ParseInt(size, 10, 64)
		if err != nil {
			writeErrorResponse(w, req, InvalidRequest, acceptsContentType, req.URL.Path)
			return
		}
	}

	var signature *donut.Signature
	if _, ok := req.Header["Authorization"]; ok {
		// Init signature V4 verification
		var err *probe.Error
		signature, err = InitSignatureV4(req)
		if err != nil {
			writeErrorResponse(w, req, InternalError, acceptsContentType, req.URL.Path)
			return
		}
	}

	metadata, err := api.Donut.CreateObject(bucket, object, md5, sizeInt64, req.Body, nil, signature)
	if err == nil {
		w.Header().Set("ETag", metadata.MD5Sum)
		writeSuccessResponse(w, acceptsContentType)
		return
	}
	switch err.ToGoError().(type) {
	case donut.BucketNotFound:
		writeErrorResponse(w, req, NoSuchBucket, acceptsContentType, req.URL.Path)
	case donut.BucketNameInvalid:
		writeErrorResponse(w, req, InvalidBucketName, acceptsContentType, req.URL.Path)
	case donut.ObjectExists:
		writeErrorResponse(w, req, MethodNotAllowed, acceptsContentType, req.URL.Path)
	case donut.BadDigest:
		writeErrorResponse(w, req, BadDigest, acceptsContentType, req.URL.Path)
	case donut.MissingDateHeader:
		writeErrorResponse(w, req, RequestTimeTooSkewed, acceptsContentType, req.URL.Path)
	case donut.SignatureDoesNotMatch:
		writeErrorResponse(w, req, SignatureDoesNotMatch, acceptsContentType, req.URL.Path)
	case donut.IncompleteBody:
		writeErrorResponse(w, req, IncompleteBody, acceptsContentType, req.URL.Path)
	case donut.EntityTooLarge:
		writeErrorResponse(w, req, EntityTooLarge, acceptsContentType, req.URL.Path)
	case donut.InvalidDigest:
		writeErrorResponse(w, req, InvalidDigest, acceptsContentType, req.URL.Path)
	default:
		log.Error.Println(err.Trace())
		writeErrorResponse(w, req, InternalError, acceptsContentType, req.URL.Path)
	}
}