Skip to content

Commit 0a2e222

Browse files
committed
Convert configuration format for git auth
1 parent ab9aa48 commit 0a2e222

File tree

12 files changed

+196
-186
lines changed

12 files changed

+196
-186
lines changed

cli/config/server.go

Lines changed: 0 additions & 56 deletions
This file was deleted.

cli/config/server_test.go

Lines changed: 0 additions & 40 deletions
This file was deleted.

cli/deployment/config.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,12 @@ func newConfig() codersdk.DeploymentConfig {
9191
Usage: "Path to read a DERP mapping from. See: https://tailscale.com/kb/1118/custom-derp-servers/",
9292
Flag: "derp-config-path",
9393
},
94+
GitAuth: codersdk.DeploymentConfigField[[]codersdk.DeploymentConfigGitAuth]{
95+
Key: "gitauth",
96+
Usage: "Git Authentication",
97+
Flag: "gitauth",
98+
Value: []codersdk.DeploymentConfigGitAuth{},
99+
},
94100
PrometheusEnable: codersdk.DeploymentConfigField[bool]{
95101
Key: "prometheus.enable",
96102
Usage: "Serve prometheus metrics on the address defined by prometheus address.",
@@ -361,6 +367,9 @@ func Config(flagset *pflag.FlagSet, vip *viper.Viper) (codersdk.DeploymentConfig
361367
value = append(value, strings.Split(entry, ",")...)
362368
}
363369
fve.FieldByName("Value").Set(reflect.ValueOf(value))
370+
case []codersdk.DeploymentConfigGitAuth:
371+
values := readSliceFromViper[codersdk.DeploymentConfigGitAuth](vip, key, value)
372+
fve.FieldByName("Value").Set(reflect.ValueOf(values))
364373
default:
365374
return dc, xerrors.Errorf("unsupported type %T", value)
366375
}
@@ -369,12 +378,47 @@ func Config(flagset *pflag.FlagSet, vip *viper.Viper) (codersdk.DeploymentConfig
369378
return dc, nil
370379
}
371380

381+
// readSliceFromViper reads a typed mapping from the key provided.
382+
// This enables environment variables like CODER_GITAUTH_<index>_CLIENT_ID.
383+
func readSliceFromViper[T any](vip *viper.Viper, key string, value any) []T {
384+
elementType := reflect.TypeOf(value).Elem()
385+
returnValues := make([]T, 0)
386+
for entry := 0; true; entry++ {
387+
// Only create an instance when the entry exists in viper...
388+
// otherwise we risk
389+
var instance *reflect.Value
390+
for i := 0; i < elementType.NumField(); i++ {
391+
fve := elementType.Field(i)
392+
prop := fve.Tag.Get("json")
393+
// For fields that are omitted in JSON, we use a YAML tag.
394+
if prop == "-" {
395+
prop = fve.Tag.Get("yaml")
396+
}
397+
value := vip.Get(fmt.Sprintf("%s.%d.%s", key, entry, prop))
398+
if value == nil {
399+
continue
400+
}
401+
if instance == nil {
402+
newType := reflect.Indirect(reflect.New(elementType))
403+
instance = &newType
404+
}
405+
instance.Field(i).Set(reflect.ValueOf(value))
406+
}
407+
if instance == nil {
408+
break
409+
}
410+
returnValues = append(returnValues, instance.Interface().(T))
411+
}
412+
return returnValues
413+
}
414+
372415
func NewViper() *viper.Viper {
373416
dc := newConfig()
374417
v := viper.New()
375418
v.SetEnvPrefix("coder")
376419
v.AutomaticEnv()
377420
v.SetEnvKeyReplacer(strings.NewReplacer("-", "_", ".", "_"))
421+
v.SetTypeByDefaultValue(true)
378422

379423
dcv := reflect.ValueOf(dc)
380424
t := dcv.Type()

cli/deployment/config_test.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,45 @@ func TestConfig(t *testing.T) {
148148
require.Equal(t, []string{"coder"}, config.OAuth2GithubAllowedTeams.Value)
149149
require.Equal(t, config.OAuth2GithubAllowSignups.Value, true)
150150
},
151+
}, {
152+
Name: "GitAuth",
153+
Env: map[string]string{
154+
"CODER_GITAUTH_0_ID": "hello",
155+
"CODER_GITAUTH_0_TYPE": "github",
156+
"CODER_GITAUTH_0_CLIENT_ID": "client",
157+
"CODER_GITAUTH_0_CLIENT_SECRET": "secret",
158+
"CODER_GITAUTH_0_AUTH_URL": "https://auth.com",
159+
"CODER_GITAUTH_0_TOKEN_URL": "https://token.com",
160+
"CODER_GITAUTH_0_REGEX": "github.com",
161+
162+
"CODER_GITAUTH_1_ID": "another",
163+
"CODER_GITAUTH_1_TYPE": "gitlab",
164+
"CODER_GITAUTH_1_CLIENT_ID": "client-2",
165+
"CODER_GITAUTH_1_CLIENT_SECRET": "secret-2",
166+
"CODER_GITAUTH_1_AUTH_URL": "https://auth-2.com",
167+
"CODER_GITAUTH_1_TOKEN_URL": "https://token-2.com",
168+
"CODER_GITAUTH_1_REGEX": "gitlab.com",
169+
},
170+
Valid: func(config codersdk.DeploymentConfig) {
171+
require.Len(t, config.GitAuth.Value, 2)
172+
require.Equal(t, []codersdk.DeploymentConfigGitAuth{{
173+
ID: "hello",
174+
Type: "github",
175+
ClientID: "client",
176+
ClientSecret: "secret",
177+
AuthURL: "https://auth.com",
178+
TokenURL: "https://token.com",
179+
Regex: "github.com",
180+
}, {
181+
ID: "another",
182+
Type: "gitlab",
183+
ClientID: "client-2",
184+
ClientSecret: "secret-2",
185+
AuthURL: "https://auth-2.com",
186+
TokenURL: "https://token-2.com",
187+
Regex: "gitlab.com",
188+
}}, config.GitAuth.Value)
189+
},
151190
}} {
152191
tc := tc
153192
t.Run(tc.Name, func(t *testing.T) {

cli/server.go

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ import (
5555
"github.com/coder/coder/coderd/database/databasefake"
5656
"github.com/coder/coder/coderd/database/migrations"
5757
"github.com/coder/coder/coderd/devtunnel"
58+
"github.com/coder/coder/coderd/gitauth"
5859
"github.com/coder/coder/coderd/gitsshkey"
5960
"github.com/coder/coder/coderd/httpapi"
6061
"github.com/coder/coder/coderd/prometheusmetrics"
@@ -325,17 +326,22 @@ func Server(vip *viper.Viper, newAPI func(context.Context, *coderd.Options) (*co
325326
}
326327
}
327328

329+
gitAuthConfigs, err := gitauth.ConvertConfig(cfg.GitAuth.Value, accessURLParsed)
330+
if err != nil {
331+
return xerrors.Errorf("parse git auth config: %w", err)
332+
}
333+
328334
options := &coderd.Options{
329-
AccessURL: accessURLParsed,
330-
AppHostname: appHostname,
331-
AppHostnameRegex: appHostnameRegex,
332-
Logger: logger.Named("coderd"),
333-
Database: databasefake.New(),
334-
DERPMap: derpMap,
335-
Pubsub: database.NewPubsubInMemory(),
336-
CacheDir: cfg.CacheDirectory.Value,
337-
GoogleTokenValidator: googleTokenValidator,
338-
// GitAuthConfigs: serverConfig.GitAuth,
335+
AccessURL: accessURLParsed,
336+
AppHostname: appHostname,
337+
AppHostnameRegex: appHostnameRegex,
338+
Logger: logger.Named("coderd"),
339+
Database: databasefake.New(),
340+
DERPMap: derpMap,
341+
Pubsub: database.NewPubsubInMemory(),
342+
CacheDir: cfg.CacheDirectory.Value,
343+
GoogleTokenValidator: googleTokenValidator,
344+
GitAuthConfigs: gitAuthConfigs,
339345
SecureAuthCookie: cfg.SecureAuthCookie.Value,
340346
SSHKeygenAlgorithm: sshKeygenAlgorithm,
341347
TracerProvider: tracerProvider,
@@ -617,7 +623,7 @@ func Server(vip *viper.Viper, newAPI func(context.Context, *coderd.Options) (*co
617623
// This is helpful for tests, but can be silently ignored.
618624
// Coder may be ran as users that don't have permission to write in the homedir,
619625
// such as via the systemd service.
620-
_ = cfg.URL().Write(client.URL.String())
626+
_ = config.URL().Write(client.URL.String())
621627

622628
// Currently there is no way to ask the server to shut
623629
// itself down, so any exit signal will result in a non-zero

coderd/gitauth/config.go

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -24,20 +24,9 @@ type Config struct {
2424
Type codersdk.GitProvider
2525
}
2626

27-
// YAML represents a serializable config.
28-
type YAML struct {
29-
ID string `yaml:"id"`
30-
Type string `yaml:"type"`
31-
ClientID string `yaml:"client_id"`
32-
ClientSecret string `yaml:"client_secret"`
33-
AuthURL string `yaml:"auth_url"`
34-
TokenURL string `yaml:"token_url"`
35-
Regex string `yaml:"regex"`
36-
}
37-
38-
// ConvertYAML converts the YAML configuration entry to the
27+
// ConvertConfig converts the YAML configuration entry to the
3928
// parsed and ready-to-consume provider type.
40-
func ConvertYAML(entries []*YAML, accessURL *url.URL) ([]*Config, error) {
29+
func ConvertConfig(entries []codersdk.DeploymentConfigGitAuth, accessURL *url.URL) ([]*Config, error) {
4130
ids := map[string]struct{}{}
4231
configs := []*Config{}
4332
for _, entry := range entries {

coderd/gitauth/config_test.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,38 +14,38 @@ func TestConvertYAML(t *testing.T) {
1414
t.Parallel()
1515
for _, tc := range []struct {
1616
Name string
17-
Input []*gitauth.YAML
17+
Input []codersdk.DeploymentConfigGitAuth
1818
Output []*gitauth.Config
1919
Error string
2020
}{{
2121
Name: "InvalidType",
22-
Input: []*gitauth.YAML{{
22+
Input: []codersdk.DeploymentConfigGitAuth{{
2323
Type: "moo",
2424
}},
2525
Error: "unknown git provider type",
2626
}, {
2727
Name: "InvalidID",
28-
Input: []*gitauth.YAML{{
28+
Input: []codersdk.DeploymentConfigGitAuth{{
2929
Type: codersdk.GitProviderGitHub,
3030
ID: "$hi$",
3131
}},
3232
Error: "doesn't have a valid id",
3333
}, {
3434
Name: "NoClientID",
35-
Input: []*gitauth.YAML{{
35+
Input: []codersdk.DeploymentConfigGitAuth{{
3636
Type: codersdk.GitProviderGitHub,
3737
}},
3838
Error: "client_id must be provided",
3939
}, {
4040
Name: "NoClientSecret",
41-
Input: []*gitauth.YAML{{
41+
Input: []codersdk.DeploymentConfigGitAuth{{
4242
Type: codersdk.GitProviderGitHub,
4343
ClientID: "example",
4444
}},
4545
Error: "client_secret must be provided",
4646
}, {
4747
Name: "DuplicateType",
48-
Input: []*gitauth.YAML{{
48+
Input: []codersdk.DeploymentConfigGitAuth{{
4949
Type: codersdk.GitProviderGitHub,
5050
ClientID: "example",
5151
ClientSecret: "example",
@@ -55,7 +55,7 @@ func TestConvertYAML(t *testing.T) {
5555
Error: "multiple github git auth providers provided",
5656
}, {
5757
Name: "InvalidRegex",
58-
Input: []*gitauth.YAML{{
58+
Input: []codersdk.DeploymentConfigGitAuth{{
5959
Type: codersdk.GitProviderGitHub,
6060
ClientID: "example",
6161
ClientSecret: "example",
@@ -66,7 +66,7 @@ func TestConvertYAML(t *testing.T) {
6666
tc := tc
6767
t.Run(tc.Name, func(t *testing.T) {
6868
t.Parallel()
69-
output, err := gitauth.ConvertYAML(tc.Input, &url.URL{})
69+
output, err := gitauth.ConvertConfig(tc.Input, &url.URL{})
7070
if tc.Error != "" {
7171
require.Error(t, err)
7272
require.Contains(t, err.Error(), tc.Error)

coderd/userauth_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"net/url"
1010
"strings"
1111
"testing"
12+
"time"
1213

1314
"github.com/coreos/go-oidc/v3/oidc"
1415
"github.com/golang-jwt/jwt"

0 commit comments

Comments
 (0)