Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit40e68cb

Browse files
Emyrkmafredri
andauthored
feat: Add template-admin + user-admin role for managing templates + users (coder#3490)
Co-authored-by: Mathias Fredriksson <mafredri@gmail.com>
1 parentc41261c commit40e68cb

File tree

16 files changed

+219
-59
lines changed

16 files changed

+219
-59
lines changed

‎coderd/coderd.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,7 @@ func New(options *Options) *API {
340340
r.Get("/",api.workspaceAgent)
341341
r.Post("/peer",api.postWorkspaceAgentWireguardPeer)
342342
r.Get("/dial",api.workspaceAgentDial)
343-
r.Get("/turn",api.workspaceAgentTurn)
343+
r.Get("/turn",api.userWorkspaceAgentTurn)
344344
r.Get("/pty",api.workspaceAgentPTY)
345345
r.Get("/iceservers",api.workspaceAgentICEServers)
346346
r.Get("/derp",api.derpMap)

‎coderd/coderd_test.go

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,7 @@ func TestAuthorizeAllEndpoints(t *testing.T) {
220220

221221
// Some quick reused objects
222222
workspaceRBACObj:=rbac.ResourceWorkspace.InOrg(organization.ID).WithOwner(workspace.OwnerID.String())
223+
workspaceExecObj:=rbac.ResourceWorkspaceExecution.InOrg(organization.ID).WithOwner(workspace.OwnerID.String())
223224

224225
// skipRoutes allows skipping routes from being checked.
225226
skipRoutes:=map[string]string{
@@ -268,7 +269,6 @@ func TestAuthorizeAllEndpoints(t *testing.T) {
268269
"GET:/api/v2/workspaceagents/me/wireguardlisten": {NoAuthorize:true},
269270
"POST:/api/v2/workspaceagents/me/keys": {NoAuthorize:true},
270271
"GET:/api/v2/workspaceagents/{workspaceagent}/iceservers": {NoAuthorize:true},
271-
"GET:/api/v2/workspaceagents/{workspaceagent}/turn": {NoAuthorize:true},
272272
"GET:/api/v2/workspaceagents/{workspaceagent}/derp": {NoAuthorize:true},
273273

274274
// These endpoints have more assertions. This is good, add more endpoints to assert if you can!
@@ -331,12 +331,16 @@ func TestAuthorizeAllEndpoints(t *testing.T) {
331331
AssertObject:workspaceRBACObj,
332332
},
333333
"GET:/api/v2/workspaceagents/{workspaceagent}/dial": {
334-
AssertAction:rbac.ActionUpdate,
335-
AssertObject:workspaceRBACObj,
334+
AssertAction:rbac.ActionCreate,
335+
AssertObject:workspaceExecObj,
336+
},
337+
"GET:/api/v2/workspaceagents/{workspaceagent}/turn": {
338+
AssertAction:rbac.ActionCreate,
339+
AssertObject:workspaceExecObj,
336340
},
337341
"GET:/api/v2/workspaceagents/{workspaceagent}/pty": {
338-
AssertAction:rbac.ActionUpdate,
339-
AssertObject:workspaceRBACObj,
342+
AssertAction:rbac.ActionCreate,
343+
AssertObject:workspaceExecObj,
340344
},
341345
"GET:/api/v2/workspaces/": {
342346
StatusCode:http.StatusOK,

‎coderd/database/modelmethods.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ func (w Workspace) RBACObject() rbac.Object {
1717
returnrbac.ResourceWorkspace.InOrg(w.OrganizationID).WithOwner(w.OwnerID.String())
1818
}
1919

20+
func (wWorkspace)ExecutionRBAC() rbac.Object {
21+
returnrbac.ResourceWorkspaceExecution.InOrg(w.OrganizationID).WithOwner(w.OwnerID.String())
22+
}
23+
2024
func (mOrganizationMember)RBACObject() rbac.Object {
2125
returnrbac.ResourceOrganizationMember.InOrg(m.OrganizationID)
2226
}

‎coderd/rbac/builtin.go

Lines changed: 45 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,11 @@ import (
99
)
1010

1111
const (
12-
adminstring="admin"
13-
memberstring="member"
14-
auditorstring="auditor"
12+
adminstring="admin"
13+
memberstring="member"
14+
templateAdminstring="template-admin"
15+
userAdminstring="user-admin"
16+
auditorstring="auditor"
1517

1618
orgAdminstring="organization-admin"
1719
orgMemberstring="organization-member"
@@ -26,6 +28,14 @@ func RoleAdmin() string {
2628
returnroleName(admin,"")
2729
}
2830

31+
funcRoleTemplateAdmin()string {
32+
returnroleName(templateAdmin,"")
33+
}
34+
35+
funcRoleUserAdmin()string {
36+
returnroleName(userAdmin,"")
37+
}
38+
2939
funcRoleMember()string {
3040
returnroleName(member,"")
3141
}
@@ -93,6 +103,31 @@ var (
93103
}
94104
},
95105

106+
templateAdmin:func(_string)Role {
107+
returnRole{
108+
Name:templateAdmin,
109+
DisplayName:"Template Admin",
110+
Site:permissions(map[Object][]Action{
111+
ResourceTemplate: {ActionCreate,ActionRead,ActionUpdate,ActionDelete},
112+
// CRUD all files, even those they did not upload.
113+
ResourceFile: {ActionCreate,ActionRead,ActionUpdate,ActionDelete},
114+
ResourceWorkspace: {ActionCreate,ActionRead,ActionUpdate,ActionDelete},
115+
// CRUD to provisioner daemons for now.
116+
ResourceProvisionerDaemon: {ActionCreate,ActionRead,ActionUpdate,ActionDelete},
117+
}),
118+
}
119+
},
120+
121+
userAdmin:func(_string)Role {
122+
returnRole{
123+
Name:userAdmin,
124+
DisplayName:"User Admin",
125+
Site:permissions(map[Object][]Action{
126+
ResourceUser: {ActionCreate,ActionRead,ActionUpdate,ActionDelete},
127+
}),
128+
}
129+
},
130+
96131
// orgAdmin returns a role with all actions allows in a given
97132
// organization scope.
98133
orgAdmin:func(organizationIDstring)Role {
@@ -153,11 +188,13 @@ var (
153188
//map[actor_role][assign_role]<can_assign>
154189
assignRoles=map[string]map[string]bool{
155190
admin: {
156-
admin:true,
157-
auditor:true,
158-
member:true,
159-
orgAdmin:true,
160-
orgMember:true,
191+
admin:true,
192+
auditor:true,
193+
member:true,
194+
orgAdmin:true,
195+
orgMember:true,
196+
templateAdmin:true,
197+
userAdmin:true,
161198
},
162199
orgAdmin: {
163200
orgAdmin:true,

‎coderd/rbac/builtin_internal_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ func TestRoleByName(t *testing.T) {
1818
}{
1919
{Role:builtInRoles[admin]("")},
2020
{Role:builtInRoles[member]("")},
21+
{Role:builtInRoles[templateAdmin]("")},
22+
{Role:builtInRoles[userAdmin]("")},
2123
{Role:builtInRoles[auditor]("")},
2224

2325
{Role:builtInRoles[orgAdmin](uuid.New().String())},

‎coderd/rbac/builtin_test.go

Lines changed: 43 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ func TestRolePermissions(t *testing.T) {
111111
// currentUser is anything that references "me", "mine", or "my".
112112
currentUser:=uuid.New()
113113
adminID:=uuid.New()
114+
templateAdminID:=uuid.New()
114115
orgID:=uuid.New()
115116
otherOrg:=uuid.New()
116117

@@ -124,9 +125,12 @@ func TestRolePermissions(t *testing.T) {
124125
otherOrgMember:=authSubject{Name:"org_member_other",UserID:uuid.NewString(),Roles: []string{rbac.RoleMember(),rbac.RoleOrgMember(otherOrg)}}
125126
otherOrgAdmin:=authSubject{Name:"org_admin_other",UserID:uuid.NewString(),Roles: []string{rbac.RoleMember(),rbac.RoleOrgMember(otherOrg),rbac.RoleOrgAdmin(otherOrg)}}
126127

128+
templateAdmin:=authSubject{Name:"template-admin",UserID:templateAdminID.String(),Roles: []string{rbac.RoleMember(),rbac.RoleTemplateAdmin()}}
129+
userAdmin:=authSubject{Name:"user-admin",UserID:templateAdminID.String(),Roles: []string{rbac.RoleMember(),rbac.RoleUserAdmin()}}
130+
127131
// requiredSubjects are required to be asserted in each test case. This is
128132
// to make sure one is not forgotten.
129-
requiredSubjects:= []authSubject{memberMe,admin,orgMemberMe,orgAdmin,otherOrgAdmin,otherOrgMember}
133+
requiredSubjects:= []authSubject{memberMe,admin,orgMemberMe,orgAdmin,otherOrgAdmin,otherOrgMember,templateAdmin,userAdmin}
130134

131135
testCases:= []struct {
132136
// Name the test case to better locate the failing test case.
@@ -146,7 +150,7 @@ func TestRolePermissions(t *testing.T) {
146150
Actions: []rbac.Action{rbac.ActionRead},
147151
Resource:rbac.ResourceUser,
148152
AuthorizeMap:map[bool][]authSubject{
149-
true: {admin,memberMe,orgMemberMe,orgAdmin,otherOrgMember,otherOrgAdmin},
153+
true: {admin,memberMe,orgMemberMe,orgAdmin,otherOrgMember,otherOrgAdmin,templateAdmin,userAdmin},
150154
false: {},
151155
},
152156
},
@@ -155,8 +159,8 @@ func TestRolePermissions(t *testing.T) {
155159
Actions: []rbac.Action{rbac.ActionCreate,rbac.ActionUpdate,rbac.ActionDelete},
156160
Resource:rbac.ResourceUser,
157161
AuthorizeMap:map[bool][]authSubject{
158-
true: {admin},
159-
false: {memberMe,orgMemberMe,orgAdmin,otherOrgMember,otherOrgAdmin},
162+
true: {admin,userAdmin},
163+
false: {memberMe,orgMemberMe,orgAdmin,otherOrgMember,otherOrgAdmin,templateAdmin},
160164
},
161165
},
162166
{
@@ -165,44 +169,54 @@ func TestRolePermissions(t *testing.T) {
165169
Actions: []rbac.Action{rbac.ActionCreate,rbac.ActionRead,rbac.ActionUpdate,rbac.ActionDelete},
166170
Resource:rbac.ResourceWorkspace.InOrg(orgID).WithOwner(currentUser.String()),
167171
AuthorizeMap:map[bool][]authSubject{
168-
true: {admin,orgMemberMe,orgAdmin},
169-
false: {memberMe,otherOrgAdmin,otherOrgMember},
172+
true: {admin,orgMemberMe,orgAdmin,templateAdmin},
173+
false: {memberMe,otherOrgAdmin,otherOrgMember,userAdmin},
174+
},
175+
},
176+
{
177+
Name:"MyWorkspaceInOrgExecution",
178+
// When creating the WithID won't be set, but it does not change the result.
179+
Actions: []rbac.Action{rbac.ActionCreate,rbac.ActionRead,rbac.ActionUpdate,rbac.ActionDelete},
180+
Resource:rbac.ResourceWorkspaceExecution.InOrg(orgID).WithOwner(currentUser.String()),
181+
AuthorizeMap:map[bool][]authSubject{
182+
true: {admin,orgAdmin,orgMemberMe},
183+
false: {memberMe,otherOrgAdmin,otherOrgMember,templateAdmin,userAdmin},
170184
},
171185
},
172186
{
173187
Name:"Templates",
174188
Actions: []rbac.Action{rbac.ActionCreate,rbac.ActionUpdate,rbac.ActionDelete},
175189
Resource:rbac.ResourceTemplate.InOrg(orgID),
176190
AuthorizeMap:map[bool][]authSubject{
177-
true: {admin,orgAdmin},
178-
false: {memberMe,orgMemberMe,otherOrgAdmin,otherOrgMember},
191+
true: {admin,orgAdmin,templateAdmin},
192+
false: {memberMe,orgMemberMe,otherOrgAdmin,otherOrgMember,userAdmin},
179193
},
180194
},
181195
{
182196
Name:"ReadTemplates",
183197
Actions: []rbac.Action{rbac.ActionRead},
184198
Resource:rbac.ResourceTemplate.InOrg(orgID),
185199
AuthorizeMap:map[bool][]authSubject{
186-
true: {admin,orgMemberMe,orgAdmin},
187-
false: {memberMe,otherOrgAdmin,otherOrgMember},
200+
true: {admin,orgMemberMe,orgAdmin,templateAdmin},
201+
false: {memberMe,otherOrgAdmin,otherOrgMember,userAdmin},
188202
},
189203
},
190204
{
191205
Name:"Files",
192206
Actions: []rbac.Action{rbac.ActionCreate},
193207
Resource:rbac.ResourceFile,
194208
AuthorizeMap:map[bool][]authSubject{
195-
true: {admin},
196-
false: {orgMemberMe,orgAdmin,memberMe,otherOrgAdmin,otherOrgMember},
209+
true: {admin,templateAdmin},
210+
false: {orgMemberMe,orgAdmin,memberMe,otherOrgAdmin,otherOrgMember,userAdmin},
197211
},
198212
},
199213
{
200214
Name:"MyFile",
201215
Actions: []rbac.Action{rbac.ActionRead,rbac.ActionUpdate,rbac.ActionDelete},
202216
Resource:rbac.ResourceFile.WithOwner(currentUser.String()),
203217
AuthorizeMap:map[bool][]authSubject{
204-
true: {admin,memberMe,orgMemberMe},
205-
false: {orgAdmin,otherOrgAdmin,otherOrgMember},
218+
true: {admin,memberMe,orgMemberMe,templateAdmin},
219+
false: {orgAdmin,otherOrgAdmin,otherOrgMember,userAdmin},
206220
},
207221
},
208222
{
@@ -211,7 +225,7 @@ func TestRolePermissions(t *testing.T) {
211225
Resource:rbac.ResourceOrganization,
212226
AuthorizeMap:map[bool][]authSubject{
213227
true: {admin},
214-
false: {orgAdmin,otherOrgAdmin,otherOrgMember,memberMe,orgMemberMe},
228+
false: {orgAdmin,otherOrgAdmin,otherOrgMember,memberMe,orgMemberMe,templateAdmin,userAdmin},
215229
},
216230
},
217231
{
@@ -220,7 +234,7 @@ func TestRolePermissions(t *testing.T) {
220234
Resource:rbac.ResourceOrganization.InOrg(orgID),
221235
AuthorizeMap:map[bool][]authSubject{
222236
true: {admin,orgAdmin},
223-
false: {otherOrgAdmin,otherOrgMember,memberMe,orgMemberMe},
237+
false: {otherOrgAdmin,otherOrgMember,memberMe,orgMemberMe,templateAdmin,userAdmin},
224238
},
225239
},
226240
{
@@ -229,7 +243,7 @@ func TestRolePermissions(t *testing.T) {
229243
Resource:rbac.ResourceOrganization.InOrg(orgID),
230244
AuthorizeMap:map[bool][]authSubject{
231245
true: {admin,orgAdmin,orgMemberMe},
232-
false: {otherOrgAdmin,otherOrgMember,memberMe},
246+
false: {otherOrgAdmin,otherOrgMember,memberMe,templateAdmin,userAdmin},
233247
},
234248
},
235249
{
@@ -238,15 +252,15 @@ func TestRolePermissions(t *testing.T) {
238252
Resource:rbac.ResourceRoleAssignment,
239253
AuthorizeMap:map[bool][]authSubject{
240254
true: {admin},
241-
false: {orgAdmin,orgMemberMe,otherOrgAdmin,otherOrgMember,memberMe},
255+
false: {orgAdmin,orgMemberMe,otherOrgAdmin,otherOrgMember,memberMe,templateAdmin,userAdmin},
242256
},
243257
},
244258
{
245259
Name:"ReadRoleAssignment",
246260
Actions: []rbac.Action{rbac.ActionRead},
247261
Resource:rbac.ResourceRoleAssignment,
248262
AuthorizeMap:map[bool][]authSubject{
249-
true: {admin,orgAdmin,orgMemberMe,otherOrgAdmin,otherOrgMember,memberMe},
263+
true: {admin,orgAdmin,orgMemberMe,otherOrgAdmin,otherOrgMember,memberMe,templateAdmin,userAdmin},
250264
false: {},
251265
},
252266
},
@@ -256,7 +270,7 @@ func TestRolePermissions(t *testing.T) {
256270
Resource:rbac.ResourceOrgRoleAssignment.InOrg(orgID),
257271
AuthorizeMap:map[bool][]authSubject{
258272
true: {admin,orgAdmin},
259-
false: {orgMemberMe,otherOrgAdmin,otherOrgMember,memberMe},
273+
false: {orgMemberMe,otherOrgAdmin,otherOrgMember,memberMe,templateAdmin,userAdmin},
260274
},
261275
},
262276
{
@@ -265,7 +279,7 @@ func TestRolePermissions(t *testing.T) {
265279
Resource:rbac.ResourceOrgRoleAssignment.InOrg(orgID),
266280
AuthorizeMap:map[bool][]authSubject{
267281
true: {admin,orgAdmin,orgMemberMe},
268-
false: {otherOrgAdmin,otherOrgMember,memberMe},
282+
false: {otherOrgAdmin,otherOrgMember,memberMe,templateAdmin,userAdmin},
269283
},
270284
},
271285
{
@@ -274,7 +288,7 @@ func TestRolePermissions(t *testing.T) {
274288
Resource:rbac.ResourceAPIKey.WithOwner(currentUser.String()),
275289
AuthorizeMap:map[bool][]authSubject{
276290
true: {admin,orgMemberMe,memberMe},
277-
false: {orgAdmin,otherOrgAdmin,otherOrgMember},
291+
false: {orgAdmin,otherOrgAdmin,otherOrgMember,templateAdmin,userAdmin},
278292
},
279293
},
280294
{
@@ -283,7 +297,7 @@ func TestRolePermissions(t *testing.T) {
283297
Resource:rbac.ResourceUserData.WithOwner(currentUser.String()),
284298
AuthorizeMap:map[bool][]authSubject{
285299
true: {admin,orgMemberMe,memberMe},
286-
false: {orgAdmin,otherOrgAdmin,otherOrgMember},
300+
false: {orgAdmin,otherOrgAdmin,otherOrgMember,templateAdmin,userAdmin},
287301
},
288302
},
289303
{
@@ -292,7 +306,7 @@ func TestRolePermissions(t *testing.T) {
292306
Resource:rbac.ResourceOrganizationMember.InOrg(orgID),
293307
AuthorizeMap:map[bool][]authSubject{
294308
true: {admin,orgAdmin},
295-
false: {orgMemberMe,memberMe,otherOrgAdmin,otherOrgMember},
309+
false: {orgMemberMe,memberMe,otherOrgAdmin,otherOrgMember,templateAdmin,userAdmin},
296310
},
297311
},
298312
{
@@ -301,7 +315,7 @@ func TestRolePermissions(t *testing.T) {
301315
Resource:rbac.ResourceOrganizationMember.InOrg(orgID),
302316
AuthorizeMap:map[bool][]authSubject{
303317
true: {admin,orgAdmin,orgMemberMe},
304-
false: {memberMe,otherOrgAdmin,otherOrgMember},
318+
false: {memberMe,otherOrgAdmin,otherOrgMember,templateAdmin,userAdmin},
305319
},
306320
},
307321
}
@@ -396,10 +410,14 @@ func TestListRoles(t *testing.T) {
396410

397411
// If this test is ever failing, just update the list to the roles
398412
// expected from the builtin set.
413+
// Always use constant strings, as if the names change, we need to write
414+
// a SQL migration to change the name on the backend.
399415
require.ElementsMatch(t, []string{
400416
"admin",
401417
"member",
402418
"auditor",
419+
"template-admin",
420+
"user-admin",
403421
},
404422
siteRoleNames)
405423

‎coderd/rbac/object.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,15 @@ var (
2222
Type:"workspace",
2323
}
2424

25+
// ResourceWorkspaceExecution CRUD. Org + User owner
26+
//create = workspace remote execution
27+
// read = ?
28+
//update = ?
29+
// delete = ?
30+
ResourceWorkspaceExecution=Object{
31+
Type:"workspace_execution",
32+
}
33+
2534
// ResourceAuditLog
2635
// read = access audit log
2736
ResourceAuditLog=Object{

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp