error: Signature errors should be returned with APIErrorCode.

The reasoning is that we can reply back with wide range of
S3 error responses, which would provide more richer context
to S3 client.

Fixes #1267
This commit is contained in:
Harshavardhana
2016-03-30 20:04:51 -07:00
parent a84c466a40
commit 02ad48466d
10 changed files with 230 additions and 255 deletions
+42 -44
View File
@@ -20,8 +20,6 @@ import (
"net/url"
"strings"
"time"
"github.com/minio/minio/pkg/probe"
)
// credentialHeader data type represents structured form of Credential
@@ -37,20 +35,20 @@ type credentialHeader struct {
}
// parse credentialHeader string into its structured form.
func parseCredentialHeader(credElement string) (credentialHeader, *probe.Error) {
func parseCredentialHeader(credElement string) (credentialHeader, APIErrorCode) {
creds := strings.Split(strings.TrimSpace(credElement), "=")
if len(creds) != 2 {
return credentialHeader{}, ErrMissingFields("Credential tag has missing fields.", credElement).Trace(credElement)
return credentialHeader{}, ErrMissingFields
}
if creds[0] != "Credential" {
return credentialHeader{}, ErrMissingCredTag("Missing credentials tag.", credElement).Trace(credElement)
return credentialHeader{}, ErrMissingCredTag
}
credElements := strings.Split(strings.TrimSpace(creds[1]), "/")
if len(credElements) != 5 {
return credentialHeader{}, ErrCredMalformed("Credential values malformed.", credElement).Trace(credElement)
return credentialHeader{}, ErrCredMalformed
}
if !isValidAccessKey.MatchString(credElements[0]) {
return credentialHeader{}, ErrInvalidAccessKey("Invalid access key id.", credElement).Trace(credElement)
return credentialHeader{}, ErrInvalidAccessKeyID
}
// Save access key id.
cred := credentialHeader{
@@ -59,47 +57,47 @@ func parseCredentialHeader(credElement string) (credentialHeader, *probe.Error)
var e error
cred.scope.date, e = time.Parse(yyyymmdd, credElements[1])
if e != nil {
return credentialHeader{}, ErrInvalidDateFormat("Invalid date format.", credElement).Trace(credElement)
return credentialHeader{}, ErrMalformedDate
}
if credElements[2] == "" {
return credentialHeader{}, ErrRegionISEmpty("Region is empty.", credElement).Trace(credElement)
return credentialHeader{}, ErrInvalidRegion
}
cred.scope.region = credElements[2]
if credElements[3] != "s3" {
return credentialHeader{}, ErrInvalidService("Invalid service detected.", credElement).Trace(credElement)
return credentialHeader{}, ErrInvalidService
}
cred.scope.service = credElements[3]
if credElements[4] != "aws4_request" {
return credentialHeader{}, ErrInvalidRequestVersion("Invalid request version detected.", credElement).Trace(credElement)
return credentialHeader{}, ErrInvalidRequestVersion
}
cred.scope.request = credElements[4]
return cred, nil
return cred, ErrNone
}
// Parse signature string.
func parseSignature(signElement string) (string, *probe.Error) {
func parseSignature(signElement string) (string, APIErrorCode) {
signFields := strings.Split(strings.TrimSpace(signElement), "=")
if len(signFields) != 2 {
return "", ErrMissingFields("Signature tag has missing fields.", signElement).Trace(signElement)
return "", ErrMissingFields
}
if signFields[0] != "Signature" {
return "", ErrMissingSignTag("Signature tag is missing", signElement).Trace(signElement)
return "", ErrMissingSignTag
}
signature := signFields[1]
return signature, nil
return signature, ErrNone
}
// Parse signed headers string.
func parseSignedHeaders(signedHdrElement string) ([]string, *probe.Error) {
func parseSignedHeaders(signedHdrElement string) ([]string, APIErrorCode) {
signedHdrFields := strings.Split(strings.TrimSpace(signedHdrElement), "=")
if len(signedHdrFields) != 2 {
return nil, ErrMissingFields("Signed headers tag has missing fields.", signedHdrElement).Trace(signedHdrElement)
return nil, ErrMissingFields
}
if signedHdrFields[0] != "SignedHeaders" {
return nil, ErrMissingSignHeadersTag("Signed headers tag is missing.", signedHdrElement).Trace(signedHdrElement)
return nil, ErrMissingSignHeadersTag
}
signedHeaders := strings.Split(signedHdrFields[1], ";")
return signedHeaders, nil
return signedHeaders, ErrNone
}
// signValues data type represents structured form of AWS Signature V4 header.
@@ -125,49 +123,49 @@ type preSignValues struct {
// querystring += &X-Amz-SignedHeaders=signed_headers
// querystring += &X-Amz-Signature=signature
//
func parsePreSignV4(query url.Values) (preSignValues, *probe.Error) {
func parsePreSignV4(query url.Values) (preSignValues, APIErrorCode) {
// Verify if the query algorithm is supported or not.
if query.Get("X-Amz-Algorithm") != signV4Algorithm {
return preSignValues{}, ErrUnsuppSignAlgo("Unsupported algorithm in query string.", query.Get("X-Amz-Algorithm"))
return preSignValues{}, ErrInvalidQuerySignatureAlgo
}
// Initialize signature version '4' structured header.
preSignV4Values := preSignValues{}
var err *probe.Error
var err APIErrorCode
// Save credential.
preSignV4Values.Credential, err = parseCredentialHeader("Credential=" + query.Get("X-Amz-Credential"))
if err != nil {
return preSignValues{}, err.Trace(query.Get("X-Amz-Credential"))
if err != ErrNone {
return preSignValues{}, err
}
var e error
// Save date in native time.Time.
preSignV4Values.Date, e = time.Parse(iso8601Format, query.Get("X-Amz-Date"))
if e != nil {
return preSignValues{}, ErrMalformedDate("Malformed date string.", query.Get("X-Amz-Date")).Trace(query.Get("X-Amz-Date"))
return preSignValues{}, ErrMalformedDate
}
// Save expires in native time.Duration.
preSignV4Values.Expires, e = time.ParseDuration(query.Get("X-Amz-Expires") + "s")
if e != nil {
return preSignValues{}, ErrMalformedExpires("Malformed expires string.", query.Get("X-Amz-Expires")).Trace(query.Get("X-Amz-Expires"))
return preSignValues{}, ErrMalformedExpires
}
// Save signed headers.
preSignV4Values.SignedHeaders, err = parseSignedHeaders("SignedHeaders=" + query.Get("X-Amz-SignedHeaders"))
if err != nil {
return preSignValues{}, err.Trace(query.Get("X-Amz-SignedHeaders"))
if err != ErrNone {
return preSignValues{}, err
}
// Save signature.
preSignV4Values.Signature, err = parseSignature("Signature=" + query.Get("X-Amz-Signature"))
if err != nil {
return preSignValues{}, err.Trace(query.Get("X-Amz-Signature"))
if err != ErrNone {
return preSignValues{}, err
}
// Return structed form of signature query string.
return preSignV4Values, nil
return preSignV4Values, ErrNone
}
// Parses signature version '4' header of the following form.
@@ -175,49 +173,49 @@ func parsePreSignV4(query url.Values) (preSignValues, *probe.Error) {
// Authorization: algorithm Credential=accessKeyID/credScope, \
// SignedHeaders=signedHeaders, Signature=signature
//
func parseSignV4(v4Auth string) (signValues, *probe.Error) {
func parseSignV4(v4Auth string) (signValues, APIErrorCode) {
// Replace all spaced strings, some clients can send spaced
// parameters and some won't. So we pro-actively remove any spaces
// to make parsing easier.
v4Auth = strings.Replace(v4Auth, " ", "", -1)
if v4Auth == "" {
return signValues{}, ErrAuthHeaderEmpty("Auth header empty.").Trace(v4Auth)
return signValues{}, ErrAuthHeaderEmpty
}
// Verify if the header algorithm is supported or not.
if !strings.HasPrefix(v4Auth, signV4Algorithm) {
return signValues{}, ErrUnsuppSignAlgo("Unsupported algorithm in authorization header.", v4Auth).Trace(v4Auth)
return signValues{}, ErrSignatureVersionNotSupported
}
// Strip off the Algorithm prefix.
v4Auth = strings.TrimPrefix(v4Auth, signV4Algorithm)
authFields := strings.Split(strings.TrimSpace(v4Auth), ",")
if len(authFields) != 3 {
return signValues{}, ErrMissingFields("Missing fields in authorization header.", v4Auth).Trace(v4Auth)
return signValues{}, ErrMissingFields
}
// Initialize signature version '4' structured header.
signV4Values := signValues{}
var err *probe.Error
var err APIErrorCode
// Save credentail values.
signV4Values.Credential, err = parseCredentialHeader(authFields[0])
if err != nil {
return signValues{}, err.Trace(v4Auth)
if err != ErrNone {
return signValues{}, err
}
// Save signed headers.
signV4Values.SignedHeaders, err = parseSignedHeaders(authFields[1])
if err != nil {
return signValues{}, err.Trace(v4Auth)
if err != ErrNone {
return signValues{}, err
}
// Save signature.
signV4Values.Signature, err = parseSignature(authFields[2])
if err != nil {
return signValues{}, err.Trace(v4Auth)
if err != ErrNone {
return signValues{}, err
}
// Return the structure here.
return signV4Values, nil
return signV4Values, ErrNone
}