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 &multiTenantSPTAuthorizer{tp: tp}
303}
304
305type multiTenantSPTAuthorizer struct {
306	tp adal.MultitenantOAuthTokenProvider
307}
308
309// WithAuthorization returns a PrepareDecorator that adds an HTTP Authorization header using the
310// primary token along with the auxiliary authorization header using the auxiliary tokens.
311//
312// By default, the token will be automatically refreshed through the Refresher interface.
313func (mt multiTenantSPTAuthorizer) WithAuthorization() PrepareDecorator {
314	return func(p Preparer) Preparer {
315		return PreparerFunc(func(r *http.Request) (*http.Request, error) {
316			r, err := p.Prepare(r)
317			if err != nil {
318				return r, err
319			}
320			if refresher, ok := mt.tp.(adal.RefresherWithContext); ok {
321				err = refresher.EnsureFreshWithContext(r.Context())
322				if err != nil {
323					var resp *http.Response
324					if tokError, ok := err.(adal.TokenRefreshError); ok {
325						resp = tokError.Response()
326					}
327					return r, NewErrorWithError(err, "azure.multiTenantSPTAuthorizer", "WithAuthorization", resp,
328						"Failed to refresh one or more Tokens for request to %s", r.URL)
329				}
330			}
331			r, err = Prepare(r, WithHeader(headerAuthorization, fmt.Sprintf("Bearer %s", mt.tp.PrimaryOAuthToken())))
332			if err != nil {
333				return r, err
334			}
335			auxTokens := mt.tp.AuxiliaryOAuthTokens()
336			for i := range auxTokens {
337				auxTokens[i] = fmt.Sprintf("Bearer %s", auxTokens[i])
338			}
339			return Prepare(r, WithHeader(headerAuxAuthorization, strings.Join(auxTokens, ", ")))
340		})
341	}
342}
343