github.com/timstclair/heapster@v0.20.0-alpha1/Godeps/_workspace/src/golang.org/x/oauth2/google/sdk.go (about)

     1  // Copyright 2015 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package google
     6  
     7  import (
     8  	"encoding/json"
     9  	"errors"
    10  	"fmt"
    11  	"net/http"
    12  	"os"
    13  	"os/user"
    14  	"path/filepath"
    15  	"runtime"
    16  	"strings"
    17  	"time"
    18  
    19  	"golang.org/x/net/context"
    20  	"golang.org/x/oauth2"
    21  	"golang.org/x/oauth2/internal"
    22  )
    23  
    24  type sdkCredentials struct {
    25  	Data []struct {
    26  		Credential struct {
    27  			ClientID     string     `json:"client_id"`
    28  			ClientSecret string     `json:"client_secret"`
    29  			AccessToken  string     `json:"access_token"`
    30  			RefreshToken string     `json:"refresh_token"`
    31  			TokenExpiry  *time.Time `json:"token_expiry"`
    32  		} `json:"credential"`
    33  		Key struct {
    34  			Account string `json:"account"`
    35  			Scope   string `json:"scope"`
    36  		} `json:"key"`
    37  	}
    38  }
    39  
    40  // An SDKConfig provides access to tokens from an account already
    41  // authorized via the Google Cloud SDK.
    42  type SDKConfig struct {
    43  	conf         oauth2.Config
    44  	initialToken *oauth2.Token
    45  }
    46  
    47  // NewSDKConfig creates an SDKConfig for the given Google Cloud SDK
    48  // account. If account is empty, the account currently active in
    49  // Google Cloud SDK properties is used.
    50  // Google Cloud SDK credentials must be created by running `gcloud auth`
    51  // before using this function.
    52  // The Google Cloud SDK is available at https://cloud.google.com/sdk/.
    53  func NewSDKConfig(account string) (*SDKConfig, error) {
    54  	configPath, err := sdkConfigPath()
    55  	if err != nil {
    56  		return nil, fmt.Errorf("oauth2/google: error getting SDK config path: %v", err)
    57  	}
    58  	credentialsPath := filepath.Join(configPath, "credentials")
    59  	f, err := os.Open(credentialsPath)
    60  	if err != nil {
    61  		return nil, fmt.Errorf("oauth2/google: failed to load SDK credentials: %v", err)
    62  	}
    63  	defer f.Close()
    64  
    65  	var c sdkCredentials
    66  	if err := json.NewDecoder(f).Decode(&c); err != nil {
    67  		return nil, fmt.Errorf("oauth2/google: failed to decode SDK credentials from %q: %v", credentialsPath, err)
    68  	}
    69  	if len(c.Data) == 0 {
    70  		return nil, fmt.Errorf("oauth2/google: no credentials found in %q, run `gcloud auth login` to create one", credentialsPath)
    71  	}
    72  	if account == "" {
    73  		propertiesPath := filepath.Join(configPath, "properties")
    74  		f, err := os.Open(propertiesPath)
    75  		if err != nil {
    76  			return nil, fmt.Errorf("oauth2/google: failed to load SDK properties: %v", err)
    77  		}
    78  		defer f.Close()
    79  		ini, err := internal.ParseINI(f)
    80  		if err != nil {
    81  			return nil, fmt.Errorf("oauth2/google: failed to parse SDK properties %q: %v", propertiesPath, err)
    82  		}
    83  		core, ok := ini["core"]
    84  		if !ok {
    85  			return nil, fmt.Errorf("oauth2/google: failed to find [core] section in %v", ini)
    86  		}
    87  		active, ok := core["account"]
    88  		if !ok {
    89  			return nil, fmt.Errorf("oauth2/google: failed to find %q attribute in %v", "account", core)
    90  		}
    91  		account = active
    92  	}
    93  
    94  	for _, d := range c.Data {
    95  		if account == "" || d.Key.Account == account {
    96  			if d.Credential.AccessToken == "" && d.Credential.RefreshToken == "" {
    97  				return nil, fmt.Errorf("oauth2/google: no token available for account %q", account)
    98  			}
    99  			var expiry time.Time
   100  			if d.Credential.TokenExpiry != nil {
   101  				expiry = *d.Credential.TokenExpiry
   102  			}
   103  			return &SDKConfig{
   104  				conf: oauth2.Config{
   105  					ClientID:     d.Credential.ClientID,
   106  					ClientSecret: d.Credential.ClientSecret,
   107  					Scopes:       strings.Split(d.Key.Scope, " "),
   108  					Endpoint:     Endpoint,
   109  					RedirectURL:  "oob",
   110  				},
   111  				initialToken: &oauth2.Token{
   112  					AccessToken:  d.Credential.AccessToken,
   113  					RefreshToken: d.Credential.RefreshToken,
   114  					Expiry:       expiry,
   115  				},
   116  			}, nil
   117  		}
   118  	}
   119  	return nil, fmt.Errorf("oauth2/google: no such credentials for account %q", account)
   120  }
   121  
   122  // Client returns an HTTP client using Google Cloud SDK credentials to
   123  // authorize requests. The token will auto-refresh as necessary. The
   124  // underlying http.RoundTripper will be obtained using the provided
   125  // context. The returned client and its Transport should not be
   126  // modified.
   127  func (c *SDKConfig) Client(ctx context.Context) *http.Client {
   128  	return &http.Client{
   129  		Transport: &oauth2.Transport{
   130  			Source: c.TokenSource(ctx),
   131  		},
   132  	}
   133  }
   134  
   135  // TokenSource returns an oauth2.TokenSource that retrieve tokens from
   136  // Google Cloud SDK credentials using the provided context.
   137  // It will returns the current access token stored in the credentials,
   138  // and refresh it when it expires, but it won't update the credentials
   139  // with the new access token.
   140  func (c *SDKConfig) TokenSource(ctx context.Context) oauth2.TokenSource {
   141  	return c.conf.TokenSource(ctx, c.initialToken)
   142  }
   143  
   144  // Scopes are the OAuth 2.0 scopes the current account is authorized for.
   145  func (c *SDKConfig) Scopes() []string {
   146  	return c.conf.Scopes
   147  }
   148  
   149  // sdkConfigPath tries to guess where the gcloud config is located.
   150  // It can be overridden during tests.
   151  var sdkConfigPath = func() (string, error) {
   152  	if runtime.GOOS == "windows" {
   153  		return filepath.Join(os.Getenv("APPDATA"), "gcloud"), nil
   154  	}
   155  	homeDir := guessUnixHomeDir()
   156  	if homeDir == "" {
   157  		return "", errors.New("unable to get current user home directory: os/user lookup failed; $HOME is empty")
   158  	}
   159  	return filepath.Join(homeDir, ".config", "gcloud"), nil
   160  }
   161  
   162  func guessUnixHomeDir() string {
   163  	usr, err := user.Current()
   164  	if err == nil {
   165  		return usr.HomeDir
   166  	}
   167  	return os.Getenv("HOME")
   168  }