Skip to content

chore: protect organization endpoints with license #14001

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 17 commits into from
Jul 25, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
move create organization test util to enterprise
  • Loading branch information
Emyrk committed Jul 25, 2024
commit 6b64343976b37bb2a1db12b5367121d7b3630b48
2 changes: 0 additions & 2 deletions cli/organizationmembers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,6 @@ func TestListOrganizationMembers(t *testing.T) {
})
}



func TestRemoveOrganizationMembers(t *testing.T) {
t.Parallel()

Expand Down
104 changes: 8 additions & 96 deletions coderd/coderdtest/coderdtest.go
Original file line number Diff line number Diff line change
Expand Up @@ -538,14 +538,18 @@ func NewWithAPI(t testing.TB, options *Options) (*codersdk.Client, io.Closer, *c
return client, provisionerCloser, coderAPI
}

// provisionerdCloser wraps a provisioner daemon as an io.Closer that can be called multiple times
type provisionerdCloser struct {
// ProvisionerdCloser wraps a provisioner daemon as an io.Closer that can be called multiple times
type ProvisionerdCloser struct {
mu sync.Mutex
closed bool
d *provisionerd.Server
}

func (c *provisionerdCloser) Close() error {
func NewProvisionerDaemonCloser(d *provisionerd.Server) *ProvisionerdCloser {
return &ProvisionerdCloser{d: d}
}

func (c *ProvisionerdCloser) Close() error {
c.mu.Lock()
defer c.mu.Unlock()
if c.closed {
Expand Down Expand Up @@ -605,71 +609,10 @@ func NewTaggedProvisionerDaemon(t testing.TB, coderAPI *coderd.API, name string,
string(database.ProvisionerTypeEcho): sdkproto.NewDRPCProvisionerClient(echoClient),
},
})
closer := &provisionerdCloser{d: daemon}
t.Cleanup(func() {
_ = closer.Close()
})
return closer
}

func NewExternalProvisionerDaemon(t testing.TB, client *codersdk.Client, org uuid.UUID, tags map[string]string) io.Closer {
t.Helper()

// Without this check, the provisioner will silently fail.
entitlements, err := client.Entitlements(context.Background())
if err != nil {
// AGPL instances will throw this error. They cannot use external
// provisioners.
t.Errorf("external provisioners requires a license with entitlements. The client failed to fetch the entitlements, is this an enterprise instance of coderd?")
t.FailNow()
return nil
}

feature := entitlements.Features[codersdk.FeatureExternalProvisionerDaemons]
if !feature.Enabled || feature.Entitlement != codersdk.EntitlementEntitled {
require.NoError(t, xerrors.Errorf("external provisioner daemons require an entitled license"))
return nil
}

echoClient, echoServer := drpc.MemTransportPipe()
ctx, cancelFunc := context.WithCancel(context.Background())
serveDone := make(chan struct{})
t.Cleanup(func() {
_ = echoClient.Close()
_ = echoServer.Close()
cancelFunc()
<-serveDone
})
go func() {
defer close(serveDone)
err := echo.Serve(ctx, &provisionersdk.ServeOptions{
Listener: echoServer,
WorkDirectory: t.TempDir(),
})
assert.NoError(t, err)
}()

daemon := provisionerd.New(func(ctx context.Context) (provisionerdproto.DRPCProvisionerDaemonClient, error) {
return client.ServeProvisionerDaemon(ctx, codersdk.ServeProvisionerDaemonRequest{
ID: uuid.New(),
Name: t.Name(),
Organization: org,
Provisioners: []codersdk.ProvisionerType{codersdk.ProvisionerTypeEcho},
Tags: tags,
})
}, &provisionerd.Options{
Logger: slogtest.Make(t, nil).Named("provisionerd").Leveled(slog.LevelDebug),
UpdateInterval: 250 * time.Millisecond,
ForceCancelInterval: 5 * time.Second,
Connector: provisionerd.LocalProvisioners{
string(database.ProvisionerTypeEcho): sdkproto.NewDRPCProvisionerClient(echoClient),
},
})
closer := &provisionerdCloser{d: daemon}
closer := NewProvisionerDaemonCloser(daemon)
t.Cleanup(func() {
_ = closer.Close()
})

return closer
}

Expand Down Expand Up @@ -841,37 +784,6 @@ func createAnotherUserRetry(t testing.TB, client *codersdk.Client, organizationI
return other, user
}

type CreateOrganizationOptions struct {
// IncludeProvisionerDaemon will spin up an external provisioner for the organization.
// This requires enterprise and the feature 'codersdk.FeatureExternalProvisionerDaemons'
IncludeProvisionerDaemon bool
}

func CreateOrganization(t *testing.T, client *codersdk.Client, opts CreateOrganizationOptions, mutators ...func(*codersdk.CreateOrganizationRequest)) codersdk.Organization {
ctx := testutil.Context(t, testutil.WaitMedium)
req := codersdk.CreateOrganizationRequest{
Name: strings.ReplaceAll(strings.ToLower(namesgenerator.GetRandomName(0)), "_", "-"),
DisplayName: namesgenerator.GetRandomName(1),
Description: namesgenerator.GetRandomName(1),
Icon: "",
}
for _, mutator := range mutators {
mutator(&req)
}

org, err := client.CreateOrganization(ctx, req)
require.NoError(t, err)

if opts.IncludeProvisionerDaemon {
closer := NewExternalProvisionerDaemon(t, client, org.ID, map[string]string{})
t.Cleanup(func() {
_ = closer.Close()
})
}

return org
}

// CreateTemplateVersion creates a template import provisioner job
// with the responses provided. It uses the "echo" provisioner for compatibility
// with testing.
Expand Down
20 changes: 0 additions & 20 deletions coderd/coderdtest/coderdtest_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,9 @@ package coderdtest_test
import (
"testing"

"github.com/google/uuid"
"github.com/stretchr/testify/require"
"go.uber.org/goleak"

"github.com/coder/coder/v2/coderd/coderdtest"
"github.com/coder/coder/v2/coderd/rbac"
)

func TestMain(m *testing.M) {
Expand All @@ -30,20 +27,3 @@ func TestNew(t *testing.T) {
_, _ = coderdtest.NewGoogleInstanceIdentity(t, "example", false)
_, _ = coderdtest.NewAWSInstanceIdentity(t, "an-instance")
}

// TestOrganizationMember checks the coderdtest helper can add organization members
// to multiple orgs.
func TestOrganizationMember(t *testing.T) {
t.Parallel()

client := coderdtest.New(t, &coderdtest.Options{})
owner := coderdtest.CreateFirstUser(t, client)

second := coderdtest.CreateOrganization(t, client, coderdtest.CreateOrganizationOptions{})
third := coderdtest.CreateOrganization(t, client, coderdtest.CreateOrganizationOptions{})

// Assign the user to 3 orgs in this 1 statement
_, user := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID, rbac.ScopedRoleOrgMember(second.ID), rbac.ScopedRoleOrgMember(third.ID))
require.Len(t, user.OrganizationIDs, 3)
require.ElementsMatch(t, user.OrganizationIDs, []uuid.UUID{owner.OrganizationID, second.ID, third.ID})
}
21 changes: 0 additions & 21 deletions coderd/organizations_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,27 +12,6 @@ import (
"github.com/coder/coder/v2/testutil"
)

func TestOrganizationsByUser(t *testing.T) {
t.Parallel()
client := coderdtest.New(t, nil)
_ = coderdtest.CreateFirstUser(t, client)
ctx := testutil.Context(t, testutil.WaitLong)

orgs, err := client.OrganizationsByUser(ctx, codersdk.Me)
require.NoError(t, err)
require.NotNil(t, orgs)
require.Len(t, orgs, 1)
require.True(t, orgs[0].IsDefault, "first org is always default")

// Make an extra org, and it should not be defaulted.
notDefault, err := client.CreateOrganization(ctx, codersdk.CreateOrganizationRequest{
Name: "another",
DisplayName: "Another",
})
require.NoError(t, err)
require.False(t, notDefault.IsDefault, "only 1 default org allowed")
}

func TestOrganizationByUserAndName(t *testing.T) {
t.Parallel()
t.Run("NoExist", func(t *testing.T) {
Expand Down
2 changes: 0 additions & 2 deletions coderd/roles_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import (
"github.com/coder/coder/v2/testutil"
)


func TestListCustomRoles(t *testing.T) {
t.Parallel()

Expand Down Expand Up @@ -55,4 +54,3 @@ func TestListCustomRoles(t *testing.T) {
require.Truef(t, found, "custom organization role listed")
})
}

41 changes: 0 additions & 41 deletions coderd/templates_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -461,47 +461,6 @@ func TestTemplatesByOrganization(t *testing.T) {
require.Equal(t, tmpl.OrganizationIcon, org.Icon, "organization display name")
}
})
t.Run("MultipleOrganizations", func(t *testing.T) {
t.Parallel()
client := coderdtest.New(t, nil)
owner := coderdtest.CreateFirstUser(t, client)
org2 := coderdtest.CreateOrganization(t, client, coderdtest.CreateOrganizationOptions{})
user, _ := coderdtest.CreateAnotherUser(t, client, org2.ID)

// 2 templates in first organization
version := coderdtest.CreateTemplateVersion(t, client, owner.OrganizationID, nil)
version2 := coderdtest.CreateTemplateVersion(t, client, owner.OrganizationID, nil)
coderdtest.CreateTemplate(t, client, owner.OrganizationID, version.ID)
coderdtest.CreateTemplate(t, client, owner.OrganizationID, version2.ID)

// 2 in the second organization
version3 := coderdtest.CreateTemplateVersion(t, client, org2.ID, nil)
version4 := coderdtest.CreateTemplateVersion(t, client, org2.ID, nil)
coderdtest.CreateTemplate(t, client, org2.ID, version3.ID)
coderdtest.CreateTemplate(t, client, org2.ID, version4.ID)

ctx := testutil.Context(t, testutil.WaitLong)

// All 4 are viewable by the owner
templates, err := client.Templates(ctx, codersdk.TemplateFilter{})
require.NoError(t, err)
require.Len(t, templates, 4)

// View a single organization from the owner
templates, err = client.Templates(ctx, codersdk.TemplateFilter{
OrganizationID: owner.OrganizationID,
})
require.NoError(t, err)
require.Len(t, templates, 2)

// Only 2 are viewable by the org user
templates, err = user.Templates(ctx, codersdk.TemplateFilter{})
require.NoError(t, err)
require.Len(t, templates, 2)
for _, tmpl := range templates {
require.Equal(t, tmpl.OrganizationName, org2.Name, "organization name on template")
}
})
}

func TestTemplateByOrganizationAndName(t *testing.T) {
Expand Down
2 changes: 0 additions & 2 deletions coderd/users_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -990,8 +990,6 @@ func TestUpdateUserPassword(t *testing.T) {
})
}



// TestInitialRoles ensures the starting roles for the first user are correct.
func TestInitialRoles(t *testing.T) {
t.Parallel()
Expand Down
2 changes: 1 addition & 1 deletion enterprise/cli/create_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ func TestEnterpriseCreate(t *testing.T) {
},
})

second := coderdtest.CreateOrganization(t, ownerClient, coderdtest.CreateOrganizationOptions{
second := coderdenttest.CreateOrganization(t, ownerClient, coderdenttest.CreateOrganizationOptions{
IncludeProvisionerDaemon: true,
})
member, _ := coderdtest.CreateAnotherUser(t, ownerClient, first.OrganizationID, rbac.ScopedRoleOrgMember(second.ID))
Expand Down
8 changes: 4 additions & 4 deletions enterprise/cli/provisionerdaemonstart_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ func TestProvisionerDaemon_PSK(t *testing.T) {
},
},
})
anotherOrg := coderdtest.CreateOrganization(t, client, coderdtest.CreateOrganizationOptions{})
anotherOrg := coderdenttest.CreateOrganization(t, client, coderdenttest.CreateOrganizationOptions{})
inv, conf := newCLI(t, "provisionerd", "start", "--psk=provisionersftw", "--name", "org-daemon", "--org", anotherOrg.ID.String())
err := conf.URL().Write(client.URL.String())
require.NoError(t, err)
Expand Down Expand Up @@ -106,7 +106,7 @@ func TestProvisionerDaemon_PSK(t *testing.T) {
},
},
})
anotherOrg := coderdtest.CreateOrganization(t, client, coderdtest.CreateOrganizationOptions{})
anotherOrg := coderdenttest.CreateOrganization(t, client, coderdenttest.CreateOrganizationOptions{})
anotherClient, _ := coderdtest.CreateAnotherUser(t, client, anotherOrg.ID, rbac.RoleTemplateAdmin())
inv, conf := newCLI(t, "provisionerd", "start", "--psk=provisionersftw", "--name", "org-daemon", "--org", anotherOrg.Name)
clitest.SetupConfig(t, anotherClient, conf)
Expand All @@ -127,7 +127,7 @@ func TestProvisionerDaemon_PSK(t *testing.T) {
},
},
})
anotherOrg := coderdtest.CreateOrganization(t, client, coderdtest.CreateOrganizationOptions{})
anotherOrg := coderdenttest.CreateOrganization(t, client, coderdenttest.CreateOrganizationOptions{})
inv, conf := newCLI(t, "provisionerd", "start", "--psk=provisionersftw", "--name", "org-daemon", "--org", anotherOrg.Name)
err := conf.URL().Write(client.URL.String())
require.NoError(t, err)
Expand Down Expand Up @@ -274,7 +274,7 @@ func TestProvisionerDaemon_SessionToken(t *testing.T) {
},
},
})
anotherOrg := coderdtest.CreateOrganization(t, client, coderdtest.CreateOrganizationOptions{})
anotherOrg := coderdenttest.CreateOrganization(t, client, coderdenttest.CreateOrganizationOptions{})
anotherClient, anotherUser := coderdtest.CreateAnotherUser(t, client, anotherOrg.ID, rbac.RoleTemplateAdmin())
inv, conf := newCLI(t, "provisionerd", "start", "--tag", "scope=user", "--name", "org-daemon", "--org", anotherOrg.ID.String())
clitest.SetupConfig(t, anotherClient, conf)
Expand Down
2 changes: 1 addition & 1 deletion enterprise/cli/templatecreate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ func TestTemplateCreate(t *testing.T) {
})

// Create the second organization
secondOrg := coderdtest.CreateOrganization(t, ownerClient, coderdtest.CreateOrganizationOptions{
secondOrg := coderdenttest.CreateOrganization(t, ownerClient, coderdenttest.CreateOrganizationOptions{
IncludeProvisionerDaemon: true,
})

Expand Down
2 changes: 1 addition & 1 deletion enterprise/cli/templatelist_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ func TestEnterpriseListTemplates(t *testing.T) {
_ = coderdtest.AwaitTemplateVersionJobCompleted(t, client, firstVersion.ID)
_ = coderdtest.CreateTemplate(t, client, owner.OrganizationID, firstVersion.ID)

secondOrg := coderdtest.CreateOrganization(t, client, coderdtest.CreateOrganizationOptions{
secondOrg := coderdenttest.CreateOrganization(t, client, coderdenttest.CreateOrganizationOptions{
IncludeProvisionerDaemon: true,
})
secondVersion := coderdtest.CreateTemplateVersion(t, client, secondOrg.ID, nil)
Expand Down
Loading