github.com/jghiloni/cli@v6.28.1-0.20170628223758-0ce05fe032a2+incompatible/api/uaa/wrapper/uaa_authentication.go (about)

     1  package wrapper
     2  
     3  import (
     4  	"bytes"
     5  	"io/ioutil"
     6  	"net/http"
     7  	"strings"
     8  
     9  	"code.cloudfoundry.org/cli/api/uaa"
    10  )
    11  
    12  //go:generate counterfeiter . UAAClient
    13  
    14  // UAAClient is the interface for getting a valid access token
    15  type UAAClient interface {
    16  	RefreshAccessToken(refreshToken string) (uaa.RefreshToken, error)
    17  }
    18  
    19  //go:generate counterfeiter . TokenCache
    20  
    21  // TokenCache is where the UAA token information is stored.
    22  type TokenCache interface {
    23  	AccessToken() string
    24  	RefreshToken() string
    25  	SetAccessToken(token string)
    26  	SetRefreshToken(token string)
    27  }
    28  
    29  // UAAAuthentication wraps connections and adds authentication headers to all
    30  // requests
    31  type UAAAuthentication struct {
    32  	connection uaa.Connection
    33  	client     UAAClient
    34  	cache      TokenCache
    35  }
    36  
    37  // NewUAAAuthentication returns a pointer to a UAAAuthentication wrapper with
    38  // the client and token cache.
    39  func NewUAAAuthentication(client UAAClient, cache TokenCache) *UAAAuthentication {
    40  	return &UAAAuthentication{
    41  		client: client,
    42  		cache:  cache,
    43  	}
    44  }
    45  
    46  // Wrap sets the connection on the UAAAuthentication and returns itself
    47  func (t *UAAAuthentication) Wrap(innerconnection uaa.Connection) uaa.Connection {
    48  	t.connection = innerconnection
    49  	return t
    50  }
    51  
    52  // Make adds authentication headers to the passed in request and then calls the
    53  // wrapped connection's Make
    54  func (t *UAAAuthentication) Make(request *http.Request, passedResponse *uaa.Response) error {
    55  	var err error
    56  	var rawRequestBody []byte
    57  
    58  	if request.Body != nil {
    59  		rawRequestBody, err = ioutil.ReadAll(request.Body)
    60  		defer request.Body.Close()
    61  		if err != nil {
    62  			return err
    63  		}
    64  
    65  		request.Body = ioutil.NopCloser(bytes.NewBuffer(rawRequestBody))
    66  
    67  		if skipAuthenticationHeader(request, rawRequestBody) {
    68  			return t.connection.Make(request, passedResponse)
    69  		}
    70  	}
    71  
    72  	request.Header.Set("Authorization", t.cache.AccessToken())
    73  
    74  	err = t.connection.Make(request, passedResponse)
    75  	if _, ok := err.(uaa.InvalidAuthTokenError); ok {
    76  		var token uaa.RefreshToken
    77  		token, err = t.client.RefreshAccessToken(t.cache.RefreshToken())
    78  		if err != nil {
    79  			return err
    80  		}
    81  
    82  		t.cache.SetAccessToken(token.AuthorizationToken())
    83  		t.cache.SetRefreshToken(token.RefreshToken)
    84  
    85  		if rawRequestBody != nil {
    86  			request.Body = ioutil.NopCloser(bytes.NewBuffer(rawRequestBody))
    87  		}
    88  		request.Header.Set("Authorization", t.cache.AccessToken())
    89  		return t.connection.Make(request, passedResponse)
    90  	}
    91  
    92  	return err
    93  }
    94  
    95  // The authentication header is not added to token refresh requests or login
    96  // requests.
    97  func skipAuthenticationHeader(request *http.Request, body []byte) bool {
    98  	stringBody := string(body)
    99  
   100  	return strings.Contains(request.URL.String(), "/oauth/token") &&
   101  		request.Method == http.MethodPost &&
   102  		(strings.Contains(stringBody, "grant_type=refresh_token") ||
   103  			strings.Contains(stringBody, "grant_type=password"))
   104  }