Skip to content

Commit c1451ca

Browse files
authored
chore: implement yaml parsing for external auth configs (#11268)
* chore: yaml parsing for external auth configs * Also unmarshal and check the output again
1 parent 016b3ef commit c1451ca

File tree

4 files changed

+122
-17
lines changed

4 files changed

+122
-17
lines changed

cli/testdata/server-config.yaml.golden

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -440,6 +440,9 @@ client:
440440
# Support links to display in the top right drop down menu.
441441
# (default: <unset>, type: struct[[]codersdk.LinkConfig])
442442
supportLinks: []
443+
# External Authentication providers.
444+
# (default: <unset>, type: struct[[]codersdk.ExternalAuthConfig])
445+
externalAuthProviders: []
443446
# Hostname of HTTPS server that runs https://github.com/coder/wgtunnel. By
444447
# default, this will pick the best available wgtunnel server hosted by Coder. e.g.
445448
# "tunnel.example.com".

codersdk/deployment.go

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -329,33 +329,33 @@ type TraceConfig struct {
329329

330330
type ExternalAuthConfig struct {
331331
// Type is the type of external auth config.
332-
Type string `json:"type"`
333-
ClientID string `json:"client_id"`
332+
Type string `json:"type" yaml:"type"`
333+
ClientID string `json:"client_id" yaml:"client_id"`
334334
ClientSecret string `json:"-" yaml:"client_secret"`
335335
// ID is a unique identifier for the auth config.
336336
// It defaults to `type` when not provided.
337-
ID string `json:"id"`
338-
AuthURL string `json:"auth_url"`
339-
TokenURL string `json:"token_url"`
340-
ValidateURL string `json:"validate_url"`
341-
AppInstallURL string `json:"app_install_url"`
342-
AppInstallationsURL string `json:"app_installations_url"`
343-
NoRefresh bool `json:"no_refresh"`
344-
Scopes []string `json:"scopes"`
345-
ExtraTokenKeys []string `json:"extra_token_keys"`
346-
DeviceFlow bool `json:"device_flow"`
347-
DeviceCodeURL string `json:"device_code_url"`
337+
ID string `json:"id" yaml:"id"`
338+
AuthURL string `json:"auth_url" yaml:"auth_url"`
339+
TokenURL string `json:"token_url" yaml:"token_url"`
340+
ValidateURL string `json:"validate_url" yaml:"validate_url"`
341+
AppInstallURL string `json:"app_install_url" yaml:"app_install_url"`
342+
AppInstallationsURL string `json:"app_installations_url" yaml:"app_installations_url"`
343+
NoRefresh bool `json:"no_refresh" yaml:"no_refresh"`
344+
Scopes []string `json:"scopes" yaml:"scopes"`
345+
ExtraTokenKeys []string `json:"extra_token_keys" yaml:"extra_token_keys"`
346+
DeviceFlow bool `json:"device_flow" yaml:"device_flow"`
347+
DeviceCodeURL string `json:"device_code_url" yaml:"device_code_url"`
348348
// Regex allows API requesters to match an auth config by
349349
// a string (e.g. coder.com) instead of by it's type.
350350
//
351351
// Git clone makes use of this by parsing the URL from:
352352
// 'Username for "https://github.com":'
353353
// And sending it to the Coder server to match against the Regex.
354-
Regex string `json:"regex"`
354+
Regex string `json:"regex" yaml:"regex"`
355355
// DisplayName is shown in the UI to identify the auth config.
356-
DisplayName string `json:"display_name"`
356+
DisplayName string `json:"display_name" yaml:"display_name"`
357357
// DisplayIcon is a URL to an icon to display in the UI.
358-
DisplayIcon string `json:"display_icon"`
358+
DisplayIcon string `json:"display_icon" yaml:"display_icon"`
359359
}
360360

361361
type ProvisionerConfig struct {
@@ -1788,7 +1788,7 @@ Write out the current server config as YAML to stdout.`,
17881788
Description: "External Authentication providers.",
17891789
// We need extra scrutiny to ensure this works, is documented, and
17901790
// tested before enabling.
1791-
// YAML: "gitAuthProviders",
1791+
YAML: "externalAuthProviders",
17921792
Value: &c.ExternalAuthConfigs,
17931793
Hidden: true,
17941794
},

codersdk/deployment_test.go

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
11
package codersdk_test
22

33
import (
4+
"bytes"
5+
"embed"
6+
"fmt"
7+
"runtime"
48
"strings"
59
"testing"
610
"time"
711

812
"github.com/stretchr/testify/require"
13+
"gopkg.in/yaml.v3"
914

1015
"github.com/coder/coder/v2/cli/clibase"
1116
"github.com/coder/coder/v2/codersdk"
@@ -277,3 +282,78 @@ func TestDeploymentValues_DurationFormatNanoseconds(t *testing.T) {
277282
t.FailNow()
278283
}
279284
}
285+
286+
//go:embed testdata/*
287+
var testData embed.FS
288+
289+
func TestExternalAuthYAMLConfig(t *testing.T) {
290+
t.Parallel()
291+
292+
if runtime.GOOS == "windows" {
293+
// The windows marshal function uses different line endings.
294+
// Not worth the effort getting this to work on windows.
295+
t.SkipNow()
296+
}
297+
298+
file := func(t *testing.T, name string) string {
299+
data, err := testData.ReadFile(fmt.Sprintf("testdata/%s", name))
300+
require.NoError(t, err, "read testdata file %q", name)
301+
return string(data)
302+
}
303+
githubCfg := codersdk.ExternalAuthConfig{
304+
Type: "github",
305+
ClientID: "client_id",
306+
ClientSecret: "client_secret",
307+
ID: "id",
308+
AuthURL: "https://example.com/auth",
309+
TokenURL: "https://example.com/token",
310+
ValidateURL: "https://example.com/validate",
311+
AppInstallURL: "https://example.com/install",
312+
AppInstallationsURL: "https://example.com/installations",
313+
NoRefresh: true,
314+
Scopes: []string{"user:email", "read:org"},
315+
ExtraTokenKeys: []string{"extra", "token"},
316+
DeviceFlow: true,
317+
DeviceCodeURL: "https://example.com/device",
318+
Regex: "^https://example.com/.*$",
319+
DisplayName: "GitHub",
320+
DisplayIcon: "/static/icons/github.svg",
321+
}
322+
323+
// Input the github section twice for testing a slice of configs.
324+
inputYAML := func() string {
325+
f := file(t, "githubcfg.yaml")
326+
lines := strings.SplitN(f, "\n", 2)
327+
// Append github config twice
328+
return f + lines[1]
329+
}()
330+
331+
expected := []codersdk.ExternalAuthConfig{
332+
githubCfg, githubCfg,
333+
}
334+
335+
dv := codersdk.DeploymentValues{}
336+
opts := dv.Options()
337+
// replace any tabs with the proper space indentation
338+
inputYAML = strings.ReplaceAll(inputYAML, "\t", " ")
339+
340+
// This is the order things are done in the cli, so just
341+
// keep it the same.
342+
var n yaml.Node
343+
err := yaml.Unmarshal([]byte(inputYAML), &n)
344+
require.NoError(t, err)
345+
346+
err = n.Decode(&opts)
347+
require.NoError(t, err)
348+
require.ElementsMatchf(t, expected, dv.ExternalAuthConfigs.Value, "from yaml")
349+
350+
var out bytes.Buffer
351+
enc := yaml.NewEncoder(&out)
352+
enc.SetIndent(2)
353+
err = enc.Encode(dv.ExternalAuthConfigs)
354+
require.NoError(t, err)
355+
356+
// Because we only marshal the 1 section, the correct section name is not applied.
357+
output := strings.Replace(out.String(), "value:", "externalAuthProviders:", 1)
358+
require.Equal(t, inputYAML, output, "re-marshaled is the same as input")
359+
}

codersdk/testdata/githubcfg.yaml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
externalAuthProviders:
2+
- type: github
3+
client_id: client_id
4+
client_secret: client_secret
5+
id: id
6+
auth_url: https://example.com/auth
7+
token_url: https://example.com/token
8+
validate_url: https://example.com/validate
9+
app_install_url: https://example.com/install
10+
app_installations_url: https://example.com/installations
11+
no_refresh: true
12+
scopes:
13+
- user:email
14+
- read:org
15+
extra_token_keys:
16+
- extra
17+
- token
18+
device_flow: true
19+
device_code_url: https://example.com/device
20+
regex: ^https://example.com/.*$
21+
display_name: GitHub
22+
display_icon: /static/icons/github.svg

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy