From 722f9045e41374660868ed499189e9ec3db6dfb0 Mon Sep 17 00:00:00 2001 From: Hiranya Jayathilaka Date: Thu, 24 Oct 2019 10:13:52 -0700 Subject: [PATCH] Implemented the OIDCProviderConfigs() API (#285) --- auth/provider_config.go | 77 ++++++++++++++++++++++++++++++ auth/provider_config_test.go | 90 ++++++++++++++++++++++++++++++++++-- 2 files changed, 164 insertions(+), 3 deletions(-) diff --git a/auth/provider_config.go b/auth/provider_config.go index 9d895ab7..e1d02324 100644 --- a/auth/provider_config.go +++ b/auth/provider_config.go @@ -245,6 +245,65 @@ func (config *OIDCProviderConfigToUpdate) buildRequest() (nestedMap, error) { return config.params, nil } +// OIDCProviderConfigIterator is an iterator over OIDC provider configurations. +type OIDCProviderConfigIterator struct { + client *providerConfigClient + ctx context.Context + nextFunc func() error + pageInfo *iterator.PageInfo + configs []*OIDCProviderConfig +} + +// PageInfo supports pagination. +func (it *OIDCProviderConfigIterator) PageInfo() *iterator.PageInfo { + return it.pageInfo +} + +// Next returns the next OIDCProviderConfig. The error value of [iterator.Done] is +// returned if there are no more results. Once Next returns [iterator.Done], all +// subsequent calls will return [iterator.Done]. +func (it *OIDCProviderConfigIterator) Next() (*OIDCProviderConfig, error) { + if err := it.nextFunc(); err != nil { + return nil, err + } + + config := it.configs[0] + it.configs = it.configs[1:] + return config, nil +} + +func (it *OIDCProviderConfigIterator) fetch(pageSize int, pageToken string) (string, error) { + params := map[string]string{ + "pageSize": strconv.Itoa(pageSize), + } + if pageToken != "" { + params["pageToken"] = pageToken + } + + req := &internal.Request{ + Method: http.MethodGet, + URL: "/oauthIdpConfigs", + Opts: []internal.HTTPOption{ + internal.WithQueryParams(params), + }, + } + + var result struct { + Configs []oidcProviderConfigDAO `json:"oauthIdpConfigs"` + NextPageToken string `json:"nextPageToken"` + } + if _, err := it.client.makeRequest(it.ctx, req, &result); err != nil { + return "", err + } + + for _, config := range result.Configs { + it.configs = append(it.configs, config.toOIDCProviderConfig()) + } + + it.pageInfo.Token = result.NextPageToken + return result.NextPageToken, nil +} + // SAMLProviderConfig is the SAML auth provider configuration. // See http://docs.oasis-open.org/security/saml/Post2.0/sstc-saml-tech-overview-2.0.html. type SAMLProviderConfig struct { @@ -652,6 +711,24 @@ func (c *providerConfigClient) DeleteOIDCProviderConfig(ctx context.Context, id return err } +// OIDCProviderConfigs returns an iterator over OIDC provider configurations. +// +// If nextPageToken is empty, the iterator will start at the beginning. Otherwise, +// iterator starts after the token. +func (c *providerConfigClient) OIDCProviderConfigs(ctx context.Context, nextPageToken string) *OIDCProviderConfigIterator { + it := &OIDCProviderConfigIterator{ + ctx: ctx, + client: c, + } + it.pageInfo, it.nextFunc = iterator.NewPageInfo( + it.fetch, + func() int { return len(it.configs) }, + func() interface{} { b := it.configs; it.configs = nil; return b }) + it.pageInfo.MaxSize = maxConfigs + it.pageInfo.Token = nextPageToken + return it +} + // SAMLProviderConfig returns the SAMLProviderConfig with the given ID. func (c *providerConfigClient) SAMLProviderConfig(ctx context.Context, id string) (*SAMLProviderConfig, error) { if err := validateSAMLConfigID(id); err != nil { diff --git a/auth/provider_config_test.go b/auth/provider_config_test.go index ad756526..a1629140 100644 --- a/auth/provider_config_test.go +++ b/auth/provider_config_test.go @@ -508,6 +508,84 @@ func TestDeleteOIDCProviderConfigError(t *testing.T) { } } +func TestOIDCProviderConfigs(t *testing.T) { + template := `{ + "oauthIdpConfigs": [ + %s, + %s, + %s + ], + "nextPageToken": "" + }` + response := fmt.Sprintf(template, oidcConfigResponse, oidcConfigResponse, oidcConfigResponse) + s := echoServer([]byte(response), t) + defer s.Close() + + want := []*OIDCProviderConfig{ + oidcProviderConfig, + oidcProviderConfig, + oidcProviderConfig, + } + wantPath := "/projects/mock-project-id/oauthIdpConfigs" + + testIterator := func(iter *OIDCProviderConfigIterator, token string, req string) { + count := 0 + for i := 0; i < len(want); i++ { + config, err := iter.Next() + if err == iterator.Done { + break + } + if err != nil { + t.Fatal(err) + } + if !reflect.DeepEqual(config, want[i]) { + t.Errorf("OIDCProviderConfigs(%q) = %#v; want = %#v", token, config, want[i]) + } + count++ + } + if count != len(want) { + t.Errorf("OIDCProviderConfigs(%q) = %d; want = %d", token, count, len(want)) + } + if _, err := iter.Next(); err != iterator.Done { + t.Errorf("OIDCProviderConfigs(%q) = %v; want = %v", token, err, iterator.Done) + } + + url := s.Req[len(s.Req)-1].URL + if url.Path != wantPath { + t.Errorf("OIDCProviderConfigs(%q) = %q; want = %q", token, url.Path, wantPath) + } + + // Check the query string of the last HTTP request made. + gotReq := url.Query().Encode() + if gotReq != req { + t.Errorf("OIDCProviderConfigs(%q) = %q; want = %v", token, gotReq, req) + } + } + + client := s.Client.pcc + testIterator( + client.OIDCProviderConfigs(context.Background(), ""), + "", + "pageSize=100") + testIterator( + client.OIDCProviderConfigs(context.Background(), "pageToken"), + "pageToken", + "pageSize=100&pageToken=pageToken") +} + +func TestOIDCProviderConfigsError(t *testing.T) { + s := echoServer([]byte("{}"), t) + defer s.Close() + s.Status = http.StatusInternalServerError + + client := s.Client.pcc + it := client.OIDCProviderConfigs(context.Background(), "") + config, err := it.Next() + if config != nil || err == nil || !IsUnknown(err) { + t.Errorf("OIDCProviderConfigs() = (%v, %v); want = (nil, %q)", config, err, "unknown-error") + } +} + func TestSAMLProviderConfig(t *testing.T) { s := echoServer([]byte(samlConfigResponse), t) defer s.Close() @@ -1074,6 +1152,7 @@ func TestSAMLProviderConfigs(t *testing.T) { samlProviderConfig, samlProviderConfig, } + wantPath := "/projects/mock-project-id/inboundSamlConfigs" testIterator := func(iter *SAMLProviderConfigIterator, token string, req string) { count := 0 @@ -1094,13 +1173,18 @@ func TestSAMLProviderConfigs(t *testing.T) { t.Errorf("SAMLProviderConfigs(%q) = %d; want = %d", token, count, len(want)) } if _, err := iter.Next(); err != iterator.Done { - t.Errorf("SAMLProviderConfigs(%q) = %v, want = %v", token, err, iterator.Done) + t.Errorf("SAMLProviderConfigs(%q) = %v; want = %v", token, err, iterator.Done) + } + + url := s.Req[len(s.Req)-1].URL + if url.Path != wantPath { + t.Errorf("SAMLProviderConfigs(%q) = %q; want = %q", token, url.Path, wantPath) } // Check the query string of the last HTTP request made. - gotReq := s.Req[len(s.Req)-1].URL.Query().Encode() + gotReq := url.Query().Encode() if gotReq != req { - t.Errorf("SAMLProviderConfigs(%q) = %q, want = %v", token, gotReq, req) + t.Errorf("SAMLProviderConfigs(%q) = %q; want = %v", token, gotReq, req) } }