diff --git a/connector/google/google.go b/connector/google/google.go index 3808157e89..b0db882f11 100644 --- a/connector/google/google.go +++ b/connector/google/google.go @@ -55,7 +55,7 @@ type Config struct { } // Open returns a connector which can be used to login users through Google. -func (c *Config) Open(id string, logger log.Logger, opts ...option.ClientOption) (conn connector.Connector, err error) { +func (c *Config) Open(id string, logger log.Logger) (conn connector.Connector, err error) { ctx, cancel := context.WithCancel(context.Background()) provider, err := oidc.NewProvider(ctx, issuerURL) @@ -71,7 +71,7 @@ func (c *Config) Open(id string, logger log.Logger, opts ...option.ClientOption) scopes = append(scopes, "profile", "email") } - srv, err := createDirectoryService(c.ServiceAccountFilePath, c.AdminEmail, opts...) + srv, err := createDirectoryService(c.ServiceAccountFilePath, c.AdminEmail) if err != nil { cancel() return nil, fmt.Errorf("could not create directory service: %v", err) @@ -282,33 +282,33 @@ func (c *googleConnector) getGroups(email string, fetchTransitiveGroupMembership // createDirectoryService sets up super user impersonation and creates an admin client for calling // the google admin api. If no serviceAccountFilePath is defined, the application default credential // is used. -func createDirectoryService(serviceAccountFilePath, email string, opts ...option.ClientOption) (*admin.Service, error) { +func createDirectoryService(serviceAccountFilePath, email string) (*admin.Service, error) { if email == "" { return nil, fmt.Errorf("directory service requires adminEmail") } + var jsonCredentials []byte + var err error + ctx := context.Background() if serviceAccountFilePath == "" { - creds, err := google.FindDefaultCredentialsWithParams(ctx, google.CredentialsParams{ - Scopes: []string{admin.AdminDirectoryGroupReadonlyScope}, - Subject: email, - }) + credential, err := google.FindDefaultCredentials(ctx) if err != nil { - return nil, fmt.Errorf("failed to fetch application default credentials: %v", err) + return nil, fmt.Errorf("failed to fetch application default credentials: %w", err) + } + jsonCredentials = credential.JSON + } else { + jsonCredentials, err = os.ReadFile(serviceAccountFilePath) + if err != nil { + return nil, fmt.Errorf("error reading credentials from file: %v", err) } - return admin.NewService(ctx, append(opts, option.WithTokenSource(creds.TokenSource))...) - } - - jsonCredentials, err := os.ReadFile(serviceAccountFilePath) - if err != nil { - return nil, fmt.Errorf("error reading credentials from file: %v", err) } config, err := google.JWTConfigFromJSON(jsonCredentials, admin.AdminDirectoryGroupReadonlyScope) if err != nil { - return nil, fmt.Errorf("unable to parse client secret file to config: %v", err) + return nil, fmt.Errorf("unable to parse credentials to config: %v", err) } config.Subject = email - return admin.NewService(ctx, append(opts, option.WithHTTPClient(config.Client(ctx)))...) + return admin.NewService(ctx, option.WithHTTPClient(config.Client(ctx))) } // uniqueGroups returns the unique groups of a slice diff --git a/connector/google/google_test.go b/connector/google/google_test.go deleted file mode 100644 index 90674e2837..0000000000 --- a/connector/google/google_test.go +++ /dev/null @@ -1,130 +0,0 @@ -package google - -import ( - "encoding/json" - "fmt" - "net/http" - "net/http/httptest" - "os" - "testing" - - "github.com/sirupsen/logrus" - "github.com/stretchr/testify/assert" - "google.golang.org/api/option" -) - -func testSetup(t *testing.T) *httptest.Server { - mux := http.NewServeMux() - // TODO: mock calls - // mux.HandleFunc("/admin/directory/v1/groups", func(w http.ResponseWriter, r *http.Request) { - // w.Header().Add("Content-Type", "application/json") - // json.NewEncoder(w).Encode(&admin.Groups{ - // Groups: []*admin.Group{}, - // }) - // }) - return httptest.NewServer(mux) -} - -func newConnector(config *Config, serverURL string) (*googleConnector, error) { - log := logrus.New() - conn, err := config.Open("id", log, option.WithEndpoint(serverURL)) - if err != nil { - return nil, err - } - - googleConn, ok := conn.(*googleConnector) - if !ok { - return nil, fmt.Errorf("failed to convert to googleConnector") - } - return googleConn, nil -} - -func tempServiceAccountKey() (string, error) { - fd, err := os.CreateTemp("", "google_service_account_key") - if err != nil { - return "", err - } - defer fd.Close() - err = json.NewEncoder(fd).Encode(map[string]string{ - "type": "service_account", - "project_id": "abc", - "private_key_id": "abc", - "private_key": "-----BEGIN PRIVATE KEY-----\nabc\n-----END PRIVATE KEY-----\n", - "client_id": "abc", - "client_x509_cert_url": "localhost", - }) - return fd.Name(), err -} - -func TestOpen(t *testing.T) { - ts := testSetup(t) - defer ts.Close() - - serviceAccountFilePath, err := tempServiceAccountKey() - assert.Nil(t, err) - - for name, expected := range map[string]struct { - serviceAccount bool - config *Config - connector *googleConnector - - // TODO: switch from fmt.Errorf to error wrapping for better - // error checking in tests - err string - }{ - "missing_admin_email": { - config: &Config{ - ClientID: "testClient", - ClientSecret: "testSecret", - RedirectURI: ts.URL + "/callback", - Scopes: []string{"openid", "groups"}, - }, - err: "requires adminEmail", - }, - "workload_identity": { - config: &Config{ - ClientID: "testClient", - ClientSecret: "testSecret", - RedirectURI: ts.URL + "/callback", - Scopes: []string{"openid", "groups"}, - AdminEmail: "foo@bar.com", - }, - err: "", - }, - "service_account_key_not_found": { - config: &Config{ - ClientID: "testClient", - ClientSecret: "testSecret", - RedirectURI: ts.URL + "/callback", - Scopes: []string{"openid", "groups"}, - AdminEmail: "foo@bar.com", - ServiceAccountFilePath: "not_found.json", - }, - err: "error reading credentials", - }, - "service_account_key_valid": { - config: &Config{ - ClientID: "testClient", - ClientSecret: "testSecret", - RedirectURI: ts.URL + "/callback", - Scopes: []string{"openid", "groups"}, - AdminEmail: "foo@bar.com", - ServiceAccountFilePath: serviceAccountFilePath, - }, - err: "", - }, - } { - expected := expected - t.Run(name, func(t *testing.T) { - assert := assert.New(t) - conn, err := newConnector(expected.config, ts.URL) - - if expected.err == "" { - assert.Nil(err) - assert.NotNil(conn) - } else { - assert.ErrorContains(err, expected.err) - } - }) - } -} diff --git a/go.mod b/go.mod index 4868a12a40..162b152173 100644 --- a/go.mod +++ b/go.mod @@ -39,4 +39,56 @@ require ( gopkg.in/square/go-jose.v2 v2.6.0 ) +require ( + ariga.io/atlas v0.3.7-0.20220303204946-787354f533c3 // indirect + cloud.google.com/go/compute v1.5.0 // indirect + github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c // indirect + github.com/Masterminds/goutils v1.1.1 // indirect + github.com/Masterminds/semver/v3 v3.1.1 // indirect + github.com/agext/levenshtein v1.2.1 // indirect + github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.1.2 // indirect + github.com/coreos/go-semver v0.3.0 // indirect + github.com/coreos/go-systemd/v22 v22.3.2 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/go-asn1-ber/asn1-ber v1.5.1 // indirect + github.com/go-openapi/inflect v0.19.0 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect + github.com/golang/protobuf v1.5.2 // indirect + github.com/google/go-cmp v0.5.7 // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/googleapis/gax-go/v2 v2.2.0 // indirect + github.com/hashicorp/hcl/v2 v2.10.0 // indirect + github.com/huandu/xstrings v1.3.1 // indirect + github.com/imdario/mergo v0.3.11 // indirect + github.com/inconshreveable/mousetrap v1.0.0 // indirect + github.com/jonboulle/clockwork v0.2.2 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect + github.com/mitchellh/copystructure v1.0.0 // indirect + github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 // indirect + github.com/mitchellh/reflectwalk v1.0.0 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/prometheus/client_model v0.2.0 // indirect + github.com/prometheus/common v0.32.1 // indirect + github.com/prometheus/procfs v0.7.3 // indirect + github.com/shopspring/decimal v1.2.0 // indirect + github.com/spf13/cast v1.4.1 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/zclconf/go-cty v1.8.0 // indirect + go.etcd.io/etcd/api/v3 v3.5.2 // indirect + go.opencensus.io v0.23.0 // indirect + go.uber.org/atomic v1.7.0 // indirect + go.uber.org/multierr v1.6.0 // indirect + go.uber.org/zap v1.17.0 // indirect + golang.org/x/mod v0.5.1 // indirect + golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886 // indirect + golang.org/x/text v0.3.7 // indirect + google.golang.org/appengine v1.6.7 // indirect + google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect +) + replace github.com/dexidp/dex/api/v2 => ./api/v2