mirror of https://github.com/coder/coder.git
feat: implement bitbucket-server external auth defaults (#10520)
* feat: implement bitbucket-server external auth defaults Bitbucket cloud != Bitbucket server Add reasonable defaults for server * change "bitbucket" to "bitbucket-cloud"
This commit is contained in:
parent
71153e2317
commit
aded7b1513
|
@ -9,6 +9,7 @@ import (
|
|||
"net/http"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"golang.org/x/oauth2"
|
||||
|
@ -494,7 +495,36 @@ func ConvertConfig(entries []codersdk.ExternalAuthConfig, accessURL *url.URL) ([
|
|||
|
||||
// applyDefaultsToConfig applies defaults to the config entry.
|
||||
func applyDefaultsToConfig(config *codersdk.ExternalAuthConfig) {
|
||||
defaults := defaults[codersdk.EnhancedExternalAuthProvider(config.Type)]
|
||||
configType := codersdk.EnhancedExternalAuthProvider(config.Type)
|
||||
if configType == "bitbucket" {
|
||||
// For backwards compatibility, we need to support the "bitbucket" string.
|
||||
configType = codersdk.EnhancedExternalAuthProviderBitBucketCloud
|
||||
defer func() {
|
||||
// The config type determines the config ID (if unset). So change the legacy
|
||||
// type to the correct new type after the defaults have been configured.
|
||||
config.Type = string(codersdk.EnhancedExternalAuthProviderBitBucketCloud)
|
||||
}()
|
||||
}
|
||||
// If static defaults exist, apply them.
|
||||
if defaults, ok := staticDefaults[configType]; ok {
|
||||
copyDefaultSettings(config, defaults)
|
||||
return
|
||||
}
|
||||
|
||||
// Dynamic defaults
|
||||
switch codersdk.EnhancedExternalAuthProvider(config.Type) {
|
||||
case codersdk.EnhancedExternalAuthProviderBitBucketServer:
|
||||
copyDefaultSettings(config, bitbucketServerDefaults(config))
|
||||
return
|
||||
default:
|
||||
// No defaults for this type. We still want to run this apply with
|
||||
// an empty set of defaults.
|
||||
copyDefaultSettings(config, codersdk.ExternalAuthConfig{})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func copyDefaultSettings(config *codersdk.ExternalAuthConfig, defaults codersdk.ExternalAuthConfig) {
|
||||
if config.AuthURL == "" {
|
||||
config.AuthURL = defaults.AuthURL
|
||||
}
|
||||
|
@ -542,7 +572,43 @@ func applyDefaultsToConfig(config *codersdk.ExternalAuthConfig) {
|
|||
}
|
||||
}
|
||||
|
||||
var defaults = map[codersdk.EnhancedExternalAuthProvider]codersdk.ExternalAuthConfig{
|
||||
func bitbucketServerDefaults(config *codersdk.ExternalAuthConfig) codersdk.ExternalAuthConfig {
|
||||
defaults := codersdk.ExternalAuthConfig{
|
||||
DisplayName: "Bitbucket Server",
|
||||
Scopes: []string{"PUBLIC_REPOS", "REPO_READ", "REPO_WRITE"},
|
||||
DisplayIcon: "/icon/bitbucket.svg",
|
||||
}
|
||||
// Bitbucket servers will have some base url, e.g. https://bitbucket.coder.com.
|
||||
// We will grab this from the Auth URL. This choice is a bit arbitrary,
|
||||
// but we need to require at least 1 field to be populated.
|
||||
if config.AuthURL == "" {
|
||||
// No auth url, means we cannot guess the urls.
|
||||
return defaults
|
||||
}
|
||||
|
||||
auth, err := url.Parse(config.AuthURL)
|
||||
if err != nil {
|
||||
// We need a valid URL to continue with.
|
||||
return defaults
|
||||
}
|
||||
|
||||
// Populate Regex, ValidateURL, and TokenURL.
|
||||
// Default regex should be anything using the same host as the auth url.
|
||||
defaults.Regex = fmt.Sprintf(`^(https?://)?%s(/.*)?$`, strings.ReplaceAll(auth.Host, ".", `\.`))
|
||||
|
||||
tokenURL := auth.ResolveReference(&url.URL{Path: "/rest/oauth2/latest/token"})
|
||||
defaults.TokenURL = tokenURL.String()
|
||||
|
||||
// validate needs to return a 200 when logged in and a 401 when unauthenticated.
|
||||
// This endpoint returns the count of the number of PR's in the authenticated
|
||||
// user's inbox. Which will work perfectly for our use case.
|
||||
validate := auth.ResolveReference(&url.URL{Path: "/rest/api/latest/inbox/pull-requests/count"})
|
||||
defaults.ValidateURL = validate.String()
|
||||
|
||||
return defaults
|
||||
}
|
||||
|
||||
var staticDefaults = map[codersdk.EnhancedExternalAuthProvider]codersdk.ExternalAuthConfig{
|
||||
codersdk.EnhancedExternalAuthProviderAzureDevops: {
|
||||
AuthURL: "https://app.vssps.visualstudio.com/oauth2/authorize",
|
||||
TokenURL: "https://app.vssps.visualstudio.com/oauth2/token",
|
||||
|
@ -551,7 +617,7 @@ var defaults = map[codersdk.EnhancedExternalAuthProvider]codersdk.ExternalAuthCo
|
|||
Regex: `^(https?://)?dev\.azure\.com(/.*)?$`,
|
||||
Scopes: []string{"vso.code_write"},
|
||||
},
|
||||
codersdk.EnhancedExternalAuthProviderBitBucket: {
|
||||
codersdk.EnhancedExternalAuthProviderBitBucketCloud: {
|
||||
AuthURL: "https://bitbucket.org/site/oauth2/authorize",
|
||||
TokenURL: "https://bitbucket.org/site/oauth2/access_token",
|
||||
ValidateURL: "https://api.bitbucket.org/2.0/user",
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
package externalauth
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/coder/coder/v2/codersdk"
|
||||
)
|
||||
|
||||
func Test_bitbucketServerConfigDefaults(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
bbType := string(codersdk.EnhancedExternalAuthProviderBitBucketServer)
|
||||
tests := []struct {
|
||||
name string
|
||||
config *codersdk.ExternalAuthConfig
|
||||
expected codersdk.ExternalAuthConfig
|
||||
}{
|
||||
{
|
||||
// Very few fields are statically defined for Bitbucket Server.
|
||||
name: "EmptyBitbucketServer",
|
||||
config: &codersdk.ExternalAuthConfig{
|
||||
Type: bbType,
|
||||
},
|
||||
expected: codersdk.ExternalAuthConfig{
|
||||
Type: bbType,
|
||||
ID: bbType,
|
||||
DisplayName: "Bitbucket Server",
|
||||
Scopes: []string{"PUBLIC_REPOS", "REPO_READ", "REPO_WRITE"},
|
||||
DisplayIcon: "/icon/bitbucket.svg",
|
||||
},
|
||||
},
|
||||
{
|
||||
// Only the AuthURL is required for defaults to work.
|
||||
name: "AuthURL",
|
||||
config: &codersdk.ExternalAuthConfig{
|
||||
Type: bbType,
|
||||
AuthURL: "https://bitbucket.example.com/login/oauth/authorize",
|
||||
},
|
||||
expected: codersdk.ExternalAuthConfig{
|
||||
Type: bbType,
|
||||
ID: bbType,
|
||||
AuthURL: "https://bitbucket.example.com/login/oauth/authorize",
|
||||
TokenURL: "https://bitbucket.example.com/rest/oauth2/latest/token",
|
||||
ValidateURL: "https://bitbucket.example.com/rest/api/latest/inbox/pull-requests/count",
|
||||
Scopes: []string{"PUBLIC_REPOS", "REPO_READ", "REPO_WRITE"},
|
||||
Regex: `^(https?://)?bitbucket\.example\.com(/.*)?$`,
|
||||
DisplayName: "Bitbucket Server",
|
||||
DisplayIcon: "/icon/bitbucket.svg",
|
||||
},
|
||||
},
|
||||
{
|
||||
// Ensure backwards compatibility. The type should update to "bitbucket-cloud",
|
||||
// but the ID and other fields should remain the same.
|
||||
name: "BitbucketLegacy",
|
||||
config: &codersdk.ExternalAuthConfig{
|
||||
Type: "bitbucket",
|
||||
},
|
||||
expected: codersdk.ExternalAuthConfig{
|
||||
Type: string(codersdk.EnhancedExternalAuthProviderBitBucketCloud),
|
||||
ID: "bitbucket", // Legacy ID remains unchanged
|
||||
AuthURL: "https://bitbucket.org/site/oauth2/authorize",
|
||||
TokenURL: "https://bitbucket.org/site/oauth2/access_token",
|
||||
ValidateURL: "https://api.bitbucket.org/2.0/user",
|
||||
DisplayName: "BitBucket",
|
||||
DisplayIcon: "/icon/bitbucket.svg",
|
||||
Regex: `^(https?://)?bitbucket\.org(/.*)?$`,
|
||||
Scopes: []string{"account", "repository:write"},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
applyDefaultsToConfig(tt.config)
|
||||
require.Equal(t, tt.expected, *tt.config)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -2452,7 +2452,8 @@ func createExternalAuthResponse(typ, token string, extra pqtype.NullRawMessage)
|
|||
Username: "oauth2",
|
||||
Password: token,
|
||||
}
|
||||
case string(codersdk.EnhancedExternalAuthProviderBitBucket):
|
||||
case string(codersdk.EnhancedExternalAuthProviderBitBucketCloud), string(codersdk.EnhancedExternalAuthProviderBitBucketServer):
|
||||
// The string "bitbucket" was a legacy parameter that needs to still be supported.
|
||||
// https://support.atlassian.com/bitbucket-cloud/docs/use-oauth-on-bitbucket-cloud/#Cloning-a-repository-with-an-access-token
|
||||
resp = agentsdk.ExternalAuthResponse{
|
||||
Username: "x-token-auth",
|
||||
|
|
|
@ -21,7 +21,8 @@ func (e EnhancedExternalAuthProvider) Git() bool {
|
|||
switch e {
|
||||
case EnhancedExternalAuthProviderGitHub,
|
||||
EnhancedExternalAuthProviderGitLab,
|
||||
EnhancedExternalAuthProviderBitBucket,
|
||||
EnhancedExternalAuthProviderBitBucketCloud,
|
||||
EnhancedExternalAuthProviderBitBucketServer,
|
||||
EnhancedExternalAuthProviderAzureDevops:
|
||||
return true
|
||||
default:
|
||||
|
@ -33,9 +34,12 @@ const (
|
|||
EnhancedExternalAuthProviderAzureDevops EnhancedExternalAuthProvider = "azure-devops"
|
||||
EnhancedExternalAuthProviderGitHub EnhancedExternalAuthProvider = "github"
|
||||
EnhancedExternalAuthProviderGitLab EnhancedExternalAuthProvider = "gitlab"
|
||||
EnhancedExternalAuthProviderBitBucket EnhancedExternalAuthProvider = "bitbucket"
|
||||
EnhancedExternalAuthProviderSlack EnhancedExternalAuthProvider = "slack"
|
||||
EnhancedExternalAuthProviderJFrog EnhancedExternalAuthProvider = "jfrog"
|
||||
// EnhancedExternalAuthProviderBitBucketCloud is the Bitbucket Cloud provider.
|
||||
// Not to be confused with the self-hosted 'EnhancedExternalAuthProviderBitBucketServer'
|
||||
EnhancedExternalAuthProviderBitBucketCloud EnhancedExternalAuthProvider = "bitbucket-cloud"
|
||||
EnhancedExternalAuthProviderBitBucketServer EnhancedExternalAuthProvider = "bitbucket-server"
|
||||
EnhancedExternalAuthProviderSlack EnhancedExternalAuthProvider = "slack"
|
||||
EnhancedExternalAuthProviderJFrog EnhancedExternalAuthProvider = "jfrog"
|
||||
)
|
||||
|
||||
type ExternalAuth struct {
|
||||
|
|
|
@ -1683,14 +1683,16 @@ export const DisplayApps: DisplayApp[] = [
|
|||
// From codersdk/externalauth.go
|
||||
export type EnhancedExternalAuthProvider =
|
||||
| "azure-devops"
|
||||
| "bitbucket"
|
||||
| "bitbucket-cloud"
|
||||
| "bitbucket-server"
|
||||
| "github"
|
||||
| "gitlab"
|
||||
| "jfrog"
|
||||
| "slack";
|
||||
export const EnhancedExternalAuthProviders: EnhancedExternalAuthProvider[] = [
|
||||
"azure-devops",
|
||||
"bitbucket",
|
||||
"bitbucket-cloud",
|
||||
"bitbucket-server",
|
||||
"github",
|
||||
"gitlab",
|
||||
"jfrog",
|
||||
|
|
Loading…
Reference in New Issue