mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-31 15:25:57 +00:00
bump(code.google.com/p/goauth2/compute/serviceaccount): ef170e7cf161bc5644976d13cadc67b285e73ee8
This commit is contained in:
parent
721f6571fa
commit
45397c46cc
172
third_party/src/code.google.com/p/goauth2/compute/serviceaccount/serviceaccount.go
vendored
Normal file
172
third_party/src/code.google.com/p/goauth2/compute/serviceaccount/serviceaccount.go
vendored
Normal file
@ -0,0 +1,172 @@
|
||||
// Copyright 2013 The goauth2 Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package serviceaccount provides support for making OAuth2-authorized
|
||||
// HTTP requests from Google Compute Engine instances using service accounts.
|
||||
//
|
||||
// See: https://developers.google.com/compute/docs/authentication
|
||||
//
|
||||
// Example usage:
|
||||
//
|
||||
// client, err := serviceaccount.NewClient(&serviceaccount.Options{})
|
||||
// if err != nil {
|
||||
// c.Errorf("failed to create service account client: %q", err)
|
||||
// return err
|
||||
// }
|
||||
// client.Post("https://www.googleapis.com/compute/...", ...)
|
||||
// client.Post("https://www.googleapis.com/bigquery/...", ...)
|
||||
//
|
||||
package serviceaccount
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"code.google.com/p/goauth2/oauth"
|
||||
)
|
||||
|
||||
const (
|
||||
metadataServer = "metadata"
|
||||
serviceAccountPath = "/computeMetadata/v1/instance/service-accounts"
|
||||
)
|
||||
|
||||
// Options configures a service account Client.
|
||||
type Options struct {
|
||||
// Underlying transport of service account Client.
|
||||
// If nil, http.DefaultTransport is used.
|
||||
Transport http.RoundTripper
|
||||
|
||||
// Service account name.
|
||||
// If empty, "default" is used.
|
||||
Account string
|
||||
}
|
||||
|
||||
// NewClient returns an *http.Client authorized with the service account
|
||||
// configured in the Google Compute Engine instance.
|
||||
func NewClient(opt *Options) (*http.Client, error) {
|
||||
tr := http.DefaultTransport
|
||||
account := "default"
|
||||
if opt != nil {
|
||||
if opt.Transport != nil {
|
||||
tr = opt.Transport
|
||||
}
|
||||
if opt.Account != "" {
|
||||
account = opt.Account
|
||||
}
|
||||
}
|
||||
t := &transport{
|
||||
Transport: tr,
|
||||
Account: account,
|
||||
}
|
||||
// Get the initial access token.
|
||||
if _, err := fetchToken(t); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &http.Client{
|
||||
Transport: t,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type tokenData struct {
|
||||
AccessToken string `json:"access_token"`
|
||||
ExpiresIn float64 `json:"expires_in"`
|
||||
TokenType string `json:"token_type"`
|
||||
}
|
||||
|
||||
// transport is an oauth.Transport with a custom Refresh and RoundTrip implementation.
|
||||
type transport struct {
|
||||
Transport http.RoundTripper
|
||||
Account string
|
||||
|
||||
mu sync.Mutex
|
||||
*oauth.Token
|
||||
}
|
||||
|
||||
// Refresh renews the transport's AccessToken.
|
||||
// t.mu sould be held when this is called.
|
||||
func (t *transport) refresh() error {
|
||||
// https://developers.google.com/compute/docs/metadata
|
||||
// v1 requires "X-Google-Metadata-Request: True" header.
|
||||
tokenURL := &url.URL{
|
||||
Scheme: "http",
|
||||
Host: metadataServer,
|
||||
Path: path.Join(serviceAccountPath, t.Account, "token"),
|
||||
}
|
||||
req, err := http.NewRequest("GET", tokenURL.String(), nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Header.Add("X-Google-Metadata-Request", "True")
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
d := json.NewDecoder(resp.Body)
|
||||
var token tokenData
|
||||
err = d.Decode(&token)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
t.Token = &oauth.Token{
|
||||
AccessToken: token.AccessToken,
|
||||
Expiry: time.Now().Add(time.Duration(token.ExpiresIn) * time.Second),
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Refresh renews the transport's AccessToken.
|
||||
func (t *transport) Refresh() error {
|
||||
t.mu.Lock()
|
||||
defer t.mu.Unlock()
|
||||
return t.refresh()
|
||||
}
|
||||
|
||||
// Fetch token from cache or generate a new one if cache miss or expired.
|
||||
func fetchToken(t *transport) (*oauth.Token, error) {
|
||||
// Get a new token using Refresh in case of a cache miss of if it has expired.
|
||||
t.mu.Lock()
|
||||
defer t.mu.Unlock()
|
||||
if t.Token == nil || t.Expired() {
|
||||
if err := t.refresh(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return t.Token, nil
|
||||
}
|
||||
|
||||
// cloneRequest returns a clone of the provided *http.Request.
|
||||
// The clone is a shallow copy of the struct and its Header map.
|
||||
func cloneRequest(r *http.Request) *http.Request {
|
||||
// shallow copy of the struct
|
||||
r2 := new(http.Request)
|
||||
*r2 = *r
|
||||
// deep copy of the Header
|
||||
r2.Header = make(http.Header)
|
||||
for k, s := range r.Header {
|
||||
r2.Header[k] = s
|
||||
}
|
||||
return r2
|
||||
}
|
||||
|
||||
// RoundTrip issues an authorized HTTP request and returns its response.
|
||||
func (t *transport) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
token, err := fetchToken(t)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// To set the Authorization header, we must make a copy of the Request
|
||||
// so that we don't modify the Request we were given.
|
||||
// This is required by the specification of http.RoundTripper.
|
||||
newReq := cloneRequest(req)
|
||||
newReq.Header.Set("Authorization", "Bearer "+token.AccessToken)
|
||||
|
||||
// Make the HTTP request.
|
||||
return t.Transport.RoundTrip(newReq)
|
||||
}
|
Loading…
Reference in New Issue
Block a user