github.com/coreos/mantle@v0.13.0/auth/google.go (about)

     1  // Copyright 2014 CoreOS, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // Package auth provides Google oauth2 and Azure credential bindings for mantle.
    16  package auth
    17  
    18  import (
    19  	"encoding/json"
    20  	"fmt"
    21  	"log"
    22  	"net/http"
    23  	"os"
    24  	"os/user"
    25  	"path/filepath"
    26  
    27  	"golang.org/x/oauth2"
    28  	"golang.org/x/oauth2/google"
    29  )
    30  
    31  // client registered under the coreos-gce-testing project as 'mantle'
    32  var conf = oauth2.Config{
    33  	ClientID:     "1053977531921-s05q1c3kf23pdq86bmqv5qcga21c0ra3.apps.googleusercontent.com",
    34  	ClientSecret: "pgt0XUBTCfMwsqf2Q6cVdxTO",
    35  	Endpoint: oauth2.Endpoint{
    36  		AuthURL:  "https://accounts.google.com/o/oauth2/auth",
    37  		TokenURL: "https://accounts.google.com/o/oauth2/token",
    38  	},
    39  	RedirectURL: "urn:ietf:wg:oauth:2.0:oob",
    40  	Scopes: []string{"https://www.googleapis.com/auth/devstorage.full_control",
    41  		"https://www.googleapis.com/auth/compute"},
    42  }
    43  
    44  func writeCache(cachePath string, tok *oauth2.Token) error {
    45  	file, err := os.OpenFile(cachePath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
    46  	if err != nil {
    47  		return err
    48  	}
    49  	defer file.Close()
    50  
    51  	if err := json.NewEncoder(file).Encode(tok); err != nil {
    52  		return err
    53  	}
    54  	return nil
    55  }
    56  
    57  func readCache(cachePath string) (*oauth2.Token, error) {
    58  	file, err := os.Open(cachePath)
    59  	if err != nil {
    60  		return nil, err
    61  	}
    62  	defer file.Close()
    63  
    64  	tok := &oauth2.Token{}
    65  	if err := json.NewDecoder(file).Decode(tok); err != nil {
    66  		return nil, err
    67  	}
    68  
    69  	// make sure token is refreshable
    70  	if tok != nil && !tok.Valid() {
    71  		ts := conf.TokenSource(oauth2.NoContext, tok)
    72  		tok, err = ts.Token()
    73  		if err != nil || !tok.Valid() {
    74  			fmt.Printf("Could not refresh cached token: %v\n", err)
    75  			return nil, nil
    76  		}
    77  	}
    78  	return tok, nil
    79  }
    80  
    81  func getToken() (*oauth2.Token, error) {
    82  	userInfo, err := user.Current()
    83  	if err != nil {
    84  		return nil, err
    85  	}
    86  
    87  	cachePath := filepath.Join(userInfo.HomeDir, ".mantle-cache-google.json")
    88  	tok, err := readCache(cachePath)
    89  	if err != nil {
    90  		log.Printf("Error reading google token cache file: %v", err)
    91  	}
    92  	if tok == nil {
    93  		url := conf.AuthCodeURL("state", oauth2.AccessTypeOffline)
    94  		fmt.Printf("Visit the URL for the auth dialog: %v\n", url)
    95  		fmt.Print("Enter token: ")
    96  
    97  		var code string
    98  		if _, err := fmt.Scan(&code); err != nil {
    99  			return nil, err
   100  		}
   101  		tok, err = conf.Exchange(oauth2.NoContext, code)
   102  		if err != nil {
   103  			return nil, err
   104  		}
   105  		err = writeCache(cachePath, tok)
   106  		if err != nil {
   107  			log.Printf("Error writing google token cache file: %v", err)
   108  		}
   109  	}
   110  	return tok, nil
   111  }
   112  
   113  // GoogleClient provides an http.Client authorized with an oauth2 token
   114  // that is automatically cached and refreshed from a file named
   115  // '.mantle-cache-google.json'. This uses interactive oauth2
   116  // authorization and requires a user follow to follow a web link and
   117  // paste in an authorization token.
   118  func GoogleClient() (*http.Client, error) {
   119  	tok, err := getToken()
   120  	if err != nil {
   121  		return nil, err
   122  	}
   123  	return conf.Client(oauth2.NoContext, tok), nil
   124  }
   125  
   126  // GoogleTokenSource provides an outh2.TokenSource authorized in the
   127  // same manner as GoogleClient.
   128  func GoogleTokenSource() (oauth2.TokenSource, error) {
   129  	tok, err := getToken()
   130  	if err != nil {
   131  		return nil, err
   132  	}
   133  	return conf.TokenSource(oauth2.NoContext, tok), nil
   134  }
   135  
   136  // GoogleServiceClient fetchs a token from Google Compute Engine's
   137  // metadata service. This should be used on GCE vms. The Default account
   138  // is used.
   139  func GoogleServiceClient() *http.Client {
   140  	return &http.Client{
   141  		Transport: &oauth2.Transport{
   142  			Source: google.ComputeTokenSource(""),
   143  		},
   144  	}
   145  }
   146  
   147  // GoogleServiceTokenSource provides an oauth2.TokenSource authorized in
   148  // the same manner as GoogleServiceClient().
   149  func GoogleServiceTokenSource() oauth2.TokenSource {
   150  	return google.ComputeTokenSource("")
   151  }
   152  
   153  // GoogleClientFromJSONKey  provides an http.Client authorized with an
   154  // oauth2 token retrieved using a Google Developers service account's
   155  // private JSON key file.
   156  func GoogleClientFromJSONKey(jsonKey []byte, scope ...string) (*http.Client, error) {
   157  	if scope == nil {
   158  		scope = conf.Scopes
   159  	}
   160  	jwtConf, err := google.JWTConfigFromJSON(jsonKey, scope...)
   161  	if err != nil {
   162  		return nil, err
   163  	}
   164  
   165  	return jwtConf.Client(oauth2.NoContext), nil
   166  
   167  }
   168  
   169  // GoogleTokenSourceFromJSONKey provides an oauth2.TokenSource
   170  // authorized in the same manner as GoogleClientFromJSONKey.
   171  func GoogleTokenSourceFromJSONKey(jsonKey []byte, scope ...string) (oauth2.TokenSource, error) {
   172  	if scope == nil {
   173  		scope = conf.Scopes
   174  	}
   175  
   176  	jwtConf, err := google.JWTConfigFromJSON(jsonKey, scope...)
   177  	if err != nil {
   178  		return nil, err
   179  	}
   180  
   181  	return jwtConf.TokenSource(oauth2.NoContext), nil
   182  }