Skip to content

Commit

Permalink
feat: add support for JSON credentials
Browse files Browse the repository at this point in the history
  • Loading branch information
enocom committed Dec 6, 2022
1 parent c843274 commit c452b9c
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 1 deletion.
12 changes: 12 additions & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,8 @@ without having to manage any client SSL certificates.`,
"Bearer token used for authorization.")
cmd.PersistentFlags().StringVarP(&c.conf.CredentialsFile, "credentials-file", "c", "",
"Path to a service account key to use for authentication.")
cmd.PersistentFlags().StringVarP(&c.conf.CredentialsJSON, "json-credentials", "j", "",
"Use service account key JSON as a source of IAM credentials.")
cmd.PersistentFlags().BoolVarP(&c.conf.GcloudAuth, "gcloud-auth", "g", false,
"Use gcloud's user configuration to retrieve a token for authentication.")
cmd.PersistentFlags().BoolVarP(&c.conf.StructuredLogs, "structured-logs", "l", false,
Expand Down Expand Up @@ -259,6 +261,16 @@ func parseConfig(cmd *Command, conf *proxy.Config, args []string) error {
if conf.CredentialsFile != "" && conf.GcloudAuth {
return newBadCommandError("cannot specify --credentials-file and --gcloud-auth flags at the same time")
}
if conf.CredentialsJSON != "" && conf.Token != "" {
return newBadCommandError("cannot specify --json-credentials and --token flags at the same time")
}
if conf.CredentialsJSON != "" && conf.CredentialsFile != "" {
return newBadCommandError("cannot specify --json-credentials and --credentials-file flags at the same time")
}
if conf.CredentialsJSON != "" && conf.GcloudAuth {
return newBadCommandError("cannot specify --json-credentials and --gcloud-auth flags at the same time")
}

if userHasSet("alloydbadmin-api-endpoint") {
_, err := url.Parse(conf.APIEndpointURL)
if err != nil {
Expand Down
33 changes: 32 additions & 1 deletion cmd/root_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,19 @@ func TestNewCommandArguments(t *testing.T) {
APIEndpointURL: "https://test.googleapis.com",
}),
},
{
desc: "using the JSON credentials",
args: []string{"--json-credentials", `{"json":"goes-here"}`, "projects/proj/locations/region/clusters/clust/instances/inst"}, want: withDefaults(&proxy.Config{
CredentialsJSON: `{"json":"goes-here"}`,
}),
},
{
desc: "using the (short) JSON credentials",
args: []string{"-j", `{"json":"goes-here"}`, "projects/proj/locations/region/clusters/clust/instances/inst"},
want: withDefaults(&proxy.Config{
CredentialsJSON: `{"json":"goes-here"}`,
}),
},
}

for _, tc := range tcs {
Expand Down Expand Up @@ -335,7 +348,25 @@ func TestNewCommandWithErrors(t *testing.T) {
desc: "when both gcloud auth and credentials file are set",
args: []string{
"--gcloud-auth",
"--credential-file", "/path/to/file", "proj:region:inst"},
"--credentials-file", "/path/to/file", "proj:region:inst"},
},
{
desc: "when both token and credentials JSON are set",
args: []string{
"--token", "a-token",
"--json-credentials", `{"json":"here"}`, "proj:region:inst"},
},
{
desc: "when both credentials file and credentials JSON are set",
args: []string{
"--credentials-file", "/a/file",
"--json-credentials", `{"json":"here"}`, "proj:region:inst"},
},
{
desc: "when both gcloud auth and credentials JSON are set",
args: []string{
"--gcloud-auth",
"--json-credentials", `{"json":"here"}`, "proj:region:inst"},
},
{
desc: "when the unix socket query param contains multiple values",
Expand Down
8 changes: 8 additions & 0 deletions internal/proxy/proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ type Config struct {
// CredentialsFile is the path to a service account key.
CredentialsFile string

// CredentialsJSON is a JSON representation of the service account key.
CredentialsJSON string

// GcloudAuth set whether to use Gcloud's config helper to retrieve a
// token for authentication.
GcloudAuth bool
Expand Down Expand Up @@ -131,6 +134,11 @@ func (c *Config) DialerOptions(l alloydb.Logger) ([]alloydbconn.Option, error) {
return nil, err
}
opts = append(opts, alloydbconn.WithTokenSource(ts))
case c.CredentialsJSON != "":
l.Infof("Authorizing with JSON credentials environment variable")
opts = append(opts, alloydbconn.WithCredentialsJSON(
[]byte(c.CredentialsJSON),
))
default:
l.Infof("Authorizing with Application Default Credentials")
}
Expand Down
13 changes: 13 additions & 0 deletions tests/connection_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package tests
import (
"context"
"database/sql"
"io/ioutil"
"os"
"testing"
"time"
Expand Down Expand Up @@ -51,6 +52,18 @@ func removeAuthEnvVar(t *testing.T) (*oauth2.Token, string, func()) {
}
}

func keyfile(t *testing.T) string {
path := os.Getenv("GOOGLE_APPLICATION_CREDENTIALS")
if path == "" {
t.Fatal("GOOGLE_APPLICATION_CREDENTIALS not set")
}
creds, err := ioutil.ReadFile(path)
if err != nil {
t.Fatalf("io.ReadAll(): %v", err)
}
return string(creds)
}

// proxyConnTest is a test helper to verify the proxy works with a basic connectivity test.
func proxyConnTest(t *testing.T, args []string, driver, dsn string) {
ctx, cancel := context.WithTimeout(context.Background(), connTestTimeout)
Expand Down

0 comments on commit c452b9c

Please sign in to comment.