Blame vendor/github.com/Azure/go-autorest/autorest/authorization.go

Packit 63bb0d
package autorest
Packit 63bb0d
Packit 63bb0d
// Copyright 2017 Microsoft Corporation
Packit 63bb0d
//
Packit 63bb0d
//  Licensed under the Apache License, Version 2.0 (the "License");
Packit 63bb0d
//  you may not use this file except in compliance with the License.
Packit 63bb0d
//  You may obtain a copy of the License at
Packit 63bb0d
//
Packit 63bb0d
//      http://www.apache.org/licenses/LICENSE-2.0
Packit 63bb0d
//
Packit 63bb0d
//  Unless required by applicable law or agreed to in writing, software
Packit 63bb0d
//  distributed under the License is distributed on an "AS IS" BASIS,
Packit 63bb0d
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Packit 63bb0d
//  See the License for the specific language governing permissions and
Packit 63bb0d
//  limitations under the License.
Packit 63bb0d
Packit 63bb0d
import (
Packit 63bb0d
	"crypto/tls"
Packit 63bb0d
	"encoding/base64"
Packit 63bb0d
	"fmt"
Packit 63bb0d
	"net/http"
Packit 63bb0d
	"net/url"
Packit 63bb0d
	"strings"
Packit 63bb0d
Packit 63bb0d
	"github.com/Azure/go-autorest/autorest/adal"
Packit 63bb0d
)
Packit 63bb0d
Packit 63bb0d
const (
Packit 63bb0d
	bearerChallengeHeader       = "Www-Authenticate"
Packit 63bb0d
	bearer                      = "Bearer"
Packit 63bb0d
	tenantID                    = "tenantID"
Packit 63bb0d
	apiKeyAuthorizerHeader      = "Ocp-Apim-Subscription-Key"
Packit 63bb0d
	bingAPISdkHeader            = "X-BingApis-SDK-Client"
Packit 63bb0d
	golangBingAPISdkHeaderValue = "Go-SDK"
Packit 63bb0d
	authorization               = "Authorization"
Packit 63bb0d
	basic                       = "Basic"
Packit 63bb0d
)
Packit 63bb0d
Packit 63bb0d
// Authorizer is the interface that provides a PrepareDecorator used to supply request
Packit 63bb0d
// authorization. Most often, the Authorizer decorator runs last so it has access to the full
Packit 63bb0d
// state of the formed HTTP request.
Packit 63bb0d
type Authorizer interface {
Packit 63bb0d
	WithAuthorization() PrepareDecorator
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// NullAuthorizer implements a default, "do nothing" Authorizer.
Packit 63bb0d
type NullAuthorizer struct{}
Packit 63bb0d
Packit 63bb0d
// WithAuthorization returns a PrepareDecorator that does nothing.
Packit 63bb0d
func (na NullAuthorizer) WithAuthorization() PrepareDecorator {
Packit 63bb0d
	return WithNothing()
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// APIKeyAuthorizer implements API Key authorization.
Packit 63bb0d
type APIKeyAuthorizer struct {
Packit 63bb0d
	headers         map[string]interface{}
Packit 63bb0d
	queryParameters map[string]interface{}
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// NewAPIKeyAuthorizerWithHeaders creates an ApiKeyAuthorizer with headers.
Packit 63bb0d
func NewAPIKeyAuthorizerWithHeaders(headers map[string]interface{}) *APIKeyAuthorizer {
Packit 63bb0d
	return NewAPIKeyAuthorizer(headers, nil)
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// NewAPIKeyAuthorizerWithQueryParameters creates an ApiKeyAuthorizer with query parameters.
Packit 63bb0d
func NewAPIKeyAuthorizerWithQueryParameters(queryParameters map[string]interface{}) *APIKeyAuthorizer {
Packit 63bb0d
	return NewAPIKeyAuthorizer(nil, queryParameters)
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// NewAPIKeyAuthorizer creates an ApiKeyAuthorizer with headers.
Packit 63bb0d
func NewAPIKeyAuthorizer(headers map[string]interface{}, queryParameters map[string]interface{}) *APIKeyAuthorizer {
Packit 63bb0d
	return &APIKeyAuthorizer{headers: headers, queryParameters: queryParameters}
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// WithAuthorization returns a PrepareDecorator that adds an HTTP headers and Query Parameters.
Packit 63bb0d
func (aka *APIKeyAuthorizer) WithAuthorization() PrepareDecorator {
Packit 63bb0d
	return func(p Preparer) Preparer {
Packit 63bb0d
		return DecoratePreparer(p, WithHeaders(aka.headers), WithQueryParameters(aka.queryParameters))
Packit 63bb0d
	}
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// CognitiveServicesAuthorizer implements authorization for Cognitive Services.
Packit 63bb0d
type CognitiveServicesAuthorizer struct {
Packit 63bb0d
	subscriptionKey string
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// NewCognitiveServicesAuthorizer is
Packit 63bb0d
func NewCognitiveServicesAuthorizer(subscriptionKey string) *CognitiveServicesAuthorizer {
Packit 63bb0d
	return &CognitiveServicesAuthorizer{subscriptionKey: subscriptionKey}
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// WithAuthorization is
Packit 63bb0d
func (csa *CognitiveServicesAuthorizer) WithAuthorization() PrepareDecorator {
Packit 63bb0d
	headers := make(map[string]interface{})
Packit 63bb0d
	headers[apiKeyAuthorizerHeader] = csa.subscriptionKey
Packit 63bb0d
	headers[bingAPISdkHeader] = golangBingAPISdkHeaderValue
Packit 63bb0d
Packit 63bb0d
	return NewAPIKeyAuthorizerWithHeaders(headers).WithAuthorization()
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// BearerAuthorizer implements the bearer authorization
Packit 63bb0d
type BearerAuthorizer struct {
Packit 63bb0d
	tokenProvider adal.OAuthTokenProvider
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// NewBearerAuthorizer crates a BearerAuthorizer using the given token provider
Packit 63bb0d
func NewBearerAuthorizer(tp adal.OAuthTokenProvider) *BearerAuthorizer {
Packit 63bb0d
	return &BearerAuthorizer{tokenProvider: tp}
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// WithAuthorization returns a PrepareDecorator that adds an HTTP Authorization header whose
Packit 63bb0d
// value is "Bearer " followed by the token.
Packit 63bb0d
//
Packit 63bb0d
// By default, the token will be automatically refreshed through the Refresher interface.
Packit 63bb0d
func (ba *BearerAuthorizer) WithAuthorization() PrepareDecorator {
Packit 63bb0d
	return func(p Preparer) Preparer {
Packit 63bb0d
		return PreparerFunc(func(r *http.Request) (*http.Request, error) {
Packit 63bb0d
			r, err := p.Prepare(r)
Packit 63bb0d
			if err == nil {
Packit 63bb0d
				// the ordering is important here, prefer RefresherWithContext if available
Packit 63bb0d
				if refresher, ok := ba.tokenProvider.(adal.RefresherWithContext); ok {
Packit 63bb0d
					err = refresher.EnsureFreshWithContext(r.Context())
Packit 63bb0d
				} else if refresher, ok := ba.tokenProvider.(adal.Refresher); ok {
Packit 63bb0d
					err = refresher.EnsureFresh()
Packit 63bb0d
				}
Packit 63bb0d
				if err != nil {
Packit 63bb0d
					var resp *http.Response
Packit 63bb0d
					if tokError, ok := err.(adal.TokenRefreshError); ok {
Packit 63bb0d
						resp = tokError.Response()
Packit 63bb0d
					}
Packit 63bb0d
					return r, NewErrorWithError(err, "azure.BearerAuthorizer", "WithAuthorization", resp,
Packit 63bb0d
						"Failed to refresh the Token for request to %s", r.URL)
Packit 63bb0d
				}
Packit 63bb0d
				return Prepare(r, WithHeader(headerAuthorization, fmt.Sprintf("Bearer %s", ba.tokenProvider.OAuthToken())))
Packit 63bb0d
			}
Packit 63bb0d
			return r, err
Packit 63bb0d
		})
Packit 63bb0d
	}
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// BearerAuthorizerCallbackFunc is the authentication callback signature.
Packit 63bb0d
type BearerAuthorizerCallbackFunc func(tenantID, resource string) (*BearerAuthorizer, error)
Packit 63bb0d
Packit 63bb0d
// BearerAuthorizerCallback implements bearer authorization via a callback.
Packit 63bb0d
type BearerAuthorizerCallback struct {
Packit 63bb0d
	sender   Sender
Packit 63bb0d
	callback BearerAuthorizerCallbackFunc
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// NewBearerAuthorizerCallback creates a bearer authorization callback.  The callback
Packit 63bb0d
// is invoked when the HTTP request is submitted.
Packit 63bb0d
func NewBearerAuthorizerCallback(s Sender, callback BearerAuthorizerCallbackFunc) *BearerAuthorizerCallback {
Packit 63bb0d
	if s == nil {
Packit 63bb0d
		s = sender(tls.RenegotiateNever)
Packit 63bb0d
	}
Packit 63bb0d
	return &BearerAuthorizerCallback{sender: s, callback: callback}
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// WithAuthorization returns a PrepareDecorator that adds an HTTP Authorization header whose value
Packit 63bb0d
// is "Bearer " followed by the token.  The BearerAuthorizer is obtained via a user-supplied callback.
Packit 63bb0d
//
Packit 63bb0d
// By default, the token will be automatically refreshed through the Refresher interface.
Packit 63bb0d
func (bacb *BearerAuthorizerCallback) WithAuthorization() PrepareDecorator {
Packit 63bb0d
	return func(p Preparer) Preparer {
Packit 63bb0d
		return PreparerFunc(func(r *http.Request) (*http.Request, error) {
Packit 63bb0d
			r, err := p.Prepare(r)
Packit 63bb0d
			if err == nil {
Packit 63bb0d
				// make a copy of the request and remove the body as it's not
Packit 63bb0d
				// required and avoids us having to create a copy of it.
Packit 63bb0d
				rCopy := *r
Packit 63bb0d
				removeRequestBody(&rCopy)
Packit 63bb0d
Packit 63bb0d
				resp, err := bacb.sender.Do(&rCopy)
Packit 63bb0d
				if err != nil {
Packit 63bb0d
					return r, err
Packit 63bb0d
				}
Packit 63bb0d
				DrainResponseBody(resp)
Packit 63bb0d
				if resp.StatusCode == 401 && hasBearerChallenge(resp.Header) {
Packit 63bb0d
					bc, err := newBearerChallenge(resp.Header)
Packit 63bb0d
					if err != nil {
Packit 63bb0d
						return r, err
Packit 63bb0d
					}
Packit 63bb0d
					if bacb.callback != nil {
Packit 63bb0d
						ba, err := bacb.callback(bc.values[tenantID], bc.values["resource"])
Packit 63bb0d
						if err != nil {
Packit 63bb0d
							return r, err
Packit 63bb0d
						}
Packit 63bb0d
						return Prepare(r, ba.WithAuthorization())
Packit 63bb0d
					}
Packit 63bb0d
				}
Packit 63bb0d
			}
Packit 63bb0d
			return r, err
Packit 63bb0d
		})
Packit 63bb0d
	}
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// returns true if the HTTP response contains a bearer challenge
Packit 63bb0d
func hasBearerChallenge(header http.Header) bool {
Packit 63bb0d
	authHeader := header.Get(bearerChallengeHeader)
Packit 63bb0d
	if len(authHeader) == 0 || strings.Index(authHeader, bearer) < 0 {
Packit 63bb0d
		return false
Packit 63bb0d
	}
Packit 63bb0d
	return true
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
type bearerChallenge struct {
Packit 63bb0d
	values map[string]string
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
func newBearerChallenge(header http.Header) (bc bearerChallenge, err error) {
Packit 63bb0d
	challenge := strings.TrimSpace(header.Get(bearerChallengeHeader))
Packit 63bb0d
	trimmedChallenge := challenge[len(bearer)+1:]
Packit 63bb0d
Packit 63bb0d
	// challenge is a set of key=value pairs that are comma delimited
Packit 63bb0d
	pairs := strings.Split(trimmedChallenge, ",")
Packit 63bb0d
	if len(pairs) < 1 {
Packit 63bb0d
		err = fmt.Errorf("challenge '%s' contains no pairs", challenge)
Packit 63bb0d
		return bc, err
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	bc.values = make(map[string]string)
Packit 63bb0d
	for i := range pairs {
Packit 63bb0d
		trimmedPair := strings.TrimSpace(pairs[i])
Packit 63bb0d
		pair := strings.Split(trimmedPair, "=")
Packit 63bb0d
		if len(pair) == 2 {
Packit 63bb0d
			// remove the enclosing quotes
Packit 63bb0d
			key := strings.Trim(pair[0], "\"")
Packit 63bb0d
			value := strings.Trim(pair[1], "\"")
Packit 63bb0d
Packit 63bb0d
			switch key {
Packit 63bb0d
			case "authorization", "authorization_uri":
Packit 63bb0d
				// strip the tenant ID from the authorization URL
Packit 63bb0d
				asURL, err := url.Parse(value)
Packit 63bb0d
				if err != nil {
Packit 63bb0d
					return bc, err
Packit 63bb0d
				}
Packit 63bb0d
				bc.values[tenantID] = asURL.Path[1:]
Packit 63bb0d
			default:
Packit 63bb0d
				bc.values[key] = value
Packit 63bb0d
			}
Packit 63bb0d
		}
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	return bc, err
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// EventGridKeyAuthorizer implements authorization for event grid using key authentication.
Packit 63bb0d
type EventGridKeyAuthorizer struct {
Packit 63bb0d
	topicKey string
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// NewEventGridKeyAuthorizer creates a new EventGridKeyAuthorizer
Packit 63bb0d
// with the specified topic key.
Packit 63bb0d
func NewEventGridKeyAuthorizer(topicKey string) EventGridKeyAuthorizer {
Packit 63bb0d
	return EventGridKeyAuthorizer{topicKey: topicKey}
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// WithAuthorization returns a PrepareDecorator that adds the aeg-sas-key authentication header.
Packit 63bb0d
func (egta EventGridKeyAuthorizer) WithAuthorization() PrepareDecorator {
Packit 63bb0d
	headers := map[string]interface{}{
Packit 63bb0d
		"aeg-sas-key": egta.topicKey,
Packit 63bb0d
	}
Packit 63bb0d
	return NewAPIKeyAuthorizerWithHeaders(headers).WithAuthorization()
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// BasicAuthorizer implements basic HTTP authorization by adding the Authorization HTTP header
Packit 63bb0d
// with the value "Basic <TOKEN>" where <TOKEN> is a base64-encoded username:password tuple.
Packit 63bb0d
type BasicAuthorizer struct {
Packit 63bb0d
	userName string
Packit 63bb0d
	password string
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// NewBasicAuthorizer creates a new BasicAuthorizer with the specified username and password.
Packit 63bb0d
func NewBasicAuthorizer(userName, password string) *BasicAuthorizer {
Packit 63bb0d
	return &BasicAuthorizer{
Packit 63bb0d
		userName: userName,
Packit 63bb0d
		password: password,
Packit 63bb0d
	}
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// WithAuthorization returns a PrepareDecorator that adds an HTTP Authorization header whose
Packit 63bb0d
// value is "Basic " followed by the base64-encoded username:password tuple.
Packit 63bb0d
func (ba *BasicAuthorizer) WithAuthorization() PrepareDecorator {
Packit 63bb0d
	headers := make(map[string]interface{})
Packit 63bb0d
	headers[authorization] = basic + " " + base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", ba.userName, ba.password)))
Packit 63bb0d
Packit 63bb0d
	return NewAPIKeyAuthorizerWithHeaders(headers).WithAuthorization()
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// MultiTenantServicePrincipalTokenAuthorizer provides authentication across tenants.
Packit 63bb0d
type MultiTenantServicePrincipalTokenAuthorizer interface {
Packit 63bb0d
	WithAuthorization() PrepareDecorator
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// NewMultiTenantServicePrincipalTokenAuthorizer crates a BearerAuthorizer using the given token provider
Packit 63bb0d
func NewMultiTenantServicePrincipalTokenAuthorizer(tp adal.MultitenantOAuthTokenProvider) MultiTenantServicePrincipalTokenAuthorizer {
Packit 63bb0d
	return &multiTenantSPTAuthorizer{tp: tp}
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
type multiTenantSPTAuthorizer struct {
Packit 63bb0d
	tp adal.MultitenantOAuthTokenProvider
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// WithAuthorization returns a PrepareDecorator that adds an HTTP Authorization header using the
Packit 63bb0d
// primary token along with the auxiliary authorization header using the auxiliary tokens.
Packit 63bb0d
//
Packit 63bb0d
// By default, the token will be automatically refreshed through the Refresher interface.
Packit 63bb0d
func (mt multiTenantSPTAuthorizer) WithAuthorization() PrepareDecorator {
Packit 63bb0d
	return func(p Preparer) Preparer {
Packit 63bb0d
		return PreparerFunc(func(r *http.Request) (*http.Request, error) {
Packit 63bb0d
			r, err := p.Prepare(r)
Packit 63bb0d
			if err != nil {
Packit 63bb0d
				return r, err
Packit 63bb0d
			}
Packit 63bb0d
			if refresher, ok := mt.tp.(adal.RefresherWithContext); ok {
Packit 63bb0d
				err = refresher.EnsureFreshWithContext(r.Context())
Packit 63bb0d
				if err != nil {
Packit 63bb0d
					var resp *http.Response
Packit 63bb0d
					if tokError, ok := err.(adal.TokenRefreshError); ok {
Packit 63bb0d
						resp = tokError.Response()
Packit 63bb0d
					}
Packit 63bb0d
					return r, NewErrorWithError(err, "azure.multiTenantSPTAuthorizer", "WithAuthorization", resp,
Packit 63bb0d
						"Failed to refresh one or more Tokens for request to %s", r.URL)
Packit 63bb0d
				}
Packit 63bb0d
			}
Packit 63bb0d
			r, err = Prepare(r, WithHeader(headerAuthorization, fmt.Sprintf("Bearer %s", mt.tp.PrimaryOAuthToken())))
Packit 63bb0d
			if err != nil {
Packit 63bb0d
				return r, err
Packit 63bb0d
			}
Packit 63bb0d
			auxTokens := mt.tp.AuxiliaryOAuthTokens()
Packit 63bb0d
			for i := range auxTokens {
Packit 63bb0d
				auxTokens[i] = fmt.Sprintf("Bearer %s", auxTokens[i])
Packit 63bb0d
			}
Packit 63bb0d
			return Prepare(r, WithHeader(headerAuxAuthorization, strings.Join(auxTokens, "; ")))
Packit 63bb0d
		})
Packit 63bb0d
	}
Packit 63bb0d
}