{"record":{"id":"cf0ace1af5d64ce2","repo":"passbolt/passbolt_api","slug":"only-administrators-can-view-reports","errorCode":null,"errorMessage":"Only administrators can view reports.","messagePattern":"Only administrators can view reports\\.","errorType":"http","errorClass":"ForbiddenException","httpStatus":403,"severity":"warning","filePath":"plugins/PassboltCe/Reports/src/Controller/Reports/ReportsViewController.php","lineNumber":66,"sourceCode":"     * @throws \\Exception\n     */\n    public function initialize(): void\n    {\n        parent::initialize();\n        $this->reportViewService = new ReportViewService();\n        $this->Users = $this->fetchTable('Users');\n    }\n\n    /**\n     * @param string $reportSlug Slug of the report to retrieve\n     * @throws \\Exception\n     * @throws \\Cake\\Http\\Exception\\BadRequestException If the requested report does not exist\n     * @return void\n     */\n    public function view(string $reportSlug)\n    {\n        if ($this->User->role() !== Role::ADMIN) {\n            throw new ForbiddenException(__('Only administrators can view reports.'));\n        }\n\n        // Retrieve the report argument passed as url parameters.\n        $arguments = func_get_args();\n        $reportArguments = array_slice($arguments, 1);\n\n        try {\n            $report = $this->reportViewService->getReport($reportSlug, $reportArguments);\n        } catch (InvalidArgumentException $exception) {\n            throw new BadRequestException(__('The requested report `{0}` does not exist.', $reportSlug));\n        }\n\n        $options = $this->formatRequestData($report->getSupportedOptions());\n\n        $creator = $this->Users->get($this->User->id(), contain: ['Profiles']);\n\n        $report\n            ->setOptions($options)","sourceCodeStart":48,"sourceCodeEnd":84,"githubUrl":"https://github.com/passbolt/passbolt_api/blob/31c1bbc10f32808a607fa9bd81891e898779c0bc/plugins/PassboltCe/Reports/src/Controller/Reports/ReportsViewController.php#L48-L84","documentation":"ReportsViewController::view() restricts the reports HTML/API endpoint to administrators. If the authenticated user's role is not Role::ADMIN, it throws Cake\\Http\\Exception\\ForbiddenException with 'Only administrators can view reports.' This is an intentional authorization gate, not a bug.","triggerScenarios":"Any authenticated non-admin user (or guest/anonymous request resolving to role 'guest') requests a report route handled by ReportsViewController::view(), e.g. GET /reports/<slug> while logged in as a regular user.","commonSituations":"A regular user bookmarking or sharing an admin-only report URL; role misconfiguration where a user expected to be an admin actually has role 'user'; testing the endpoint without admin credentials; proxies forwarding unauthenticated requests.","solutions":["Log in as (or use credentials of) an administrator before requesting the report.","Check the user's role in the database (users.role_id -> roles.name) and promote to admin if they should have access.","Catch Cake\\Http\\Exception\\ForbiddenException (HTTP 403) in the client and show an access-denied message.","If programmatic access is needed, use an admin service account rather than weakening the controller check."],"exampleFix":"// client-side\ntry { await api.get('/reports/workspace.json'); }\ncatch (e) { if (e.response?.status === 403) showAccessDenied(); }","handlingStrategy":"try-catch","validationCode":"$role = $this->User->role(); if ($role !== Role::ADMIN) { /* do not call the endpoint, or show access denied UI */ }","typeGuard":null,"tryCatchPattern":"try { $res = $client->get('/reports/' . $slug); } catch (\\Cake\\Http\\Exception\\ForbiddenException $e) { /* HTTP 403: render access denied */ }","preventionTips":["Check the logged-in user role before linking to report pages","Hide admin-only report links in the UI for non-admins","Use an admin service account for automated report retrieval","Verify role assignment (users.role_id) when users report missing access"],"tags":["php","authorization","forbidden","rbac"],"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"}