1package 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 17import ( 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 28const ( 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. 42type Authorizer interface { 43 WithAuthorization() PrepareDecorator 44} 45 46// NullAuthorizer implements a default, "do nothing" Authorizer. 47type NullAuthorizer struct{} 48 49// WithAuthorization returns a PrepareDecorator that does nothing. 50func (na NullAuthorizer) WithAuthorization() PrepareDecorator { 51 return WithNothing() 52} 53 54// APIKeyAuthorizer implements API Key authorization. 55type APIKeyAuthorizer struct { 56 headers map[string]interface{} 57 queryParameters map[string]interface{} 58} 59 60// NewAPIKeyAuthorizerWithHeaders creates an ApiKeyAuthorizer with headers. 61func NewAPIKeyAuthorizerWithHeaders(headers map[string]interface{}) *APIKeyAuthorizer { 62 return NewAPIKeyAuthorizer(headers, nil) 63} 64 65// NewAPIKeyAuthorizerWithQueryParameters creates an ApiKeyAuthorizer with query parameters. 66func NewAPIKeyAuthorizerWithQueryParameters(queryParameters map[string]interface{}) *APIKeyAuthorizer { 67 return NewAPIKeyAuthorizer(nil, queryParameters) 68} 69 70// NewAPIKeyAuthorizer creates an ApiKeyAuthorizer with headers. 71func 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. 76func (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. 83type CognitiveServicesAuthorizer struct { 84 subscriptionKey string 85} 86 87// NewCognitiveServicesAuthorizer is 88func NewCognitiveServicesAuthorizer(subscriptionKey string) *CognitiveServicesAuthorizer { 89 return &CognitiveServicesAuthorizer{subscriptionKey: subscriptionKey} 90} 91 92// WithAuthorization is 93func (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 102type BearerAuthorizer struct { 103 tokenProvider adal.OAuthTokenProvider 104} 105 106// NewBearerAuthorizer crates a BearerAuthorizer using the given token provider 107func 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. 115func (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. 142func (ba *BearerAuthorizer) TokenProvider() adal.OAuthTokenProvider { 143 return ba.tokenProvider 144} 145 146// BearerAuthorizerCallbackFunc is the authentication callback signature. 147type BearerAuthorizerCallbackFunc func(tenantID, resource string) (*BearerAuthorizer, error) 148 149// BearerAuthorizerCallback implements bearer authorization via a callback. 150type 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. 157func 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. 168func (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 203func 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 211type bearerChallenge struct { 212 values map[string]string 213} 214 215func 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. 253type EventGridKeyAuthorizer struct { 254 topicKey string 255} 256 257// NewEventGridKeyAuthorizer creates a new EventGridKeyAuthorizer 258// with the specified topic key. 259func NewEventGridKeyAuthorizer(topicKey string) EventGridKeyAuthorizer { 260 return EventGridKeyAuthorizer{topicKey: topicKey} 261} 262 263// WithAuthorization returns a PrepareDecorator that adds the aeg-sas-key authentication header. 264func (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. 273type BasicAuthorizer struct { 274 userName string 275 password string 276} 277 278// NewBasicAuthorizer creates a new BasicAuthorizer with the specified username and password. 279func 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. 288func (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. 296type MultiTenantServicePrincipalTokenAuthorizer interface { 297 WithAuthorization() PrepareDecorator 298} 299 300// NewMultiTenantServicePrincipalTokenAuthorizer crates a BearerAuthorizer using the given token provider 301func NewMultiTenantServicePrincipalTokenAuthorizer(tp adal.MultitenantOAuthTokenProvider) MultiTenantServicePrincipalTokenAuthorizer { 302 return NewMultiTenantBearerAuthorizer(tp) 303} 304 305// MultiTenantBearerAuthorizer implements bearer authorization across multiple tenants. 306type MultiTenantBearerAuthorizer struct { 307 tp adal.MultitenantOAuthTokenProvider 308} 309 310// NewMultiTenantBearerAuthorizer creates a MultiTenantBearerAuthorizer using the given token provider. 311func 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. 319func (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. 351func (mt *MultiTenantBearerAuthorizer) TokenProvider() adal.MultitenantOAuthTokenProvider { 352 return mt.tp 353} 354