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
Steps to reproduce
- As an
owner, create two password users and grant one the user-admin site role.
- Obtain a session token for the
user-admin user.
- 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:
UpdateUserHashedPassword → ActionUpdatePersonal or ActionUpdate on ResourceUser.
DeleteAPIKeysByUserID → ActionDelete 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:
UpdateUserHashedPassword succeeds (User Admin has ActionUpdate on ResourceUser).
DeleteAPIKeysByUserID fails with unauthorized: rbac: forbidden.
- The enclosing transaction rolls back, so the password is left unchanged.
- 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.
Description
A user with the built-in User Admin (
user-admin) site role cannot reset another user'spassword. Every attempt returns HTTP 500 and the change is rolled back, so the password is
never updated. Only the
ownerrole can reset passwords. This breaks the primary purpose ofthe 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 releaseline; live-reproduced on
v2.32.4. The defect is structural (the role has never hadResourceApiKeypermissions while the password-reset transaction deletes API keys, a stepadded in #4659), so earlier patch releases within each line are affected as well.
main(dev) — affectedv2.34.0-rc.0— affectedv2.33.6(latest stable) — affectedv2.32.4— affected (reproduced live)v2.31.14— affectedv2.30.9— affectedv2.29.15— affectedv2.24.6— affectedEnvironment
Steps to reproduce
owner, create two password users and grant one theuser-adminsite role.user-adminuser.user-admin, call:Actual behavior
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
403instead of a
500.Control / scoping
ownerreturnsHTTP 204and the new password works.user-adminoperations work fine: suspend (200), activate (200), delete user (200).The failure is specific to the password-reset flow.
login_type: password. For OIDC/GitHub/other login types theendpoint returns a clean
400("Users without password login type cannot change theirpassword") 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
putUserPasswordincoderd/users.go. After authorizing the request, the handlerupdates 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"):
These two calls require different permissions in
coderd/database/dbauthz/dbauthz.go:UpdateUserHashedPassword→ActionUpdatePersonalorActionUpdateonResourceUser.DeleteAPIKeysByUserID→ActionDeleteonResourceApiKey.WithOwner(targetUserID).The
user-adminrole (coderd/rbac/roles.go) grants fullResourceUseraccess but noResourceApiKeypermissions at all:Resulting flow when a User Admin resets another (password-login) user's password:
UpdateUserHashedPasswordsucceeds (User Admin hasActionUpdateonResourceUser).DeleteAPIKeysByUserIDfails withunauthorized: rbac: forbidden.HTTP 500"Internal error updating user's password.".Only the
ownerrole can reset other users' passwords today, because Owner has wildcardpermissions that include
ResourceApiKey.Filed by Coder Agents on behalf of @uzair-coder07.