passbolt/passbolt_api · error · Cake\Http\Exception\ForbiddenException
You are not authorized to edit the role.
Error message
You are not authorized to edit the role.
What it means
ForbiddenException (HTTP 403) from assertCanEdit: a non-admin user included `role` or `role_id` in the edit payload. Role management is admin-only, so any self-edit that attempts to alter the role is rejected even when editing one's own record.
Solutions
- Remove `role`/`role_id` from the payload; send only the fields you intend to change.
- If a role change is genuinely needed, perform it as an admin account.
- Patch client code to strip server-managed fields (role, role_id) before sending updates.
Example fix
// before
await api.editUser(id, { ...user, profile: updatedProfile }); // includes role_id
// after
const { role, role_id, ...payload } = user;
await api.editUser(id, { ...payload, profile: updatedProfile }); Defensive patterns
Strategy: type-guard
Validate before calling
function stripRoleFields(data) { const { role, role_id, ...rest } = data; return rest; } Type guard
function hasRoleFields(data) { return 'role' in data || 'role_id' in data; }
if (hasRoleFields(payload) && session.role !== 'admin') payload = stripRoleFields(payload); Try / catch
try { await api.editUser(id, data); } catch (e) { if (e.code === 403 && /edit the role/.test(e.message)) { data = stripRoleFields(data); return api.editUser(id, data); } throw e; } Prevention
- Always send a minimal diff, never the full fetched entity.
- Strip server-managed fields (role, role_id) from update payloads in client code.
- Perform role changes through admin-only workflows.
When it happens
Trigger: PUT /users/{id}.json with `role_id` (or `role`) present in the body while authenticated as a non-admin — including a user editing themselves and echoing back the full entity including role_id.
Common situations: Clients that serialize the entire fetched user object (including role_id) back on update instead of sending only changed fields; scripts attempting self-promotion to admin.
Understand the failure class
Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.
Related errors
- You are not authorized to access that location.
- Access restricted to administrators.
- Only administrators can add new users.
- This operation is not allowed for this user.
- You are not authorized to access that location.
AI-assisted analysis of passbolt/passbolt_api@31c1bbc10f (2026-09-17).
Data as JSON: /api/errors/7033f2bb8a2b6ffa.
Report an issue: GitHub.
Appendix: source
Thrown at src/Controller/Users/UsersEditController.php:154
$this->success(__('The user has been updated successfully.'), $user);
}
/**
* Validate if the user is authorized to edit the data
*
* @param array $data user data
* @return void
* @throws \Cake\Http\Exception\ForbiddenException if the user is not admin or not editing themselves
* @throws \Cake\Http\Exception\ForbiddenException if the user is not admin and editing role
*/
protected function assertCanEdit(array $data): void
{
// Admin can edit all users, other users can only edit themselves
if ($this->User->role() !== Role::ADMIN && $data['id'] !== $this->User->id()) {
throw new ForbiddenException(__('You are not authorized to access that location.'));
}
if ($this->User->role() !== Role::ADMIN && (isset($data['role']) || isset($data['role_id']))) {
throw new ForbiddenException(__('You are not authorized to edit the role.'));
}
}
/**
* Validate the data coming from the request
*
* @param array $data user data
* @return void
* @throws \Cake\Http\Exception\BadRequestException if gpgkey is sent (v2 only)
* @throws \Cake\Http\Exception\BadRequestException if groups data is sent (v2 only)
* @throws \Cake\Http\Exception\BadRequestException if data is not provided or invalid
*/
protected function assertRequestData(array $data): void
{
if (empty($data) || count($data) < 2) {
throw new BadRequestException(__('Some user data should be provided.'));
}
if (isset($data['gpgkey'])) {View on GitHub (pinned to 31c1bbc10f)