{"record":{"id":"4726f640a3794873","repo":"passbolt/passbolt_api","slug":"an-administrator-user-cannot-be-deleted-via-scim","errorCode":null,"errorMessage":"An administrator user cannot be deleted via SCIM.","messagePattern":"An administrator user cannot be deleted via SCIM\\.","errorType":"http","errorClass":"ForbiddenException","httpStatus":403,"severity":"error","filePath":"plugins/PassboltEe/Scim/src/Utility/Resource/UserScimResource.php","lineNumber":734,"sourceCode":"     * Assert that the user being deleted is not an administrator.\n     *\n     * @return void\n     * @throws \\Cake\\Http\\Exception\\ForbiddenException If trying to delete an admin and the config flag is not set.\n     */\n    protected function assertAdminDeleteAllowed(): void\n    {\n        $allowed = Configure::read('passbolt.plugins.scim.security.allowDeleteAdministrators');\n        // Fallback on allowSuspendAdministrators configuration\n        if (!is_bool($allowed)) {\n            $allowed = Configure::read('passbolt.plugins.scim.security.allowSuspendAdministrators');\n        }\n\n        if ($allowed) {\n            return;\n        }\n\n        if ($this->isUserAdmin()) {\n            throw new ForbiddenException(__('An administrator user cannot be deleted via SCIM.'));\n        }\n    }\n\n    /**\n     * Whether the loaded user holds the administrator role.\n     *\n     * @return bool\n     */\n    protected function isUserAdmin(): bool\n    {\n        $query = $this->Users\n            ->unhydratedFind()\n            ->select(['existing' => 1])\n            ->contain(['Roles'])\n            ->where([\n                $this->Users->aliasField('id') => $this->userEntity->id,\n                'Roles.name' => Role::ADMIN,\n            ])","sourceCodeStart":716,"sourceCodeEnd":752,"githubUrl":"https://github.com/passbolt/passbolt_api/blob/31c1bbc10f32808a607fa9bd81891e898779c0bc/plugins/PassboltEe/Scim/src/Utility/Resource/UserScimResource.php#L716-L752","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Demote the user from administrator (passbolt UI/CLI) before deleting via SCIM.","Exclude admin accounts from SCIM delete/deprovisioning flows on the IdP side.","If policy allows, enable the admin-deletion allowance in the SCIM security configuration.","Prefer suspending the user (active=false) instead of a hard delete."],"exampleFix":"// before: DELETE /scim/v2/Users/<admin-id>  => 403\n// after: demote first, then delete\n// bin/cake passbolt users remove_admin_role <user-id>\n// DELETE /scim/v2/Users/<user-id>","handlingStrategy":"try-catch","validationCode":"const user = await scim.getUser(id);\nif ((await passbolt.getUserRoles(id)).includes('admin')) throw new Error('SCIM delete forbidden for admins');","typeGuard":null,"tryCatchPattern":"try { await scim.deleteUser(id); } catch (e) { if (e.status === 403 && /administrator/.test(e.message)) { /* demote or skip this user */ } else throw e; }","preventionTips":["Prefer active=false suspension over hard DELETE","Keep admins out of the IdP's SCIM-provisioned group","Audit SCIM DELETE webhook sources before enabling cascading deletes"],"tags":["scim","forbidden","admin","delete"],"backgroundTag":"permission-denied","analyzedSha":"31c1bbc10f32808a607fa9bd81891e898779c0bc","analyzedAt":"2026-09-17T00:04:38.960Z","contentChangedAt":"2026-09-17T00:04:38.960Z","schemaVersion":2},"datasetVersion":"2026-09-21T04:17:39.646Z"}