authorization.go raw

   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