Skip to content

Commit 18ded82

Browse files
f0sselsreya
andauthored
fix(coderd): require update permission to recreate devcontainers (#25812) (#26320)
Backport of #25812 to `release/2.29` (SEC-84 / DEVEX-399). Conflict resolution: 2.29 has no `WorkspaceAgentAndWorkspaceParam` middleware, so the handler authorizes against `httpmw.WorkspaceParam(r)`. The authorization test resolves the agent ID via the workspace API since `dbfake.WorkspaceResponse` has no `Agents` field on this branch. Dropped the out-of-scope `TestWorkspaceAgentDeleteDevcontainer` (DELETE endpoint does not exist on 2.29). > 🤖 Backport prepared by Coder Agents on behalf of @f0ssel. Co-authored-by: Jon Ayers <jon@coder.com>
1 parent 0951f90 commit 18ded82

2 files changed

Lines changed: 57 additions & 0 deletions

File tree

coderd/workspaceagents.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1134,6 +1134,12 @@ func (api *API) workspaceAgentListContainers(rw http.ResponseWriter, r *http.Req
11341134
func (api *API) workspaceAgentRecreateDevcontainer(rw http.ResponseWriter, r *http.Request) {
11351135
ctx := r.Context()
11361136
workspaceAgent := httpmw.WorkspaceAgentParam(r)
1137+
workspace := httpmw.WorkspaceParam(r)
1138+
1139+
if !api.Authorize(r, policy.ActionUpdate, workspace) {
1140+
httpapi.Forbidden(rw)
1141+
return
1142+
}
11371143

11381144
devcontainer := chi.URLParam(r, "devcontainer")
11391145
if devcontainer == "" {

coderd/workspaceagents_test.go

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1571,6 +1571,57 @@ func TestWorkspaceAgentRecreateDevcontainer(t *testing.T) {
15711571
})
15721572
}
15731573

1574+
func TestWorkspaceAgentRecreateDevcontainerAuthorization(t *testing.T) {
1575+
t.Parallel()
1576+
1577+
for _, tc := range []struct {
1578+
name string
1579+
role func(uuid.UUID) rbac.RoleIdentifier
1580+
}{
1581+
{
1582+
name: "TemplateAdmin",
1583+
role: func(uuid.UUID) rbac.RoleIdentifier {
1584+
return rbac.RoleTemplateAdmin()
1585+
},
1586+
},
1587+
{
1588+
name: "OrgTemplateAdmin",
1589+
role: rbac.ScopedRoleOrgTemplateAdmin,
1590+
},
1591+
} {
1592+
t.Run(tc.name, func(t *testing.T) {
1593+
t.Parallel()
1594+
1595+
var (
1596+
ctx = testutil.Context(t, testutil.WaitMedium)
1597+
client, db = coderdtest.NewWithDatabase(t, nil)
1598+
admin = coderdtest.CreateFirstUser(t, client)
1599+
_, workspaceOwner = coderdtest.CreateAnotherUser(t, client, admin.OrganizationID)
1600+
templateAdminClient, _ = coderdtest.CreateAnotherUser(t, client, admin.OrganizationID, tc.role(admin.OrganizationID))
1601+
)
1602+
r := dbfake.WorkspaceBuild(t, db, database.WorkspaceTable{
1603+
OrganizationID: admin.OrganizationID,
1604+
OwnerID: workspaceOwner.ID,
1605+
}).WithAgent(func(agents []*proto.Agent) []*proto.Agent {
1606+
return agents
1607+
}).Do()
1608+
1609+
ws, err := client.Workspace(ctx, r.Workspace.ID)
1610+
require.NoError(t, err)
1611+
require.NotEmpty(t, ws.LatestBuild.Resources)
1612+
require.NotEmpty(t, ws.LatestBuild.Resources[0].Agents)
1613+
agentID := ws.LatestBuild.Resources[0].Agents[0].ID
1614+
1615+
_, err = templateAdminClient.WorkspaceAgentRecreateDevcontainer(ctx, agentID, uuid.NewString())
1616+
require.Error(t, err)
1617+
1618+
var sdkErr *codersdk.Error
1619+
require.ErrorAs(t, err, &sdkErr)
1620+
require.Equal(t, http.StatusForbidden, sdkErr.StatusCode())
1621+
})
1622+
}
1623+
}
1624+
15741625
func TestWorkspaceAgentAppHealth(t *testing.T) {
15751626
t.Parallel()
15761627
client, db := coderdtest.NewWithDatabase(t, nil)

0 commit comments

Comments
 (0)