From 1e461b6a647827dab66196a3c5e25be78017f51b Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Mon, 29 Apr 2024 14:19:02 -0500 Subject: [PATCH 1/2] chore: dynamically determine gitlab external auth defaults Static defaults work for github cloud, but not self hosted. Self hosted setups will now have sane defaults if omitted. --- coderd/externalauth/externalauth.go | 50 +++++++-- .../externalauth_internal_test.go | 105 ++++++++++++++++++ 2 files changed, 146 insertions(+), 9 deletions(-) diff --git a/coderd/externalauth/externalauth.go b/coderd/externalauth/externalauth.go index e430a798e6a29..85e53f2e91f33 100644 --- a/coderd/externalauth/externalauth.go +++ b/coderd/externalauth/externalauth.go @@ -563,6 +563,9 @@ func applyDefaultsToConfig(config *codersdk.ExternalAuthConfig) { // Dynamic defaults switch codersdk.EnhancedExternalAuthProvider(config.Type) { + case codersdk.EnhancedExternalAuthProviderGitLab: + copyDefaultSettings(config, gitlabDefaults(config)) + return case codersdk.EnhancedExternalAuthProviderBitBucketServer: copyDefaultSettings(config, bitbucketServerDefaults(config)) return @@ -667,6 +670,44 @@ func bitbucketServerDefaults(config *codersdk.ExternalAuthConfig) codersdk.Exter return defaults } +// gitlabDefaults returns a static config if using the gitlab cloud offering. +// The values are dynamic if using a self-hosted gitlab. +// When the decision is not obvious, just defer to the cloud defaults. +// Any user specific fields will override this if provided. +func gitlabDefaults(config *codersdk.ExternalAuthConfig) codersdk.ExternalAuthConfig { + cloud := codersdk.ExternalAuthConfig{ + AuthURL: "https://gitlab.com/oauth/authorize", + TokenURL: "https://gitlab.com/oauth/token", + ValidateURL: "https://gitlab.com/oauth/token/info", + DisplayName: "GitLab", + DisplayIcon: "/icon/gitlab.svg", + Regex: `^(https?://)?gitlab\.com(/.*)?$`, + Scopes: []string{"write_repository"}, + } + + if config.AuthURL == "" || config.AuthURL == cloud.AuthURL { + return cloud + } + + au, err := url.Parse(config.AuthURL) + if err != nil || au.Host == "gitlab.com" { + // If the AuthURL is not a valid URL or is using the cloud, + // use the cloud static defaults. + return cloud + } + + // At this point, assume it is self-hosted and use the AuthURL + return codersdk.ExternalAuthConfig{ + DisplayName: cloud.DisplayName, + Scopes: cloud.Scopes, + DisplayIcon: cloud.DisplayIcon, + AuthURL: au.ResolveReference(&url.URL{Path: "/oauth/authorize"}).String(), + TokenURL: au.ResolveReference(&url.URL{Path: "/oauth/token"}).String(), + ValidateURL: au.ResolveReference(&url.URL{Path: "/oauth/token/info"}).String(), + Regex: fmt.Sprintf(`^(https?://)?%s(/.*)?$`, strings.ReplaceAll(au.Host, ".", `\.`)), + } +} + func jfrogArtifactoryDefaults(config *codersdk.ExternalAuthConfig) codersdk.ExternalAuthConfig { defaults := codersdk.ExternalAuthConfig{ DisplayName: "JFrog Artifactory", @@ -789,15 +830,6 @@ var staticDefaults = map[codersdk.EnhancedExternalAuthProvider]codersdk.External Regex: `^(https?://)?bitbucket\.org(/.*)?$`, Scopes: []string{"account", "repository:write"}, }, - codersdk.EnhancedExternalAuthProviderGitLab: { - AuthURL: "https://gitlab.com/oauth/authorize", - TokenURL: "https://gitlab.com/oauth/token", - ValidateURL: "https://gitlab.com/oauth/token/info", - DisplayName: "GitLab", - DisplayIcon: "/icon/gitlab.svg", - Regex: `^(https?://)?gitlab\.com(/.*)?$`, - Scopes: []string{"write_repository"}, - }, codersdk.EnhancedExternalAuthProviderGitHub: { AuthURL: xgithub.Endpoint.AuthURL, TokenURL: xgithub.Endpoint.TokenURL, diff --git a/coderd/externalauth/externalauth_internal_test.go b/coderd/externalauth/externalauth_internal_test.go index 5bb14a4c8f697..6c3d172b77b77 100644 --- a/coderd/externalauth/externalauth_internal_test.go +++ b/coderd/externalauth/externalauth_internal_test.go @@ -8,6 +8,111 @@ import ( "github.com/coder/coder/v2/codersdk" ) +func TestGitlabDefaults(t *testing.T) { + t.Parallel() + + // The default cloud setup. Copying this here as hard coded + // values. + cloud := codersdk.ExternalAuthConfig{ + Type: string(codersdk.EnhancedExternalAuthProviderGitLab), + ID: string(codersdk.EnhancedExternalAuthProviderGitLab), + AuthURL: "https://gitlab.com/oauth/authorize", + TokenURL: "https://gitlab.com/oauth/token", + ValidateURL: "https://gitlab.com/oauth/token/info", + DisplayName: "GitLab", + DisplayIcon: "/icon/gitlab.svg", + Regex: `^(https?://)?gitlab\.com(/.*)?$`, + Scopes: []string{"write_repository"}, + } + + tests := []struct { + name string + input codersdk.ExternalAuthConfig + expected codersdk.ExternalAuthConfig + mutateExpected func(*codersdk.ExternalAuthConfig) + }{ + // Cloud + { + name: "OnlyType", + input: codersdk.ExternalAuthConfig{ + Type: string(codersdk.EnhancedExternalAuthProviderGitLab), + }, + expected: cloud, + }, + { + // If someone was to manually configure the gitlab cli. + name: "CloudByConfig", + input: codersdk.ExternalAuthConfig{ + Type: string(codersdk.EnhancedExternalAuthProviderGitLab), + AuthURL: "https://gitlab.com/oauth/authorize", + }, + expected: cloud, + }, + { + // Changing some of the defaults of the cloud option + name: "CloudWithChanges", + input: codersdk.ExternalAuthConfig{ + Type: string(codersdk.EnhancedExternalAuthProviderGitLab), + // Adding an extra query param intentionally to break simple + // string comparisons. + AuthURL: "https://gitlab.com/oauth/authorize?foo=bar", + DisplayName: "custom", + Regex: ".*", + }, + expected: cloud, + mutateExpected: func(config *codersdk.ExternalAuthConfig) { + config.DisplayName = "custom" + config.Regex = ".*" + }, + }, + // Self-hosted + { + // Dynamically figures out the Validate, Token, and Regex fields. + name: "SelfHostedOnlyAuthURL", + input: codersdk.ExternalAuthConfig{ + Type: string(codersdk.EnhancedExternalAuthProviderGitLab), + AuthURL: "https://gitlab.company.org/oauth/authorize?foo=bar", + }, + expected: cloud, + mutateExpected: func(config *codersdk.ExternalAuthConfig) { + config.AuthURL = "https://gitlab.company.org/oauth/authorize?foo=bar" + config.ValidateURL = "https://gitlab.company.org/oauth/token/info" + config.TokenURL = "https://gitlab.company.org/oauth/token" + config.Regex = `^(https?://)?gitlab\.company\.org(/.*)?$` + }, + }, + { + // Strange values + name: "RandomValues", + input: codersdk.ExternalAuthConfig{ + Type: string(codersdk.EnhancedExternalAuthProviderGitLab), + AuthURL: "https://auth.com/auth", + ValidateURL: "https://validate.com/validate", + TokenURL: "https://token.com/token", + Regex: "random", + }, + expected: cloud, + mutateExpected: func(config *codersdk.ExternalAuthConfig) { + config.AuthURL = "https://auth.com/auth" + config.ValidateURL = "https://validate.com/validate" + config.TokenURL = "https://token.com/token" + config.Regex = `random` + }, + }, + } + for _, c := range tests { + c := c + t.Run(c.name, func(t *testing.T) { + t.Parallel() + applyDefaultsToConfig(&c.input) + if c.mutateExpected != nil { + c.mutateExpected(&c.expected) + } + require.Equal(t, c.input, c.expected) + }) + } +} + func Test_bitbucketServerConfigDefaults(t *testing.T) { t.Parallel() From 0b15643855e3de255a12c23a22cdea0b1ef75aff Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Mon, 29 Apr 2024 14:35:41 -0500 Subject: [PATCH 2/2] Fixup test --- coderd/externalauth/externalauth_internal_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/coderd/externalauth/externalauth_internal_test.go b/coderd/externalauth/externalauth_internal_test.go index 6c3d172b77b77..26515ff78f215 100644 --- a/coderd/externalauth/externalauth_internal_test.go +++ b/coderd/externalauth/externalauth_internal_test.go @@ -61,6 +61,7 @@ func TestGitlabDefaults(t *testing.T) { }, expected: cloud, mutateExpected: func(config *codersdk.ExternalAuthConfig) { + config.AuthURL = "https://gitlab.com/oauth/authorize?foo=bar" config.DisplayName = "custom" config.Regex = ".*" },