passbolt/passbolt_api · error · ForbiddenException
An administrator user cannot be suspended via SCIM.
Error message
An administrator user cannot be suspended via SCIM.
What it means
UserScimResource::assertAdminSuspendAllowed() forbids deactivating (suspending) a user holding the administrator role via SCIM, unless the config flag `passbolt.plugins.scim.security.allowSuspendAdministrators` is enabled. This protects the last-resort admin accounts from being disabled by an IdP synchronization. Throws 403 ForbiddenException.
Solutions
- Remove the admin role from the user first (passbolt UI/CLI) so they are a normal user, then suspend via SCIM.
- If suspension of admins via SCIM is intended, set passbolt.plugins.scim.security.allowSuspendAdministrators to true in config.
- Exclude administrator accounts from the IdP's SCIM sync scope.
- Verify with `passbolt users` which users are admins before running bulk deactivations.
Example fix
// before (config/passbolt.php) //'scim' => ['security' => ['allowSuspendAdministrators' => false]] // after (only if policy allows admins to be suspended by the IdP) 'scim' => ['security' => ['allowSuspendAdministrators' => true]]
Defensive patterns
Strategy: try-catch
Validate before calling
// before deactivating via SCIM
const user = await scim.getUser(id);
const roles = user.roles || (await passbolt.getUserRoles(id));
if (roles.includes('admin')) throw new Error('target is an admin; SCIM suspend forbidden'); Try / catch
try { await scim.patchUser(adminId, [{op:'replace',path:'active',value:false}]); } catch (e) { if (e.status === 403 && /administrator/.test(e.message)) { /* demote admin first or skip */ } else throw e; } Prevention
- Exclude admin accounts from IdP deactivation syncs
- Check the user's role before sending active=false
- Only enable allowSuspendAdministrators if policy explicitly permits it
When it happens
Trigger: PATCH /scim/v2/Users/<id-of-admin> with {"op":"replace","path":"active","value":false} (or add/remove with active false) when the target user is an admin and the allowSuspendAdministrators config is false/unset.
Common situations: An IdP deactivates a leaving employee who also happened to be a passbolt admin; a bulk SCIM sync disables all users not present in the IdP, including admins; operators are unaware the opt-in config flag exists.
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
- An administrator user cannot be deleted via SCIM.
- Only administrators are allowed to create/update user…
- Only administrators can add new users.
- SCIM settings endpoints are disabled.
- You are not allowed to access this location.
AI-assisted analysis of passbolt/passbolt_api@31c1bbc10f (2026-09-17).
Data as JSON: /api/errors/d6a9e91e16d24d3a.
Report an issue: GitHub.
Appendix: source
Thrown at plugins/PassboltEe/Scim/src/Utility/Resource/UserScimResource.php:711
protected function assertAdminSuspendAllowed(array $userPatchData): void
{
// Only check when user is being disabled (disabled field is being set to a non-null value)
if (empty($userPatchData['disabled'])) {
return;
}
// If already disabled, no change — skip guard
if ($this->userEntity->disabled) {
return;
}
// Check if the config allows suspending administrators
if (Configure::read('passbolt.plugins.scim.security.allowSuspendAdministrators')) {
return;
}
if ($this->isUserAdmin()) {
throw new ForbiddenException(__('An administrator user cannot be suspended via SCIM.'));
}
}
/**
* Assert that the user being deleted is not an administrator.
*
* @return void
* @throws \Cake\Http\Exception\ForbiddenException If trying to delete an admin and the config flag is not set.
*/
protected function assertAdminDeleteAllowed(): void
{
$allowed = Configure::read('passbolt.plugins.scim.security.allowDeleteAdministrators');
// Fallback on allowSuspendAdministrators configuration
if (!is_bool($allowed)) {
$allowed = Configure::read('passbolt.plugins.scim.security.allowSuspendAdministrators');
}
if ($allowed) {View on GitHub (pinned to 31c1bbc10f)