Skip to content

Commit e2b330f

Browse files
authored
chore: change sql parameter for custom roles to be a (name,org_id) tuple (#13480)
* chore: sql parameter to custom roles to be a (name,org) tuple CustomRole lookup takes (name,org_id) tuples as the search criteria.
1 parent 1adc19b commit e2b330f

File tree

12 files changed

+339
-26
lines changed

12 files changed

+339
-26
lines changed

coderd/database/dbauthz/customroles_test.go

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ func TestUpsertCustomRoles(t *testing.T) {
3838
Name: "can-assign",
3939
DisplayName: "",
4040
Site: rbac.Permissions(map[string][]policy.Action{
41-
rbac.ResourceAssignRole.Type: {policy.ActionCreate},
41+
rbac.ResourceAssignRole.Type: {policy.ActionRead, policy.ActionCreate},
4242
}),
4343
}
4444

@@ -243,6 +243,20 @@ func TestUpsertCustomRoles(t *testing.T) {
243243
require.ErrorContains(t, err, tc.errorContains)
244244
} else {
245245
require.NoError(t, err)
246+
247+
// Verify we can fetch the role
248+
roles, err := az.CustomRoles(ctx, database.CustomRolesParams{
249+
LookupRoles: []database.NameOrganizationPair{
250+
{
251+
Name: "test-role",
252+
OrganizationID: tc.organizationID.UUID,
253+
},
254+
},
255+
ExcludeOrgRoles: false,
256+
OrganizationID: uuid.UUID{},
257+
})
258+
require.NoError(t, err)
259+
require.Len(t, roles, 1)
246260
}
247261
})
248262
}

coderd/database/dbmem/dbmem.go

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1187,12 +1187,17 @@ func (q *FakeQuerier) CustomRoles(_ context.Context, arg database.CustomRolesPar
11871187
for _, role := range q.data.customRoles {
11881188
role := role
11891189
if len(arg.LookupRoles) > 0 {
1190-
if !slices.ContainsFunc(arg.LookupRoles, func(s string) bool {
1191-
roleName := rbac.RoleName(role.Name, "")
1192-
if role.OrganizationID.UUID != uuid.Nil {
1193-
roleName = rbac.RoleName(role.Name, role.OrganizationID.UUID.String())
1190+
if !slices.ContainsFunc(arg.LookupRoles, func(pair database.NameOrganizationPair) bool {
1191+
if pair.Name != role.Name {
1192+
return false
11941193
}
1195-
return strings.EqualFold(s, roleName)
1194+
1195+
if role.OrganizationID.Valid {
1196+
// Expect org match
1197+
return role.OrganizationID.UUID == pair.OrganizationID
1198+
}
1199+
// Expect no org
1200+
return pair.OrganizationID == uuid.Nil
11961201
}) {
11971202
continue
11981203
}

coderd/database/dbtestutil/postgres.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ func Open() (string, func(), error) {
2828
if err != nil {
2929
return "", nil, xerrors.Errorf("connect to ci postgres: %w", err)
3030
}
31+
3132
defer db.Close()
3233

3334
dbName, err := cryptorand.StringCharset(cryptorand.Lower, 10)

coderd/database/dump.sql

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
DROP TYPE name_organization_pair;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
CREATE TYPE name_organization_pair AS (name text, organization_id uuid);

coderd/database/querier_test.go

Lines changed: 230 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"context"
77
"database/sql"
88
"encoding/json"
9+
"fmt"
910
"sort"
1011
"testing"
1112
"time"
@@ -14,6 +15,7 @@ import (
1415
"github.com/stretchr/testify/require"
1516

1617
"github.com/coder/coder/v2/coderd/database"
18+
"github.com/coder/coder/v2/coderd/database/db2sdk"
1719
"github.com/coder/coder/v2/coderd/database/dbgen"
1820
"github.com/coder/coder/v2/coderd/database/dbtime"
1921
"github.com/coder/coder/v2/coderd/database/migrations"
@@ -514,6 +516,234 @@ func TestDefaultOrg(t *testing.T) {
514516
require.True(t, all[0].IsDefault, "first org should always be default")
515517
}
516518

519+
// TestReadCustomRoles tests the input params returns the correct set of roles.
520+
func TestReadCustomRoles(t *testing.T) {
521+
t.Parallel()
522+
523+
if testing.Short() {
524+
t.SkipNow()
525+
}
526+
527+
sqlDB := testSQLDB(t)
528+
err := migrations.Up(sqlDB)
529+
require.NoError(t, err)
530+
531+
db := database.New(sqlDB)
532+
ctx := testutil.Context(t, testutil.WaitLong)
533+
534+
// Make a few site roles, and a few org roles
535+
orgIDs := make([]uuid.UUID, 3)
536+
for i := range orgIDs {
537+
orgIDs[i] = uuid.New()
538+
}
539+
540+
allRoles := make([]database.CustomRole, 0)
541+
siteRoles := make([]database.CustomRole, 0)
542+
orgRoles := make([]database.CustomRole, 0)
543+
for i := 0; i < 15; i++ {
544+
orgID := uuid.NullUUID{
545+
UUID: orgIDs[i%len(orgIDs)],
546+
Valid: true,
547+
}
548+
if i%4 == 0 {
549+
// Some should be site wide
550+
orgID = uuid.NullUUID{}
551+
}
552+
553+
role, err := db.UpsertCustomRole(ctx, database.UpsertCustomRoleParams{
554+
Name: fmt.Sprintf("role-%d", i),
555+
OrganizationID: orgID,
556+
})
557+
require.NoError(t, err)
558+
allRoles = append(allRoles, role)
559+
if orgID.Valid {
560+
orgRoles = append(orgRoles, role)
561+
} else {
562+
siteRoles = append(siteRoles, role)
563+
}
564+
}
565+
566+
// normalizedRoleName allows for the simple ElementsMatch to work properly.
567+
normalizedRoleName := func(role database.CustomRole) string {
568+
return role.Name + ":" + role.OrganizationID.UUID.String()
569+
}
570+
571+
roleToLookup := func(role database.CustomRole) database.NameOrganizationPair {
572+
return database.NameOrganizationPair{
573+
Name: role.Name,
574+
OrganizationID: role.OrganizationID.UUID,
575+
}
576+
}
577+
578+
testCases := []struct {
579+
Name string
580+
Params database.CustomRolesParams
581+
Match func(role database.CustomRole) bool
582+
}{
583+
{
584+
Name: "NilRoles",
585+
Params: database.CustomRolesParams{
586+
LookupRoles: nil,
587+
ExcludeOrgRoles: false,
588+
OrganizationID: uuid.UUID{},
589+
},
590+
Match: func(role database.CustomRole) bool {
591+
return true
592+
},
593+
},
594+
{
595+
// Empty params should return all roles
596+
Name: "Empty",
597+
Params: database.CustomRolesParams{
598+
LookupRoles: []database.NameOrganizationPair{},
599+
ExcludeOrgRoles: false,
600+
OrganizationID: uuid.UUID{},
601+
},
602+
Match: func(role database.CustomRole) bool {
603+
return true
604+
},
605+
},
606+
{
607+
Name: "Organization",
608+
Params: database.CustomRolesParams{
609+
LookupRoles: []database.NameOrganizationPair{},
610+
ExcludeOrgRoles: false,
611+
OrganizationID: orgIDs[1],
612+
},
613+
Match: func(role database.CustomRole) bool {
614+
return role.OrganizationID.UUID == orgIDs[1]
615+
},
616+
},
617+
{
618+
Name: "SpecificOrgRole",
619+
Params: database.CustomRolesParams{
620+
LookupRoles: []database.NameOrganizationPair{
621+
{
622+
Name: orgRoles[0].Name,
623+
OrganizationID: orgRoles[0].OrganizationID.UUID,
624+
},
625+
},
626+
},
627+
Match: func(role database.CustomRole) bool {
628+
return role.Name == orgRoles[0].Name && role.OrganizationID.UUID == orgRoles[0].OrganizationID.UUID
629+
},
630+
},
631+
{
632+
Name: "SpecificSiteRole",
633+
Params: database.CustomRolesParams{
634+
LookupRoles: []database.NameOrganizationPair{
635+
{
636+
Name: siteRoles[0].Name,
637+
OrganizationID: siteRoles[0].OrganizationID.UUID,
638+
},
639+
},
640+
},
641+
Match: func(role database.CustomRole) bool {
642+
return role.Name == siteRoles[0].Name && role.OrganizationID.UUID == siteRoles[0].OrganizationID.UUID
643+
},
644+
},
645+
{
646+
Name: "FewSpecificRoles",
647+
Params: database.CustomRolesParams{
648+
LookupRoles: []database.NameOrganizationPair{
649+
{
650+
Name: orgRoles[0].Name,
651+
OrganizationID: orgRoles[0].OrganizationID.UUID,
652+
},
653+
{
654+
Name: orgRoles[1].Name,
655+
OrganizationID: orgRoles[1].OrganizationID.UUID,
656+
},
657+
{
658+
Name: siteRoles[0].Name,
659+
OrganizationID: siteRoles[0].OrganizationID.UUID,
660+
},
661+
},
662+
},
663+
Match: func(role database.CustomRole) bool {
664+
return (role.Name == orgRoles[0].Name && role.OrganizationID.UUID == orgRoles[0].OrganizationID.UUID) ||
665+
(role.Name == orgRoles[1].Name && role.OrganizationID.UUID == orgRoles[1].OrganizationID.UUID) ||
666+
(role.Name == siteRoles[0].Name && role.OrganizationID.UUID == siteRoles[0].OrganizationID.UUID)
667+
},
668+
},
669+
{
670+
Name: "AllRolesByLookup",
671+
Params: database.CustomRolesParams{
672+
LookupRoles: db2sdk.List(allRoles, roleToLookup),
673+
},
674+
Match: func(role database.CustomRole) bool {
675+
return true
676+
},
677+
},
678+
{
679+
Name: "NotExists",
680+
Params: database.CustomRolesParams{
681+
LookupRoles: []database.NameOrganizationPair{
682+
{
683+
Name: "not-exists",
684+
OrganizationID: uuid.New(),
685+
},
686+
{
687+
Name: "not-exists",
688+
OrganizationID: uuid.Nil,
689+
},
690+
},
691+
},
692+
Match: func(role database.CustomRole) bool {
693+
return false
694+
},
695+
},
696+
{
697+
Name: "Mixed",
698+
Params: database.CustomRolesParams{
699+
LookupRoles: []database.NameOrganizationPair{
700+
{
701+
Name: "not-exists",
702+
OrganizationID: uuid.New(),
703+
},
704+
{
705+
Name: "not-exists",
706+
OrganizationID: uuid.Nil,
707+
},
708+
{
709+
Name: orgRoles[0].Name,
710+
OrganizationID: orgRoles[0].OrganizationID.UUID,
711+
},
712+
{
713+
Name: siteRoles[0].Name,
714+
},
715+
},
716+
},
717+
Match: func(role database.CustomRole) bool {
718+
return (role.Name == orgRoles[0].Name && role.OrganizationID.UUID == orgRoles[0].OrganizationID.UUID) ||
719+
(role.Name == siteRoles[0].Name && role.OrganizationID.UUID == siteRoles[0].OrganizationID.UUID)
720+
},
721+
},
722+
}
723+
724+
for _, tc := range testCases {
725+
tc := tc
726+
727+
t.Run(tc.Name, func(t *testing.T) {
728+
t.Parallel()
729+
730+
ctx := testutil.Context(t, testutil.WaitLong)
731+
found, err := db.CustomRoles(ctx, tc.Params)
732+
require.NoError(t, err)
733+
filtered := make([]database.CustomRole, 0)
734+
for _, role := range allRoles {
735+
if tc.Match(role) {
736+
filtered = append(filtered, role)
737+
}
738+
}
739+
740+
a := db2sdk.List(filtered, normalizedRoleName)
741+
b := db2sdk.List(found, normalizedRoleName)
742+
require.Equal(t, a, b)
743+
})
744+
}
745+
}
746+
517747
type tvArgs struct {
518748
Status database.ProvisionerJobStatus
519749
// CreateWorkspace is true if we should create a workspace for the template version

coderd/database/queries.sql.go

Lines changed: 11 additions & 11 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy