Skip to content

Commit

Permalink
parse error responses for STS requests (#1602)
Browse files Browse the repository at this point in the history
provide more descriptive error messages
to the caller instead of just HTTP status
  • Loading branch information
harshavardhana authored Dec 22, 2021
1 parent 52ffedf commit 2ef8cbb
Show file tree
Hide file tree
Showing 8 changed files with 118 additions and 7 deletions.
1 change: 1 addition & 0 deletions api-error-response.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ type ErrorResponse struct {
Message string
BucketName string
Key string
Resource string
RequestID string `xml:"RequestId"`
HostID string `xml:"HostId"`

Expand Down
9 changes: 7 additions & 2 deletions pkg/credentials/assume_role.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
74 changes: 74 additions & 0 deletions pkg/credentials/error_response.go
Original file line number Diff line number Diff line change
@@ -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)
}
File renamed without changes.
10 changes: 9 additions & 1 deletion pkg/credentials/sts_client_grants.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{}
Expand Down
11 changes: 9 additions & 2 deletions pkg/credentials/sts_ldap_identity.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ package credentials

import (
"encoding/xml"
"errors"
"fmt"
"net/http"
"net/url"
Expand Down Expand Up @@ -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{}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
10 changes: 9 additions & 1 deletion pkg/credentials/sts_web_identity.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{}
Expand Down

0 comments on commit 2ef8cbb

Please sign in to comment.