...

Source file src/github.com/Azure/go-autorest/autorest/authorization.go

Documentation: github.com/Azure/go-autorest/autorest

     1  package autorest
     2  
     3  // Copyright 2017 Microsoft Corporation
     4  //
     5  //  Licensed under the Apache License, Version 2.0 (the "License");
     6  //  you may not use this file except in compliance with the License.
     7  //  You may obtain a copy of the License at
     8  //
     9  //      http://www.apache.org/licenses/LICENSE-2.0
    10  //
    11  //  Unless required by applicable law or agreed to in writing, software
    12  //  distributed under the License is distributed on an "AS IS" BASIS,
    13  //  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14  //  See the License for the specific language governing permissions and
    15  //  limitations under the License.
    16  
    17  import (
    18  	"crypto/tls"
    19  	"encoding/base64"
    20  	"fmt"
    21  	"net/http"
    22  	"net/url"
    23  	"strings"
    24  
    25  	"github.com/Azure/go-autorest/autorest/adal"
    26  )
    27  
    28  const (
    29  	bearerChallengeHeader       = "Www-Authenticate"
    30  	bearer                      = "Bearer"
    31  	tenantID                    = "tenantID"
    32  	apiKeyAuthorizerHeader      = "Ocp-Apim-Subscription-Key"
    33  	bingAPISdkHeader            = "X-BingApis-SDK-Client"
    34  	golangBingAPISdkHeaderValue = "Go-SDK"
    35  	authorization               = "Authorization"
    36  	basic                       = "Basic"
    37  )
    38  
    39  // Authorizer is the interface that provides a PrepareDecorator used to supply request
    40  // authorization. Most often, the Authorizer decorator runs last so it has access to the full
    41  // state of the formed HTTP request.
    42  type Authorizer interface {
    43  	WithAuthorization() PrepareDecorator
    44  }
    45  
    46  // NullAuthorizer implements a default, "do nothing" Authorizer.
    47  type NullAuthorizer struct{}
    48  
    49  // WithAuthorization returns a PrepareDecorator that does nothing.
    50  func (na NullAuthorizer) WithAuthorization() PrepareDecorator {
    51  	return WithNothing()
    52  }
    53  
    54  // APIKeyAuthorizer implements API Key authorization.
    55  type APIKeyAuthorizer struct {
    56  	headers         map[string]interface{}
    57  	queryParameters map[string]interface{}
    58  }
    59  
    60  // NewAPIKeyAuthorizerWithHeaders creates an ApiKeyAuthorizer with headers.
    61  func NewAPIKeyAuthorizerWithHeaders(headers map[string]interface{}) *APIKeyAuthorizer {
    62  	return NewAPIKeyAuthorizer(headers, nil)
    63  }
    64  
    65  // NewAPIKeyAuthorizerWithQueryParameters creates an ApiKeyAuthorizer with query parameters.
    66  func NewAPIKeyAuthorizerWithQueryParameters(queryParameters map[string]interface{}) *APIKeyAuthorizer {
    67  	return NewAPIKeyAuthorizer(nil, queryParameters)
    68  }
    69  
    70  // NewAPIKeyAuthorizer creates an ApiKeyAuthorizer with headers.
    71  func NewAPIKeyAuthorizer(headers map[string]interface{}, queryParameters map[string]interface{}) *APIKeyAuthorizer {
    72  	return &APIKeyAuthorizer{headers: headers, queryParameters: queryParameters}
    73  }
    74  
    75  // WithAuthorization returns a PrepareDecorator that adds an HTTP headers and Query Parameters.
    76  func (aka *APIKeyAuthorizer) WithAuthorization() PrepareDecorator {
    77  	return func(p Preparer) Preparer {
    78  		return DecoratePreparer(p, WithHeaders(aka.headers), WithQueryParameters(aka.queryParameters))
    79  	}
    80  }
    81  
    82  // CognitiveServicesAuthorizer implements authorization for Cognitive Services.
    83  type CognitiveServicesAuthorizer struct {
    84  	subscriptionKey string
    85  }
    86  
    87  // NewCognitiveServicesAuthorizer is
    88  func NewCognitiveServicesAuthorizer(subscriptionKey string) *CognitiveServicesAuthorizer {
    89  	return &CognitiveServicesAuthorizer{subscriptionKey: subscriptionKey}
    90  }
    91  
    92  // WithAuthorization is
    93  func (csa *CognitiveServicesAuthorizer) WithAuthorization() PrepareDecorator {
    94  	headers := make(map[string]interface{})
    95  	headers[apiKeyAuthorizerHeader] = csa.subscriptionKey
    96  	headers[bingAPISdkHeader] = golangBingAPISdkHeaderValue
    97  
    98  	return NewAPIKeyAuthorizerWithHeaders(headers).WithAuthorization()
    99  }
   100  
   101  // BearerAuthorizer implements the bearer authorization
   102  type BearerAuthorizer struct {
   103  	tokenProvider adal.OAuthTokenProvider
   104  }
   105  
   106  // NewBearerAuthorizer crates a BearerAuthorizer using the given token provider
   107  func NewBearerAuthorizer(tp adal.OAuthTokenProvider) *BearerAuthorizer {
   108  	return &BearerAuthorizer{tokenProvider: tp}
   109  }
   110  
   111  // WithAuthorization returns a PrepareDecorator that adds an HTTP Authorization header whose
   112  // value is "Bearer " followed by the token.
   113  //
   114  // By default, the token will be automatically refreshed through the Refresher interface.
   115  func (ba *BearerAuthorizer) WithAuthorization() PrepareDecorator {
   116  	return func(p Preparer) Preparer {
   117  		return PreparerFunc(func(r *http.Request) (*http.Request, error) {
   118  			r, err := p.Prepare(r)
   119  			if err == nil {
   120  				// the ordering is important here, prefer RefresherWithContext if available
   121  				if refresher, ok := ba.tokenProvider.(adal.RefresherWithContext); ok {
   122  					err = refresher.EnsureFreshWithContext(r.Context())
   123  				} else if refresher, ok := ba.tokenProvider.(adal.Refresher); ok {
   124  					err = refresher.EnsureFresh()
   125  				}
   126  				if err != nil {
   127  					var resp *http.Response
   128  					if tokError, ok := err.(adal.TokenRefreshError); ok {
   129  						resp = tokError.Response()
   130  					}
   131  					return r, NewErrorWithError(err, "azure.BearerAuthorizer", "WithAuthorization", resp,
   132  						"Failed to refresh the Token for request to %s", r.URL)
   133  				}
   134  				return Prepare(r, WithHeader(headerAuthorization, fmt.Sprintf("Bearer %s", ba.tokenProvider.OAuthToken())))
   135  			}
   136  			return r, err
   137  		})
   138  	}
   139  }
   140  
   141  // TokenProvider returns OAuthTokenProvider so that it can be used for authorization outside the REST.
   142  func (ba *BearerAuthorizer) TokenProvider() adal.OAuthTokenProvider {
   143  	return ba.tokenProvider
   144  }
   145  
   146  // BearerAuthorizerCallbackFunc is the authentication callback signature.
   147  type BearerAuthorizerCallbackFunc func(tenantID, resource string) (*BearerAuthorizer, error)
   148  
   149  // BearerAuthorizerCallback implements bearer authorization via a callback.
   150  type BearerAuthorizerCallback struct {
   151  	sender   Sender
   152  	callback BearerAuthorizerCallbackFunc
   153  }
   154  
   155  // NewBearerAuthorizerCallback creates a bearer authorization callback.  The callback
   156  // is invoked when the HTTP request is submitted.
   157  func NewBearerAuthorizerCallback(s Sender, callback BearerAuthorizerCallbackFunc) *BearerAuthorizerCallback {
   158  	if s == nil {
   159  		s = sender(tls.RenegotiateNever)
   160  	}
   161  	return &BearerAuthorizerCallback{sender: s, callback: callback}
   162  }
   163  
   164  // WithAuthorization returns a PrepareDecorator that adds an HTTP Authorization header whose value
   165  // is "Bearer " followed by the token.  The BearerAuthorizer is obtained via a user-supplied callback.
   166  //
   167  // By default, the token will be automatically refreshed through the Refresher interface.
   168  func (bacb *BearerAuthorizerCallback) WithAuthorization() PrepareDecorator {
   169  	return func(p Preparer) Preparer {
   170  		return PreparerFunc(func(r *http.Request) (*http.Request, error) {
   171  			r, err := p.Prepare(r)
   172  			if err == nil {
   173  				// make a copy of the request and remove the body as it's not
   174  				// required and avoids us having to create a copy of it.
   175  				rCopy := *r
   176  				removeRequestBody(&rCopy)
   177  
   178  				resp, err := bacb.sender.Do(&rCopy)
   179  				if err != nil {
   180  					return r, err
   181  				}
   182  				DrainResponseBody(resp)
   183  				if resp.StatusCode == 401 && hasBearerChallenge(resp.Header) {
   184  					bc, err := newBearerChallenge(resp.Header)
   185  					if err != nil {
   186  						return r, err
   187  					}
   188  					if bacb.callback != nil {
   189  						ba, err := bacb.callback(bc.values[tenantID], bc.values["resource"])
   190  						if err != nil {
   191  							return r, err
   192  						}
   193  						return Prepare(r, ba.WithAuthorization())
   194  					}
   195  				}
   196  			}
   197  			return r, err
   198  		})
   199  	}
   200  }
   201  
   202  // returns true if the HTTP response contains a bearer challenge
   203  func hasBearerChallenge(header http.Header) bool {
   204  	authHeader := header.Get(bearerChallengeHeader)
   205  	if len(authHeader) == 0 || strings.Index(authHeader, bearer) < 0 {
   206  		return false
   207  	}
   208  	return true
   209  }
   210  
   211  type bearerChallenge struct {
   212  	values map[string]string
   213  }
   214  
   215  func newBearerChallenge(header http.Header) (bc bearerChallenge, err error) {
   216  	challenge := strings.TrimSpace(header.Get(bearerChallengeHeader))
   217  	trimmedChallenge := challenge[len(bearer)+1:]
   218  
   219  	// challenge is a set of key=value pairs that are comma delimited
   220  	pairs := strings.Split(trimmedChallenge, ",")
   221  	if len(pairs) < 1 {
   222  		err = fmt.Errorf("challenge '%s' contains no pairs", challenge)
   223  		return bc, err
   224  	}
   225  
   226  	bc.values = make(map[string]string)
   227  	for i := range pairs {
   228  		trimmedPair := strings.TrimSpace(pairs[i])
   229  		pair := strings.Split(trimmedPair, "=")
   230  		if len(pair) == 2 {
   231  			// remove the enclosing quotes
   232  			key := strings.Trim(pair[0], "\"")
   233  			value := strings.Trim(pair[1], "\"")
   234  
   235  			switch key {
   236  			case "authorization", "authorization_uri":
   237  				// strip the tenant ID from the authorization URL
   238  				asURL, err := url.Parse(value)
   239  				if err != nil {
   240  					return bc, err
   241  				}
   242  				bc.values[tenantID] = asURL.Path[1:]
   243  			default:
   244  				bc.values[key] = value
   245  			}
   246  		}
   247  	}
   248  
   249  	return bc, err
   250  }
   251  
   252  // EventGridKeyAuthorizer implements authorization for event grid using key authentication.
   253  type EventGridKeyAuthorizer struct {
   254  	topicKey string
   255  }
   256  
   257  // NewEventGridKeyAuthorizer creates a new EventGridKeyAuthorizer
   258  // with the specified topic key.
   259  func NewEventGridKeyAuthorizer(topicKey string) EventGridKeyAuthorizer {
   260  	return EventGridKeyAuthorizer{topicKey: topicKey}
   261  }
   262  
   263  // WithAuthorization returns a PrepareDecorator that adds the aeg-sas-key authentication header.
   264  func (egta EventGridKeyAuthorizer) WithAuthorization() PrepareDecorator {
   265  	headers := map[string]interface{}{
   266  		"aeg-sas-key": egta.topicKey,
   267  	}
   268  	return NewAPIKeyAuthorizerWithHeaders(headers).WithAuthorization()
   269  }
   270  
   271  // BasicAuthorizer implements basic HTTP authorization by adding the Authorization HTTP header
   272  // with the value "Basic <TOKEN>" where <TOKEN> is a base64-encoded username:password tuple.
   273  type BasicAuthorizer struct {
   274  	userName string
   275  	password string
   276  }
   277  
   278  // NewBasicAuthorizer creates a new BasicAuthorizer with the specified username and password.
   279  func NewBasicAuthorizer(userName, password string) *BasicAuthorizer {
   280  	return &BasicAuthorizer{
   281  		userName: userName,
   282  		password: password,
   283  	}
   284  }
   285  
   286  // WithAuthorization returns a PrepareDecorator that adds an HTTP Authorization header whose
   287  // value is "Basic " followed by the base64-encoded username:password tuple.
   288  func (ba *BasicAuthorizer) WithAuthorization() PrepareDecorator {
   289  	headers := make(map[string]interface{})
   290  	headers[authorization] = basic + " " + base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", ba.userName, ba.password)))
   291  
   292  	return NewAPIKeyAuthorizerWithHeaders(headers).WithAuthorization()
   293  }
   294  
   295  // MultiTenantServicePrincipalTokenAuthorizer provides authentication across tenants.
   296  type MultiTenantServicePrincipalTokenAuthorizer interface {
   297  	WithAuthorization() PrepareDecorator
   298  }
   299  
   300  // NewMultiTenantServicePrincipalTokenAuthorizer crates a BearerAuthorizer using the given token provider
   301  func NewMultiTenantServicePrincipalTokenAuthorizer(tp adal.MultitenantOAuthTokenProvider) MultiTenantServicePrincipalTokenAuthorizer {
   302  	return NewMultiTenantBearerAuthorizer(tp)
   303  }
   304  
   305  // MultiTenantBearerAuthorizer implements bearer authorization across multiple tenants.
   306  type MultiTenantBearerAuthorizer struct {
   307  	tp adal.MultitenantOAuthTokenProvider
   308  }
   309  
   310  // NewMultiTenantBearerAuthorizer creates a MultiTenantBearerAuthorizer using the given token provider.
   311  func NewMultiTenantBearerAuthorizer(tp adal.MultitenantOAuthTokenProvider) *MultiTenantBearerAuthorizer {
   312  	return &MultiTenantBearerAuthorizer{tp: tp}
   313  }
   314  
   315  // WithAuthorization returns a PrepareDecorator that adds an HTTP Authorization header using the
   316  // primary token along with the auxiliary authorization header using the auxiliary tokens.
   317  //
   318  // By default, the token will be automatically refreshed through the Refresher interface.
   319  func (mt *MultiTenantBearerAuthorizer) WithAuthorization() PrepareDecorator {
   320  	return func(p Preparer) Preparer {
   321  		return PreparerFunc(func(r *http.Request) (*http.Request, error) {
   322  			r, err := p.Prepare(r)
   323  			if err != nil {
   324  				return r, err
   325  			}
   326  			if refresher, ok := mt.tp.(adal.RefresherWithContext); ok {
   327  				err = refresher.EnsureFreshWithContext(r.Context())
   328  				if err != nil {
   329  					var resp *http.Response
   330  					if tokError, ok := err.(adal.TokenRefreshError); ok {
   331  						resp = tokError.Response()
   332  					}
   333  					return r, NewErrorWithError(err, "azure.multiTenantSPTAuthorizer", "WithAuthorization", resp,
   334  						"Failed to refresh one or more Tokens for request to %s", r.URL)
   335  				}
   336  			}
   337  			r, err = Prepare(r, WithHeader(headerAuthorization, fmt.Sprintf("Bearer %s", mt.tp.PrimaryOAuthToken())))
   338  			if err != nil {
   339  				return r, err
   340  			}
   341  			auxTokens := mt.tp.AuxiliaryOAuthTokens()
   342  			for i := range auxTokens {
   343  				auxTokens[i] = fmt.Sprintf("Bearer %s", auxTokens[i])
   344  			}
   345  			return Prepare(r, WithHeader(headerAuxAuthorization, strings.Join(auxTokens, ", ")))
   346  		})
   347  	}
   348  }
   349  
   350  // TokenProvider returns the underlying MultitenantOAuthTokenProvider for this authorizer.
   351  func (mt *MultiTenantBearerAuthorizer) TokenProvider() adal.MultitenantOAuthTokenProvider {
   352  	return mt.tp
   353  }
   354  

View as plain text