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	"fmt"
19	"net/http"
20	"net/url"
21	"strings"
22
23	"github.com/Azure/go-autorest/autorest/adal"
24)
25
26const (
27	bearerChallengeHeader       = "Www-Authenticate"
28	bearer                      = "Bearer"
29	tenantID                    = "tenantID"
30	apiKeyAuthorizerHeader      = "Ocp-Apim-Subscription-Key"
31	bingAPISdkHeader            = "X-BingApis-SDK-Client"
32	golangBingAPISdkHeaderValue = "Go-SDK"
33)
34
35// Authorizer is the interface that provides a PrepareDecorator used to supply request
36// authorization. Most often, the Authorizer decorator runs last so it has access to the full
37// state of the formed HTTP request.
38type Authorizer interface {
39	WithAuthorization() PrepareDecorator
40}
41
42// NullAuthorizer implements a default, "do nothing" Authorizer.
43type NullAuthorizer struct{}
44
45// WithAuthorization returns a PrepareDecorator that does nothing.
46func (na NullAuthorizer) WithAuthorization() PrepareDecorator {
47	return WithNothing()
48}
49
50// APIKeyAuthorizer implements API Key authorization.
51type APIKeyAuthorizer struct {
52	headers         map[string]interface{}
53	queryParameters map[string]interface{}
54}
55
56// NewAPIKeyAuthorizerWithHeaders creates an ApiKeyAuthorizer with headers.
57func NewAPIKeyAuthorizerWithHeaders(headers map[string]interface{}) *APIKeyAuthorizer {
58	return NewAPIKeyAuthorizer(headers, nil)
59}
60
61// NewAPIKeyAuthorizerWithQueryParameters creates an ApiKeyAuthorizer with query parameters.
62func NewAPIKeyAuthorizerWithQueryParameters(queryParameters map[string]interface{}) *APIKeyAuthorizer {
63	return NewAPIKeyAuthorizer(nil, queryParameters)
64}
65
66// NewAPIKeyAuthorizer creates an ApiKeyAuthorizer with headers.
67func NewAPIKeyAuthorizer(headers map[string]interface{}, queryParameters map[string]interface{}) *APIKeyAuthorizer {
68	return &APIKeyAuthorizer{headers: headers, queryParameters: queryParameters}
69}
70
71// WithAuthorization returns a PrepareDecorator that adds an HTTP headers and Query Paramaters
72func (aka *APIKeyAuthorizer) WithAuthorization() PrepareDecorator {
73	return func(p Preparer) Preparer {
74		return DecoratePreparer(p, WithHeaders(aka.headers), WithQueryParameters(aka.queryParameters))
75	}
76}
77
78// CognitiveServicesAuthorizer implements authorization for Cognitive Services.
79type CognitiveServicesAuthorizer struct {
80	subscriptionKey string
81}
82
83// NewCognitiveServicesAuthorizer is
84func NewCognitiveServicesAuthorizer(subscriptionKey string) *CognitiveServicesAuthorizer {
85	return &CognitiveServicesAuthorizer{subscriptionKey: subscriptionKey}
86}
87
88// WithAuthorization is
89func (csa *CognitiveServicesAuthorizer) WithAuthorization() PrepareDecorator {
90	headers := make(map[string]interface{})
91	headers[apiKeyAuthorizerHeader] = csa.subscriptionKey
92	headers[bingAPISdkHeader] = golangBingAPISdkHeaderValue
93
94	return NewAPIKeyAuthorizerWithHeaders(headers).WithAuthorization()
95}
96
97// BearerAuthorizer implements the bearer authorization
98type BearerAuthorizer struct {
99	tokenProvider adal.OAuthTokenProvider
100}
101
102// NewBearerAuthorizer crates a BearerAuthorizer using the given token provider
103func NewBearerAuthorizer(tp adal.OAuthTokenProvider) *BearerAuthorizer {
104	return &BearerAuthorizer{tokenProvider: tp}
105}
106
107// WithAuthorization returns a PrepareDecorator that adds an HTTP Authorization header whose
108// value is "Bearer " followed by the token.
109//
110// By default, the token will be automatically refreshed through the Refresher interface.
111func (ba *BearerAuthorizer) WithAuthorization() PrepareDecorator {
112	return func(p Preparer) Preparer {
113		return PreparerFunc(func(r *http.Request) (*http.Request, error) {
114			r, err := p.Prepare(r)
115			if err == nil {
116				// the ordering is important here, prefer RefresherWithContext if available
117				if refresher, ok := ba.tokenProvider.(adal.RefresherWithContext); ok {
118					err = refresher.EnsureFreshWithContext(r.Context())
119				} else if refresher, ok := ba.tokenProvider.(adal.Refresher); ok {
120					err = refresher.EnsureFresh()
121				}
122				if err != nil {
123					var resp *http.Response
124					if tokError, ok := err.(adal.TokenRefreshError); ok {
125						resp = tokError.Response()
126					}
127					return r, NewErrorWithError(err, "azure.BearerAuthorizer", "WithAuthorization", resp,
128						"Failed to refresh the Token for request to %s", r.URL)
129				}
130				return Prepare(r, WithHeader(headerAuthorization, fmt.Sprintf("Bearer %s", ba.tokenProvider.OAuthToken())))
131			}
132			return r, err
133		})
134	}
135}
136
137// BearerAuthorizerCallbackFunc is the authentication callback signature.
138type BearerAuthorizerCallbackFunc func(tenantID, resource string) (*BearerAuthorizer, error)
139
140// BearerAuthorizerCallback implements bearer authorization via a callback.
141type BearerAuthorizerCallback struct {
142	sender   Sender
143	callback BearerAuthorizerCallbackFunc
144}
145
146// NewBearerAuthorizerCallback creates a bearer authorization callback.  The callback
147// is invoked when the HTTP request is submitted.
148func NewBearerAuthorizerCallback(sender Sender, callback BearerAuthorizerCallbackFunc) *BearerAuthorizerCallback {
149	if sender == nil {
150		sender = &http.Client{}
151	}
152	return &BearerAuthorizerCallback{sender: sender, callback: callback}
153}
154
155// WithAuthorization returns a PrepareDecorator that adds an HTTP Authorization header whose value
156// is "Bearer " followed by the token.  The BearerAuthorizer is obtained via a user-supplied callback.
157//
158// By default, the token will be automatically refreshed through the Refresher interface.
159func (bacb *BearerAuthorizerCallback) WithAuthorization() PrepareDecorator {
160	return func(p Preparer) Preparer {
161		return PreparerFunc(func(r *http.Request) (*http.Request, error) {
162			r, err := p.Prepare(r)
163			if err == nil {
164				// make a copy of the request and remove the body as it's not
165				// required and avoids us having to create a copy of it.
166				rCopy := *r
167				removeRequestBody(&rCopy)
168
169				resp, err := bacb.sender.Do(&rCopy)
170				if err == nil && resp.StatusCode == 401 {
171					defer resp.Body.Close()
172					if hasBearerChallenge(resp) {
173						bc, err := newBearerChallenge(resp)
174						if err != nil {
175							return r, err
176						}
177						if bacb.callback != nil {
178							ba, err := bacb.callback(bc.values[tenantID], bc.values["resource"])
179							if err != nil {
180								return r, err
181							}
182							return Prepare(r, ba.WithAuthorization())
183						}
184					}
185				}
186			}
187			return r, err
188		})
189	}
190}
191
192// returns true if the HTTP response contains a bearer challenge
193func hasBearerChallenge(resp *http.Response) bool {
194	authHeader := resp.Header.Get(bearerChallengeHeader)
195	if len(authHeader) == 0 || strings.Index(authHeader, bearer) < 0 {
196		return false
197	}
198	return true
199}
200
201type bearerChallenge struct {
202	values map[string]string
203}
204
205func newBearerChallenge(resp *http.Response) (bc bearerChallenge, err error) {
206	challenge := strings.TrimSpace(resp.Header.Get(bearerChallengeHeader))
207	trimmedChallenge := challenge[len(bearer)+1:]
208
209	// challenge is a set of key=value pairs that are comma delimited
210	pairs := strings.Split(trimmedChallenge, ",")
211	if len(pairs) < 1 {
212		err = fmt.Errorf("challenge '%s' contains no pairs", challenge)
213		return bc, err
214	}
215
216	bc.values = make(map[string]string)
217	for i := range pairs {
218		trimmedPair := strings.TrimSpace(pairs[i])
219		pair := strings.Split(trimmedPair, "=")
220		if len(pair) == 2 {
221			// remove the enclosing quotes
222			key := strings.Trim(pair[0], "\"")
223			value := strings.Trim(pair[1], "\"")
224
225			switch key {
226			case "authorization", "authorization_uri":
227				// strip the tenant ID from the authorization URL
228				asURL, err := url.Parse(value)
229				if err != nil {
230					return bc, err
231				}
232				bc.values[tenantID] = asURL.Path[1:]
233			default:
234				bc.values[key] = value
235			}
236		}
237	}
238
239	return bc, err
240}
241
242// EventGridKeyAuthorizer implements authorization for event grid using key authentication.
243type EventGridKeyAuthorizer struct {
244	topicKey string
245}
246
247// NewEventGridKeyAuthorizer creates a new EventGridKeyAuthorizer
248// with the specified topic key.
249func NewEventGridKeyAuthorizer(topicKey string) EventGridKeyAuthorizer {
250	return EventGridKeyAuthorizer{topicKey: topicKey}
251}
252
253// WithAuthorization returns a PrepareDecorator that adds the aeg-sas-key authentication header.
254func (egta EventGridKeyAuthorizer) WithAuthorization() PrepareDecorator {
255	headers := map[string]interface{}{
256		"aeg-sas-key": egta.topicKey,
257	}
258	return NewAPIKeyAuthorizerWithHeaders(headers).WithAuthorization()
259}
260