diff --git a/api-error-response.go b/api-error-response.go index 39df7eec6..dd781cae3 100644 --- a/api-error-response.go +++ b/api-error-response.go @@ -47,6 +47,7 @@ type ErrorResponse struct { Message string BucketName string Key string + Resource string RequestID string `xml:"RequestId"` HostID string `xml:"HostId"` diff --git a/pkg/credentials/assume_role.go b/pkg/credentials/assume_role.go index 3b1b547b9..d14f5239f 100644 --- a/pkg/credentials/assume_role.go +++ b/pkg/credentials/assume_role.go @@ -184,11 +184,16 @@ func getAssumeRoleCredentials(clnt *http.Client, endpoint string, opts STSAssume } defer closeResponse(resp) if resp.StatusCode != http.StatusOK { - return AssumeRoleResponse{}, errors.New(resp.Status) + var errResp ErrorResponse + _, err = xmlDecodeAndBody(resp.Body, &errResp) + if err != nil { + return AssumeRoleResponse{}, err + } + return AssumeRoleResponse{}, errResp } a := AssumeRoleResponse{} - if err = xml.NewDecoder(resp.Body).Decode(&a); err != nil { + if _, err = xmlDecodeAndBody(resp.Body, &a); err != nil { return AssumeRoleResponse{}, err } return a, nil diff --git a/pkg/credentials/error_response.go b/pkg/credentials/error_response.go new file mode 100644 index 000000000..798b44137 --- /dev/null +++ b/pkg/credentials/error_response.go @@ -0,0 +1,74 @@ +/* + * MinIO Go Library for Amazon S3 Compatible Cloud Storage + * Copyright 2021 MinIO, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package credentials + +import ( + "bytes" + "encoding/xml" + "fmt" + "io" + "io/ioutil" +) + +// ErrorResponse - Is the typed error returned. +// ErrorResponse struct should be comparable since it is compared inside +// golang http API (https://github.com/golang/go/issues/29768) +type ErrorResponse struct { + XMLName xml.Name `xml:"Error" json:"-"` + Code string + Message string + RequestID string `xml:"RequestId"` + HostID string `xml:"HostId"` + + // Region where the bucket is located. This header is returned + // only in HEAD bucket and ListObjects response. + Region string + + // Captures the server string returned in response header. + Server string + + // Underlying HTTP status code for the returned error + StatusCode int `xml:"-" json:"-"` +} + +// Error - Returns STS error string. +func (e ErrorResponse) Error() string { + if e.Message == "" { + return fmt.Sprintf("Error response code %s.", e.Code) + } + return e.Message +} + +// xmlDecoder provide decoded value in xml. +func xmlDecoder(body io.Reader, v interface{}) error { + d := xml.NewDecoder(body) + return d.Decode(v) +} + +// xmlDecodeAndBody reads the whole body up to 1MB and +// tries to XML decode it into v. +// The body that was read and any error from reading or decoding is returned. +func xmlDecodeAndBody(bodyReader io.Reader, v interface{}) ([]byte, error) { + // read the whole body (up to 1MB) + const maxBodyLength = 1 << 20 + body, err := ioutil.ReadAll(io.LimitReader(bodyReader, maxBodyLength)) + if err != nil { + return nil, err + } + return bytes.TrimSpace(body), xmlDecoder(bytes.NewReader(body), v) +} diff --git a/pkg/credentials/signature-type.go b/pkg/credentials/signature_type.go similarity index 100% rename from pkg/credentials/signature-type.go rename to pkg/credentials/signature_type.go diff --git a/pkg/credentials/sts_client_grants.go b/pkg/credentials/sts_client_grants.go index b79f920f5..a40381da0 100644 --- a/pkg/credentials/sts_client_grants.go +++ b/pkg/credentials/sts_client_grants.go @@ -132,7 +132,15 @@ func getClientGrantsCredentials(clnt *http.Client, endpoint string, } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { - return AssumeRoleWithClientGrantsResponse{}, errors.New(resp.Status) + var errResp ErrorResponse + _, err = xmlDecodeAndBody(resp.Body, &errResp) + if err != nil { + return AssumeRoleWithClientGrantsResponse{}, ErrorResponse{ + Code: "InvalidArgument", + Message: err.Error(), + } + } + return AssumeRoleWithClientGrantsResponse{}, errResp } a := AssumeRoleWithClientGrantsResponse{} diff --git a/pkg/credentials/sts_ldap_identity.go b/pkg/credentials/sts_ldap_identity.go index bdde1fa3c..26d9e0361 100644 --- a/pkg/credentials/sts_ldap_identity.go +++ b/pkg/credentials/sts_ldap_identity.go @@ -19,7 +19,6 @@ package credentials import ( "encoding/xml" - "errors" "fmt" "net/http" "net/url" @@ -169,7 +168,15 @@ func (k *LDAPIdentity) Retrieve() (value Value, err error) { defer resp.Body.Close() if resp.StatusCode != http.StatusOK { - return value, errors.New(resp.Status) + var errResp ErrorResponse + _, err = xmlDecodeAndBody(resp.Body, &errResp) + if err != nil { + return value, ErrorResponse{ + Code: "InvalidArgument", + Message: err.Error(), + } + } + return value, errResp } r := AssumeRoleWithLDAPResponse{} diff --git a/pkg/credentials/sts-tls-identity.go b/pkg/credentials/sts_tls_identity.go similarity index 96% rename from pkg/credentials/sts-tls-identity.go rename to pkg/credentials/sts_tls_identity.go index 2e37025a2..e56e7be67 100644 --- a/pkg/credentials/sts-tls-identity.go +++ b/pkg/credentials/sts_tls_identity.go @@ -149,7 +149,15 @@ func (i *STSCertificateIdentity) Retrieve() (Value, error) { defer resp.Body.Close() } if resp.StatusCode != http.StatusOK { - return Value{}, errors.New(resp.Status) + var errResp ErrorResponse + _, err = xmlDecodeAndBody(resp.Body, &errResp) + if err != nil { + return Value{}, ErrorResponse{ + Code: "InvalidArgument", + Message: err.Error(), + } + } + return Value{}, errResp } const MaxSize = 10 * 1 << 20 diff --git a/pkg/credentials/sts_web_identity.go b/pkg/credentials/sts_web_identity.go index 25ca751de..72352188d 100644 --- a/pkg/credentials/sts_web_identity.go +++ b/pkg/credentials/sts_web_identity.go @@ -150,7 +150,15 @@ func getWebIdentityCredentials(clnt *http.Client, endpoint, roleARN, roleSession defer resp.Body.Close() if resp.StatusCode != http.StatusOK { - return AssumeRoleWithWebIdentityResponse{}, errors.New(resp.Status) + var errResp ErrorResponse + _, err = xmlDecodeAndBody(resp.Body, &errResp) + if err != nil { + return AssumeRoleWithWebIdentityResponse{}, ErrorResponse{ + Code: "InvalidArgument", + Message: err.Error(), + } + } + return AssumeRoleWithWebIdentityResponse{}, errResp } a := AssumeRoleWithWebIdentityResponse{}