Files
coder/enterprise/coderd/roles_test.go
Steven Masley cccdf1ecac feat: implement WorkspaceCreationBan org role (#16686)
Using negative permissions, this role prevents a user's ability to
create & delete a workspace within a given organization.

Workspaces are uniquely owned by an org and a user, so the org has to
supercede the user permission with a negative permission.

# Use case

Organizations must be able to restrict a member's ability to create a
workspace. This permission is implicitly granted (see
https://github.com/coder/coder/issues/16546#issuecomment-2655437860).

To revoke this permission, the solution chosen was to use negative
permissions in a built in role called `WorkspaceCreationBan`.

# Rational

Using negative permissions is new territory, and not ideal. However,
workspaces are in a unique position.

Workspaces have 2 owners. The organization and the user. To prevent
users from creating a workspace in another organization, an [implied
negative
permission](36d9f5ddb3/coderd/rbac/policy.rego (L172-L192))
is used. So the truth table looks like: _how to read this table
[here](36d9f5ddb3/coderd/rbac/README.md (roles))_

| Role (example)  | Site | Org  | User | Result |
|-----------------|------|------|------|--------|
| non-org-member  | \_   | N    | YN\_ | N      |
| user            | \_   | \_   | Y    | Y      |
| WorkspaceBan    | \_   | N    | Y    | Y      |
| unauthenticated | \_   | \_   | \_   | N      |


This new role, `WorkspaceCreationBan` is the same truth table condition
as if the user was not a member of the organization (when doing a
workspace create/delete). So this behavior **is not entirely new**.

<details>

<summary>How to do it without a negative permission</summary>

The alternate approach would be to remove the implied permission, and
grant it via and organization role. However this would add new behavior
that an organizational role has the ability to grant a user permissions
on their own resources?

It does not make sense for an org role to prevent user from changing
their profile information for example. So the only option is to create a
new truth table column for resources that are owned by both an
organization and a user.

| Role (example)  | Site | Org  |User+Org| User | Result |
|-----------------|------|------|--------|------|--------|
| non-org-member  | \_   | N    |  \_    | \_   | N      |
| user            | \_   | \_   |  \_    | \_   | N      |
| WorkspaceAllow  | \_   | \_   |   Y    | \_   | Y      |
| unauthenticated | \_   | \_   |  \_    | \_   | N      |

Now a user has no opinion on if they can create a workspace, which feels
a little wrong. A user should have the authority over what is theres.

There is fundamental _philosophical_ question of "Who does a workspace
belong to?". The user has some set of autonomy, yet it is the
organization that controls it's existence. A head scratcher 🤔

</details>

## Will we need more negative built in roles?

There are few resources that have shared ownership. Only
`ResourceOrganizationMember` and `ResourceGroupMember`. Since negative
permissions is intended to revoke access to a shared resource, then
**no.** **This is the only one we need**.

Classic resources like `ResourceTemplate` are entirely controlled by the
Organization permissions. And resources entirely in the user control
(like user profile) are only controlled by `User` permissions.


![Uploading Screenshot 2025-02-26 at 22.26.52.png…]()

---------

Co-authored-by: Jaayden Halko <jaayden.halko@gmail.com>
Co-authored-by: ケイラ <mckayla@hey.com>
2025-02-27 06:23:18 -05:00

569 lines
21 KiB
Go

package coderd_test
import (
"bytes"
"context"
"net/http"
"slices"
"testing"
"github.com/google/uuid"
"github.com/stretchr/testify/require"
"github.com/coder/coder/v2/coderd/coderdtest"
"github.com/coder/coder/v2/coderd/database/db2sdk"
"github.com/coder/coder/v2/coderd/rbac"
"github.com/coder/coder/v2/codersdk"
"github.com/coder/coder/v2/enterprise/coderd/coderdenttest"
"github.com/coder/coder/v2/enterprise/coderd/license"
"github.com/coder/coder/v2/provisioner/echo"
"github.com/coder/coder/v2/testutil"
)
func TestCustomOrganizationRole(t *testing.T) {
t.Parallel()
templateAdminCustom := func(orgID uuid.UUID) codersdk.Role {
return codersdk.Role{
Name: "test-role",
DisplayName: "Testing Purposes",
OrganizationID: orgID.String(),
// Basically creating a template admin manually
SitePermissions: nil,
OrganizationPermissions: codersdk.CreatePermissions(map[codersdk.RBACResource][]codersdk.RBACAction{
codersdk.ResourceTemplate: {codersdk.ActionCreate, codersdk.ActionRead, codersdk.ActionUpdate, codersdk.ActionViewInsights},
codersdk.ResourceFile: {codersdk.ActionCreate, codersdk.ActionRead},
codersdk.ResourceWorkspace: {codersdk.ActionRead},
}),
UserPermissions: nil,
}
}
// Create, assign, and use a custom role
t.Run("Success", 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 // owner is required for this
role, err := owner.CreateOrganizationRole(ctx, templateAdminCustom(first.OrganizationID))
require.NoError(t, err, "upsert role")
// Assign the custom template admin role
tmplAdmin, _ := coderdtest.CreateAnotherUser(t, owner, first.OrganizationID, rbac.RoleIdentifier{Name: role.Name, OrganizationID: first.OrganizationID})
// Assert the role exists
// TODO: At present user roles are not returned by the user endpoints.
// Changing this might mess up the UI in how it renders the roles on the
// users page. When the users endpoint is updated, this should be uncommented.
// roleNamesF := func(role codersdk.SlimRole) string { return role.Name }
// require.Contains(t, db2sdk.List(user.Roles, roleNamesF), role.Name)
// Try to create a template version
coderdtest.CreateTemplateVersion(t, tmplAdmin, first.OrganizationID, nil)
// Verify the role exists in the list
allRoles, err := tmplAdmin.ListOrganizationRoles(ctx, first.OrganizationID)
require.NoError(t, err)
var foundRole codersdk.AssignableRoles
require.True(t, slices.ContainsFunc(allRoles, func(selected codersdk.AssignableRoles) bool {
if selected.Name == role.Name {
foundRole = selected
return true
}
return false
}), "role missing from org role list")
require.Len(t, foundRole.SitePermissions, 0)
require.Len(t, foundRole.OrganizationPermissions, 7)
require.Len(t, foundRole.UserPermissions, 0)
})
// Revoked licenses cannot modify/create custom roles, but they can
// use the existing roles.
t.Run("RevokedLicense", 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 // owner is required for this
role, err := owner.CreateOrganizationRole(ctx, templateAdminCustom(first.OrganizationID))
require.NoError(t, err, "upsert role")
// Remove the license to block premium functionality
licenses, err := owner.Licenses(ctx)
require.NoError(t, err, "get licenses")
for _, license := range licenses {
// Should be only 1...
err := owner.DeleteLicense(ctx, license.ID)
require.NoError(t, err, "delete license")
}
// Verify functionality is lost
_, err = owner.UpdateOrganizationRole(ctx, templateAdminCustom(first.OrganizationID))
require.ErrorContains(t, err, "Custom Roles is a Premium feature")
// Assign the custom template admin role
tmplAdmin, _ := coderdtest.CreateAnotherUser(t, owner, first.OrganizationID, rbac.RoleIdentifier{Name: role.Name, OrganizationID: first.OrganizationID})
// Try to create a template version, eg using the custom role
coderdtest.CreateTemplateVersion(t, tmplAdmin, first.OrganizationID, nil)
})
// Role patches are complete, as in the request overrides the existing role.
t.Run("RoleOverrides", 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 // owner is required for this
role, err := owner.CreateOrganizationRole(ctx, templateAdminCustom(first.OrganizationID))
require.NoError(t, err, "upsert role")
// Assign the custom template admin role
tmplAdmin, _ := coderdtest.CreateAnotherUser(t, owner, first.OrganizationID, rbac.RoleIdentifier{Name: role.Name, OrganizationID: first.OrganizationID})
// Try to create a template version, eg using the custom role
coderdtest.CreateTemplateVersion(t, tmplAdmin, first.OrganizationID, nil)
//nolint:gocritic // owner is required for this
newRole := templateAdminCustom(first.OrganizationID)
// These are all left nil, which sets the custom role to have 0
// permissions. Omitting this does not "inherit" what already
// exists.
newRole.SitePermissions = nil
newRole.OrganizationPermissions = nil
newRole.UserPermissions = nil
_, err = owner.UpdateOrganizationRole(ctx, newRole)
require.NoError(t, err, "upsert role with override")
// The role should no longer have template perms
data, err := echo.TarWithOptions(ctx, tmplAdmin.Logger(), nil)
require.NoError(t, err)
file, err := tmplAdmin.Upload(ctx, codersdk.ContentTypeTar, bytes.NewReader(data))
require.NoError(t, err)
_, err = tmplAdmin.CreateTemplateVersion(ctx, first.OrganizationID, codersdk.CreateTemplateVersionRequest{
FileID: file.ID,
StorageMethod: codersdk.ProvisionerStorageMethodFile,
Provisioner: codersdk.ProvisionerTypeEcho,
})
require.ErrorContains(t, err, "forbidden")
})
t.Run("InvalidName", 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 // owner is required for this
_, err := owner.CreateOrganizationRole(ctx, codersdk.Role{
Name: "Bad_Name", // No underscores allowed
DisplayName: "Testing Purposes",
OrganizationID: first.OrganizationID.String(),
SitePermissions: nil,
OrganizationPermissions: nil,
UserPermissions: nil,
})
require.ErrorContains(t, err, "Validation")
})
t.Run("ReservedName", 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 // owner is required for this
_, err := owner.CreateOrganizationRole(ctx, codersdk.Role{
Name: "owner", // Reserved
DisplayName: "Testing Purposes",
OrganizationID: first.OrganizationID.String(),
SitePermissions: nil,
OrganizationPermissions: nil,
UserPermissions: nil,
})
require.ErrorContains(t, err, "Reserved")
})
// Attempt to add site & user permissions, which is not allowed
t.Run("ExcessPermissions", 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)
siteRole := templateAdminCustom(first.OrganizationID)
siteRole.SitePermissions = []codersdk.Permission{
{
ResourceType: codersdk.ResourceWorkspace,
Action: codersdk.ActionRead,
},
}
//nolint:gocritic // owner is required for this
_, err := owner.CreateOrganizationRole(ctx, siteRole)
require.ErrorContains(t, err, "site wide permissions")
userRole := templateAdminCustom(first.OrganizationID)
userRole.UserPermissions = []codersdk.Permission{
{
ResourceType: codersdk.ResourceWorkspace,
Action: codersdk.ActionRead,
},
}
//nolint:gocritic // owner is required for this
_, err = owner.UpdateOrganizationRole(ctx, userRole)
require.ErrorContains(t, err, "not allowed to assign user permissions")
})
t.Run("NotFound", 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)
newRole := templateAdminCustom(first.OrganizationID)
newRole.OrganizationID = "0000" // This is not a valid uuid
//nolint:gocritic // owner is required for this
_, err := owner.CreateOrganizationRole(ctx, newRole)
require.ErrorContains(t, err, "Resource not found")
})
t.Run("Delete", func(t *testing.T) {
t.Parallel()
owner, first := coderdenttest.New(t, &coderdenttest.Options{
LicenseOptions: &coderdenttest.LicenseOptions{
Features: license.Features{
codersdk.FeatureCustomRoles: 1,
},
},
})
orgAdmin, orgAdminUser := coderdtest.CreateAnotherUser(t, owner, first.OrganizationID, rbac.ScopedRoleOrgAdmin(first.OrganizationID))
ctx := testutil.Context(t, testutil.WaitMedium)
createdRole, err := orgAdmin.CreateOrganizationRole(ctx, templateAdminCustom(first.OrganizationID))
require.NoError(t, err, "upsert role")
//nolint:gocritic // org_admin cannot assign to themselves
_, err = owner.UpdateOrganizationMemberRoles(ctx, first.OrganizationID, orgAdminUser.ID.String(), codersdk.UpdateRoles{
// Give the user this custom role, to ensure when it is deleted, the user
// is ok to be used.
Roles: []string{createdRole.Name, rbac.ScopedRoleOrgAdmin(first.OrganizationID).Name},
})
require.NoError(t, err, "assign custom role to user")
existingRoles, err := orgAdmin.ListOrganizationRoles(ctx, first.OrganizationID)
require.NoError(t, err)
exists := slices.ContainsFunc(existingRoles, func(role codersdk.AssignableRoles) bool {
return role.Name == createdRole.Name
})
require.True(t, exists, "custom role should exist")
// Delete the role
err = orgAdmin.DeleteOrganizationRole(ctx, first.OrganizationID, createdRole.Name)
require.NoError(t, err)
existingRoles, err = orgAdmin.ListOrganizationRoles(ctx, first.OrganizationID)
require.NoError(t, err)
exists = slices.ContainsFunc(existingRoles, func(role codersdk.AssignableRoles) bool {
return role.Name == createdRole.Name
})
require.False(t, exists, "custom role should be deleted")
// Verify you can still assign roles.
// There used to be a bug that if a member had a delete role, they
// could not be assigned roles anymore.
//nolint:gocritic // org_admin cannot assign to themselves
_, err = owner.UpdateOrganizationMemberRoles(ctx, first.OrganizationID, orgAdminUser.ID.String(), codersdk.UpdateRoles{
Roles: []string{rbac.ScopedRoleOrgAdmin(first.OrganizationID).Name},
})
require.NoError(t, err)
})
// Verify deleting a custom role cascades to all members
t.Run("DeleteRoleCascadeMembers", func(t *testing.T) {
t.Parallel()
owner, first := coderdenttest.New(t, &coderdenttest.Options{
LicenseOptions: &coderdenttest.LicenseOptions{
Features: license.Features{
codersdk.FeatureCustomRoles: 1,
},
},
})
orgAdmin, orgAdminUser := coderdtest.CreateAnotherUser(t, owner, first.OrganizationID, rbac.ScopedRoleOrgAdmin(first.OrganizationID))
ctx := testutil.Context(t, testutil.WaitMedium)
createdRole, err := orgAdmin.CreateOrganizationRole(ctx, templateAdminCustom(first.OrganizationID))
require.NoError(t, err, "upsert role")
customRoleIdentifier := rbac.RoleIdentifier{
Name: createdRole.Name,
OrganizationID: first.OrganizationID,
}
// Create a few members with the role
coderdtest.CreateAnotherUser(t, owner, first.OrganizationID, customRoleIdentifier)
coderdtest.CreateAnotherUser(t, owner, first.OrganizationID, rbac.ScopedRoleOrgAdmin(first.OrganizationID), customRoleIdentifier)
coderdtest.CreateAnotherUser(t, owner, first.OrganizationID, rbac.ScopedRoleOrgTemplateAdmin(first.OrganizationID), rbac.ScopedRoleOrgAuditor(first.OrganizationID), customRoleIdentifier)
// Verify members have the custom role
originalMembers, err := orgAdmin.OrganizationMembers(ctx, first.OrganizationID)
require.NoError(t, err)
require.Len(t, originalMembers, 5) // 3 members + org admin + owner
for _, member := range originalMembers {
if member.UserID == orgAdminUser.ID || member.UserID == first.UserID {
continue
}
require.True(t, slices.ContainsFunc(member.Roles, func(role codersdk.SlimRole) bool {
return role.Name == customRoleIdentifier.Name
}), "member should have custom role")
}
err = orgAdmin.DeleteOrganizationRole(ctx, first.OrganizationID, createdRole.Name)
require.NoError(t, err)
// Verify the role was removed from all members
members, err := orgAdmin.OrganizationMembers(ctx, first.OrganizationID)
require.NoError(t, err)
require.Len(t, members, 5) // 3 members + org admin + owner
for _, member := range members {
require.False(t, slices.ContainsFunc(member.Roles, func(role codersdk.SlimRole) bool {
return role.Name == customRoleIdentifier.Name
}), "role should be removed from all users")
// Verify the rest of the member's roles are unchanged
original := originalMembers[slices.IndexFunc(originalMembers, func(haystack codersdk.OrganizationMemberWithUserData) bool {
return haystack.UserID == member.UserID
})]
originalWithoutCustom := slices.DeleteFunc(original.Roles, func(role codersdk.SlimRole) bool {
return role.Name == customRoleIdentifier.Name
})
require.ElementsMatch(t, originalWithoutCustom, member.Roles, "original roles are unchanged")
}
})
}
func TestListRoles(t *testing.T) {
t.Parallel()
client, owner := coderdenttest.New(t, &coderdenttest.Options{
LicenseOptions: &coderdenttest.LicenseOptions{
Features: license.Features{
codersdk.FeatureExternalProvisionerDaemons: 1,
codersdk.FeatureMultipleOrganizations: 1,
},
},
})
// Create owner, member, and org admin
member, _ := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID)
orgAdmin, _ := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID, rbac.ScopedRoleOrgAdmin(owner.OrganizationID))
otherOrg := coderdenttest.CreateOrganization(t, client, coderdenttest.CreateOrganizationOptions{})
const notFound = "Resource not found"
testCases := []struct {
Name string
Client *codersdk.Client
APICall func(context.Context) ([]codersdk.AssignableRoles, error)
ExpectedRoles []codersdk.AssignableRoles
AuthorizedError string
}{
{
// Members cannot assign any roles
Name: "MemberListSite",
APICall: func(ctx context.Context) ([]codersdk.AssignableRoles, error) {
x, err := member.ListSiteRoles(ctx)
return x, err
},
ExpectedRoles: convertRoles(map[rbac.RoleIdentifier]bool{
{Name: codersdk.RoleOwner}: false,
{Name: codersdk.RoleAuditor}: false,
{Name: codersdk.RoleTemplateAdmin}: false,
{Name: codersdk.RoleUserAdmin}: false,
}),
},
{
Name: "OrgMemberListOrg",
APICall: func(ctx context.Context) ([]codersdk.AssignableRoles, error) {
return member.ListOrganizationRoles(ctx, owner.OrganizationID)
},
ExpectedRoles: convertRoles(map[rbac.RoleIdentifier]bool{
{Name: codersdk.RoleOrganizationAdmin, OrganizationID: owner.OrganizationID}: false,
{Name: codersdk.RoleOrganizationAuditor, OrganizationID: owner.OrganizationID}: false,
{Name: codersdk.RoleOrganizationTemplateAdmin, OrganizationID: owner.OrganizationID}: false,
{Name: codersdk.RoleOrganizationUserAdmin, OrganizationID: owner.OrganizationID}: false,
{Name: codersdk.RoleOrganizationWorkspaceCreationBan, OrganizationID: owner.OrganizationID}: false,
}),
},
{
Name: "NonOrgMemberListOrg",
APICall: func(ctx context.Context) ([]codersdk.AssignableRoles, error) {
return member.ListOrganizationRoles(ctx, otherOrg.ID)
},
AuthorizedError: notFound,
},
// Org admin
{
Name: "OrgAdminListSite",
APICall: func(ctx context.Context) ([]codersdk.AssignableRoles, error) {
return orgAdmin.ListSiteRoles(ctx)
},
ExpectedRoles: convertRoles(map[rbac.RoleIdentifier]bool{
{Name: codersdk.RoleOwner}: false,
{Name: codersdk.RoleAuditor}: false,
{Name: codersdk.RoleTemplateAdmin}: false,
{Name: codersdk.RoleUserAdmin}: false,
}),
},
{
Name: "OrgAdminListOrg",
APICall: func(ctx context.Context) ([]codersdk.AssignableRoles, error) {
return orgAdmin.ListOrganizationRoles(ctx, owner.OrganizationID)
},
ExpectedRoles: convertRoles(map[rbac.RoleIdentifier]bool{
{Name: codersdk.RoleOrganizationAdmin, OrganizationID: owner.OrganizationID}: true,
{Name: codersdk.RoleOrganizationAuditor, OrganizationID: owner.OrganizationID}: true,
{Name: codersdk.RoleOrganizationTemplateAdmin, OrganizationID: owner.OrganizationID}: true,
{Name: codersdk.RoleOrganizationUserAdmin, OrganizationID: owner.OrganizationID}: true,
{Name: codersdk.RoleOrganizationWorkspaceCreationBan, OrganizationID: owner.OrganizationID}: true,
}),
},
{
Name: "OrgAdminListOtherOrg",
APICall: func(ctx context.Context) ([]codersdk.AssignableRoles, error) {
return orgAdmin.ListOrganizationRoles(ctx, otherOrg.ID)
},
AuthorizedError: notFound,
},
// Admin
{
Name: "AdminListSite",
APICall: func(ctx context.Context) ([]codersdk.AssignableRoles, error) {
return client.ListSiteRoles(ctx)
},
ExpectedRoles: convertRoles(map[rbac.RoleIdentifier]bool{
{Name: codersdk.RoleOwner}: true,
{Name: codersdk.RoleAuditor}: true,
{Name: codersdk.RoleTemplateAdmin}: true,
{Name: codersdk.RoleUserAdmin}: true,
}),
},
{
Name: "AdminListOrg",
APICall: func(ctx context.Context) ([]codersdk.AssignableRoles, error) {
return client.ListOrganizationRoles(ctx, owner.OrganizationID)
},
ExpectedRoles: convertRoles(map[rbac.RoleIdentifier]bool{
{Name: codersdk.RoleOrganizationAdmin, OrganizationID: owner.OrganizationID}: true,
{Name: codersdk.RoleOrganizationAuditor, OrganizationID: owner.OrganizationID}: true,
{Name: codersdk.RoleOrganizationTemplateAdmin, OrganizationID: owner.OrganizationID}: true,
{Name: codersdk.RoleOrganizationUserAdmin, OrganizationID: owner.OrganizationID}: true,
{Name: codersdk.RoleOrganizationWorkspaceCreationBan, OrganizationID: owner.OrganizationID}: true,
}),
},
}
for _, c := range testCases {
c := c
t.Run(c.Name, func(t *testing.T) {
t.Parallel()
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
defer cancel()
roles, err := c.APICall(ctx)
if c.AuthorizedError != "" {
var apiErr *codersdk.Error
require.ErrorAs(t, err, &apiErr)
require.Equal(t, http.StatusNotFound, apiErr.StatusCode())
require.Contains(t, apiErr.Message, c.AuthorizedError)
} else {
require.NoError(t, err)
ignorePerms := func(f codersdk.AssignableRoles) codersdk.AssignableRoles {
return codersdk.AssignableRoles{
Role: codersdk.Role{
Name: f.Name,
DisplayName: f.DisplayName,
},
Assignable: f.Assignable,
BuiltIn: true,
}
}
expected := db2sdk.List(c.ExpectedRoles, ignorePerms)
found := db2sdk.List(roles, ignorePerms)
require.ElementsMatch(t, expected, found)
}
})
}
}
func convertRole(roleName rbac.RoleIdentifier) codersdk.Role {
role, _ := rbac.RoleByName(roleName)
return db2sdk.RBACRole(role)
}
func convertRoles(assignableRoles map[rbac.RoleIdentifier]bool) []codersdk.AssignableRoles {
converted := make([]codersdk.AssignableRoles, 0, len(assignableRoles))
for roleName, assignable := range assignableRoles {
role := convertRole(roleName)
converted = append(converted, codersdk.AssignableRoles{
Role: role,
Assignable: assignable,
})
}
return converted
}