Skip to content

bug: User Admin role cannot reset another user's password (HTTP 500) #25873

@uzair-coder07

Description

@uzair-coder07

Description

A user with the built-in User Admin (user-admin) site role cannot reset another user's
password. Every attempt returns HTTP 500 and the change is rolled back, so the password is
never updated. Only the owner role can reset passwords. This breaks the primary purpose of
the User Admin role on password-login deployments and affects the dashboard's admin
"Reset password" action, which uses the same endpoint.

Affected versions

Confirmed by source inspection on main, the rc, and the latest patch of every active release
line; live-reproduced on v2.32.4. The defect is structural (the role has never had
ResourceApiKey permissions while the password-reset transaction deletes API keys, a step
added in #4659), so earlier patch releases within each line are affected as well.

  • main (dev) — affected
  • v2.34.0-rc.0 — affected
  • v2.33.6 (latest stable) — affected
  • v2.32.4 — affected (reproduced live)
  • v2.31.14 — affected
  • v2.30.9 — affected
  • v2.29.15 — affected
  • v2.24.6 — affected

Environment

  • Login type: password

Steps to reproduce

  1. As an owner, create two password users and grant one the user-admin site role.
  2. Obtain a session token for the user-admin user.
  3. As the user-admin, call:
    PUT /api/v2/users/{target_id}/password
    Coder-Session-Token: <user-admin token>
    {"password":"NewPass-123!"}
    

Actual behavior

HTTP 500
{"message":"Internal error updating user's password.",
 "detail":"execute transaction: delete api keys by user ID: unauthorized: rbac: forbidden"}
  • Same result whether the target is a plain member or an owner.
  • The password is not changed (transaction rolls back; login with the new password fails).
  • An internal RBAC error string is leaked to the client in detail.

Expected behavior

A User Admin can reset other users' passwords (consistent with the role's purpose and the UI
"Reset password" action), or, if intentionally disallowed, the API returns a clean 403
instead of a 500.

Control / scoping

  • The identical request as owner returns HTTP 204 and the new password works.
  • Other user-admin operations work fine: suspend (200), activate (200), delete user (200).
    The failure is specific to the password-reset flow.
  • Only affects targets with login_type: password. For OIDC/GitHub/other login types the
    endpoint returns a clean 400 ("Users without password login type cannot change their
    password") before the buggy transaction, so they are unaffected.

Root cause

The dashboard "Reset password" action and the API both call PUT /api/v2/users/{user}/password,
handled by putUserPassword in coderd/users.go. After authorizing the request, the handler
updates the password and deletes the target user's API keys in a single transaction (the
API-key wipe was added in #4659, "delete all sessions on password change"):

// Handler gate passes for User Admin: it has ActionUpdate/ActionUpdatePersonal on ResourceUser
if !api.Authorize(r, policy.ActionUpdatePersonal, user) { httpapi.ResourceNotFound(...); return }
...
// Non-password users are rejected here, BEFORE the transaction below:
if user.LoginType != database.LoginTypePassword { http 400; return }
...
api.Database.InTx(func(tx database.Store) error {
    tx.UpdateUserHashedPassword(...)   // (1) succeeds for User Admin
    tx.DeleteAPIKeysByUserID(...)      // (2) FORBIDDEN for User Admin
})

These two calls require different permissions in coderd/database/dbauthz/dbauthz.go:

  • UpdateUserHashedPasswordActionUpdatePersonal or ActionUpdate on ResourceUser.
  • DeleteAPIKeysByUserIDActionDelete on ResourceApiKey.WithOwner(targetUserID).

The user-admin role (coderd/rbac/roles.go) grants full ResourceUser access but no
ResourceApiKey permissions at all
:

userAdminRole := Role{
    Site: Permissions(map[string][]policy.Action{
        ResourceAssignRole.Type:         {Assign, Unassign, Read},
        ResourceAssignOrgRole.Type:      {Assign, Unassign, Read},
        ResourceUser.Type:               {Create, Read, Update, Delete, UpdatePersonal, ReadPersonal},
        ResourceGroup.Type:              {Create, Read, Update, Delete},
        ResourceGroupMember.Type:        {Read},
        ResourceOrganization.Type:       {Read},
        ResourceOrganizationMember.Type: {Create, Read, Update, Delete},
        ResourceIdpsyncSettings.Type:    {Read, Update},
        // No ResourceApiKey permissions!
    }),
}

Resulting flow when a User Admin resets another (password-login) user's password:

  1. UpdateUserHashedPassword succeeds (User Admin has ActionUpdate on ResourceUser).
  2. DeleteAPIKeysByUserID fails with unauthorized: rbac: forbidden.
  3. The enclosing transaction rolls back, so the password is left unchanged.
  4. The handler returns HTTP 500 "Internal error updating user's password.".

Only the owner role can reset other users' passwords today, because Owner has wildcard
permissions that include ResourceApiKey.


Filed by Coder Agents on behalf of @uzair-coder07.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions