Workload identity support (#2126)
* WIP: support for workload identity * WIP: bugfixes to support WI * Added support for Workload Identity * Added missing flag * Refactoring and typo * Updated CHANGELOG.md * Updated docs * Updated changelog * Improved readability and fixed codeclimate issues * Update CHANGELOG.md Co-authored-by: Joel Speed <Joel.speed@hotmail.co.uk> * Fixed if statement * Apply suggestions from code review Co-authored-by: Jan Larwig <jan@larwig.com> * Cleanup * Removed target principal * Removed references to target principal * Added docs * Fixed header anchor linking * Update auth.md * Updated generated code * Improved code * Fixed tests --------- Co-authored-by: Joel Speed <Joel.speed@hotmail.co.uk> Co-authored-by: Jan Larwig <jan@larwig.com>
This commit is contained in:
		
							parent
							
								
									738c09095b
								
							
						
					
					
						commit
						a6e8ec81e8
					
				|  | @ -22,6 +22,7 @@ | ||||||
| - [#1988](https://github.com/oauth2-proxy/oauth2-proxy/pull/1988) Ensure sign-in page background is uniform throughout the page | - [#1988](https://github.com/oauth2-proxy/oauth2-proxy/pull/1988) Ensure sign-in page background is uniform throughout the page | ||||||
| - [#2013](https://github.com/oauth2-proxy/oauth2-proxy/pull/2013) Upgrade alpine to version 3.17.2 and library dependencies (@miguelborges99) | - [#2013](https://github.com/oauth2-proxy/oauth2-proxy/pull/2013) Upgrade alpine to version 3.17.2 and library dependencies (@miguelborges99) | ||||||
| - [#2047](https://github.com/oauth2-proxy/oauth2-proxy/pull/2047) CVE-2022-41717: DoS in Go net/http may lead to DoS (@miguelborges99) | - [#2047](https://github.com/oauth2-proxy/oauth2-proxy/pull/2047) CVE-2022-41717: DoS in Go net/http may lead to DoS (@miguelborges99) | ||||||
|  | - [#2126](https://github.com/oauth2-proxy/oauth2-proxy/pull/2126) Added support for GKE Workload Identity (@kvanzuijlen) | ||||||
| - [#1921](https://github.com/oauth2-proxy/oauth2-proxy/pull/1921) Check jsonpath syntax before interpretation | - [#1921](https://github.com/oauth2-proxy/oauth2-proxy/pull/1921) Check jsonpath syntax before interpretation | ||||||
| - [#2025](https://github.com/oauth2-proxy/oauth2-proxy/pull/2025) Embed static stylesheets and dependencies | - [#2025](https://github.com/oauth2-proxy/oauth2-proxy/pull/2025) Embed static stylesheets and dependencies | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -236,6 +236,7 @@ Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h". | ||||||
| | `group` | _[]string_ | Groups sets restrict logins to members of this google group | | | `group` | _[]string_ | Groups sets restrict logins to members of this google group | | ||||||
| | `adminEmail` | _string_ | AdminEmail is the google admin to impersonate for api calls | | | `adminEmail` | _string_ | AdminEmail is the google admin to impersonate for api calls | | ||||||
| | `serviceAccountJson` | _string_ | ServiceAccountJSON is the path to the service account json credentials | | | `serviceAccountJson` | _string_ | ServiceAccountJSON is the path to the service account json credentials | | ||||||
|  | | `useApplicationDefaultCredentials` | _bool_ | UseApplicationDefaultCredentials is a boolean whether to use Application Default Credentials instead of a ServiceAccountJSON | | ||||||
| 
 | 
 | ||||||
| ### Header | ### Header | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -50,12 +50,22 @@ It's recommended to refresh sessions on a short interval (1h) with `cookie-refre | ||||||
| 
 | 
 | ||||||
| #### Restrict auth to specific Google groups on your domain. (optional) | #### Restrict auth to specific Google groups on your domain. (optional) | ||||||
| 
 | 
 | ||||||
| 1.  Create a service account: https://developers.google.com/identity/protocols/OAuth2ServiceAccount and make sure to download the json file. | 1.  Create a [service account](https://developers.google.com/identity/protocols/OAuth2ServiceAccount) and download the json  | ||||||
|  | file if you're not using [Application Default Credentials / Workload Identity / Workload Identity Federation (recommended)](#using-application-default-credentials-adc--workload-identity--workload-identity-federation-recommended). | ||||||
| 2.  Make note of the Client ID for a future step. | 2.  Make note of the Client ID for a future step. | ||||||
| 3.  Under "APIs & Auth", choose APIs. | 3.  Under "APIs & Auth", choose APIs. | ||||||
| 4.  Click on Admin SDK and then Enable API. | 4.  Click on Admin SDK and then Enable API. | ||||||
| 5.  Follow the steps on https://developers.google.com/admin-sdk/directory/v1/guides/delegation#delegate_domain-wide_authority_to_your_service_account and give the client id from step 2 the following oauth scopes: | 5.  Follow the steps on https://developers.google.com/admin-sdk/directory/v1/guides/delegation#delegate_domain-wide_authority_to_your_service_account and give the client id from step 2 the following oauth scopes: | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|  | ##### Using Application Default Credentials (ADC) / Workload Identity / Workload Identity Federation (recommended) | ||||||
|  | oauth2-proxy can make use of [Application Default Credentials](https://cloud.google.com/docs/authentication/application-default-credentials). | ||||||
|  | When deployed within GCP, this means that it can automatically use the service account attached to the resource. When deployed to GKE, ADC | ||||||
|  | can be leveraged through a feature called Workload Identity. Follow Google's [guide](https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity) | ||||||
|  | to set up Workload Identity. | ||||||
|  | 
 | ||||||
|  | When deployed outside of GCP, [Workload Identity Federation](https://cloud.google.com/docs/authentication/provide-credentials-adc#wlif) might be an option. | ||||||
|  | 
 | ||||||
| ``` | ``` | ||||||
| https://www.googleapis.com/auth/admin.directory.group.readonly | https://www.googleapis.com/auth/admin.directory.group.readonly | ||||||
| https://www.googleapis.com/auth/admin.directory.user.readonly | https://www.googleapis.com/auth/admin.directory.user.readonly | ||||||
|  |  | ||||||
|  | @ -120,6 +120,7 @@ An example [oauth2-proxy.cfg](https://github.com/oauth2-proxy/oauth2-proxy/blob/ | ||||||
| | `--google-admin-email` | string | the google admin to impersonate for api calls | | | | `--google-admin-email` | string | the google admin to impersonate for api calls | | | ||||||
| | `--google-group` | string | restrict logins to members of this google group (may be given multiple times). | | | | `--google-group` | string | restrict logins to members of this google group (may be given multiple times). | | | ||||||
| | `--google-service-account-json` | string | the path to the service account json credentials | | | | `--google-service-account-json` | string | the path to the service account json credentials | | | ||||||
|  | | `--google-use-application-default-credentials` | bool | use application default credentials instead of service account json (i.e. GKE Workload Identity) | | | ||||||
| | `--htpasswd-file` | string | additionally authenticate against a htpasswd file. Entries must be created with `htpasswd -B` for bcrypt encryption | | | | `--htpasswd-file` | string | additionally authenticate against a htpasswd file. Entries must be created with `htpasswd -B` for bcrypt encryption | | | ||||||
| | `--htpasswd-user-group` | string \| list | the groups to be set on sessions for htpasswd users | | | | `--htpasswd-user-group` | string \| list | the groups to be set on sessions for htpasswd users | | | ||||||
| | `--http-address` | string | `[http://]<addr>:<port>` or `unix://<path>` to listen on for HTTP clients. Square brackets are required for ipv6 address, e.g. `http://[::1]:4180` | `"127.0.0.1:4180"` | | | `--http-address` | string | `[http://]<addr>:<port>` or `unix://<path>` to listen on for HTTP clients. Square brackets are required for ipv6 address, e.g. `http://[::1]:4180` | `"127.0.0.1:4180"` | | ||||||
|  |  | ||||||
|  | @ -70,6 +70,7 @@ func NewLegacyFlagSet() *pflag.FlagSet { | ||||||
| 	flagSet.AddFlagSet(legacyHeadersFlagSet()) | 	flagSet.AddFlagSet(legacyHeadersFlagSet()) | ||||||
| 	flagSet.AddFlagSet(legacyServerFlagset()) | 	flagSet.AddFlagSet(legacyServerFlagset()) | ||||||
| 	flagSet.AddFlagSet(legacyProviderFlagSet()) | 	flagSet.AddFlagSet(legacyProviderFlagSet()) | ||||||
|  | 	flagSet.AddFlagSet(legacyGoogleFlagSet()) | ||||||
| 
 | 
 | ||||||
| 	return flagSet | 	return flagSet | ||||||
| } | } | ||||||
|  | @ -481,21 +482,22 @@ type LegacyProvider struct { | ||||||
| 	ClientSecret     string `flag:"client-secret" cfg:"client_secret"` | 	ClientSecret     string `flag:"client-secret" cfg:"client_secret"` | ||||||
| 	ClientSecretFile string `flag:"client-secret-file" cfg:"client_secret_file"` | 	ClientSecretFile string `flag:"client-secret-file" cfg:"client_secret_file"` | ||||||
| 
 | 
 | ||||||
| 	KeycloakGroups           []string `flag:"keycloak-group" cfg:"keycloak_groups"` | 	KeycloakGroups                         []string `flag:"keycloak-group" cfg:"keycloak_groups"` | ||||||
| 	AzureTenant              string   `flag:"azure-tenant" cfg:"azure_tenant"` | 	AzureTenant                            string   `flag:"azure-tenant" cfg:"azure_tenant"` | ||||||
| 	AzureGraphGroupField     string   `flag:"azure-graph-group-field" cfg:"azure_graph_group_field"` | 	AzureGraphGroupField                   string   `flag:"azure-graph-group-field" cfg:"azure_graph_group_field"` | ||||||
| 	BitbucketTeam            string   `flag:"bitbucket-team" cfg:"bitbucket_team"` | 	BitbucketTeam                          string   `flag:"bitbucket-team" cfg:"bitbucket_team"` | ||||||
| 	BitbucketRepository      string   `flag:"bitbucket-repository" cfg:"bitbucket_repository"` | 	BitbucketRepository                    string   `flag:"bitbucket-repository" cfg:"bitbucket_repository"` | ||||||
| 	GitHubOrg                string   `flag:"github-org" cfg:"github_org"` | 	GitHubOrg                              string   `flag:"github-org" cfg:"github_org"` | ||||||
| 	GitHubTeam               string   `flag:"github-team" cfg:"github_team"` | 	GitHubTeam                             string   `flag:"github-team" cfg:"github_team"` | ||||||
| 	GitHubRepo               string   `flag:"github-repo" cfg:"github_repo"` | 	GitHubRepo                             string   `flag:"github-repo" cfg:"github_repo"` | ||||||
| 	GitHubToken              string   `flag:"github-token" cfg:"github_token"` | 	GitHubToken                            string   `flag:"github-token" cfg:"github_token"` | ||||||
| 	GitHubUsers              []string `flag:"github-user" cfg:"github_users"` | 	GitHubUsers                            []string `flag:"github-user" cfg:"github_users"` | ||||||
| 	GitLabGroup              []string `flag:"gitlab-group" cfg:"gitlab_groups"` | 	GitLabGroup                            []string `flag:"gitlab-group" cfg:"gitlab_groups"` | ||||||
| 	GitLabProjects           []string `flag:"gitlab-project" cfg:"gitlab_projects"` | 	GitLabProjects                         []string `flag:"gitlab-project" cfg:"gitlab_projects"` | ||||||
| 	GoogleGroups             []string `flag:"google-group" cfg:"google_group"` | 	GoogleGroups                           []string `flag:"google-group" cfg:"google_group"` | ||||||
| 	GoogleAdminEmail         string   `flag:"google-admin-email" cfg:"google_admin_email"` | 	GoogleAdminEmail                       string   `flag:"google-admin-email" cfg:"google_admin_email"` | ||||||
| 	GoogleServiceAccountJSON string   `flag:"google-service-account-json" cfg:"google_service_account_json"` | 	GoogleServiceAccountJSON               string   `flag:"google-service-account-json" cfg:"google_service_account_json"` | ||||||
|  | 	GoogleUseApplicationDefaultCredentials bool     `flag:"google-use-application-default-credentials" cfg:"google_use_application_default_credentials"` | ||||||
| 
 | 
 | ||||||
| 	// These options allow for other providers besides Google, with
 | 	// These options allow for other providers besides Google, with
 | ||||||
| 	// potential overrides.
 | 	// potential overrides.
 | ||||||
|  | @ -549,9 +551,6 @@ func legacyProviderFlagSet() *pflag.FlagSet { | ||||||
| 	flagSet.StringSlice("github-user", []string{}, "allow users with these usernames to login even if they do not belong to the specified org and team or collaborators (may be given multiple times)") | 	flagSet.StringSlice("github-user", []string{}, "allow users with these usernames to login even if they do not belong to the specified org and team or collaborators (may be given multiple times)") | ||||||
| 	flagSet.StringSlice("gitlab-group", []string{}, "restrict logins to members of this group (may be given multiple times)") | 	flagSet.StringSlice("gitlab-group", []string{}, "restrict logins to members of this group (may be given multiple times)") | ||||||
| 	flagSet.StringSlice("gitlab-project", []string{}, "restrict logins to members of this project (may be given multiple times) (eg `group/project=accesslevel`). Access level should be a value matching Gitlab access levels (see https://docs.gitlab.com/ee/api/members.html#valid-access-levels), defaulted to 20 if absent") | 	flagSet.StringSlice("gitlab-project", []string{}, "restrict logins to members of this project (may be given multiple times) (eg `group/project=accesslevel`). Access level should be a value matching Gitlab access levels (see https://docs.gitlab.com/ee/api/members.html#valid-access-levels), defaulted to 20 if absent") | ||||||
| 	flagSet.StringSlice("google-group", []string{}, "restrict logins to members of this google group (may be given multiple times).") |  | ||||||
| 	flagSet.String("google-admin-email", "", "the google admin to impersonate for api calls") |  | ||||||
| 	flagSet.String("google-service-account-json", "", "the path to the service account json credentials") |  | ||||||
| 	flagSet.String("client-id", "", "the OAuth Client ID: ie: \"123456.apps.googleusercontent.com\"") | 	flagSet.String("client-id", "", "the OAuth Client ID: ie: \"123456.apps.googleusercontent.com\"") | ||||||
| 	flagSet.String("client-secret", "", "the OAuth Client Secret") | 	flagSet.String("client-secret", "", "the OAuth Client Secret") | ||||||
| 	flagSet.String("client-secret-file", "", "the file with OAuth Client Secret") | 	flagSet.String("client-secret-file", "", "the file with OAuth Client Secret") | ||||||
|  | @ -592,6 +591,17 @@ func legacyProviderFlagSet() *pflag.FlagSet { | ||||||
| 	return flagSet | 	return flagSet | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func legacyGoogleFlagSet() *pflag.FlagSet { | ||||||
|  | 	flagSet := pflag.NewFlagSet("google", pflag.ExitOnError) | ||||||
|  | 
 | ||||||
|  | 	flagSet.StringSlice("google-group", []string{}, "restrict logins to members of this google group (may be given multiple times).") | ||||||
|  | 	flagSet.String("google-admin-email", "", "the google admin to impersonate for api calls") | ||||||
|  | 	flagSet.String("google-service-account-json", "", "the path to the service account json credentials") | ||||||
|  | 	flagSet.String("google-use-application-default-credentials", "", "use application default credentials instead of service account json (i.e. GKE Workload Identity)") | ||||||
|  | 
 | ||||||
|  | 	return flagSet | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func (l LegacyServer) convert() (Server, Server) { | func (l LegacyServer) convert() (Server, Server) { | ||||||
| 	appServer := Server{ | 	appServer := Server{ | ||||||
| 		BindAddress:       l.HTTPAddress, | 		BindAddress:       l.HTTPAddress, | ||||||
|  | @ -718,9 +728,10 @@ func (l *LegacyProvider) convert() (Providers, error) { | ||||||
| 		} | 		} | ||||||
| 	case "google": | 	case "google": | ||||||
| 		provider.GoogleConfig = GoogleOptions{ | 		provider.GoogleConfig = GoogleOptions{ | ||||||
| 			Groups:             l.GoogleGroups, | 			Groups:                           l.GoogleGroups, | ||||||
| 			AdminEmail:         l.GoogleAdminEmail, | 			AdminEmail:                       l.GoogleAdminEmail, | ||||||
| 			ServiceAccountJSON: l.GoogleServiceAccountJSON, | 			ServiceAccountJSON:               l.GoogleServiceAccountJSON, | ||||||
|  | 			UseApplicationDefaultCredentials: l.GoogleUseApplicationDefaultCredentials, | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -189,6 +189,8 @@ type GoogleOptions struct { | ||||||
| 	AdminEmail string `json:"adminEmail,omitempty"` | 	AdminEmail string `json:"adminEmail,omitempty"` | ||||||
| 	// ServiceAccountJSON is the path to the service account json credentials
 | 	// ServiceAccountJSON is the path to the service account json credentials
 | ||||||
| 	ServiceAccountJSON string `json:"serviceAccountJson,omitempty"` | 	ServiceAccountJSON string `json:"serviceAccountJson,omitempty"` | ||||||
|  | 	// UseApplicationDefaultCredentials is a boolean whether to use Application Default Credentials instead of a ServiceAccountJSON
 | ||||||
|  | 	UseApplicationDefaultCredentials bool `json:"useApplicationDefaultCredentials,omitempty"` | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type OIDCOptions struct { | type OIDCOptions struct { | ||||||
|  |  | ||||||
|  | @ -63,7 +63,7 @@ func TestGoogleGroupOptions(t *testing.T) { | ||||||
| 
 | 
 | ||||||
| 	expected := errorMsg([]string{ | 	expected := errorMsg([]string{ | ||||||
| 		"missing setting: google-admin-email", | 		"missing setting: google-admin-email", | ||||||
| 		"missing setting: google-service-account-json"}) | 		"missing setting: google-service-account-json or google-use-application-default-credentials"}) | ||||||
| 	assert.Equal(t, expected, err.Error()) | 	assert.Equal(t, expected, err.Error()) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -76,7 +76,7 @@ func TestGoogleGroupInvalidFile(t *testing.T) { | ||||||
| 	assert.NotEqual(t, nil, err) | 	assert.NotEqual(t, nil, err) | ||||||
| 
 | 
 | ||||||
| 	expected := errorMsg([]string{ | 	expected := errorMsg([]string{ | ||||||
| 		"invalid Google credentials file: file_doesnt_exist.json", | 		"Google credentials file not found: file_doesnt_exist.json", | ||||||
| 	}) | 	}) | ||||||
| 	assert.Equal(t, expected, err.Error()) | 	assert.Equal(t, expected, err.Error()) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -66,20 +66,32 @@ func validateProvider(provider options.Provider, providerIDs map[string]struct{} | ||||||
| 
 | 
 | ||||||
| func validateGoogleConfig(provider options.Provider) []string { | func validateGoogleConfig(provider options.Provider) []string { | ||||||
| 	msgs := []string{} | 	msgs := []string{} | ||||||
| 	if len(provider.GoogleConfig.Groups) > 0 || | 
 | ||||||
| 		provider.GoogleConfig.AdminEmail != "" || | 	hasGoogleGroups := len(provider.GoogleConfig.Groups) >= 1 | ||||||
| 		provider.GoogleConfig.ServiceAccountJSON != "" { | 	hasAdminEmail := provider.GoogleConfig.AdminEmail != "" | ||||||
| 		if len(provider.GoogleConfig.Groups) < 1 { | 	hasSAJSON := provider.GoogleConfig.ServiceAccountJSON != "" | ||||||
| 			msgs = append(msgs, "missing setting: google-group") | 	useADC := provider.GoogleConfig.UseApplicationDefaultCredentials | ||||||
| 		} | 
 | ||||||
| 		if provider.GoogleConfig.AdminEmail == "" { | 	if !hasGoogleGroups && !hasAdminEmail && !hasSAJSON && !useADC { | ||||||
| 			msgs = append(msgs, "missing setting: google-admin-email") | 		return msgs | ||||||
| 		} | 	} | ||||||
| 		if provider.GoogleConfig.ServiceAccountJSON == "" { | 
 | ||||||
| 			msgs = append(msgs, "missing setting: google-service-account-json") | 	if !hasGoogleGroups { | ||||||
| 		} else if _, err := os.Stat(provider.GoogleConfig.ServiceAccountJSON); err != nil { | 		msgs = append(msgs, "missing setting: google-group") | ||||||
| 			msgs = append(msgs, fmt.Sprintf("invalid Google credentials file: %s", provider.GoogleConfig.ServiceAccountJSON)) | 	} | ||||||
|  | 	if !hasAdminEmail { | ||||||
|  | 		msgs = append(msgs, "missing setting: google-admin-email") | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	_, err := os.Stat(provider.GoogleConfig.ServiceAccountJSON) | ||||||
|  | 	if !useADC { | ||||||
|  | 		if !hasSAJSON { | ||||||
|  | 			msgs = append(msgs, "missing setting: google-service-account-json or google-use-application-default-credentials") | ||||||
|  | 		} else if err != nil { | ||||||
|  | 			msgs = append(msgs, fmt.Sprintf("Google credentials file not found: %s", provider.GoogleConfig.ServiceAccountJSON)) | ||||||
| 		} | 		} | ||||||
|  | 	} else if hasSAJSON { | ||||||
|  | 		msgs = append(msgs, "invalid setting: can't use both google-service-account-json and google-use-application-default-credentials") | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return msgs | 	return msgs | ||||||
|  |  | ||||||
|  | @ -8,6 +8,7 @@ import ( | ||||||
| 	"errors" | 	"errors" | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"io" | 	"io" | ||||||
|  | 	"net/http" | ||||||
| 	"net/url" | 	"net/url" | ||||||
| 	"os" | 	"os" | ||||||
| 	"strings" | 	"strings" | ||||||
|  | @ -17,6 +18,7 @@ import ( | ||||||
| 	"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions" | 	"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions" | ||||||
| 	"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger" | 	"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger" | ||||||
| 	"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/requests" | 	"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/requests" | ||||||
|  | 	"golang.org/x/oauth2" | ||||||
| 	"golang.org/x/oauth2/google" | 	"golang.org/x/oauth2/google" | ||||||
| 	admin "google.golang.org/api/admin/directory/v1" | 	admin "google.golang.org/api/admin/directory/v1" | ||||||
| 	"google.golang.org/api/googleapi" | 	"google.golang.org/api/googleapi" | ||||||
|  | @ -98,17 +100,13 @@ func NewGoogleProvider(p *ProviderData, opts options.GoogleOptions) (*GoogleProv | ||||||
| 		}, | 		}, | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if opts.ServiceAccountJSON != "" { | 	if opts.ServiceAccountJSON != "" || opts.UseApplicationDefaultCredentials { | ||||||
| 		file, err := os.Open(opts.ServiceAccountJSON) |  | ||||||
| 		if err != nil { |  | ||||||
| 			return nil, fmt.Errorf("invalid Google credentials file: %s", opts.ServiceAccountJSON) |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		// Backwards compatibility with `--google-group` option
 | 		// Backwards compatibility with `--google-group` option
 | ||||||
| 		if len(opts.Groups) > 0 { | 		if len(opts.Groups) > 0 { | ||||||
| 			provider.setAllowedGroups(opts.Groups) | 			provider.setAllowedGroups(opts.Groups) | ||||||
| 		} | 		} | ||||||
| 		provider.setGroupRestriction(opts.Groups, opts.AdminEmail, file) | 
 | ||||||
|  | 		provider.setGroupRestriction(opts) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return provider, nil | 	return provider, nil | ||||||
|  | @ -214,13 +212,13 @@ func (p *GoogleProvider) EnrichSession(_ context.Context, s *sessions.SessionSta | ||||||
| // account credentials.
 | // account credentials.
 | ||||||
| //
 | //
 | ||||||
| // TODO (@NickMeves) - Unit Test this OR refactor away from groupValidator func
 | // TODO (@NickMeves) - Unit Test this OR refactor away from groupValidator func
 | ||||||
| func (p *GoogleProvider) setGroupRestriction(groups []string, adminEmail string, credentialsReader io.Reader) { | func (p *GoogleProvider) setGroupRestriction(opts options.GoogleOptions) { | ||||||
| 	adminService := getAdminService(adminEmail, credentialsReader) | 	adminService := getAdminService(opts) | ||||||
| 	p.groupValidator = func(s *sessions.SessionState) bool { | 	p.groupValidator = func(s *sessions.SessionState) bool { | ||||||
| 		// Reset our saved Groups in case membership changed
 | 		// Reset our saved Groups in case membership changed
 | ||||||
| 		// This is used by `Authorize` on every request
 | 		// This is used by `Authorize` on every request
 | ||||||
| 		s.Groups = make([]string, 0, len(groups)) | 		s.Groups = make([]string, 0, len(opts.Groups)) | ||||||
| 		for _, group := range groups { | 		for _, group := range opts.Groups { | ||||||
| 			if userInGroup(adminService, group, s.Email) { | 			if userInGroup(adminService, group, s.Email) { | ||||||
| 				s.Groups = append(s.Groups, group) | 				s.Groups = append(s.Groups, group) | ||||||
| 			} | 			} | ||||||
|  | @ -229,19 +227,37 @@ func (p *GoogleProvider) setGroupRestriction(groups []string, adminEmail string, | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func getAdminService(adminEmail string, credentialsReader io.Reader) *admin.Service { | func getAdminService(opts options.GoogleOptions) *admin.Service { | ||||||
| 	data, err := io.ReadAll(credentialsReader) |  | ||||||
| 	if err != nil { |  | ||||||
| 		logger.Fatal("can't read Google credentials file:", err) |  | ||||||
| 	} |  | ||||||
| 	conf, err := google.JWTConfigFromJSON(data, admin.AdminDirectoryUserReadonlyScope, admin.AdminDirectoryGroupReadonlyScope) |  | ||||||
| 	if err != nil { |  | ||||||
| 		logger.Fatal("can't load Google credentials file:", err) |  | ||||||
| 	} |  | ||||||
| 	conf.Subject = adminEmail |  | ||||||
| 
 |  | ||||||
| 	ctx := context.Background() | 	ctx := context.Background() | ||||||
| 	client := conf.Client(ctx) | 	var client *http.Client | ||||||
|  | 	if opts.UseApplicationDefaultCredentials { | ||||||
|  | 		ts, err := google.FindDefaultCredentialsWithParams(ctx, google.CredentialsParams{ | ||||||
|  | 			Subject: opts.AdminEmail, | ||||||
|  | 			Scopes:  []string{admin.AdminDirectoryGroupReadonlyScope, admin.AdminDirectoryUserReadonlyScope}, | ||||||
|  | 		}) | ||||||
|  | 		if err != nil { | ||||||
|  | 			logger.Fatal("failed to fetch application default credentials: ", err) | ||||||
|  | 		} | ||||||
|  | 		client = oauth2.NewClient(ctx, ts.TokenSource) | ||||||
|  | 	} else { | ||||||
|  | 		credentialsReader, err := os.Open(opts.ServiceAccountJSON) | ||||||
|  | 		if err != nil { | ||||||
|  | 			logger.Fatal("couldn't open Google credentials file: ", err) | ||||||
|  | 			return nil | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		data, err := io.ReadAll(credentialsReader) | ||||||
|  | 		if err != nil { | ||||||
|  | 			logger.Fatal("can't read Google credentials file:", err) | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		conf, err := google.JWTConfigFromJSON(data, admin.AdminDirectoryUserReadonlyScope, admin.AdminDirectoryGroupReadonlyScope) | ||||||
|  | 		if err != nil { | ||||||
|  | 			logger.Fatal("can't load Google credentials file:", err) | ||||||
|  | 		} | ||||||
|  | 		conf.Subject = opts.AdminEmail | ||||||
|  | 		client = conf.Client(ctx) | ||||||
|  | 	} | ||||||
| 	adminService, err := admin.NewService(ctx, option.WithHTTPClient(client)) | 	adminService, err := admin.NewService(ctx, option.WithHTTPClient(client)) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		logger.Fatal(err) | 		logger.Fatal(err) | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue