Skip to content

Commit 56a880d

Browse files
committed
tests
1 parent 6837b57 commit 56a880d

File tree

4 files changed

+167
-12
lines changed

4 files changed

+167
-12
lines changed

coderd/database/dbauthz/dbauthz.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,7 @@ var (
260260
rbac.ResourceOrganization.Type: {policy.ActionCreate, policy.ActionRead},
261261
rbac.ResourceOrganizationMember.Type: {policy.ActionCreate},
262262
rbac.ResourceProvisionerDaemon.Type: {policy.ActionCreate, policy.ActionUpdate},
263+
rbac.ResourceProvisionerKeys.Type: {policy.ActionCreate, policy.ActionRead, policy.ActionDelete},
263264
rbac.ResourceUser.Type: rbac.ResourceUser.AvailableActions(),
264265
rbac.ResourceWorkspaceDormant.Type: {policy.ActionUpdate, policy.ActionDelete, policy.ActionWorkspaceStop},
265266
rbac.ResourceWorkspace.Type: {policy.ActionUpdate, policy.ActionDelete, policy.ActionWorkspaceStart, policy.ActionWorkspaceStop, policy.ActionSSH},

coderd/httpmw/provisionerdaemon.go

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,13 @@ func ExtractProvisionerDaemonAuthenticated(opts ExtractProvisionerAuthConfig) fu
4040
}
4141

4242
if !opts.MultiOrgEnabled {
43+
if opts.PSK == "" {
44+
handleOptional(http.StatusUnauthorized, codersdk.Response{
45+
Message: "External provisioner daemons not enabled",
46+
})
47+
return
48+
}
49+
4350
fallbackToPSK(ctx, opts.PSK, next, w, r, handleOptional)
4451
return
4552
}
@@ -65,7 +72,8 @@ func ExtractProvisionerDaemonAuthenticated(opts ExtractProvisionerAuthConfig) fu
6572
return
6673
}
6774

68-
pk, err := opts.DB.GetProvisionerKeyByID(ctx, id)
75+
// nolint:gocritic // System must check if the provisioner key is valid.
76+
pk, err := opts.DB.GetProvisionerKeyByID(dbauthz.AsSystemRestricted(ctx), id)
6977
if err != nil {
7078
if httpapi.Is404Error(err) {
7179
handleOptional(http.StatusUnauthorized, codersdk.Response{
@@ -99,13 +107,6 @@ func ExtractProvisionerDaemonAuthenticated(opts ExtractProvisionerAuthConfig) fu
99107
}
100108

101109
func fallbackToPSK(ctx context.Context, psk string, next http.Handler, w http.ResponseWriter, r *http.Request, handleOptional func(code int, response codersdk.Response)) {
102-
if psk == "" {
103-
handleOptional(http.StatusUnauthorized, codersdk.Response{
104-
Message: "External provisioner daemons not enabled",
105-
})
106-
return
107-
}
108-
109110
token := r.Header.Get(codersdk.ProvisionerDaemonPSK)
110111
if subtle.ConstantTimeCompare([]byte(token), []byte(psk)) != 1 {
111112
handleOptional(http.StatusUnauthorized, codersdk.Response{

codersdk/provisionerdaemons.go

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -225,13 +225,18 @@ func (c *Client) ServeProvisionerDaemon(ctx context.Context, req ServeProvisione
225225
headers := http.Header{}
226226

227227
headers.Set(BuildVersionHeader, buildinfo.Version())
228-
// nolint:gocritic // Need to support multiple exclusive auth flows.
228+
229+
if req.ProvisionerKey != "" && req.PreSharedKey != "" {
230+
return nil, xerrors.Errorf("cannot provide both a provisioner key and a pre-shared key")
231+
}
229232
if req.ProvisionerKey != "" {
230233
headers.Set(ProvisionerDaemonKey, req.ProvisionerKey)
231-
} else if req.PreSharedKey != "" {
234+
}
235+
if req.PreSharedKey != "" {
232236
headers.Set(ProvisionerDaemonPSK, req.PreSharedKey)
233-
} else {
234-
// use session token if we don't have a PSK.
237+
}
238+
if req.ProvisionerKey == "" && req.PreSharedKey == "" {
239+
// use session token if we don't have a PSK or provisioner key.
235240
jar, err := cookiejar.New(nil)
236241
if err != nil {
237242
return nil, xerrors.Errorf("create cookie jar: %w", err)

enterprise/coderd/provisionerdaemons_test.go

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ import (
1818
"github.com/coder/coder/v2/buildinfo"
1919
"github.com/coder/coder/v2/coderd/coderdtest"
2020
"github.com/coder/coder/v2/coderd/database"
21+
"github.com/coder/coder/v2/coderd/database/dbauthz"
22+
"github.com/coder/coder/v2/coderd/provisionerkey"
2123
"github.com/coder/coder/v2/coderd/rbac"
2224
"github.com/coder/coder/v2/coderd/util/ptr"
2325
"github.com/coder/coder/v2/codersdk"
@@ -552,6 +554,152 @@ func TestProvisionerDaemonServe(t *testing.T) {
552554
require.NoError(t, err)
553555
require.Len(t, daemons, 0)
554556
})
557+
558+
t.Run("ProvisionerKeyAuth", func(t *testing.T) {
559+
t.Parallel()
560+
561+
insertParams, token, err := provisionerkey.New(uuid.Nil, "dont-TEST-me")
562+
require.NoError(t, err)
563+
564+
tcs := []struct {
565+
name string
566+
psk string
567+
multiOrgFeatureEnabled bool
568+
multiOrgExperimentEnabled bool
569+
insertParams database.InsertProvisionerKeyParams
570+
requestProvisionerKey string
571+
requestPSK string
572+
errStatusCode int
573+
}{
574+
{
575+
name: "MultiOrgDisabledPSKAuthOK",
576+
psk: "provisionersftw",
577+
requestPSK: "provisionersftw",
578+
},
579+
{
580+
name: "MultiOrgExperimentDisabledPSKAuthOK",
581+
multiOrgFeatureEnabled: true,
582+
psk: "provisionersftw",
583+
requestPSK: "provisionersftw",
584+
},
585+
{
586+
name: "MultiOrgFeatureDisabledPSKAuthOK",
587+
multiOrgExperimentEnabled: true,
588+
psk: "provisionersftw",
589+
requestPSK: "provisionersftw",
590+
},
591+
{
592+
name: "MultiOrgEnabledPSKAuthOK",
593+
psk: "provisionersftw",
594+
multiOrgFeatureEnabled: true,
595+
multiOrgExperimentEnabled: true,
596+
requestPSK: "provisionersftw",
597+
},
598+
{
599+
name: "MultiOrgEnabledKeyAuthOK",
600+
psk: "provisionersftw",
601+
multiOrgFeatureEnabled: true,
602+
multiOrgExperimentEnabled: true,
603+
insertParams: insertParams,
604+
requestProvisionerKey: token,
605+
},
606+
{
607+
name: "MultiOrgEnabledPSKAuthDisabled",
608+
multiOrgFeatureEnabled: true,
609+
multiOrgExperimentEnabled: true,
610+
requestPSK: "provisionersftw",
611+
errStatusCode: http.StatusUnauthorized,
612+
},
613+
{
614+
name: "WrongKey",
615+
multiOrgFeatureEnabled: true,
616+
multiOrgExperimentEnabled: true,
617+
requestProvisionerKey: "provisionersftw",
618+
errStatusCode: http.StatusUnauthorized,
619+
},
620+
{
621+
name: "IdOKKeyWrong",
622+
multiOrgFeatureEnabled: true,
623+
multiOrgExperimentEnabled: true,
624+
requestProvisionerKey: insertParams.ID.String() + ":" + "wrong",
625+
errStatusCode: http.StatusUnauthorized,
626+
},
627+
{
628+
name: "IdWrongKeyOK",
629+
multiOrgFeatureEnabled: true,
630+
multiOrgExperimentEnabled: true,
631+
requestProvisionerKey: uuid.NewString() + ":" + token,
632+
errStatusCode: http.StatusUnauthorized,
633+
},
634+
{
635+
name: "TokenOnly",
636+
multiOrgFeatureEnabled: true,
637+
multiOrgExperimentEnabled: true,
638+
requestProvisionerKey: token,
639+
errStatusCode: http.StatusUnauthorized,
640+
},
641+
}
642+
643+
for _, tc := range tcs {
644+
t.Run(tc.name, func(t *testing.T) {
645+
t.Parallel()
646+
features := license.Features{
647+
codersdk.FeatureExternalProvisionerDaemons: 1,
648+
}
649+
if tc.multiOrgFeatureEnabled {
650+
features[codersdk.FeatureMultipleOrganizations] = 1
651+
}
652+
dv := coderdtest.DeploymentValues(t)
653+
if tc.multiOrgExperimentEnabled {
654+
dv.Experiments.Append(string(codersdk.ExperimentMultiOrganization))
655+
}
656+
client, db, user := coderdenttest.NewWithDatabase(t, &coderdenttest.Options{
657+
LicenseOptions: &coderdenttest.LicenseOptions{
658+
Features: features,
659+
},
660+
ProvisionerDaemonPSK: tc.psk,
661+
Options: &coderdtest.Options{
662+
DeploymentValues: dv,
663+
},
664+
})
665+
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
666+
defer cancel()
667+
668+
if tc.insertParams.Name != "" {
669+
tc.insertParams.OrganizationID = user.OrganizationID
670+
// nolint:gocritic // test
671+
_, err := db.InsertProvisionerKey(dbauthz.AsSystemRestricted(ctx), tc.insertParams)
672+
require.NoError(t, err)
673+
}
674+
675+
another := codersdk.New(client.URL)
676+
srv, err := another.ServeProvisionerDaemon(ctx, codersdk.ServeProvisionerDaemonRequest{
677+
ID: uuid.New(),
678+
Name: testutil.MustRandString(t, 63),
679+
Organization: user.OrganizationID,
680+
Provisioners: []codersdk.ProvisionerType{
681+
codersdk.ProvisionerTypeEcho,
682+
},
683+
Tags: map[string]string{
684+
provisionersdk.TagScope: provisionersdk.ScopeOrganization,
685+
},
686+
PreSharedKey: tc.requestPSK,
687+
ProvisionerKey: tc.requestProvisionerKey,
688+
})
689+
if tc.errStatusCode != 0 {
690+
require.Error(t, err)
691+
var apiError *codersdk.Error
692+
require.ErrorAs(t, err, &apiError)
693+
require.Equal(t, http.StatusUnauthorized, apiError.StatusCode())
694+
return
695+
}
696+
697+
require.NoError(t, err)
698+
err = srv.DRPCConn().Close()
699+
require.NoError(t, err)
700+
})
701+
}
702+
})
555703
}
556704

557705
func TestGetProvisionerDaemons(t *testing.T) {

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