passbolt/passbolt_api · error · ForbiddenException

An administrator user cannot be deleted via SCIM.

Error message

An administrator user cannot be deleted via SCIM.

What it means

UserScimResource::assertAdminDeleteAllowed() (called from delete()) forbids permanently deleting a user holding the administrator role via SCIM, subject to the same allowSuspendAdministrators-style config gate checked before it. This prevents an IdP sync from destroying admin accounts. Throws 403 ForbiddenException.

Solutions

  1. Demote the user from administrator (passbolt UI/CLI) before deleting via SCIM.
  2. Exclude admin accounts from SCIM delete/deprovisioning flows on the IdP side.
  3. If policy allows, enable the admin-deletion allowance in the SCIM security configuration.
  4. Prefer suspending the user (active=false) instead of a hard delete.

Example fix

// before: DELETE /scim/v2/Users/<admin-id>  => 403
// after: demote first, then delete
// bin/cake passbolt users remove_admin_role <user-id>
// DELETE /scim/v2/Users/<user-id>
Defensive patterns

Strategy: try-catch

Validate before calling

const user = await scim.getUser(id);
if ((await passbolt.getUserRoles(id)).includes('admin')) throw new Error('SCIM delete forbidden for admins');

Try / catch

try { await scim.deleteUser(id); } catch (e) { if (e.status === 403 && /administrator/.test(e.message)) { /* demote or skip this user */ } else throw e; }

Prevention

When it happens

Trigger: DELETE /scim/v2/Users/<id-of-admin> (or a PATCH with the passive-delete pattern) when the loaded user has the administrator role and admin deletion is not allowed by configuration.

Common situations: The IdP user is deleted, triggering a cascading SCIM DELETE for a passbolt admin; a cleanup script purges stale SCIM users including admins; operators assume SCIM delete only deactivates rather than hard-deletes.

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


AI-assisted analysis of passbolt/passbolt_api@31c1bbc10f (2026-09-17). Data as JSON: /api/errors/4726f640a3794873. Report an issue: GitHub.

Appendix: source

Thrown at plugins/PassboltEe/Scim/src/Utility/Resource/UserScimResource.php:734

     * 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) {
            return;
        }

        if ($this->isUserAdmin()) {
            throw new ForbiddenException(__('An administrator user cannot be deleted via SCIM.'));
        }
    }

    /**
     * Whether the loaded user holds the administrator role.
     *
     * @return bool
     */
    protected function isUserAdmin(): bool
    {
        $query = $this->Users
            ->unhydratedFind()
            ->select(['existing' => 1])
            ->contain(['Roles'])
            ->where([
                $this->Users->aliasField('id') => $this->userEntity->id,
                'Roles.name' => Role::ADMIN,
            ])

View on GitHub (pinned to 31c1bbc10f)