diff --git a/cli/templatelist_test.go b/cli/templatelist_test.go index 5181720cc30b2..5b7962e1c42d1 100644 --- a/cli/templatelist_test.go +++ b/cli/templatelist_test.go @@ -122,7 +122,9 @@ func TestTemplateList(t *testing.T) { _ = coderdtest.CreateTemplate(t, client, owner.OrganizationID, firstVersion.ID) secondOrg := coderdtest.CreateOrganization(t, client, coderdtest.CreateOrganizationOptions{ - IncludeProvisionerDaemon: true, + // Listing templates does not require the template actually completes. + // We cannot provision an external provisioner in AGPL tests. + IncludeProvisionerDaemon: false, }) secondVersion := coderdtest.CreateTemplateVersion(t, client, secondOrg.ID, nil) _ = coderdtest.CreateTemplate(t, client, secondOrg.ID, secondVersion.ID) diff --git a/coderd/coderdtest/coderdtest.go b/coderd/coderdtest/coderdtest.go index 472c380926ec4..97541ea927c98 100644 --- a/coderd/coderdtest/coderdtest.go +++ b/coderd/coderdtest/coderdtest.go @@ -605,12 +605,18 @@ func NewExternalProvisionerDaemon(t testing.TB, client *codersdk.Client, org uui // Without this check, the provisioner will silently fail. entitlements, err := client.Entitlements(context.Background()) - if err == 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 - } + 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() @@ -796,13 +802,30 @@ func createAnotherUserRetry(t testing.TB, client *codersdk.Client, organizationI user, err = client.UpdateUserRoles(context.Background(), user.ID.String(), codersdk.UpdateRoles{Roles: db2sdk.List(siteRoles, onlyName)}) require.NoError(t, err, "update site roles") + // isMember keeps track of which orgs the user was added to as a member + isMember := map[uuid.UUID]bool{ + organizationID: true, + } + // Update org roles for orgID, roles := range orgRoles { + // The user must be an organization of any orgRoles, so insert + // the organization member, then assign the roles. + if !isMember[orgID] { + _, err = client.PostOrganizationMember(context.Background(), orgID, user.ID.String()) + require.NoError(t, err, "add user to organization as member") + } + _, err = client.UpdateOrganizationMemberRoles(context.Background(), orgID, user.ID.String(), codersdk.UpdateRoles{Roles: db2sdk.List(roles, onlyName)}) require.NoError(t, err, "update org membership roles") + isMember[orgID] = true } } + + user, err = client.User(context.Background(), user.Username) + require.NoError(t, err, "update final user") + return other, user } diff --git a/coderd/coderdtest/coderdtest_test.go b/coderd/coderdtest/coderdtest_test.go index 455a03dc119b7..4d03f21ef8ea6 100644 --- a/coderd/coderdtest/coderdtest_test.go +++ b/coderd/coderdtest/coderdtest_test.go @@ -3,9 +3,12 @@ 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) { @@ -27,3 +30,20 @@ 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}) +} diff --git a/coderd/members.go b/coderd/members.go index 24f712b8154c7..f505645ec4d50 100644 --- a/coderd/members.go +++ b/coderd/members.go @@ -33,10 +33,11 @@ func (api *API) postOrganizationMember(rw http.ResponseWriter, r *http.Request) user = httpmw.UserParam(r) auditor = api.Auditor.Load() aReq, commitAudit = audit.InitRequest[database.AuditableOrganizationMember](rw, &audit.RequestParams{ - Audit: *auditor, - Log: api.Logger, - Request: r, - Action: database.AuditActionCreate, + OrganizationID: organization.ID, + Audit: *auditor, + Log: api.Logger, + Request: r, + Action: database.AuditActionCreate, }) ) aReq.Old = database.AuditableOrganizationMember{} @@ -95,10 +96,11 @@ func (api *API) deleteOrganizationMember(rw http.ResponseWriter, r *http.Request member = httpmw.OrganizationMemberParam(r) auditor = api.Auditor.Load() aReq, commitAudit = audit.InitRequest[database.AuditableOrganizationMember](rw, &audit.RequestParams{ - Audit: *auditor, - Log: api.Logger, - Request: r, - Action: database.AuditActionDelete, + OrganizationID: organization.ID, + Audit: *auditor, + Log: api.Logger, + Request: r, + Action: database.AuditActionDelete, }) ) aReq.Old = member.OrganizationMember.Auditable(member.Username)