Skip to content

Commit e94d9a3

Browse files
committed
Merge branch 'main' into colin/chore_codersdk_move_all_wireguard_functions_to_workspacesdk_
2 parents 1cd79f6 + 0bea890 commit e94d9a3

File tree

23 files changed

+185
-205
lines changed

23 files changed

+185
-205
lines changed

.github/workflows/contrib.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ jobs:
3434
steps:
3535
- name: cla
3636
if: (github.event.comment.body == 'recheck' || github.event.comment.body == 'I have read the CLA Document and I hereby sign the CLA') || github.event_name == 'pull_request_target'
37-
uses: contributor-assistant/github-action@v2.3.1
37+
uses: contributor-assistant/github-action@v2.3.2
3838
env:
3939
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
4040
# the below token should have repo scope and must be manually added by you in the repository's secret

cli/ssh_test.go

+26-2
Original file line numberDiff line numberDiff line change
@@ -117,14 +117,26 @@ func TestSSH(t *testing.T) {
117117
clitest.SetupConfig(t, client, root)
118118
pty := ptytest.New(t).Attach(inv)
119119

120-
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
120+
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitSuperLong)
121121
defer cancel()
122122

123123
cmdDone := tGo(t, func() {
124124
err := inv.WithContext(ctx).Run()
125125
assert.NoError(t, err)
126126
})
127127

128+
// Delay until workspace is starting, otherwise the agent may be
129+
// booted due to outdated build.
130+
var err error
131+
for {
132+
workspace, err = client.Workspace(ctx, workspace.ID)
133+
require.NoError(t, err)
134+
if workspace.LatestBuild.Transition == codersdk.WorkspaceTransitionStart {
135+
break
136+
}
137+
time.Sleep(testutil.IntervalFast)
138+
}
139+
128140
// When the agent connects, the workspace was started, and we should
129141
// have access to the shell.
130142
_ = agenttest.New(t, client.URL, authToken)
@@ -365,7 +377,7 @@ func TestSSH(t *testing.T) {
365377
workspaceBuild := coderdtest.CreateWorkspaceBuild(t, client, workspace, database.WorkspaceTransitionStop)
366378
coderdtest.AwaitWorkspaceBuildJobCompleted(t, client, workspaceBuild.ID)
367379

368-
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
380+
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitSuperLong)
369381
defer cancel()
370382

371383
clientStdinR, clientStdinW := io.Pipe()
@@ -461,6 +473,18 @@ func TestSSH(t *testing.T) {
461473
assert.NoError(t, err)
462474
})
463475

476+
// Delay until workspace is starting, otherwise the agent may be
477+
// booted due to outdated build.
478+
var err error
479+
for {
480+
workspace, err = client.Workspace(ctx, workspace.ID)
481+
require.NoError(t, err)
482+
if workspace.LatestBuild.Transition == codersdk.WorkspaceTransitionStart {
483+
break
484+
}
485+
time.Sleep(testutil.IntervalFast)
486+
}
487+
464488
// When the agent connects, the workspace was started, and we should
465489
// have access to the shell.
466490
_ = agenttest.New(t, client.URL, authToken)

coderd/coderd.go

+73-3
Original file line numberDiff line numberDiff line change
@@ -689,6 +689,34 @@ func New(options *Options) *API {
689689
})
690690
}
691691

692+
// OAuth2 linking routes do not make sense under the /api/v2 path. These are
693+
// for an external application to use Coder as an OAuth2 provider, not for
694+
// logging into Coder with an external OAuth2 provider.
695+
r.Route("/oauth2", func(r chi.Router) {
696+
r.Use(
697+
api.oAuth2ProviderMiddleware,
698+
// Fetch the app as system because in the /tokens route there will be no
699+
// authenticated user.
700+
httpmw.AsAuthzSystem(httpmw.ExtractOAuth2ProviderApp(options.Database)),
701+
)
702+
r.Route("/authorize", func(r chi.Router) {
703+
r.Use(apiKeyMiddlewareRedirect)
704+
r.Get("/", api.getOAuth2ProviderAppAuthorize())
705+
})
706+
r.Route("/tokens", func(r chi.Router) {
707+
r.Group(func(r chi.Router) {
708+
r.Use(apiKeyMiddleware)
709+
// DELETE on /tokens is not part of the OAuth2 spec. It is our own
710+
// route used to revoke permissions from an application. It is here for
711+
// parity with POST on /tokens.
712+
r.Delete("/", api.deleteOAuth2ProviderAppTokens())
713+
})
714+
// The POST /tokens endpoint will be called from an unauthorized client so
715+
// we cannot require an API key.
716+
r.Post("/", api.postOAuth2ProviderAppToken())
717+
})
718+
})
719+
692720
r.Route("/api/v2", func(r chi.Router) {
693721
api.APIHandler = r
694722

@@ -1098,6 +1126,34 @@ func New(options *Options) *API {
10981126
}
10991127
r.Method("GET", "/expvar", expvar.Handler()) // contains DERP metrics as well as cmdline and memstats
11001128
})
1129+
// Manage OAuth2 applications that can use Coder as an OAuth2 provider.
1130+
r.Route("/oauth2-provider", func(r chi.Router) {
1131+
r.Use(
1132+
apiKeyMiddleware,
1133+
api.oAuth2ProviderMiddleware,
1134+
)
1135+
r.Route("/apps", func(r chi.Router) {
1136+
r.Get("/", api.oAuth2ProviderApps)
1137+
r.Post("/", api.postOAuth2ProviderApp)
1138+
1139+
r.Route("/{app}", func(r chi.Router) {
1140+
r.Use(httpmw.ExtractOAuth2ProviderApp(options.Database))
1141+
r.Get("/", api.oAuth2ProviderApp)
1142+
r.Put("/", api.putOAuth2ProviderApp)
1143+
r.Delete("/", api.deleteOAuth2ProviderApp)
1144+
1145+
r.Route("/secrets", func(r chi.Router) {
1146+
r.Get("/", api.oAuth2ProviderAppSecrets)
1147+
r.Post("/", api.postOAuth2ProviderAppSecret)
1148+
1149+
r.Route("/{secretID}", func(r chi.Router) {
1150+
r.Use(httpmw.ExtractOAuth2ProviderAppSecret(options.Database))
1151+
r.Delete("/", api.deleteOAuth2ProviderAppSecret)
1152+
})
1153+
})
1154+
})
1155+
})
1156+
})
11011157
})
11021158

11031159
if options.SwaggerEndpoint {
@@ -1229,9 +1285,23 @@ func (api *API) Close() error {
12291285
api.derpCloseFunc()
12301286
}
12311287

1232-
api.WebsocketWaitMutex.Lock()
1233-
api.WebsocketWaitGroup.Wait()
1234-
api.WebsocketWaitMutex.Unlock()
1288+
wsDone := make(chan struct{})
1289+
timer := time.NewTimer(10 * time.Second)
1290+
defer timer.Stop()
1291+
go func() {
1292+
api.WebsocketWaitMutex.Lock()
1293+
defer api.WebsocketWaitMutex.Unlock()
1294+
api.WebsocketWaitGroup.Wait()
1295+
close(wsDone)
1296+
}()
1297+
// This will technically leak the above func if the timer fires, but this is
1298+
// maintly a last ditch effort to un-stuck coderd on shutdown. This
1299+
// shouldn't affect tests at all.
1300+
select {
1301+
case <-wsDone:
1302+
case <-timer.C:
1303+
api.Logger.Warn(api.ctx, "websocket shutdown timed out after 10 seconds")
1304+
}
12351305

12361306
api.dbRolluper.Close()
12371307
api.metricsCache.Close()

coderd/database/queries.sql.go

+3-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/database/queries/insights.sql

+3-1
Original file line numberDiff line numberDiff line change
@@ -599,7 +599,9 @@ WITH
599599
JOIN
600600
workspace_agent_stats AS was
601601
ON
602-
date_trunc('minute', was.created_at) = mb.minute_bucket
602+
was.created_at >= (SELECT t FROM latest_start)
603+
AND was.created_at < NOW()
604+
AND date_trunc('minute', was.created_at) = mb.minute_bucket
603605
AND was.template_id = mb.template_id
604606
AND was.user_id = mb.user_id
605607
AND was.connection_median_latency_ms >= 0

enterprise/coderd/oauth2.go renamed to coderd/oauth2.go

+7-18
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,11 @@ import (
1313
"github.com/coder/coder/v2/coderd/database/dbtime"
1414
"github.com/coder/coder/v2/coderd/httpapi"
1515
"github.com/coder/coder/v2/coderd/httpmw"
16+
"github.com/coder/coder/v2/coderd/identityprovider"
1617
"github.com/coder/coder/v2/codersdk"
17-
"github.com/coder/coder/v2/enterprise/coderd/identityprovider"
1818
)
1919

20-
func (api *API) oAuth2ProviderMiddleware(next http.Handler) http.Handler {
20+
func (*API) oAuth2ProviderMiddleware(next http.Handler) http.Handler {
2121
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
2222
if !buildinfo.IsDev() {
2323
httpapi.Write(r.Context(), rw, http.StatusForbidden, codersdk.Response{
@@ -26,17 +26,6 @@ func (api *API) oAuth2ProviderMiddleware(next http.Handler) http.Handler {
2626
return
2727
}
2828

29-
api.entitlementsMu.RLock()
30-
entitled := api.entitlements.Features[codersdk.FeatureOAuth2Provider].Entitlement != codersdk.EntitlementNotEntitled
31-
api.entitlementsMu.RUnlock()
32-
33-
if !entitled {
34-
httpapi.Write(r.Context(), rw, http.StatusForbidden, codersdk.Response{
35-
Message: "OAuth2 provider is an Enterprise feature. Contact sales!",
36-
})
37-
return
38-
}
39-
4029
next.ServeHTTP(rw, r)
4130
})
4231
}
@@ -111,7 +100,7 @@ func (api *API) oAuth2ProviderApp(rw http.ResponseWriter, r *http.Request) {
111100
func (api *API) postOAuth2ProviderApp(rw http.ResponseWriter, r *http.Request) {
112101
var (
113102
ctx = r.Context()
114-
auditor = api.AGPL.Auditor.Load()
103+
auditor = api.Auditor.Load()
115104
aReq, commitAudit = audit.InitRequest[database.OAuth2ProviderApp](rw, &audit.RequestParams{
116105
Audit: *auditor,
117106
Log: api.Logger,
@@ -157,7 +146,7 @@ func (api *API) putOAuth2ProviderApp(rw http.ResponseWriter, r *http.Request) {
157146
var (
158147
ctx = r.Context()
159148
app = httpmw.OAuth2ProviderApp(r)
160-
auditor = api.AGPL.Auditor.Load()
149+
auditor = api.Auditor.Load()
161150
aReq, commitAudit = audit.InitRequest[database.OAuth2ProviderApp](rw, &audit.RequestParams{
162151
Audit: *auditor,
163152
Log: api.Logger,
@@ -200,7 +189,7 @@ func (api *API) deleteOAuth2ProviderApp(rw http.ResponseWriter, r *http.Request)
200189
var (
201190
ctx = r.Context()
202191
app = httpmw.OAuth2ProviderApp(r)
203-
auditor = api.AGPL.Auditor.Load()
192+
auditor = api.Auditor.Load()
204193
aReq, commitAudit = audit.InitRequest[database.OAuth2ProviderApp](rw, &audit.RequestParams{
205194
Audit: *auditor,
206195
Log: api.Logger,
@@ -263,7 +252,7 @@ func (api *API) postOAuth2ProviderAppSecret(rw http.ResponseWriter, r *http.Requ
263252
var (
264253
ctx = r.Context()
265254
app = httpmw.OAuth2ProviderApp(r)
266-
auditor = api.AGPL.Auditor.Load()
255+
auditor = api.Auditor.Load()
267256
aReq, commitAudit = audit.InitRequest[database.OAuth2ProviderAppSecret](rw, &audit.RequestParams{
268257
Audit: *auditor,
269258
Log: api.Logger,
@@ -317,7 +306,7 @@ func (api *API) deleteOAuth2ProviderAppSecret(rw http.ResponseWriter, r *http.Re
317306
var (
318307
ctx = r.Context()
319308
secret = httpmw.OAuth2ProviderAppSecret(r)
320-
auditor = api.AGPL.Auditor.Load()
309+
auditor = api.Auditor.Load()
321310
aReq, commitAudit = audit.InitRequest[database.OAuth2ProviderAppSecret](rw, &audit.RequestParams{
322311
Audit: *auditor,
323312
Log: api.Logger,

0 commit comments

Comments
 (0)