Skip to content

Commit 0e5bcd0

Browse files
committed
chore: reorder prebuilt workspace authorization logic
1 parent 0a12ec5 commit 0e5bcd0

File tree

4 files changed

+47
-28
lines changed

4 files changed

+47
-28
lines changed

coderd/database/dbauthz/dbauthz.go

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -151,26 +151,28 @@ func (q *querier) authorizeContext(ctx context.Context, action policy.Action, ob
151151

152152
// authorizePrebuiltWorkspace handles authorization for workspace resource types.
153153
// prebuilt_workspaces are a subset of workspaces, currently limited to
154-
// supporting delete operations. Therefore, if the action is delete or
155-
// update and the workspace is a prebuild, a prebuilt-specific authorization
156-
// is attempted first. If that fails, it falls back to normal workspace
157-
// authorization.
154+
// supporting delete operations. This function first attempts normal workspace
155+
// authorization. If that fails, the action is delete or update and the workspace
156+
// is a prebuild, a prebuilt-specific authorization is attempted.
158157
// Note: Delete operations of workspaces requires both update and delete
159158
// permissions.
160159
func (q *querier) authorizePrebuiltWorkspace(ctx context.Context, action policy.Action, workspace database.Workspace) error {
161-
var prebuiltErr error
162-
// Special handling for prebuilt_workspace deletion authorization check
160+
// Try default workspace authorization first
161+
var workspaceErr error
162+
if workspaceErr = q.authorizeContext(ctx, action, workspace); workspaceErr == nil {
163+
return nil
164+
}
165+
166+
// Special handling for prebuilt workspace deletion
163167
if (action == policy.ActionUpdate || action == policy.ActionDelete) && workspace.IsPrebuild() {
164-
// Try prebuilt-specific authorization first
168+
var prebuiltErr error
165169
if prebuiltErr = q.authorizeContext(ctx, action, workspace.AsPrebuild()); prebuiltErr == nil {
166170
return nil
167171
}
172+
return xerrors.Errorf("authorize context: %w", errors.Join(workspaceErr, prebuiltErr))
168173
}
169-
// Fallback to normal workspace authorization check
170-
if err := q.authorizeContext(ctx, action, workspace); err != nil {
171-
return xerrors.Errorf("authorize context: %w", errors.Join(prebuiltErr, err))
172-
}
173-
return nil
174+
175+
return xerrors.Errorf("authorize context: %w", errors.Join(workspaceErr))
174176
}
175177

176178
type authContextKey struct{}

coderd/database/dbauthz/dbauthz_test.go

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5590,7 +5590,17 @@ func (s *MethodTestSuite) TestAuthorizePrebuiltWorkspace() {
55905590
Reason: database.BuildReasonInitiator,
55915591
TemplateVersionID: tv.ID,
55925592
JobID: pj.ID,
5593-
}).Asserts(w.AsPrebuild(), policy.ActionDelete)
5593+
}).
5594+
// Simulate a fallback authorization flow:
5595+
// - First, the default workspace authorization fails (simulated by returning an error).
5596+
// - Then, authorization is retried using the prebuilt workspace object, which succeeds.
5597+
// The test asserts that both authorization attempts occur in the correct order.
5598+
WithSuccessAuthorizer(func(ctx context.Context, subject rbac.Subject, action policy.Action, obj rbac.Object) error {
5599+
if obj.Type == rbac.ResourceWorkspace.Type {
5600+
return xerrors.Errorf("not authorized for workspace type")
5601+
}
5602+
return nil
5603+
}).Asserts(w, policy.ActionDelete, w.AsPrebuild(), policy.ActionDelete)
55945604
}))
55955605
s.Run("PrebuildUpdate/InsertWorkspaceBuildParameters", s.Subtest(func(db database.Store, check *expects) {
55965606
u := dbgen.User(s.T(), db, database.User{})
@@ -5619,6 +5629,16 @@ func (s *MethodTestSuite) TestAuthorizePrebuiltWorkspace() {
56195629
})
56205630
check.Args(database.InsertWorkspaceBuildParametersParams{
56215631
WorkspaceBuildID: wb.ID,
5622-
}).Asserts(w.AsPrebuild(), policy.ActionUpdate)
5632+
}).
5633+
// Simulate a fallback authorization flow:
5634+
// - First, the default workspace authorization fails (simulated by returning an error).
5635+
// - Then, authorization is retried using the prebuilt workspace object, which succeeds.
5636+
// The test asserts that both authorization attempts occur in the correct order.
5637+
WithSuccessAuthorizer(func(ctx context.Context, subject rbac.Subject, action policy.Action, obj rbac.Object) error {
5638+
if obj.Type == rbac.ResourceWorkspace.Type {
5639+
return xerrors.Errorf("not authorized for workspace type")
5640+
}
5641+
return nil
5642+
}).Asserts(w, policy.ActionUpdate, w.AsPrebuild(), policy.ActionUpdate)
56235643
}))
56245644
}

coderd/workspacebuilds.go

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -392,17 +392,16 @@ func (api *API) postWorkspaceBuilds(rw http.ResponseWriter, r *http.Request) {
392392
ctx,
393393
tx,
394394
func(action policy.Action, object rbac.Objecter) bool {
395+
if auth := api.Authorize(r, action, object); auth {
396+
return true
397+
}
395398
// Special handling for prebuilt workspace deletion
396399
if object.RBACObject().Type == rbac.ResourceWorkspace.Type && action == policy.ActionDelete {
397-
if workspaceObj, ok := object.(database.Workspace); ok {
398-
// Try prebuilt-specific authorization first
399-
if auth := api.Authorize(r, action, workspaceObj.AsPrebuild()); auth {
400-
return auth
401-
}
400+
if workspaceObj, ok := object.(database.Workspace); ok && workspaceObj.IsPrebuild() {
401+
return api.Authorize(r, action, workspaceObj.AsPrebuild())
402402
}
403403
}
404-
// Fallback to default authorization
405-
return api.Authorize(r, action, object)
404+
return false
406405
},
407406
audit.WorkspaceBuildBaggageFromRequest(r),
408407
)

coderd/wsbuilder/wsbuilder.go

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -919,14 +919,12 @@ func (b *Builder) authorize(authFunc func(action policy.Action, object rbac.Obje
919919
return BuildError{http.StatusBadRequest, msg, xerrors.New(msg)}
920920
}
921921

922+
// Try default workspace authorization first
923+
authorized := authFunc(action, b.workspace)
924+
922925
// Special handling for prebuilt workspace deletion
923-
authorized := false
924-
if action == policy.ActionDelete && b.workspace.IsPrebuild() && authFunc(action, b.workspace.AsPrebuild()) {
925-
authorized = true
926-
}
927-
// Fallback to default authorization
928-
if !authorized && authFunc(action, b.workspace) {
929-
authorized = true
926+
if !authorized && action == policy.ActionDelete && b.workspace.IsPrebuild() {
927+
authorized = authFunc(action, b.workspace.AsPrebuild())
930928
}
931929

932930
if !authorized {

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