github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/provider/gce/credentials.go (about)

     1  // Copyright 2016 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package gce
     5  
     6  import (
     7  	"fmt"
     8  	"os"
     9  	"path/filepath"
    10  	"runtime"
    11  
    12  	"github.com/juju/errors"
    13  	"github.com/juju/utils"
    14  
    15  	"github.com/juju/juju/cloud"
    16  	"github.com/juju/juju/provider/gce/google"
    17  )
    18  
    19  type environProviderCredentials struct{}
    20  
    21  // CredentialSchemas is part of the environs.ProviderCredentials interface.
    22  func (environProviderCredentials) CredentialSchemas() map[cloud.AuthType]cloud.CredentialSchema {
    23  	return map[cloud.AuthType]cloud.CredentialSchema{
    24  		cloud.OAuth2AuthType: {
    25  			{
    26  				"client-id", cloud.CredentialAttr{Description: "client ID"},
    27  			}, {
    28  				"client-email", cloud.CredentialAttr{Description: "client e-mail address"},
    29  			}, {
    30  				"private-key",
    31  				cloud.CredentialAttr{
    32  					Description: "client secret",
    33  					Hidden:      true,
    34  				},
    35  			}, {
    36  				"project-id", cloud.CredentialAttr{Description: "project ID"},
    37  			},
    38  		},
    39  		cloud.JSONFileAuthType: {
    40  			{
    41  				"file", cloud.CredentialAttr{
    42  					Description: "path to the .json file containing your Google Compute Engine project credentials",
    43  					FilePath:    true,
    44  				},
    45  			},
    46  		},
    47  	}
    48  }
    49  
    50  // DetectCredentials is part of the environs.ProviderCredentials interface.
    51  func (environProviderCredentials) DetectCredentials() (*cloud.CloudCredential, error) {
    52  	// Google recommends credentials in a json file:
    53  	// 1. whose path is specified by the GOOGLE_APPLICATION_CREDENTIALS environment variable.
    54  	// 2. whose location is known to the gcloud command-line tool.
    55  	//   On Windows, this is %APPDATA%/gcloud/application_default_credentials.json.
    56  	//   On other systems, $HOME/.config/gcloud/application_default_credentials.json.
    57  
    58  	validatePath := func(possbleFilePath string) string {
    59  		if possbleFilePath == "" {
    60  			return ""
    61  		}
    62  		fi, err := os.Stat(possbleFilePath)
    63  		if err != nil || fi.IsDir() {
    64  			return ""
    65  		}
    66  		return possbleFilePath
    67  	}
    68  
    69  	possbleFilePath := validatePath(os.Getenv("GOOGLE_APPLICATION_CREDENTIALS"))
    70  	if possbleFilePath == "" {
    71  		possbleFilePath = validatePath(wellKnownCredentialsFile())
    72  	}
    73  	if possbleFilePath == "" {
    74  		return nil, errors.NotFoundf("gce credentials")
    75  	}
    76  	parsedCred, err := parseJSONAuthFile(possbleFilePath)
    77  	if err != nil {
    78  		return nil, errors.Annotatef(err, "invalid json credential file %s", possbleFilePath)
    79  	}
    80  
    81  	user, err := utils.LocalUsername()
    82  	if err != nil {
    83  		return nil, errors.Trace(err)
    84  	}
    85  	cred := cloud.NewCredential(cloud.JSONFileAuthType, map[string]string{
    86  		"file": possbleFilePath,
    87  	})
    88  	credName := parsedCred.Attributes()["client-email"]
    89  	if credName == "" {
    90  		credName = parsedCred.Attributes()["client-id"]
    91  	}
    92  	cred.Label = fmt.Sprintf("google credential %q", credName)
    93  	return &cloud.CloudCredential{
    94  		DefaultRegion: os.Getenv("CLOUDSDK_COMPUTE_REGION"),
    95  		AuthCredentials: map[string]cloud.Credential{
    96  			user: cred,
    97  		}}, nil
    98  }
    99  
   100  func wellKnownCredentialsFile() string {
   101  	const f = "application_default_credentials.json"
   102  	if runtime.GOOS == "windows" {
   103  		return filepath.Join(os.Getenv("APPDATA"), "gcloud", f)
   104  	}
   105  	return filepath.Join(utils.Home(), ".config", "gcloud", f)
   106  }
   107  
   108  // parseJSONAuthFile parses the file with the given path, and extracts
   109  // the OAuth2 credentials within.
   110  func parseJSONAuthFile(filename string) (cloud.Credential, error) {
   111  	authFile, err := os.Open(filename)
   112  	if err != nil {
   113  		return cloud.Credential{}, errors.Trace(err)
   114  	}
   115  	defer authFile.Close()
   116  	creds, err := google.ParseJSONKey(authFile)
   117  	if err != nil {
   118  		return cloud.Credential{}, errors.Trace(err)
   119  	}
   120  	return cloud.NewCredential(cloud.OAuth2AuthType, map[string]string{
   121  		"project-id":   creds.ProjectID,
   122  		"client-id":    creds.ClientID,
   123  		"client-email": creds.ClientEmail,
   124  		"private-key":  string(creds.PrivateKey),
   125  	}), nil
   126  }