Skip to content

Commit b9fed91

Browse files
committed
WithID unit tests and model method support
- Add ID to compileSQL matchers
1 parent f924578 commit b9fed91

File tree

3 files changed

+105
-12
lines changed

3 files changed

+105
-12
lines changed

coderd/database/modelmethods.go

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ func (s APIKeyScope) ToRBAC() rbac.Scope {
1919

2020
func (t Template) RBACObject() rbac.Object {
2121
obj := rbac.ResourceTemplate
22-
return obj.InOrg(t.OrganizationID).
22+
return obj.WithID(t.ID).
23+
InOrg(t.OrganizationID).
2324
WithACLUserList(t.UserACL).
2425
WithGroupACL(t.GroupACL)
2526
}
@@ -30,42 +31,57 @@ func (TemplateVersion) RBACObject(template Template) rbac.Object {
3031
}
3132

3233
func (g Group) RBACObject() rbac.Object {
33-
return rbac.ResourceGroup.InOrg(g.OrganizationID)
34+
return rbac.ResourceGroup.WithID(g.ID).
35+
InOrg(g.OrganizationID)
3436
}
3537

3638
func (w Workspace) RBACObject() rbac.Object {
37-
return rbac.ResourceWorkspace.InOrg(w.OrganizationID).WithOwner(w.OwnerID.String())
39+
return rbac.ResourceWorkspace.WithID(w.ID).
40+
InOrg(w.OrganizationID).
41+
WithOwner(w.OwnerID.String())
3842
}
3943

4044
func (w Workspace) ExecutionRBAC() rbac.Object {
41-
return rbac.ResourceWorkspaceExecution.InOrg(w.OrganizationID).WithOwner(w.OwnerID.String())
45+
return rbac.ResourceWorkspaceExecution.
46+
WithID(w.ID).
47+
InOrg(w.OrganizationID).
48+
WithOwner(w.OwnerID.String())
4249
}
4350

4451
func (w Workspace) ApplicationConnectRBAC() rbac.Object {
45-
return rbac.ResourceWorkspaceApplicationConnect.InOrg(w.OrganizationID).WithOwner(w.OwnerID.String())
52+
return rbac.ResourceWorkspaceApplicationConnect.
53+
WithID(w.ID).
54+
InOrg(w.OrganizationID).
55+
WithOwner(w.OwnerID.String())
4656
}
4757

4858
func (m OrganizationMember) RBACObject() rbac.Object {
49-
return rbac.ResourceOrganizationMember.InOrg(m.OrganizationID)
59+
return rbac.ResourceOrganizationMember.
60+
WithID(m.UserID).
61+
InOrg(m.OrganizationID)
5062
}
5163

5264
func (o Organization) RBACObject() rbac.Object {
53-
return rbac.ResourceOrganization.InOrg(o.ID)
65+
return rbac.ResourceOrganization.
66+
WithID(o.ID).
67+
InOrg(o.ID)
5468
}
5569

56-
func (ProvisionerDaemon) RBACObject() rbac.Object {
57-
return rbac.ResourceProvisionerDaemon
70+
func (p ProvisionerDaemon) RBACObject() rbac.Object {
71+
return rbac.ResourceProvisionerDaemon.WithID(p.ID)
5872
}
5973

6074
func (f File) RBACObject() rbac.Object {
61-
return rbac.ResourceFile.WithOwner(f.CreatedBy.String())
75+
return rbac.ResourceFile.
76+
WithID(f.ID).
77+
WithOwner(f.CreatedBy.String())
6278
}
6379

6480
// RBACObject returns the RBAC object for the site wide user resource.
6581
// If you are trying to get the RBAC object for the UserData, use
6682
// rbac.ResourceUserData
67-
func (User) RBACObject() rbac.Object {
68-
return rbac.ResourceUser
83+
func (u User) RBACObject() rbac.Object {
84+
return rbac.ResourceUser.WithID(u.ID)
6985
}
7086

7187
func (License) RBACObject() rbac.Object {

coderd/rbac/authz_internal_test.go

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -788,6 +788,76 @@ func TestAuthorizeScope(t *testing.T) {
788788
{resource: ResourceWorkspaceApplicationConnect.InOrg(unusedID).WithOwner("not-me"), actions: []Action{ActionCreate}, allow: false},
789789
},
790790
)
791+
792+
workspaceID := uuid.New()
793+
user = subject{
794+
UserID: "me",
795+
Roles: []Role{
796+
must(RoleByName(RoleMember())),
797+
must(RoleByName(RoleOrgMember(defOrg))),
798+
},
799+
Scope: ScopeRole{
800+
Role: Role{
801+
Name: "workspace_agent",
802+
DisplayName: "Workspace Agent",
803+
Site: permissions(map[string][]Action{
804+
// Only read access for workspaces.
805+
ResourceWorkspace.Type: {ActionRead},
806+
}),
807+
Org: map[string][]Permission{},
808+
User: []Permission{},
809+
},
810+
AllowIDList: []string{workspaceID.String()},
811+
},
812+
}
813+
814+
testAuthorize(t, "User_WorkspaceAgent", user,
815+
// Test all cases with the workspace id
816+
cases(func(c authTestCase) authTestCase {
817+
c.actions = []Action{ActionCreate, ActionUpdate, ActionDelete}
818+
c.allow = false
819+
c.resource.WithID(workspaceID)
820+
return c
821+
}, []authTestCase{
822+
{resource: ResourceWorkspace.InOrg(defOrg).WithOwner(user.UserID)},
823+
{resource: ResourceWorkspace.InOrg(defOrg)},
824+
{resource: ResourceWorkspace.WithOwner(user.UserID)},
825+
{resource: ResourceWorkspace.All()},
826+
{resource: ResourceWorkspace.InOrg(unusedID).WithOwner(user.UserID)},
827+
{resource: ResourceWorkspace.InOrg(unusedID)},
828+
{resource: ResourceWorkspace.InOrg(defOrg).WithOwner("not-me")},
829+
{resource: ResourceWorkspace.WithOwner("not-me")},
830+
{resource: ResourceWorkspace.InOrg(unusedID).WithOwner("not-me")},
831+
{resource: ResourceWorkspace.InOrg(unusedID)},
832+
{resource: ResourceWorkspace.WithOwner("not-me")},
833+
}),
834+
// Test cases with random ids. These should always fail from the scope.
835+
cases(func(c authTestCase) authTestCase {
836+
c.actions = []Action{ActionRead, ActionCreate, ActionUpdate, ActionDelete}
837+
c.allow = false
838+
c.resource.WithID(uuid.New())
839+
return c
840+
}, []authTestCase{
841+
{resource: ResourceWorkspace.InOrg(defOrg).WithOwner(user.UserID)},
842+
{resource: ResourceWorkspace.InOrg(defOrg)},
843+
{resource: ResourceWorkspace.WithOwner(user.UserID)},
844+
{resource: ResourceWorkspace.All()},
845+
{resource: ResourceWorkspace.InOrg(unusedID).WithOwner(user.UserID)},
846+
{resource: ResourceWorkspace.InOrg(unusedID)},
847+
{resource: ResourceWorkspace.InOrg(defOrg).WithOwner("not-me")},
848+
{resource: ResourceWorkspace.WithOwner("not-me")},
849+
{resource: ResourceWorkspace.InOrg(unusedID).WithOwner("not-me")},
850+
{resource: ResourceWorkspace.InOrg(unusedID)},
851+
{resource: ResourceWorkspace.WithOwner("not-me")},
852+
}),
853+
// Allowed by scope:
854+
[]authTestCase{
855+
{resource: ResourceWorkspace.WithID(workspaceID).InOrg(defOrg).WithOwner(user.UserID), actions: []Action{ActionRead}, allow: true},
856+
// The scope will return true, but the user perms return false for resources not owned by the user.
857+
{resource: ResourceWorkspace.WithID(workspaceID).InOrg(defOrg).WithOwner("not-me"), actions: []Action{ActionRead}, allow: false},
858+
{resource: ResourceWorkspace.WithID(workspaceID).InOrg(unusedID).WithOwner("not-me"), actions: []Action{ActionRead}, allow: false},
859+
},
860+
)
791861
}
792862

793863
// cases applies a given function to all test cases. This makes generalities easier to create.

coderd/rbac/regosql/configs.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@ package regosql
22

33
import "github.com/coder/coder/coderd/rbac/regosql/sqltypes"
44

5+
func resourceIDMatcher() sqltypes.VariableMatcher {
6+
return sqltypes.StringVarMatcher("id :: text", []string{"input", "object", "id"})
7+
}
8+
59
func organizationOwnerMatcher() sqltypes.VariableMatcher {
610
return sqltypes.StringVarMatcher("organization_id :: text", []string{"input", "object", "org_owner"})
711
}
@@ -20,6 +24,7 @@ func userACLMatcher(m sqltypes.VariableMatcher) sqltypes.VariableMatcher {
2024

2125
func TemplateConverter() *sqltypes.VariableConverter {
2226
matcher := sqltypes.NewVariableConverter().RegisterMatcher(
27+
resourceIDMatcher(),
2328
organizationOwnerMatcher(),
2429
// Templates have no user owner, only owner by an organization.
2530
sqltypes.AlwaysFalse(userOwnerMatcher()),
@@ -35,6 +40,7 @@ func TemplateConverter() *sqltypes.VariableConverter {
3540
// group or user ACL columns.
3641
func NoACLConverter() *sqltypes.VariableConverter {
3742
matcher := sqltypes.NewVariableConverter().RegisterMatcher(
43+
resourceIDMatcher(),
3844
organizationOwnerMatcher(),
3945
userOwnerMatcher(),
4046
)
@@ -48,6 +54,7 @@ func NoACLConverter() *sqltypes.VariableConverter {
4854

4955
func DefaultVariableConverter() *sqltypes.VariableConverter {
5056
matcher := sqltypes.NewVariableConverter().RegisterMatcher(
57+
resourceIDMatcher(),
5158
organizationOwnerMatcher(),
5259
userOwnerMatcher(),
5360
)

0 commit comments

Comments
 (0)