Skip to content

Commit c219a9a

Browse files
fix: use system context for querying workspaces when deleting users (#19211) (#19227)
Backport of #19211 to our ESR, v2.24, since this is a kind of bad (albeit rare) bug and very easy to fix. ### Original PR Closes #19209. In `templates.go`, we do this to make sure we count ALL workspaces for a template before we try and delete that template: https://github.com/coder/coder/blob/dc598856e3be0926573dbbe2ec680e95a139093a/coderd/templates.go#L81-L99 However, we weren't doing the same when attempting to delete users, leading to the linked issue. We can solve the issue the same way as we do for templates.
1 parent 5ff7496 commit c219a9a

File tree

2 files changed

+41
-1
lines changed

2 files changed

+41
-1
lines changed

coderd/users.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -542,7 +542,10 @@ func (api *API) deleteUser(rw http.ResponseWriter, r *http.Request) {
542542
return
543543
}
544544

545-
workspaces, err := api.Database.GetWorkspaces(ctx, database.GetWorkspacesParams{
545+
// This query is ONLY done to get the workspace count, so we use a system
546+
// context to return ALL workspaces. Not just workspaces the user can view.
547+
// nolint:gocritic
548+
workspaces, err := api.Database.GetWorkspaces(dbauthz.AsSystemRestricted(ctx), database.GetWorkspacesParams{
546549
OwnerID: user.ID,
547550
})
548551
if err != nil {

coderd/users_test.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,43 @@ func TestDeleteUser(t *testing.T) {
378378
require.ErrorAs(t, err, &apiErr, "should be a coderd error")
379379
require.Equal(t, http.StatusForbidden, apiErr.StatusCode(), "should be forbidden")
380380
})
381+
t.Run("CountCheckIncludesAllWorkspaces", func(t *testing.T) {
382+
t.Parallel()
383+
client, _ := coderdtest.NewWithProvisionerCloser(t, nil)
384+
firstUser := coderdtest.CreateFirstUser(t, client)
385+
386+
// Create a target user who will own a workspace
387+
targetUserClient, targetUser := coderdtest.CreateAnotherUser(t, client, firstUser.OrganizationID)
388+
389+
// Create a User Admin who should not have permission to see the target user's workspace
390+
userAdminClient, userAdmin := coderdtest.CreateAnotherUser(t, client, firstUser.OrganizationID)
391+
392+
// Grant User Admin role to the userAdmin
393+
userAdmin, err := client.UpdateUserRoles(context.Background(), userAdmin.ID.String(), codersdk.UpdateRoles{
394+
Roles: []string{rbac.RoleUserAdmin().String()},
395+
})
396+
require.NoError(t, err)
397+
398+
// Create a template and workspace owned by the target user
399+
version := coderdtest.CreateTemplateVersion(t, client, firstUser.OrganizationID, nil)
400+
coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID)
401+
template := coderdtest.CreateTemplate(t, client, firstUser.OrganizationID, version.ID)
402+
_ = coderdtest.CreateWorkspace(t, targetUserClient, template.ID)
403+
404+
workspaces, err := userAdminClient.Workspaces(context.Background(), codersdk.WorkspaceFilter{
405+
Owner: targetUser.Username,
406+
})
407+
require.NoError(t, err)
408+
require.Len(t, workspaces.Workspaces, 0)
409+
410+
// Attempt to delete the target user - this should fail because the
411+
// user has a workspace not visible to the deleting user.
412+
err = userAdminClient.DeleteUser(context.Background(), targetUser.ID)
413+
var apiErr *codersdk.Error
414+
require.ErrorAs(t, err, &apiErr)
415+
require.Equal(t, http.StatusExpectationFailed, apiErr.StatusCode())
416+
require.Contains(t, apiErr.Message, "has workspaces")
417+
})
381418
}
382419

383420
func TestNotifyUserStatusChanged(t *testing.T) {

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