Skip to content

Commit 1b4ca00

Browse files
authored
chore: include custom roles in list org roles (coder#13336)
* chore: include custom roles in list org roles * move cli show roles to org scope
1 parent d748c6d commit 1b4ca00

24 files changed

+312
-117
lines changed

cli/organization.go

+1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ func (r *RootCmd) organizations() *serpent.Command {
3030
r.currentOrganization(),
3131
r.switchOrganization(),
3232
r.createOrganization(),
33+
r.organizationRoles(),
3334
},
3435
}
3536

enterprise/cli/rolescmd.go renamed to cli/organizationroles.go

+10-8
Original file line numberDiff line numberDiff line change
@@ -12,26 +12,23 @@ import (
1212
"github.com/coder/serpent"
1313
)
1414

15-
// **NOTE** Only covers site wide roles at present. Org scoped roles maybe
16-
// should be nested under some command that scopes to an org??
17-
18-
func (r *RootCmd) roles() *serpent.Command {
15+
func (r *RootCmd) organizationRoles() *serpent.Command {
1916
cmd := &serpent.Command{
2017
Use: "roles",
21-
Short: "Manage site-wide roles.",
18+
Short: "Manage organization roles.",
2219
Aliases: []string{"role"},
2320
Handler: func(inv *serpent.Invocation) error {
2421
return inv.Command.HelpHandler(inv)
2522
},
2623
Hidden: true,
2724
Children: []*serpent.Command{
28-
r.showRole(),
25+
r.showOrganizationRoles(),
2926
},
3027
}
3128
return cmd
3229
}
3330

34-
func (r *RootCmd) showRole() *serpent.Command {
31+
func (r *RootCmd) showOrganizationRoles() *serpent.Command {
3532
formatter := cliui.NewOutputFormatter(
3633
cliui.ChangeFormatterData(
3734
cliui.TableFormat([]assignableRolesTableRow{}, []string{"name", "display_name", "built_in", "site_permissions", "org_permissions", "user_permissions"}),
@@ -67,7 +64,12 @@ func (r *RootCmd) showRole() *serpent.Command {
6764
),
6865
Handler: func(inv *serpent.Invocation) error {
6966
ctx := inv.Context()
70-
roles, err := client.ListSiteRoles(ctx)
67+
org, err := CurrentOrganization(r, inv, client)
68+
if err != nil {
69+
return err
70+
}
71+
72+
roles, err := client.ListOrganizationRoles(ctx, org.ID)
7173
if err != nil {
7274
return xerrors.Errorf("listing roles: %w", err)
7375
}

cli/organizationroles_test.go

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package cli_test
2+
3+
import (
4+
"bytes"
5+
"testing"
6+
7+
"github.com/google/uuid"
8+
"github.com/stretchr/testify/require"
9+
10+
"github.com/coder/coder/v2/cli/clitest"
11+
"github.com/coder/coder/v2/coderd/coderdtest"
12+
"github.com/coder/coder/v2/coderd/database"
13+
"github.com/coder/coder/v2/coderd/database/dbgen"
14+
"github.com/coder/coder/v2/coderd/rbac"
15+
"github.com/coder/coder/v2/testutil"
16+
)
17+
18+
func TestShowOrganizationRoles(t *testing.T) {
19+
t.Parallel()
20+
21+
t.Run("OK", func(t *testing.T) {
22+
t.Parallel()
23+
24+
ownerClient, db := coderdtest.NewWithDatabase(t, &coderdtest.Options{})
25+
owner := coderdtest.CreateFirstUser(t, ownerClient)
26+
client, _ := coderdtest.CreateAnotherUser(t, ownerClient, owner.OrganizationID, rbac.RoleUserAdmin())
27+
28+
const expectedRole = "test-role"
29+
dbgen.CustomRole(t, db, database.CustomRole{
30+
Name: expectedRole,
31+
DisplayName: "Expected",
32+
SitePermissions: nil,
33+
OrgPermissions: nil,
34+
UserPermissions: nil,
35+
OrganizationID: uuid.NullUUID{
36+
UUID: owner.OrganizationID,
37+
Valid: true,
38+
},
39+
})
40+
41+
ctx := testutil.Context(t, testutil.WaitMedium)
42+
inv, root := clitest.New(t, "organization", "roles", "show")
43+
clitest.SetupConfig(t, client, root)
44+
45+
buf := new(bytes.Buffer)
46+
inv.Stdout = buf
47+
err := inv.WithContext(ctx).Run()
48+
require.NoError(t, err)
49+
require.Contains(t, buf.String(), expectedRole)
50+
})
51+
}

coderd/apidoc/docs.go

+8
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/apidoc/swagger.json

+8
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/database/db2sdk/db2sdk.go

+8-3
Original file line numberDiff line numberDiff line change
@@ -527,12 +527,17 @@ func ProvisionerDaemon(dbDaemon database.ProvisionerDaemon) codersdk.Provisioner
527527
}
528528

529529
func Role(role rbac.Role) codersdk.Role {
530+
roleName, orgIDStr, err := rbac.RoleSplit(role.Name)
531+
if err != nil {
532+
roleName = role.Name
533+
}
530534
return codersdk.Role{
531-
Name: role.Name,
535+
Name: roleName,
536+
OrganizationID: orgIDStr,
532537
DisplayName: role.DisplayName,
533538
SitePermissions: List(role.Site, Permission),
534539
OrganizationPermissions: Map(role.Org, ListLazy(Permission)),
535-
UserPermissions: List(role.Site, Permission),
540+
UserPermissions: List(role.User, Permission),
536541
}
537542
}
538543

@@ -546,7 +551,7 @@ func Permission(permission rbac.Permission) codersdk.Permission {
546551

547552
func RoleToRBAC(role codersdk.Role) rbac.Role {
548553
return rbac.Role{
549-
Name: role.Name,
554+
Name: rbac.RoleName(role.Name, role.OrganizationID),
550555
DisplayName: role.DisplayName,
551556
Site: List(role.SitePermissions, PermissionToRBAC),
552557
Org: Map(role.OrganizationPermissions, ListLazy(PermissionToRBAC)),

coderd/database/dbgen/dbgen.go

+14
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"encoding/json"
99
"fmt"
1010
"net"
11+
"strings"
1112
"testing"
1213
"time"
1314

@@ -817,6 +818,19 @@ func OAuth2ProviderAppToken(t testing.TB, db database.Store, seed database.OAuth
817818
return token
818819
}
819820

821+
func CustomRole(t testing.TB, db database.Store, seed database.CustomRole) database.CustomRole {
822+
role, err := db.UpsertCustomRole(genCtx, database.UpsertCustomRoleParams{
823+
Name: takeFirst(seed.Name, strings.ToLower(namesgenerator.GetRandomName(1))),
824+
DisplayName: namesgenerator.GetRandomName(1),
825+
OrganizationID: seed.OrganizationID,
826+
SitePermissions: takeFirstSlice(seed.SitePermissions, []byte("[]")),
827+
OrgPermissions: takeFirstSlice(seed.SitePermissions, []byte("{}")),
828+
UserPermissions: takeFirstSlice(seed.SitePermissions, []byte("[]")),
829+
})
830+
require.NoError(t, err, "insert custom role")
831+
return role
832+
}
833+
820834
func must[V any](v V, err error) V {
821835
if err != nil {
822836
panic(err)

coderd/database/dbmem/dbmem.go

+11-1
Original file line numberDiff line numberDiff line change
@@ -1187,7 +1187,11 @@ func (q *FakeQuerier) CustomRoles(_ context.Context, arg database.CustomRolesPar
11871187
role := role
11881188
if len(arg.LookupRoles) > 0 {
11891189
if !slices.ContainsFunc(arg.LookupRoles, func(s string) bool {
1190-
return strings.EqualFold(s, role.Name)
1190+
roleName := rbac.RoleName(role.Name, "")
1191+
if role.OrganizationID.UUID != uuid.Nil {
1192+
roleName = rbac.RoleName(role.Name, role.OrganizationID.UUID.String())
1193+
}
1194+
return strings.EqualFold(s, roleName)
11911195
}) {
11921196
continue
11931197
}
@@ -1197,6 +1201,10 @@ func (q *FakeQuerier) CustomRoles(_ context.Context, arg database.CustomRolesPar
11971201
continue
11981202
}
11991203

1204+
if arg.OrganizationID != uuid.Nil && role.OrganizationID.UUID != arg.OrganizationID {
1205+
continue
1206+
}
1207+
12001208
found = append(found, role)
12011209
}
12021210

@@ -8377,6 +8385,7 @@ func (q *FakeQuerier) UpsertCustomRole(_ context.Context, arg database.UpsertCus
83778385
for i := range q.customRoles {
83788386
if strings.EqualFold(q.customRoles[i].Name, arg.Name) {
83798387
q.customRoles[i].DisplayName = arg.DisplayName
8388+
q.customRoles[i].OrganizationID = arg.OrganizationID
83808389
q.customRoles[i].SitePermissions = arg.SitePermissions
83818390
q.customRoles[i].OrgPermissions = arg.OrgPermissions
83828391
q.customRoles[i].UserPermissions = arg.UserPermissions
@@ -8388,6 +8397,7 @@ func (q *FakeQuerier) UpsertCustomRole(_ context.Context, arg database.UpsertCus
83888397
role := database.CustomRole{
83898398
Name: arg.Name,
83908399
DisplayName: arg.DisplayName,
8400+
OrganizationID: arg.OrganizationID,
83918401
SitePermissions: arg.SitePermissions,
83928402
OrgPermissions: arg.OrgPermissions,
83938403
UserPermissions: arg.UserPermissions,

coderd/database/queries.sql.go

+21-9
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/database/queries/roles.sql

+12-3
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,32 @@ FROM
55
custom_roles
66
WHERE
77
true
8-
-- Lookup roles filter
8+
-- Lookup roles filter expects the role names to be in the rbac package
9+
-- format. Eg: name[:<organization_id>]
910
AND CASE WHEN array_length(@lookup_roles :: text[], 1) > 0 THEN
10-
-- Case insensitive
11-
name ILIKE ANY(@lookup_roles :: text [])
11+
-- Case insensitive lookup with org_id appended (if non-null).
12+
-- This will return just the name if org_id is null. It'll append
13+
-- the org_id if not null
14+
concat(name, NULLIF(concat(':', organization_id), ':')) ILIKE ANY(@lookup_roles :: text [])
1215
ELSE true
1316
END
1417
-- Org scoping filter, to only fetch site wide roles
1518
AND CASE WHEN @exclude_org_roles :: boolean THEN
1619
organization_id IS null
1720
ELSE true
1821
END
22+
AND CASE WHEN @organization_id :: uuid != '00000000-0000-0000-0000-000000000000'::uuid THEN
23+
organization_id = @organization_id
24+
ELSE true
25+
END
1926
;
2027

2128
-- name: UpsertCustomRole :one
2229
INSERT INTO
2330
custom_roles (
2431
name,
2532
display_name,
33+
organization_id,
2634
site_permissions,
2735
org_permissions,
2836
user_permissions,
@@ -33,6 +41,7 @@ VALUES (
3341
-- Always force lowercase names
3442
lower(@name),
3543
@display_name,
44+
@organization_id,
3645
@site_permissions,
3746
@org_permissions,
3847
@user_permissions,

0 commit comments

Comments
 (0)