Code refactoring for bpa operator
[icn.git] / cmd / bpa-operator / vendor / github.com / Azure / go-autorest / autorest / authorization.go
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         "encoding/base64"
19         "fmt"
20         "net/http"
21         "net/url"
22         "strings"
23
24         "github.com/Azure/go-autorest/autorest/adal"
25         "github.com/Azure/go-autorest/tracing"
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 // BearerAuthorizerCallbackFunc is the authentication callback signature.
142 type BearerAuthorizerCallbackFunc func(tenantID, resource string) (*BearerAuthorizer, error)
143
144 // BearerAuthorizerCallback implements bearer authorization via a callback.
145 type BearerAuthorizerCallback struct {
146         sender   Sender
147         callback BearerAuthorizerCallbackFunc
148 }
149
150 // NewBearerAuthorizerCallback creates a bearer authorization callback.  The callback
151 // is invoked when the HTTP request is submitted.
152 func NewBearerAuthorizerCallback(sender Sender, callback BearerAuthorizerCallbackFunc) *BearerAuthorizerCallback {
153         if sender == nil {
154                 sender = &http.Client{Transport: tracing.Transport}
155         }
156         return &BearerAuthorizerCallback{sender: sender, callback: callback}
157 }
158
159 // WithAuthorization returns a PrepareDecorator that adds an HTTP Authorization header whose value
160 // is "Bearer " followed by the token.  The BearerAuthorizer is obtained via a user-supplied callback.
161 //
162 // By default, the token will be automatically refreshed through the Refresher interface.
163 func (bacb *BearerAuthorizerCallback) WithAuthorization() PrepareDecorator {
164         return func(p Preparer) Preparer {
165                 return PreparerFunc(func(r *http.Request) (*http.Request, error) {
166                         r, err := p.Prepare(r)
167                         if err == nil {
168                                 // make a copy of the request and remove the body as it's not
169                                 // required and avoids us having to create a copy of it.
170                                 rCopy := *r
171                                 removeRequestBody(&rCopy)
172
173                                 resp, err := bacb.sender.Do(&rCopy)
174                                 if err == nil && resp.StatusCode == 401 {
175                                         defer resp.Body.Close()
176                                         if hasBearerChallenge(resp) {
177                                                 bc, err := newBearerChallenge(resp)
178                                                 if err != nil {
179                                                         return r, err
180                                                 }
181                                                 if bacb.callback != nil {
182                                                         ba, err := bacb.callback(bc.values[tenantID], bc.values["resource"])
183                                                         if err != nil {
184                                                                 return r, err
185                                                         }
186                                                         return Prepare(r, ba.WithAuthorization())
187                                                 }
188                                         }
189                                 }
190                         }
191                         return r, err
192                 })
193         }
194 }
195
196 // returns true if the HTTP response contains a bearer challenge
197 func hasBearerChallenge(resp *http.Response) bool {
198         authHeader := resp.Header.Get(bearerChallengeHeader)
199         if len(authHeader) == 0 || strings.Index(authHeader, bearer) < 0 {
200                 return false
201         }
202         return true
203 }
204
205 type bearerChallenge struct {
206         values map[string]string
207 }
208
209 func newBearerChallenge(resp *http.Response) (bc bearerChallenge, err error) {
210         challenge := strings.TrimSpace(resp.Header.Get(bearerChallengeHeader))
211         trimmedChallenge := challenge[len(bearer)+1:]
212
213         // challenge is a set of key=value pairs that are comma delimited
214         pairs := strings.Split(trimmedChallenge, ",")
215         if len(pairs) < 1 {
216                 err = fmt.Errorf("challenge '%s' contains no pairs", challenge)
217                 return bc, err
218         }
219
220         bc.values = make(map[string]string)
221         for i := range pairs {
222                 trimmedPair := strings.TrimSpace(pairs[i])
223                 pair := strings.Split(trimmedPair, "=")
224                 if len(pair) == 2 {
225                         // remove the enclosing quotes
226                         key := strings.Trim(pair[0], "\"")
227                         value := strings.Trim(pair[1], "\"")
228
229                         switch key {
230                         case "authorization", "authorization_uri":
231                                 // strip the tenant ID from the authorization URL
232                                 asURL, err := url.Parse(value)
233                                 if err != nil {
234                                         return bc, err
235                                 }
236                                 bc.values[tenantID] = asURL.Path[1:]
237                         default:
238                                 bc.values[key] = value
239                         }
240                 }
241         }
242
243         return bc, err
244 }
245
246 // EventGridKeyAuthorizer implements authorization for event grid using key authentication.
247 type EventGridKeyAuthorizer struct {
248         topicKey string
249 }
250
251 // NewEventGridKeyAuthorizer creates a new EventGridKeyAuthorizer
252 // with the specified topic key.
253 func NewEventGridKeyAuthorizer(topicKey string) EventGridKeyAuthorizer {
254         return EventGridKeyAuthorizer{topicKey: topicKey}
255 }
256
257 // WithAuthorization returns a PrepareDecorator that adds the aeg-sas-key authentication header.
258 func (egta EventGridKeyAuthorizer) WithAuthorization() PrepareDecorator {
259         headers := map[string]interface{}{
260                 "aeg-sas-key": egta.topicKey,
261         }
262         return NewAPIKeyAuthorizerWithHeaders(headers).WithAuthorization()
263 }
264
265 // BasicAuthorizer implements basic HTTP authorization by adding the Authorization HTTP header
266 // with the value "Basic <TOKEN>" where <TOKEN> is a base64-encoded username:password tuple.
267 type BasicAuthorizer struct {
268         userName string
269         password string
270 }
271
272 // NewBasicAuthorizer creates a new BasicAuthorizer with the specified username and password.
273 func NewBasicAuthorizer(userName, password string) *BasicAuthorizer {
274         return &BasicAuthorizer{
275                 userName: userName,
276                 password: password,
277         }
278 }
279
280 // WithAuthorization returns a PrepareDecorator that adds an HTTP Authorization header whose
281 // value is "Basic " followed by the base64-encoded username:password tuple.
282 func (ba *BasicAuthorizer) WithAuthorization() PrepareDecorator {
283         headers := make(map[string]interface{})
284         headers[authorization] = basic + " " + base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", ba.userName, ba.password)))
285
286         return NewAPIKeyAuthorizerWithHeaders(headers).WithAuthorization()
287 }