{"record":{"id":"7022af204bf7f3db","repo":"passbolt/passbolt_api","slug":"only-administrators-can-delete-the-subscription","errorCode":null,"errorMessage":"Only administrators can delete the subscription.","messagePattern":"Only administrators can delete the subscription\\.","errorType":"http","errorClass":"ForbiddenException","httpStatus":403,"severity":"error","filePath":"plugins/PassboltEe/Subscription/src/Service/Subscriptions/SubscriptionKeyDeleteService.php","lineNumber":43,"sourceCode":" * Removes the persisted subscription key from `organization_settings`.\n *\n * Intended to be called from EditionDowngradeService as the first step of\n * the in-product downgrade flow. Idempotent: succeeds without error when\n * no subscription row exists.\n */\nclass SubscriptionKeyDeleteService\n{\n    use LocatorAwareTrait;\n\n    /**\n     * @param \\App\\Utility\\UserAccessControl $uac User access control.\n     * @return void\n     * @throws \\Cake\\Http\\Exception\\ForbiddenException When the UAC is not admin.\n     */\n    public function delete(UserAccessControl $uac): void\n    {\n        if (!$uac->isAdmin()) {\n            throw new ForbiddenException(__('Only administrators can delete the subscription.'));\n        }\n\n        /** @var \\Passbolt\\Subscription\\Model\\Table\\SubscriptionsTable $Subscriptions */\n        $Subscriptions = $this->fetchTable('Passbolt/Subscription.Subscriptions');\n        $row = $Subscriptions->find()->first();\n        if ($row instanceof EntityInterface) {\n            $Subscriptions->deleteOrFail($row);\n        }\n    }\n}\n","sourceCodeStart":25,"sourceCodeEnd":54,"githubUrl":"https://github.com/passbolt/passbolt_api/blob/31c1bbc10f32808a607fa9bd81891e898779c0bc/plugins/PassboltEe/Subscription/src/Service/Subscriptions/SubscriptionKeyDeleteService.php#L25-L54","documentation":"SubscriptionKeyDeleteService::delete() throws ForbiddenException when the provided UserAccessControl does not belong to an administrator, refusing to delete the subscription key. Only admins may remove the subscription record from the server.","triggerScenarios":"Calling delete() (backing the DELETE /subscription endpoint) with a UAC of a non-admin user role — !uac->isAdmin() — regardless of the subscription's existence.","commonSituations":"A non-admin user (or API client with user role credentials) attempting to remove the license; automation running with service-account credentials that lack the admin role; misconfigured role assignment for the executing user.","solutions":["Authenticate as or impersonate an admin user when calling DELETE /subscription or the service.","Check the user's role: `SELECT role_id FROM users WHERE id = '<id>';` must be the admin role id.","Update the service account / automation user to hold the admin role if deletion via automation is intended."],"exampleFix":"// before: delete with a plain user UAC\n$uac = new UserAccessControl(ROLE_USER, $userId);\n$this->SubscriptionKeyDeleteService->delete($uac);\n// after\n$uac = new UserAccessControl(ROLE_ADMIN, $adminUserId);\n$this->SubscriptionKeyDeleteService->delete($uac);","handlingStrategy":"validation","validationCode":"if (!$uac->isAdmin()) {\n    throw new \\Cake\\Http\\Exception\\ForbiddenException('Subscription deletion requires admin');\n}\n$service->delete($uac);","typeGuard":null,"tryCatchPattern":"try {\n    $service->delete($uac);\n} catch (\\Cake\\Http\\Exception\\ForbiddenException $e) {\n    // surface 403: only administrators can delete the subscription\n}","preventionTips":["Restrict DELETE /subscription usage to admin credentials","Audit automation/service-account users for the admin role","Never expose subscription management endpoints to non-admin UIs"],"tags":["authorization","permissions","subscription-key","php"],"backgroundTag":"insufficient-permissions","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"}