mirror of https://github.com/coder/coder.git
chore: implement yaml parsing for external auth configs (#11268)
* chore: yaml parsing for external auth configs * Also unmarshal and check the output again
This commit is contained in:
parent
016b3ef5a2
commit
c1451ca4da
|
@ -440,6 +440,9 @@ client:
|
|||
# Support links to display in the top right drop down menu.
|
||||
# (default: <unset>, type: struct[[]codersdk.LinkConfig])
|
||||
supportLinks: []
|
||||
# External Authentication providers.
|
||||
# (default: <unset>, type: struct[[]codersdk.ExternalAuthConfig])
|
||||
externalAuthProviders: []
|
||||
# Hostname of HTTPS server that runs https://github.com/coder/wgtunnel. By
|
||||
# default, this will pick the best available wgtunnel server hosted by Coder. e.g.
|
||||
# "tunnel.example.com".
|
||||
|
|
|
@ -329,33 +329,33 @@ type TraceConfig struct {
|
|||
|
||||
type ExternalAuthConfig struct {
|
||||
// Type is the type of external auth config.
|
||||
Type string `json:"type"`
|
||||
ClientID string `json:"client_id"`
|
||||
Type string `json:"type" yaml:"type"`
|
||||
ClientID string `json:"client_id" yaml:"client_id"`
|
||||
ClientSecret string `json:"-" yaml:"client_secret"`
|
||||
// ID is a unique identifier for the auth config.
|
||||
// It defaults to `type` when not provided.
|
||||
ID string `json:"id"`
|
||||
AuthURL string `json:"auth_url"`
|
||||
TokenURL string `json:"token_url"`
|
||||
ValidateURL string `json:"validate_url"`
|
||||
AppInstallURL string `json:"app_install_url"`
|
||||
AppInstallationsURL string `json:"app_installations_url"`
|
||||
NoRefresh bool `json:"no_refresh"`
|
||||
Scopes []string `json:"scopes"`
|
||||
ExtraTokenKeys []string `json:"extra_token_keys"`
|
||||
DeviceFlow bool `json:"device_flow"`
|
||||
DeviceCodeURL string `json:"device_code_url"`
|
||||
ID string `json:"id" yaml:"id"`
|
||||
AuthURL string `json:"auth_url" yaml:"auth_url"`
|
||||
TokenURL string `json:"token_url" yaml:"token_url"`
|
||||
ValidateURL string `json:"validate_url" yaml:"validate_url"`
|
||||
AppInstallURL string `json:"app_install_url" yaml:"app_install_url"`
|
||||
AppInstallationsURL string `json:"app_installations_url" yaml:"app_installations_url"`
|
||||
NoRefresh bool `json:"no_refresh" yaml:"no_refresh"`
|
||||
Scopes []string `json:"scopes" yaml:"scopes"`
|
||||
ExtraTokenKeys []string `json:"extra_token_keys" yaml:"extra_token_keys"`
|
||||
DeviceFlow bool `json:"device_flow" yaml:"device_flow"`
|
||||
DeviceCodeURL string `json:"device_code_url" yaml:"device_code_url"`
|
||||
// Regex allows API requesters to match an auth config by
|
||||
// a string (e.g. coder.com) instead of by it's type.
|
||||
//
|
||||
// Git clone makes use of this by parsing the URL from:
|
||||
// 'Username for "https://github.com":'
|
||||
// And sending it to the Coder server to match against the Regex.
|
||||
Regex string `json:"regex"`
|
||||
Regex string `json:"regex" yaml:"regex"`
|
||||
// DisplayName is shown in the UI to identify the auth config.
|
||||
DisplayName string `json:"display_name"`
|
||||
DisplayName string `json:"display_name" yaml:"display_name"`
|
||||
// DisplayIcon is a URL to an icon to display in the UI.
|
||||
DisplayIcon string `json:"display_icon"`
|
||||
DisplayIcon string `json:"display_icon" yaml:"display_icon"`
|
||||
}
|
||||
|
||||
type ProvisionerConfig struct {
|
||||
|
@ -1788,7 +1788,7 @@ Write out the current server config as YAML to stdout.`,
|
|||
Description: "External Authentication providers.",
|
||||
// We need extra scrutiny to ensure this works, is documented, and
|
||||
// tested before enabling.
|
||||
// YAML: "gitAuthProviders",
|
||||
YAML: "externalAuthProviders",
|
||||
Value: &c.ExternalAuthConfigs,
|
||||
Hidden: true,
|
||||
},
|
||||
|
|
|
@ -1,11 +1,16 @@
|
|||
package codersdk_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"embed"
|
||||
"fmt"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"gopkg.in/yaml.v3"
|
||||
|
||||
"github.com/coder/coder/v2/cli/clibase"
|
||||
"github.com/coder/coder/v2/codersdk"
|
||||
|
@ -277,3 +282,78 @@ func TestDeploymentValues_DurationFormatNanoseconds(t *testing.T) {
|
|||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
//go:embed testdata/*
|
||||
var testData embed.FS
|
||||
|
||||
func TestExternalAuthYAMLConfig(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
if runtime.GOOS == "windows" {
|
||||
// The windows marshal function uses different line endings.
|
||||
// Not worth the effort getting this to work on windows.
|
||||
t.SkipNow()
|
||||
}
|
||||
|
||||
file := func(t *testing.T, name string) string {
|
||||
data, err := testData.ReadFile(fmt.Sprintf("testdata/%s", name))
|
||||
require.NoError(t, err, "read testdata file %q", name)
|
||||
return string(data)
|
||||
}
|
||||
githubCfg := codersdk.ExternalAuthConfig{
|
||||
Type: "github",
|
||||
ClientID: "client_id",
|
||||
ClientSecret: "client_secret",
|
||||
ID: "id",
|
||||
AuthURL: "https://example.com/auth",
|
||||
TokenURL: "https://example.com/token",
|
||||
ValidateURL: "https://example.com/validate",
|
||||
AppInstallURL: "https://example.com/install",
|
||||
AppInstallationsURL: "https://example.com/installations",
|
||||
NoRefresh: true,
|
||||
Scopes: []string{"user:email", "read:org"},
|
||||
ExtraTokenKeys: []string{"extra", "token"},
|
||||
DeviceFlow: true,
|
||||
DeviceCodeURL: "https://example.com/device",
|
||||
Regex: "^https://example.com/.*$",
|
||||
DisplayName: "GitHub",
|
||||
DisplayIcon: "/static/icons/github.svg",
|
||||
}
|
||||
|
||||
// Input the github section twice for testing a slice of configs.
|
||||
inputYAML := func() string {
|
||||
f := file(t, "githubcfg.yaml")
|
||||
lines := strings.SplitN(f, "\n", 2)
|
||||
// Append github config twice
|
||||
return f + lines[1]
|
||||
}()
|
||||
|
||||
expected := []codersdk.ExternalAuthConfig{
|
||||
githubCfg, githubCfg,
|
||||
}
|
||||
|
||||
dv := codersdk.DeploymentValues{}
|
||||
opts := dv.Options()
|
||||
// replace any tabs with the proper space indentation
|
||||
inputYAML = strings.ReplaceAll(inputYAML, "\t", " ")
|
||||
|
||||
// This is the order things are done in the cli, so just
|
||||
// keep it the same.
|
||||
var n yaml.Node
|
||||
err := yaml.Unmarshal([]byte(inputYAML), &n)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = n.Decode(&opts)
|
||||
require.NoError(t, err)
|
||||
require.ElementsMatchf(t, expected, dv.ExternalAuthConfigs.Value, "from yaml")
|
||||
|
||||
var out bytes.Buffer
|
||||
enc := yaml.NewEncoder(&out)
|
||||
enc.SetIndent(2)
|
||||
err = enc.Encode(dv.ExternalAuthConfigs)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Because we only marshal the 1 section, the correct section name is not applied.
|
||||
output := strings.Replace(out.String(), "value:", "externalAuthProviders:", 1)
|
||||
require.Equal(t, inputYAML, output, "re-marshaled is the same as input")
|
||||
}
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
externalAuthProviders:
|
||||
- type: github
|
||||
client_id: client_id
|
||||
client_secret: client_secret
|
||||
id: id
|
||||
auth_url: https://example.com/auth
|
||||
token_url: https://example.com/token
|
||||
validate_url: https://example.com/validate
|
||||
app_install_url: https://example.com/install
|
||||
app_installations_url: https://example.com/installations
|
||||
no_refresh: true
|
||||
scopes:
|
||||
- user:email
|
||||
- read:org
|
||||
extra_token_keys:
|
||||
- extra
|
||||
- token
|
||||
device_flow: true
|
||||
device_code_url: https://example.com/device
|
||||
regex: ^https://example.com/.*$
|
||||
display_name: GitHub
|
||||
display_icon: /static/icons/github.svg
|
Loading…
Reference in New Issue