feat(coderd/rbac): make organization-member a per-org system custom role (#21359)

Migrated the built-in organization-member role to DB storage so it can be customized per org.

Closes https://github.com/coder/internal/issues/1073 (part 1)
This commit is contained in:
George K
2026-01-12 18:19:19 -08:00
committed by GitHub
parent 2b448c7178
commit cc2efe9e1f
46 changed files with 1845 additions and 438 deletions
+83 -19
View File
@@ -13,7 +13,10 @@ import (
"github.com/coder/coder/v2/coderd/coderdtest"
"github.com/coder/coder/v2/coderd/database"
"github.com/coder/coder/v2/coderd/database/db2sdk"
"github.com/coder/coder/v2/coderd/database/dbauthz"
"github.com/coder/coder/v2/coderd/database/dbtestutil"
"github.com/coder/coder/v2/coderd/rbac"
"github.com/coder/coder/v2/coderd/rbac/rolestore"
"github.com/coder/coder/v2/coderd/util/ptr"
"github.com/coder/coder/v2/codersdk"
"github.com/coder/coder/v2/enterprise/coderd/coderdenttest"
@@ -694,22 +697,75 @@ func TestGroup(t *testing.T) {
t.Run("RegularUserReadGroup", func(t *testing.T) {
t.Parallel()
client, user := coderdenttest.New(t, &coderdenttest.Options{LicenseOptions: &coderdenttest.LicenseOptions{
Features: license.Features{
codersdk.FeatureTemplateRBAC: 1,
},
}})
client1, _ := coderdtest.CreateAnotherUser(t, client, user.OrganizationID)
t.Run("WorkspaceSharingEnabled", func(t *testing.T) {
t.Parallel()
ctx := testutil.Context(t, testutil.WaitLong)
//nolint:gocritic // test setup
group, err := client.CreateGroup(ctx, user.OrganizationID, codersdk.CreateGroupRequest{
Name: "hi",
client, user := coderdenttest.New(t, &coderdenttest.Options{LicenseOptions: &coderdenttest.LicenseOptions{
Features: license.Features{
codersdk.FeatureTemplateRBAC: 1,
},
}})
client1, _ := coderdtest.CreateAnotherUser(t, client, user.OrganizationID)
ctx := testutil.Context(t, testutil.WaitLong)
//nolint:gocritic // test setup
group, err := client.CreateGroup(ctx, user.OrganizationID, codersdk.CreateGroupRequest{
Name: "hi",
})
require.NoError(t, err)
ggroup, err := client1.Group(ctx, group.ID)
require.NoError(t, err, "regular users can read groups unless workspace sharing is disabled")
normalizeGroupMembers(&group)
normalizeGroupMembers(&ggroup)
require.Equal(t, group, ggroup)
})
require.NoError(t, err)
_, err = client1.Group(ctx, group.ID)
require.Error(t, err, "regular users cannot read groups")
t.Run("WorkspaceSharingDisabled", func(t *testing.T) {
t.Parallel()
db, ps, sqlDB := dbtestutil.NewDBWithSQLDB(t)
client, _, api, user := coderdenttest.NewWithAPI(t, &coderdenttest.Options{
Options: &coderdtest.Options{
Database: db,
Pubsub: ps,
},
LicenseOptions: &coderdenttest.LicenseOptions{
Features: license.Features{
codersdk.FeatureTemplateRBAC: 1,
},
},
})
ctx := testutil.Context(t, testutil.WaitLong)
_, err := sqlDB.ExecContext(ctx, "UPDATE organizations SET workspace_sharing_disabled = true WHERE id = $1", user.OrganizationID)
require.NoError(t, err)
//nolint:gocritic // ReconcileOrgMemberRole needs the system:update
// permission that the test context doesn't have.
sysCtx := dbauthz.AsSystemRestricted(ctx)
_, _, err = rolestore.ReconcileOrgMemberRole(sysCtx, api.Database, database.CustomRole{
Name: rbac.RoleOrgMember(),
OrganizationID: uuid.NullUUID{
UUID: user.OrganizationID,
Valid: true,
},
}, true)
require.NoError(t, err)
client1, _ := coderdtest.CreateAnotherUser(t, client, user.OrganizationID)
//nolint:gocritic // test setup
group, err := client.CreateGroup(ctx, user.OrganizationID, codersdk.CreateGroupRequest{
Name: "hi",
})
require.NoError(t, err)
_, err = client1.Group(ctx, group.ID)
require.Error(t, err, "regular users cannot read groups when workspace sharing is disabled")
cerr, ok := codersdk.AsError(err)
require.True(t, ok)
require.Equal(t, http.StatusNotFound, cerr.StatusCode())
})
})
t.Run("FilterDeletedUsers", func(t *testing.T) {
@@ -947,22 +1003,30 @@ func TestGroups(t *testing.T) {
// Query from the user's perspective
user5View, err := user5Client.Groups(ctx, codersdk.GroupArguments{})
require.NoError(t, err)
normalizeAllGroups(user5Groups)
normalizeAllGroups(user5View)
// Everyone group and group 2
require.Len(t, user5View, 2)
// Org members can read all groups when workspace sharing is not
// disabled, but group membership is limited to the requesting user.
// TODO(geokat): add another test with workspace sharing disabled.
require.Len(t, user5View, 3)
user5ViewIDs := db2sdk.List(user5View, func(g codersdk.Group) uuid.UUID {
return g.ID
})
require.ElementsMatch(t, []uuid.UUID{
everyoneGroup.ID,
group1.ID,
group2.ID,
}, user5ViewIDs)
for _, g := range user5View {
// Only expect the 1 member, themselves
require.Len(t, g.Members, 1)
require.Equal(t, user5.ReducedUser.ID, g.Members[0].MinimalUser.ID)
if g.ID == everyoneGroup.ID || g.ID == group2.ID {
// Only expect the 1 member, themselves.
require.Len(t, g.Members, 1)
require.Equal(t, user5.ReducedUser.ID, g.Members[0].MinimalUser.ID)
continue
}
require.Empty(t, g.Members)
}
})
}
+29
View File
@@ -12,9 +12,12 @@ import (
"github.com/coder/coder/v2/coderd/audit"
"github.com/coder/coder/v2/coderd/database"
"github.com/coder/coder/v2/coderd/database/db2sdk"
"github.com/coder/coder/v2/coderd/database/dbauthz"
"github.com/coder/coder/v2/coderd/database/dbtime"
"github.com/coder/coder/v2/coderd/httpapi"
"github.com/coder/coder/v2/coderd/httpmw"
"github.com/coder/coder/v2/coderd/rbac"
"github.com/coder/coder/v2/coderd/rbac/rolestore"
"github.com/coder/coder/v2/codersdk"
)
@@ -265,6 +268,14 @@ func (api *API) postOrganizations(rw http.ResponseWriter, r *http.Request) {
var organization database.Organization
err = api.Database.InTx(func(tx database.Store) error {
// Serialize creation and reconciliation of the org-member
// system role across coderd instances (e.g. during rolling
// restarts).
err := tx.AcquireLock(ctx, database.LockIDReconcileSystemRoles)
if err != nil {
return xerrors.Errorf("acquire system roles reconciliation lock: %w", err)
}
if req.DisplayName == "" {
req.DisplayName = req.Name
}
@@ -281,6 +292,24 @@ func (api *API) postOrganizations(rw http.ResponseWriter, r *http.Request) {
if err != nil {
return xerrors.Errorf("create organization: %w", err)
}
// Populate the placeholder system role(s) that the DB trigger
// created for us.
//nolint:gocritic // ReconcileOrgMemberRole needs the system:update
// permission that user doesn't have.
sysCtx := dbauthz.AsSystemRestricted(ctx)
_, _, err = rolestore.ReconcileOrgMemberRole(sysCtx, tx, database.CustomRole{
Name: rbac.RoleOrgMember(),
OrganizationID: uuid.NullUUID{
UUID: organizationID,
Valid: true,
},
}, organization.WorkspaceSharingDisabled)
if err != nil {
return xerrors.Errorf("reconcile organization-member role for organization %s: %w",
organizationID, err)
}
_, err = tx.InsertOrganizationMember(ctx, database.InsertOrganizationMemberParams{
OrganizationID: organization.ID,
UserID: apiKey.UserID,
+20 -7
View File
@@ -65,6 +65,9 @@ func (api *API) postOrgRoles(rw http.ResponseWriter, r *http.Request) {
SitePermissions: db2sdk.List(req.SitePermissions, sdkPermissionToDB),
OrgPermissions: db2sdk.List(req.OrganizationPermissions, sdkPermissionToDB),
UserPermissions: db2sdk.List(req.UserPermissions, sdkPermissionToDB),
// Satisfy the linter (we don't support member permissions in non-system roles).
MemberPermissions: database.CustomRolePermissions{},
IsSystem: false,
})
if httpapi.Is404Error(err) {
httpapi.ResourceNotFound(rw)
@@ -82,15 +85,15 @@ func (api *API) postOrgRoles(rw http.ResponseWriter, r *http.Request) {
httpapi.Write(ctx, rw, http.StatusOK, db2sdk.Role(inserted))
}
// patchRole will allow creating a custom organization role
// putOrgRoles will allow updating a custom organization role
//
// @Summary Upsert a custom organization role
// @ID upsert-a-custom-organization-role
// @Summary Update a custom organization role
// @ID update-a-custom-organization-role
// @Security CoderSessionToken
// @Accept json
// @Produce json
// @Param organization path string true "Organization ID" format(uuid)
// @Param request body codersdk.CustomRoleRequest true "Upsert role request"
// @Param request body codersdk.CustomRoleRequest true "Update role request"
// @Tags Members
// @Success 200 {array} codersdk.Role
// @Router /organizations/{organization}/members/roles [put]
@@ -126,8 +129,9 @@ func (api *API) putOrgRoles(rw http.ResponseWriter, r *http.Request) {
OrganizationID: organization.ID,
},
},
ExcludeOrgRoles: false,
OrganizationID: organization.ID,
ExcludeOrgRoles: false,
OrganizationID: organization.ID,
IncludeSystemRoles: false,
})
// If it is a 404 (not found) error, ignore it.
if err != nil && !httpapi.Is404Error(err) {
@@ -153,6 +157,8 @@ func (api *API) putOrgRoles(rw http.ResponseWriter, r *http.Request) {
SitePermissions: db2sdk.List(filterInvalidPermissions(req.SitePermissions), sdkPermissionToDB),
OrgPermissions: db2sdk.List(filterInvalidPermissions(req.OrganizationPermissions), sdkPermissionToDB),
UserPermissions: db2sdk.List(filterInvalidPermissions(req.UserPermissions), sdkPermissionToDB),
// Satisfy the linter (we don't support member permissions in non-system roles).
MemberPermissions: database.CustomRolePermissions{},
})
if httpapi.Is404Error(err) {
httpapi.ResourceNotFound(rw)
@@ -197,6 +203,12 @@ func (api *API) deleteOrgRole(rw http.ResponseWriter, r *http.Request) {
defer commitAudit()
rolename := chi.URLParam(r, "roleName")
// Catch requests that try to delete system roles.
if !validOrganizationRoleRequest(ctx, codersdk.CustomRoleRequest{Name: rolename}, rw) {
return
}
roles, err := api.Database.CustomRoles(ctx, database.CustomRolesParams{
LookupRoles: []database.NameOrganizationPair{
{
@@ -204,7 +216,8 @@ func (api *API) deleteOrgRole(rw http.ResponseWriter, r *http.Request) {
OrganizationID: organization.ID,
},
},
ExcludeOrgRoles: false,
ExcludeOrgRoles: false,
IncludeSystemRoles: false,
// Linter requires all fields to be set. This field is not actually required.
OrganizationID: organization.ID,
})
+53
View File
@@ -256,6 +256,59 @@ func TestCustomOrganizationRole(t *testing.T) {
require.ErrorContains(t, err, "not allowed to assign user permissions")
})
// Attempt to add org member permissions, which is not allowed.
t.Run("MemberPermissions", func(t *testing.T) {
t.Parallel()
owner, first := coderdenttest.New(t, &coderdenttest.Options{
LicenseOptions: &coderdenttest.LicenseOptions{
Features: license.Features{
codersdk.FeatureCustomRoles: 1,
},
},
})
ctx := testutil.Context(t, testutil.WaitMedium)
role := templateAdminCustom(first.OrganizationID)
role.Name = "test-role-member-perms"
role.OrganizationMemberPermissions = []codersdk.Permission{
{
ResourceType: codersdk.ResourceWorkspace,
Action: codersdk.ActionRead,
},
}
//nolint:gocritic // we want unrestricted permissions for the test
_, err := owner.CreateOrganizationRole(ctx, role)
var apiErr *codersdk.Error
require.ErrorAs(t, err, &apiErr)
require.Equal(t, http.StatusBadRequest, apiErr.StatusCode())
require.ErrorContains(t, err, "not allowed to assign organization member permissions for an organization role")
})
// Attempt to delete a system role, which is not allowed.
t.Run("DeleteSystemRole", func(t *testing.T) {
t.Parallel()
owner, first := coderdenttest.New(t, &coderdenttest.Options{
LicenseOptions: &coderdenttest.LicenseOptions{
Features: license.Features{
codersdk.FeatureCustomRoles: 1,
},
},
})
ctx := testutil.Context(t, testutil.WaitMedium)
//nolint:gocritic // we want unrestricted permissions for the test
err := owner.DeleteOrganizationRole(ctx, first.OrganizationID, rbac.RoleOrgMember())
var apiErr *codersdk.Error
require.ErrorAs(t, err, &apiErr)
require.Equal(t, http.StatusBadRequest, apiErr.StatusCode())
require.ErrorContains(t, err, "Reserved role name")
})
t.Run("NotFound", func(t *testing.T) {
t.Parallel()
owner, first := coderdenttest.New(t, &coderdenttest.Options{
+70 -63
View File
@@ -3639,41 +3639,45 @@ func TestWorkspacesFiltering(t *testing.T) {
dv := coderdtest.DeploymentValues(t)
dv.Experiments = []string{string(codersdk.ExperimentWorkspaceSharing)}
var (
client, db, orgOwner = coderdenttest.NewWithDatabase(t, &coderdenttest.Options{
Options: &coderdtest.Options{
DeploymentValues: dv,
ownerClient, db, owner := coderdenttest.NewWithDatabase(t, &coderdenttest.Options{
Options: &coderdtest.Options{
DeploymentValues: dv,
},
LicenseOptions: &coderdenttest.LicenseOptions{
Features: license.Features{
codersdk.FeatureTemplateRBAC: 1,
},
LicenseOptions: &coderdenttest.LicenseOptions{
Features: license.Features{
codersdk.FeatureTemplateRBAC: 1,
},
},
})
_, workspaceOwner = coderdtest.CreateAnotherUser(t, client, orgOwner.OrganizationID, rbac.ScopedRoleOrgAuditor(orgOwner.OrganizationID))
sharedWorkspace = dbfake.WorkspaceBuild(t, db, database.WorkspaceTable{
OwnerID: workspaceOwner.ID,
OrganizationID: orgOwner.OrganizationID,
}).Do().Workspace
_ = dbfake.WorkspaceBuild(t, db, database.WorkspaceTable{
OwnerID: workspaceOwner.ID,
OrganizationID: orgOwner.OrganizationID,
}).Do().Workspace
ctx = testutil.Context(t, testutil.WaitMedium)
)
},
})
group, err := client.CreateGroup(ctx, orgOwner.OrganizationID, codersdk.CreateGroupRequest{
workspaceOwnerClient, workspaceOwner := coderdtest.CreateAnotherUser(t, ownerClient, owner.OrganizationID, rbac.ScopedRoleOrgAuditor(owner.OrganizationID))
sharedWorkspace := dbfake.WorkspaceBuild(t, db, database.WorkspaceTable{
OwnerID: workspaceOwner.ID,
OrganizationID: owner.OrganizationID,
}).Do().Workspace
// Unshared workspace.
_ = dbfake.WorkspaceBuild(t, db, database.WorkspaceTable{
OwnerID: workspaceOwner.ID,
OrganizationID: owner.OrganizationID,
}).Do().Workspace
ctx := testutil.Context(t, testutil.WaitMedium)
group, err := ownerClient.CreateGroup(ctx, owner.OrganizationID, codersdk.CreateGroupRequest{
Name: "wibble",
})
require.NoError(t, err, "create group")
client.UpdateWorkspaceACL(ctx, sharedWorkspace.ID, codersdk.UpdateWorkspaceACL{
err = workspaceOwnerClient.UpdateWorkspaceACL(ctx, sharedWorkspace.ID, codersdk.UpdateWorkspaceACL{
GroupRoles: map[string]codersdk.WorkspaceRole{
group.ID.String(): codersdk.WorkspaceRoleUse,
},
})
require.NoError(t, err, "update workspace ACL")
workspaces, err := client.Workspaces(ctx, codersdk.WorkspaceFilter{
workspaces, err := ownerClient.Workspaces(ctx, codersdk.WorkspaceFilter{
Shared: ptr.Ref(true),
})
require.NoError(t, err, "fetch workspaces")
@@ -3688,7 +3692,7 @@ func TestWorkspacesFiltering(t *testing.T) {
dv.Experiments = []string{string(codersdk.ExperimentWorkspaceSharing)}
var (
client, db, orgOwner = coderdenttest.NewWithDatabase(t, &coderdenttest.Options{
ownerClient, db, owner = coderdenttest.NewWithDatabase(t, &coderdenttest.Options{
Options: &coderdtest.Options{
DeploymentValues: dv,
},
@@ -3698,25 +3702,26 @@ func TestWorkspacesFiltering(t *testing.T) {
},
},
})
_, workspaceOwner = coderdtest.CreateAnotherUser(t, client, orgOwner.OrganizationID, rbac.ScopedRoleOrgAuditor(orgOwner.OrganizationID))
sharedWorkspace = dbfake.WorkspaceBuild(t, db, database.WorkspaceTable{
workspaceOwnerClient, workspaceOwner = coderdtest.CreateAnotherUser(t, ownerClient, owner.OrganizationID, rbac.ScopedRoleOrgAuditor(owner.OrganizationID))
sharedWorkspace = dbfake.WorkspaceBuild(t, db, database.WorkspaceTable{
OwnerID: workspaceOwner.ID,
OrganizationID: orgOwner.OrganizationID,
OrganizationID: owner.OrganizationID,
}).Do().Workspace
_ = dbfake.WorkspaceBuild(t, db, database.WorkspaceTable{
OwnerID: workspaceOwner.ID,
OrganizationID: orgOwner.OrganizationID,
OrganizationID: owner.OrganizationID,
}).Do().Workspace
_, toShareWithUser = coderdtest.CreateAnotherUser(t, client, orgOwner.OrganizationID)
_, toShareWithUser = coderdtest.CreateAnotherUser(t, ownerClient, owner.OrganizationID)
ctx = testutil.Context(t, testutil.WaitMedium)
)
group, err := client.CreateGroup(ctx, orgOwner.OrganizationID, codersdk.CreateGroupRequest{
group, err := ownerClient.CreateGroup(ctx, owner.OrganizationID, codersdk.CreateGroupRequest{
Name: "wibble",
})
require.NoError(t, err, "create group")
client.UpdateWorkspaceACL(ctx, sharedWorkspace.ID, codersdk.UpdateWorkspaceACL{
err = workspaceOwnerClient.UpdateWorkspaceACL(ctx, sharedWorkspace.ID, codersdk.UpdateWorkspaceACL{
UserRoles: map[string]codersdk.WorkspaceRole{
toShareWithUser.ID.String(): codersdk.WorkspaceRoleUse,
},
@@ -3724,8 +3729,9 @@ func TestWorkspacesFiltering(t *testing.T) {
group.ID.String(): codersdk.WorkspaceRoleUse,
},
})
require.NoError(t, err, "update workspace ACL")
workspaces, err := client.Workspaces(ctx, codersdk.WorkspaceFilter{
workspaces, err := ownerClient.Workspaces(ctx, codersdk.WorkspaceFilter{
Shared: ptr.Ref(true),
})
require.NoError(t, err, "fetch workspaces")
@@ -3740,7 +3746,7 @@ func TestWorkspacesFiltering(t *testing.T) {
dv.Experiments = []string{string(codersdk.ExperimentWorkspaceSharing)}
var (
client, db, orgOwner = coderdenttest.NewWithDatabase(t, &coderdenttest.Options{
ownerClient, db, owner = coderdenttest.NewWithDatabase(t, &coderdenttest.Options{
Options: &coderdtest.Options{
DeploymentValues: dv,
},
@@ -3750,30 +3756,31 @@ func TestWorkspacesFiltering(t *testing.T) {
},
},
})
_, workspaceOwner = coderdtest.CreateAnotherUser(t, client, orgOwner.OrganizationID, rbac.ScopedRoleOrgAuditor(orgOwner.OrganizationID))
sharedWorkspace = dbfake.WorkspaceBuild(t, db, database.WorkspaceTable{
workspaceOwnerClient, workspaceOwner = coderdtest.CreateAnotherUser(t, ownerClient, owner.OrganizationID, rbac.ScopedRoleOrgAuditor(owner.OrganizationID))
sharedWorkspace = dbfake.WorkspaceBuild(t, db, database.WorkspaceTable{
OwnerID: workspaceOwner.ID,
OrganizationID: orgOwner.OrganizationID,
OrganizationID: owner.OrganizationID,
}).Do().Workspace
notSharedWorkspace = dbfake.WorkspaceBuild(t, db, database.WorkspaceTable{
OwnerID: workspaceOwner.ID,
OrganizationID: orgOwner.OrganizationID,
OrganizationID: owner.OrganizationID,
}).Do().Workspace
ctx = testutil.Context(t, testutil.WaitMedium)
)
group, err := client.CreateGroup(ctx, orgOwner.OrganizationID, codersdk.CreateGroupRequest{
group, err := ownerClient.CreateGroup(ctx, owner.OrganizationID, codersdk.CreateGroupRequest{
Name: "wibble",
})
require.NoError(t, err, "create group")
client.UpdateWorkspaceACL(ctx, sharedWorkspace.ID, codersdk.UpdateWorkspaceACL{
err = workspaceOwnerClient.UpdateWorkspaceACL(ctx, sharedWorkspace.ID, codersdk.UpdateWorkspaceACL{
GroupRoles: map[string]codersdk.WorkspaceRole{
group.ID.String(): codersdk.WorkspaceRoleUse,
},
})
require.NoError(t, err, "update workspace ACL")
workspaces, err := client.Workspaces(ctx, codersdk.WorkspaceFilter{
workspaces, err := ownerClient.Workspaces(ctx, codersdk.WorkspaceFilter{
Shared: ptr.Ref(false),
})
require.NoError(t, err, "fetch workspaces")
@@ -3788,7 +3795,7 @@ func TestWorkspacesFiltering(t *testing.T) {
dv.Experiments = []string{string(codersdk.ExperimentWorkspaceSharing)}
var (
client, db, orgOwner = coderdenttest.NewWithDatabase(t, &coderdenttest.Options{
ownerClient, db, owner = coderdenttest.NewWithDatabase(t, &coderdenttest.Options{
Options: &coderdtest.Options{
DeploymentValues: dv,
},
@@ -3798,30 +3805,30 @@ func TestWorkspacesFiltering(t *testing.T) {
},
},
})
_, workspaceOwner = coderdtest.CreateAnotherUser(t, client, orgOwner.OrganizationID, rbac.ScopedRoleOrgAuditor(orgOwner.OrganizationID))
sharedWorkspace = dbfake.WorkspaceBuild(t, db, database.WorkspaceTable{
workspaceOwnerClient, workspaceOwner = coderdtest.CreateAnotherUser(t, ownerClient, owner.OrganizationID, rbac.ScopedRoleOrgAuditor(owner.OrganizationID))
sharedWorkspace = dbfake.WorkspaceBuild(t, db, database.WorkspaceTable{
OwnerID: workspaceOwner.ID,
OrganizationID: orgOwner.OrganizationID,
OrganizationID: owner.OrganizationID,
}).Do().Workspace
_ = dbfake.WorkspaceBuild(t, db, database.WorkspaceTable{
OwnerID: workspaceOwner.ID,
OrganizationID: orgOwner.OrganizationID,
OrganizationID: owner.OrganizationID,
}).Do().Workspace
ctx = testutil.Context(t, testutil.WaitMedium)
)
group, err := client.CreateGroup(ctx, orgOwner.OrganizationID, codersdk.CreateGroupRequest{
group, err := ownerClient.CreateGroup(ctx, owner.OrganizationID, codersdk.CreateGroupRequest{
Name: "wibble",
})
require.NoError(t, err, "create group")
err = client.UpdateWorkspaceACL(ctx, sharedWorkspace.ID, codersdk.UpdateWorkspaceACL{
err = workspaceOwnerClient.UpdateWorkspaceACL(ctx, sharedWorkspace.ID, codersdk.UpdateWorkspaceACL{
GroupRoles: map[string]codersdk.WorkspaceRole{
group.ID.String(): codersdk.WorkspaceRoleUse,
},
})
require.NoError(t, err)
workspaces, err := client.Workspaces(ctx, codersdk.WorkspaceFilter{
workspaces, err := ownerClient.Workspaces(ctx, codersdk.WorkspaceFilter{
SharedWithGroup: group.ID.String(),
})
require.NoError(t, err)
@@ -3836,7 +3843,7 @@ func TestWorkspacesFiltering(t *testing.T) {
dv.Experiments = []string{string(codersdk.ExperimentWorkspaceSharing)}
var (
client, db, orgOwner = coderdenttest.NewWithDatabase(t, &coderdenttest.Options{
ownerClient, db, owner = coderdenttest.NewWithDatabase(t, &coderdenttest.Options{
Options: &coderdtest.Options{
DeploymentValues: dv,
},
@@ -3846,44 +3853,44 @@ func TestWorkspacesFiltering(t *testing.T) {
},
},
})
_, workspaceOwner = coderdtest.CreateAnotherUser(t, client, orgOwner.OrganizationID, rbac.ScopedRoleOrgAuditor(orgOwner.OrganizationID))
sharedWorkspace = dbfake.WorkspaceBuild(t, db, database.WorkspaceTable{
workspaceOwnerClient, workspaceOwner = coderdtest.CreateAnotherUser(t, ownerClient, owner.OrganizationID, rbac.ScopedRoleOrgAuditor(owner.OrganizationID))
sharedWorkspace = dbfake.WorkspaceBuild(t, db, database.WorkspaceTable{
OwnerID: workspaceOwner.ID,
OrganizationID: orgOwner.OrganizationID,
OrganizationID: owner.OrganizationID,
}).Do().Workspace
_ = dbfake.WorkspaceBuild(t, db, database.WorkspaceTable{
OwnerID: workspaceOwner.ID,
OrganizationID: orgOwner.OrganizationID,
OrganizationID: owner.OrganizationID,
}).Do().Workspace
ctx = testutil.Context(t, testutil.WaitMedium)
)
group, err := client.CreateGroup(ctx, orgOwner.OrganizationID, codersdk.CreateGroupRequest{
group, err := ownerClient.CreateGroup(ctx, owner.OrganizationID, codersdk.CreateGroupRequest{
Name: "wibble",
})
require.NoError(t, err, "create group")
err = client.UpdateWorkspaceACL(ctx, sharedWorkspace.ID, codersdk.UpdateWorkspaceACL{
err = workspaceOwnerClient.UpdateWorkspaceACL(ctx, sharedWorkspace.ID, codersdk.UpdateWorkspaceACL{
GroupRoles: map[string]codersdk.WorkspaceRole{
group.ID.String(): codersdk.WorkspaceRoleUse,
},
})
require.NoError(t, err)
workspacesByID, err := client.Workspaces(ctx, codersdk.WorkspaceFilter{
workspacesByID, err := ownerClient.Workspaces(ctx, codersdk.WorkspaceFilter{
SharedWithGroup: group.ID.String(),
})
require.NoError(t, err)
require.Equal(t, 1, workspacesByID.Count)
require.Equal(t, sharedWorkspace.ID, workspacesByID.Workspaces[0].ID)
workspacesByName, err := client.Workspaces(ctx, codersdk.WorkspaceFilter{
workspacesByName, err := ownerClient.Workspaces(ctx, codersdk.WorkspaceFilter{
SharedWithGroup: group.Name,
})
require.NoError(t, err)
require.Equal(t, 1, workspacesByName.Count)
require.Equal(t, sharedWorkspace.ID, workspacesByName.Workspaces[0].ID)
workspacesByOrgAndName, err := client.Workspaces(ctx, codersdk.WorkspaceFilter{
workspacesByOrgAndName, err := ownerClient.Workspaces(ctx, codersdk.WorkspaceFilter{
SharedWithGroup: fmt.Sprintf("coder/%s", group.Name),
})
require.NoError(t, err)
@@ -4535,7 +4542,7 @@ func TestWorkspacesSharedWith(t *testing.T) {
},
})
_, workspaceOwner := coderdtest.CreateAnotherUser(t, client, user.OrganizationID)
workspaceOwnerClient, workspaceOwner := coderdtest.CreateAnotherUser(t, client, user.OrganizationID)
workspace := dbfake.WorkspaceBuild(t, db, database.WorkspaceTable{
OwnerID: workspaceOwner.ID,
@@ -4563,7 +4570,7 @@ func TestWorkspacesSharedWith(t *testing.T) {
require.NoError(t, err)
// Share workspace with user and group
err = client.UpdateWorkspaceACL(ctx, workspace.ID, codersdk.UpdateWorkspaceACL{
err = workspaceOwnerClient.UpdateWorkspaceACL(ctx, workspace.ID, codersdk.UpdateWorkspaceACL{
UserRoles: map[string]codersdk.WorkspaceRole{
sharedWithUser.ID.String(): codersdk.WorkspaceRoleUse,
},
@@ -4623,7 +4630,7 @@ func TestWorkspacesSharedWith(t *testing.T) {
},
})
_, workspaceOwner := coderdtest.CreateAnotherUser(t, client, user.OrganizationID)
workspaceOwnerClient, workspaceOwner := coderdtest.CreateAnotherUser(t, client, user.OrganizationID)
workspace := dbfake.WorkspaceBuild(t, db, database.WorkspaceTable{
OwnerID: workspaceOwner.ID,
@@ -4651,7 +4658,7 @@ func TestWorkspacesSharedWith(t *testing.T) {
require.NoError(t, err)
// Share workspace with user and group
err = client.UpdateWorkspaceACL(ctx, workspace.ID, codersdk.UpdateWorkspaceACL{
err = workspaceOwnerClient.UpdateWorkspaceACL(ctx, workspace.ID, codersdk.UpdateWorkspaceACL{
UserRoles: map[string]codersdk.WorkspaceRole{
sharedWithUser.ID.String(): codersdk.WorkspaceRoleUse,
},