Skip to content

Commit b34a67e

Browse files
authored
fix: Allow custom Git OAuth URLs (#4758)
Fixes an issue reported in Discord where custom endpoints weren't working.
1 parent 3e15ee3 commit b34a67e

File tree

8 files changed

+63
-13
lines changed

8 files changed

+63
-13
lines changed

agent/agent.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,11 @@ func (a *agent) createTailnet(ctx context.Context, derpMap *tailcfg.DERPMap) (*t
242242
if err != nil {
243243
return nil, xerrors.Errorf("listen on the ssh port: %w", err)
244244
}
245+
a.closeMutex.Lock()
246+
a.connCloseWait.Add(1)
247+
a.closeMutex.Unlock()
245248
go func() {
249+
defer a.connCloseWait.Done()
246250
for {
247251
conn, err := sshListener.Accept()
248252
if err != nil {
@@ -256,7 +260,11 @@ func (a *agent) createTailnet(ctx context.Context, derpMap *tailcfg.DERPMap) (*t
256260
if err != nil {
257261
return nil, xerrors.Errorf("listen for reconnecting pty: %w", err)
258262
}
263+
a.closeMutex.Lock()
264+
a.connCloseWait.Add(1)
265+
a.closeMutex.Unlock()
259266
go func() {
267+
defer a.connCloseWait.Done()
260268
for {
261269
conn, err := reconnectingPTYListener.Accept()
262270
if err != nil {
@@ -290,7 +298,11 @@ func (a *agent) createTailnet(ctx context.Context, derpMap *tailcfg.DERPMap) (*t
290298
if err != nil {
291299
return nil, xerrors.Errorf("listen for speedtest: %w", err)
292300
}
301+
a.closeMutex.Lock()
302+
a.connCloseWait.Add(1)
303+
a.closeMutex.Unlock()
293304
go func() {
305+
defer a.connCloseWait.Done()
294306
for {
295307
conn, err := speedtestListener.Accept()
296308
if err != nil {
@@ -311,7 +323,11 @@ func (a *agent) createTailnet(ctx context.Context, derpMap *tailcfg.DERPMap) (*t
311323
if err != nil {
312324
return nil, xerrors.Errorf("listen for statistics: %w", err)
313325
}
326+
a.closeMutex.Lock()
327+
a.connCloseWait.Add(1)
328+
a.closeMutex.Unlock()
314329
go func() {
330+
defer a.connCloseWait.Done()
315331
defer statisticsListener.Close()
316332
server := &http.Server{
317333
Handler: a.statisticsHandler(),

cli/deployment/config.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -462,14 +462,20 @@ func readSliceFromViper[T any](vip *viper.Viper, key string, value any) []T {
462462
if prop == "-" {
463463
prop = fve.Tag.Get("yaml")
464464
}
465-
value := vip.Get(fmt.Sprintf("%s.%d.%s", key, entry, prop))
465+
configKey := fmt.Sprintf("%s.%d.%s", key, entry, prop)
466+
value := vip.Get(configKey)
466467
if value == nil {
467468
continue
468469
}
469470
if instance == nil {
470471
newType := reflect.Indirect(reflect.New(elementType))
471472
instance = &newType
472473
}
474+
switch instance.Field(i).Type().String() {
475+
case "[]string":
476+
value = vip.GetStringSlice(configKey)
477+
default:
478+
}
473479
instance.Field(i).Set(reflect.ValueOf(value))
474480
}
475481
if instance == nil {

cli/deployment/config_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,7 @@ func TestConfig(t *testing.T) {
158158
"CODER_GITAUTH_0_AUTH_URL": "https://auth.com",
159159
"CODER_GITAUTH_0_TOKEN_URL": "https://token.com",
160160
"CODER_GITAUTH_0_REGEX": "github.com",
161+
"CODER_GITAUTH_0_SCOPES": "read write",
161162

162163
"CODER_GITAUTH_1_ID": "another",
163164
"CODER_GITAUTH_1_TYPE": "gitlab",
@@ -177,6 +178,7 @@ func TestConfig(t *testing.T) {
177178
AuthURL: "https://auth.com",
178179
TokenURL: "https://token.com",
179180
Regex: "github.com",
181+
Scopes: []string{"read", "write"},
180182
}, {
181183
ID: "another",
182184
Type: "gitlab",

coderd/gitauth/config.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,16 @@ func ConvertConfig(entries []codersdk.GitAuthConfig, accessURL *url.URL) ([]*Con
8686
Scopes: scope[typ],
8787
}
8888

89+
if entry.AuthURL != "" {
90+
oauth2Config.Endpoint.AuthURL = entry.AuthURL
91+
}
92+
if entry.TokenURL != "" {
93+
oauth2Config.Endpoint.TokenURL = entry.TokenURL
94+
}
95+
if entry.Scopes != nil && len(entry.Scopes) > 0 {
96+
oauth2Config.Scopes = entry.Scopes
97+
}
98+
8999
var oauthConfig httpmw.OAuth2Config = oauth2Config
90100
// Azure DevOps uses JWT token authentication!
91101
if typ == codersdk.GitProviderAzureDevops {

coderd/gitauth/config_test.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,4 +75,18 @@ func TestConvertYAML(t *testing.T) {
7575
require.Equal(t, tc.Output, output)
7676
})
7777
}
78+
79+
t.Run("CustomScopesAndEndpoint", func(t *testing.T) {
80+
t.Parallel()
81+
config, err := gitauth.ConvertConfig([]codersdk.GitAuthConfig{{
82+
Type: codersdk.GitProviderGitLab,
83+
ClientID: "id",
84+
ClientSecret: "secret",
85+
AuthURL: "https://auth.com",
86+
TokenURL: "https://token.com",
87+
Scopes: []string{"read"},
88+
}}, &url.URL{})
89+
require.NoError(t, err)
90+
require.Equal(t, "https://auth.com?client_id=id&redirect_uri=%2Fgitauth%2Fgitlab%2Fcallback&response_type=code&scope=read", config[0].AuthCodeURL(""))
91+
})
7892
}

codersdk/client_internal_test.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ func Test_readBodyAsError(t *testing.T) {
3434
longResponse += "a"
3535
}
3636

37-
unexpectedJSON := marshalJSON(map[string]any{
37+
unexpectedJSON := marshal(map[string]any{
3838
"hello": "world",
3939
"foo": "bar",
4040
})
@@ -49,7 +49,7 @@ func Test_readBodyAsError(t *testing.T) {
4949
{
5050
name: "JSONWithRequest",
5151
req: httptest.NewRequest(http.MethodGet, exampleURL, nil),
52-
res: newResponse(http.StatusNotFound, jsonCT, marshalJSON(simpleResponse)),
52+
res: newResponse(http.StatusNotFound, jsonCT, marshal(simpleResponse)),
5353
assert: func(t *testing.T, err error) {
5454
sdkErr := assertSDKError(t, err)
5555

@@ -72,7 +72,7 @@ func Test_readBodyAsError(t *testing.T) {
7272
{
7373
name: "JSONWithoutRequest",
7474
req: nil,
75-
res: newResponse(http.StatusNotFound, jsonCT, marshalJSON(simpleResponse)),
75+
res: newResponse(http.StatusNotFound, jsonCT, marshal(simpleResponse)),
7676
assert: func(t *testing.T, err error) {
7777
sdkErr := assertSDKError(t, err)
7878

@@ -86,7 +86,7 @@ func Test_readBodyAsError(t *testing.T) {
8686
{
8787
name: "UnauthorizedHelper",
8888
req: nil,
89-
res: newResponse(http.StatusUnauthorized, jsonCT, marshalJSON(simpleResponse)),
89+
res: newResponse(http.StatusUnauthorized, jsonCT, marshal(simpleResponse)),
9090
assert: func(t *testing.T, err error) {
9191
sdkErr := assertSDKError(t, err)
9292

@@ -190,7 +190,7 @@ func newResponse(status int, contentType string, body interface{}) *http.Respons
190190
}
191191
}
192192

193-
func marshalJSON(res any) string {
193+
func marshal(res any) string {
194194
b, err := json.Marshal(res)
195195
if err != nil {
196196
panic(err)

codersdk/deploymentconfig.go

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -108,13 +108,14 @@ type TLSConfig struct {
108108
}
109109

110110
type GitAuthConfig struct {
111-
ID string `json:"id"`
112-
Type string `json:"type"`
113-
ClientID string `json:"client_id"`
114-
ClientSecret string `json:"-" yaml:"client_secret"`
115-
AuthURL string `json:"auth_url"`
116-
TokenURL string `json:"token_url"`
117-
Regex string `json:"regex"`
111+
ID string `json:"id"`
112+
Type string `json:"type"`
113+
ClientID string `json:"client_id"`
114+
ClientSecret string `json:"-" yaml:"client_secret"`
115+
AuthURL string `json:"auth_url"`
116+
TokenURL string `json:"token_url"`
117+
Regex string `json:"regex"`
118+
Scopes []string `json:"scopes"`
118119
}
119120

120121
type Flaggable interface {

site/src/api/typesGenerated.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,7 @@ export interface GitAuthConfig {
354354
readonly auth_url: string
355355
readonly token_url: string
356356
readonly regex: string
357+
readonly scopes: string[]
357358
}
358359

359360
// From codersdk/gitsshkey.go

0 commit comments

Comments
 (0)