Skip to content

Commit 2ba15c5

Browse files
committed
feat: POC for allowing TemplateAdmin to delete prebuild workspaces via auth layer
1 parent af4a668 commit 2ba15c5

File tree

15 files changed

+245
-54
lines changed

15 files changed

+245
-54
lines changed

coderd/apidoc/docs.go

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

coderd/apidoc/swagger.json

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

coderd/database/dbauthz/dbauthz.go

Lines changed: 55 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -412,6 +412,9 @@ var (
412412
policy.ActionCreate, policy.ActionDelete, policy.ActionRead, policy.ActionUpdate,
413413
policy.ActionWorkspaceStart, policy.ActionWorkspaceStop,
414414
},
415+
rbac.ResourcePrebuiltWorkspace.Type: {
416+
policy.ActionRead, policy.ActionUpdate, policy.ActionDelete,
417+
},
415418
// Should be able to add the prebuilds system user as a member to any organization that needs prebuilds.
416419
rbac.ResourceOrganizationMember.Type: {
417420
policy.ActionCreate,
@@ -527,9 +530,9 @@ func As(ctx context.Context, actor rbac.Subject) context.Context {
527530
// running the insertFunc. The insertFunc is expected to return the object that
528531
// was inserted.
529532
func insert[
530-
ObjectType any,
531-
ArgumentType any,
532-
Insert func(ctx context.Context, arg ArgumentType) (ObjectType, error),
533+
ObjectType any,
534+
ArgumentType any,
535+
Insert func(ctx context.Context, arg ArgumentType) (ObjectType, error),
533536
](
534537
logger slog.Logger,
535538
authorizer rbac.Authorizer,
@@ -540,9 +543,9 @@ func insert[
540543
}
541544

542545
func insertWithAction[
543-
ObjectType any,
544-
ArgumentType any,
545-
Insert func(ctx context.Context, arg ArgumentType) (ObjectType, error),
546+
ObjectType any,
547+
ArgumentType any,
548+
Insert func(ctx context.Context, arg ArgumentType) (ObjectType, error),
546549
](
547550
logger slog.Logger,
548551
authorizer rbac.Authorizer,
@@ -569,10 +572,10 @@ func insertWithAction[
569572
}
570573

571574
func deleteQ[
572-
ObjectType rbac.Objecter,
573-
ArgumentType any,
574-
Fetch func(ctx context.Context, arg ArgumentType) (ObjectType, error),
575-
Delete func(ctx context.Context, arg ArgumentType) error,
575+
ObjectType rbac.Objecter,
576+
ArgumentType any,
577+
Fetch func(ctx context.Context, arg ArgumentType) (ObjectType, error),
578+
Delete func(ctx context.Context, arg ArgumentType) error,
576579
](
577580
logger slog.Logger,
578581
authorizer rbac.Authorizer,
@@ -584,10 +587,10 @@ func deleteQ[
584587
}
585588

586589
func updateWithReturn[
587-
ObjectType rbac.Objecter,
588-
ArgumentType any,
589-
Fetch func(ctx context.Context, arg ArgumentType) (ObjectType, error),
590-
UpdateQuery func(ctx context.Context, arg ArgumentType) (ObjectType, error),
590+
ObjectType rbac.Objecter,
591+
ArgumentType any,
592+
Fetch func(ctx context.Context, arg ArgumentType) (ObjectType, error),
593+
UpdateQuery func(ctx context.Context, arg ArgumentType) (ObjectType, error),
591594
](
592595
logger slog.Logger,
593596
authorizer rbac.Authorizer,
@@ -598,10 +601,10 @@ func updateWithReturn[
598601
}
599602

600603
func update[
601-
ObjectType rbac.Objecter,
602-
ArgumentType any,
603-
Fetch func(ctx context.Context, arg ArgumentType) (ObjectType, error),
604-
Exec func(ctx context.Context, arg ArgumentType) error,
604+
ObjectType rbac.Objecter,
605+
ArgumentType any,
606+
Fetch func(ctx context.Context, arg ArgumentType) (ObjectType, error),
607+
Exec func(ctx context.Context, arg ArgumentType) error,
605608
](
606609
logger slog.Logger,
607610
authorizer rbac.Authorizer,
@@ -619,9 +622,9 @@ func update[
619622
// user cannot read the resource. This is because the resource details are
620623
// required to run a proper authorization check.
621624
func fetchWithAction[
622-
ArgumentType any,
623-
ObjectType rbac.Objecter,
624-
DatabaseFunc func(ctx context.Context, arg ArgumentType) (ObjectType, error),
625+
ArgumentType any,
626+
ObjectType rbac.Objecter,
627+
DatabaseFunc func(ctx context.Context, arg ArgumentType) (ObjectType, error),
625628
](
626629
logger slog.Logger,
627630
authorizer rbac.Authorizer,
@@ -652,9 +655,9 @@ func fetchWithAction[
652655
}
653656

654657
func fetch[
655-
ArgumentType any,
656-
ObjectType rbac.Objecter,
657-
DatabaseFunc func(ctx context.Context, arg ArgumentType) (ObjectType, error),
658+
ArgumentType any,
659+
ObjectType rbac.Objecter,
660+
DatabaseFunc func(ctx context.Context, arg ArgumentType) (ObjectType, error),
658661
](
659662
logger slog.Logger,
660663
authorizer rbac.Authorizer,
@@ -667,10 +670,10 @@ func fetch[
667670
// from SQL 'exec' functions which only return an error.
668671
// See fetchAndQuery for more information.
669672
func fetchAndExec[
670-
ObjectType rbac.Objecter,
671-
ArgumentType any,
672-
Fetch func(ctx context.Context, arg ArgumentType) (ObjectType, error),
673-
Exec func(ctx context.Context, arg ArgumentType) error,
673+
ObjectType rbac.Objecter,
674+
ArgumentType any,
675+
Fetch func(ctx context.Context, arg ArgumentType) (ObjectType, error),
676+
Exec func(ctx context.Context, arg ArgumentType) error,
674677
](
675678
logger slog.Logger,
676679
authorizer rbac.Authorizer,
@@ -693,10 +696,10 @@ func fetchAndExec[
693696
// **before** the query runs. The returns from the fetch are only used to
694697
// assert rbac. The final return of this function comes from the Query function.
695698
func fetchAndQuery[
696-
ObjectType rbac.Objecter,
697-
ArgumentType any,
698-
Fetch func(ctx context.Context, arg ArgumentType) (ObjectType, error),
699-
Query func(ctx context.Context, arg ArgumentType) (ObjectType, error),
699+
ObjectType rbac.Objecter,
700+
ArgumentType any,
701+
Fetch func(ctx context.Context, arg ArgumentType) (ObjectType, error),
702+
Query func(ctx context.Context, arg ArgumentType) (ObjectType, error),
700703
](
701704
logger slog.Logger,
702705
authorizer rbac.Authorizer,
@@ -730,9 +733,9 @@ func fetchAndQuery[
730733
// fetchWithPostFilter is like fetch, but works with lists of objects.
731734
// SQL filters are much more optimal.
732735
func fetchWithPostFilter[
733-
ArgumentType any,
734-
ObjectType rbac.Objecter,
735-
DatabaseFunc func(ctx context.Context, arg ArgumentType) ([]ObjectType, error),
736+
ArgumentType any,
737+
ObjectType rbac.Objecter,
738+
DatabaseFunc func(ctx context.Context, arg ArgumentType) ([]ObjectType, error),
736739
](
737740
authorizer rbac.Authorizer,
738741
action policy.Action,
@@ -3909,7 +3912,14 @@ func (q *querier) InsertWorkspaceBuild(ctx context.Context, arg database.InsertW
39093912
action = policy.ActionWorkspaceStop
39103913
}
39113914

3912-
if err = q.authorizeContext(ctx, action, w); err != nil {
3915+
if action == policy.ActionDelete && w.IsPrebuild() {
3916+
if err := q.authorizeContext(ctx, action, w.PrebuildRBAC()); err != nil {
3917+
// Fallback to normal workspace auth check
3918+
if err = q.authorizeContext(ctx, action, w); err != nil {
3919+
return xerrors.Errorf("authorize context: %w", err)
3920+
}
3921+
}
3922+
} else if err = q.authorizeContext(ctx, action, w); err != nil {
39133923
return xerrors.Errorf("authorize context: %w", err)
39143924
}
39153925

@@ -3949,7 +3959,15 @@ func (q *querier) InsertWorkspaceBuildParameters(ctx context.Context, arg databa
39493959
return err
39503960
}
39513961

3952-
err = q.authorizeContext(ctx, policy.ActionUpdate, workspace)
3962+
if workspace.IsPrebuild() {
3963+
err = q.authorizeContext(ctx, policy.ActionUpdate, workspace.PrebuildRBAC())
3964+
// Fallback to normal workspace auth check
3965+
if err != nil {
3966+
err = q.authorizeContext(ctx, policy.ActionUpdate, workspace)
3967+
}
3968+
} else {
3969+
err = q.authorizeContext(ctx, policy.ActionUpdate, workspace)
3970+
}
39533971
if err != nil {
39543972
return err
39553973
}

coderd/database/modelmethods.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,9 +226,26 @@ func (w Workspace) WorkspaceTable() WorkspaceTable {
226226
}
227227

228228
func (w Workspace) RBACObject() rbac.Object {
229+
// if w.IsPrebuild() {
230+
// return w.PrebuildRBAC()
231+
//}
229232
return w.WorkspaceTable().RBACObject()
230233
}
231234

235+
func (w Workspace) IsPrebuild() bool {
236+
// TODO: avoid import cycle
237+
return w.OwnerID == uuid.MustParse("c42fdf75-3097-471c-8c33-fb52454d81c0")
238+
}
239+
240+
func (w Workspace) PrebuildRBAC() rbac.Object {
241+
if w.IsPrebuild() {
242+
return rbac.ResourcePrebuiltWorkspace.WithID(w.ID).
243+
InOrg(w.OrganizationID).
244+
WithOwner(w.OwnerID.String())
245+
}
246+
return w.RBACObject()
247+
}
248+
232249
func (w WorkspaceTable) RBACObject() rbac.Object {
233250
if w.DormantAt.Valid {
234251
return w.DormantRBAC()

coderd/rbac/object_gen.go

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

coderd/rbac/policy/policy.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,13 @@ var RBACPermissions = map[string]PermissionDefinition{
102102
"workspace_dormant": {
103103
Actions: workspaceActions,
104104
},
105+
"prebuilt_workspace": {
106+
Actions: map[Action]ActionDefinition{
107+
ActionRead: actDef("read prebuilt workspace"),
108+
ActionUpdate: actDef("update prebuilt workspace"),
109+
ActionDelete: actDef("delete prebuilt workspace"),
110+
},
111+
},
105112
"workspace_proxy": {
106113
Actions: map[Action]ActionDefinition{
107114
ActionCreate: actDef("create a workspace proxy"),

coderd/rbac/roles.go

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -335,8 +335,9 @@ func ReloadBuiltinRoles(opts *RoleOptions) {
335335
ResourceAssignOrgRole.Type: {policy.ActionRead},
336336
ResourceTemplate.Type: ResourceTemplate.AvailableActions(),
337337
// CRUD all files, even those they did not upload.
338-
ResourceFile.Type: {policy.ActionCreate, policy.ActionRead},
339-
ResourceWorkspace.Type: {policy.ActionRead},
338+
ResourceFile.Type: {policy.ActionCreate, policy.ActionRead},
339+
ResourceWorkspace.Type: {policy.ActionRead},
340+
ResourcePrebuiltWorkspace.Type: {policy.ActionRead, policy.ActionUpdate, policy.ActionDelete},
340341
// CRUD to provisioner daemons for now.
341342
ResourceProvisionerDaemon.Type: {policy.ActionCreate, policy.ActionRead, policy.ActionUpdate, policy.ActionDelete},
342343
// Needs to read all organizations since
@@ -493,9 +494,10 @@ func ReloadBuiltinRoles(opts *RoleOptions) {
493494
Site: []Permission{},
494495
Org: map[string][]Permission{
495496
organizationID.String(): Permissions(map[string][]policy.Action{
496-
ResourceTemplate.Type: ResourceTemplate.AvailableActions(),
497-
ResourceFile.Type: {policy.ActionCreate, policy.ActionRead},
498-
ResourceWorkspace.Type: {policy.ActionRead},
497+
ResourceTemplate.Type: ResourceTemplate.AvailableActions(),
498+
ResourceFile.Type: {policy.ActionCreate, policy.ActionRead},
499+
ResourceWorkspace.Type: {policy.ActionRead},
500+
ResourcePrebuiltWorkspace.Type: {policy.ActionRead, policy.ActionUpdate, policy.ActionDelete},
499501
// Assigning template perms requires this permission.
500502
ResourceOrganization.Type: {policy.ActionRead},
501503
ResourceOrganizationMember.Type: {policy.ActionRead},

coderd/workspacebuilds.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -404,6 +404,16 @@ func (api *API) postWorkspaceBuilds(rw http.ResponseWriter, r *http.Request) {
404404
ctx,
405405
tx,
406406
func(action policy.Action, object rbac.Objecter) bool {
407+
if object.RBACObject().Type == rbac.ResourceWorkspace.Type && action == policy.ActionDelete {
408+
workspaceObj, ok := object.(database.Workspace)
409+
if ok {
410+
prebuild := workspaceObj.PrebuildRBAC()
411+
// Fallback to normal workspace auth check
412+
if auth := api.Authorize(r, action, prebuild); auth {
413+
return auth
414+
}
415+
}
416+
}
407417
return api.Authorize(r, action, object)
408418
},
409419
audit.WorkspaceBuildBaggageFromRequest(r),

codersdk/rbacresources_gen.go

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

docs/reference/api/members.md

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

docs/reference/api/schemas.md

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

0 commit comments

Comments
 (0)