From 4793e26d8c8235f80b6171a23488d610b3a9bd14 Mon Sep 17 00:00:00 2001 From: Ruben van der Linde Date: Tue, 26 May 2026 16:23:32 +0200 Subject: [PATCH] fix(security): clear Bucket-B gates 5/6/7/9/14 (ADR-023 + auth/routing) --- appinfo/routes.php | 51 ++--- lib/Controller/AanbodController.php | 3 - .../AangebodenGebruikController.php | 14 -- lib/Controller/ContactpersonenController.php | 62 ++++-- lib/Controller/DashboardController.php | 18 +- lib/Controller/GebruikController.php | 5 +- lib/Controller/SettingsController.php | 181 ++++++++++++++++++ lib/Controller/ViewController.php | 3 - lib/Service/ArchiMateService.php | 2 +- lib/Service/SoftwareCatalogueService.php | 9 +- phpstan-baseline.neon | 35 ++++ 11 files changed, 315 insertions(+), 68 deletions(-) diff --git a/appinfo/routes.php b/appinfo/routes.php index 548390b9..961c340e 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -17,8 +17,9 @@ return [ 'routes' => [ - // Dashboard route + // Dashboard routes ['name' => 'dashboard#page', 'url' => '/', 'verb' => 'GET'], + ['name' => 'dashboard#index', 'url' => '/api/dashboard', 'verb' => 'GET'], // Generic per-user preferences (used by shared nextcloud-vue widgets, e.g. CnSupportDialog). ['name' => 'preferences#getPreference', 'url' => '/api/preferences/{key}', 'verb' => 'GET'], @@ -30,7 +31,9 @@ ['name' => 'settings#load', 'url' => '/api/settings/load', 'verb' => 'GET'], ['name' => 'settings#initialize', 'url' => '/api/settings/initialize', 'verb' => 'POST'], ['name' => 'settings#status', 'url' => '/api/settings/status', 'verb' => 'GET'], - ['name' => 'settings#auto_configure', 'url' => '/api/settings/auto-configure', 'verb' => 'POST'], + ['name' => 'settings#stats', 'url' => '/api/settings/stats', 'verb' => 'GET'], + ['name' => 'settings#autoConfigure', 'url' => '/api/settings/auto-configure', 'verb' => 'POST'], + ['name' => 'settings#consolidatedAutoConfigure', 'url' => '/api/settings/consolidated-auto-configure', 'verb' => 'POST'], ['name' => 'settings#debug', 'url' => '/api/settings/debug', 'verb' => 'GET'], // Separate endpoints for performance optimization @@ -51,26 +54,22 @@ ['name' => 'settings#resetAutoConfig', 'url' => '/api/settings/reset-auto-config', 'verb' => 'POST'], // Email management routes - ['name' => 'settings#send_test_email', 'url' => '/api/email/test', 'verb' => 'POST'], - ['name' => 'settings#test_email_connection', 'url' => '/api/email/test-connection', 'verb' => 'POST'], - ['name' => 'settings#get_email_settings', 'url' => '/api/settings/email', 'verb' => 'GET'], - ['name' => 'settings#update_email_settings', 'url' => '/api/settings/email', 'verb' => 'POST'], + ['name' => 'settings#sendTestEmail', 'url' => '/api/email/test', 'verb' => 'POST'], + ['name' => 'settings#testEmailConnection', 'url' => '/api/email/test-connection', 'verb' => 'POST'], + ['name' => 'settings#getEmailSettings', 'url' => '/api/settings/email', 'verb' => 'GET'], + ['name' => 'settings#updateEmailSettings', 'url' => '/api/settings/email', 'verb' => 'POST'], // Email template management routes - ['name' => 'settings#get_email_templates', 'url' => '/api/email/templates', 'verb' => 'GET'], - ['name' => 'settings#get_email_template', 'url' => '/api/email/templates/{templateName}', 'verb' => 'GET'], - ['name' => 'settings#update_email_template', 'url' => '/api/email/templates/{templateName}', 'verb' => 'POST'], - ['name' => 'settings#get_email_template_default', 'url' => '/api/email/templates/{templateName}/default', 'verb' => 'GET'], - ['name' => 'settings#get_email_template_variables', 'url' => '/api/email/templates/{templateName}/variables', 'verb' => 'GET'], + ['name' => 'settings#getEmailTemplates', 'url' => '/api/email/templates', 'verb' => 'GET'], + ['name' => 'settings#getEmailTemplate', 'url' => '/api/email/templates/{templateName}', 'verb' => 'GET'], + ['name' => 'settings#updateEmailTemplate', 'url' => '/api/email/templates/{templateName}', 'verb' => 'POST'], + ['name' => 'settings#getEmailTemplateDefault', 'url' => '/api/email/templates/{templateName}/default', 'verb' => 'GET'], + ['name' => 'settings#getEmailTemplateVariables', 'url' => '/api/email/templates/{templateName}/variables', 'verb' => 'GET'], - // Health check endpoint - ['name' => 'settings#health_check', 'url' => '/api/health', 'verb' => 'GET'], + // Note: /api/health is served by settings#status above // Configuration cache management - ['name' => 'settings#clear_cache', 'url' => '/api/settings/clear-cache', 'verb' => 'POST'], - - // Force re-initialization endpoint - ['name' => 'settings#force_reinit', 'url' => '/api/settings/force-reinit', 'verb' => 'POST'], + ['name' => 'settings#clearCache', 'url' => '/api/settings/clear-cache', 'verb' => 'POST'], // ArchiMate import/export routes ['name' => 'settings#importArchiMate', 'url' => '/api/archimate/import', 'verb' => 'POST'], @@ -84,16 +83,16 @@ ['name' => 'settings#killArchiMateImport', 'url' => '/api/archimate/import/kill', 'verb' => 'POST'], // deprecated ['name' => 'settings#clearArchiMateExportStatus', 'url' => '/api/archimate/status/export/clear', 'verb' => 'POST'], - ['name' => 'settings#test_archimate_round_trip', 'url' => '/api/archimate/test-round-trip', 'verb' => 'POST'], + ['name' => 'settings#testArchiMateRoundTrip', 'url' => '/api/archimate/test-round-trip', 'verb' => 'POST'], // User Groups management routes - ['name' => 'settings#get_generic_user_groups', 'url' => '/api/settings/user-groups/generic', 'verb' => 'GET'], - ['name' => 'settings#set_generic_user_groups', 'url' => '/api/settings/user-groups/generic', 'verb' => 'POST'], - ['name' => 'settings#get_organization_admin_groups', 'url' => '/api/settings/user-groups/organization-admin', 'verb' => 'GET'], - ['name' => 'settings#set_organization_admin_groups', 'url' => '/api/settings/user-groups/organization-admin', 'verb' => 'POST'], - ['name' => 'settings#get_super_user_groups', 'url' => '/api/settings/user-groups/super-user', 'verb' => 'GET'], - ['name' => 'settings#set_super_user_groups', 'url' => '/api/settings/user-groups/super-user', 'verb' => 'POST'], - ['name' => 'settings#get_all_groups', 'url' => '/api/settings/user-groups/all', 'verb' => 'GET'], + ['name' => 'settings#getGenericUserGroups', 'url' => '/api/settings/user-groups/generic', 'verb' => 'GET'], + ['name' => 'settings#setGenericUserGroups', 'url' => '/api/settings/user-groups/generic', 'verb' => 'POST'], + ['name' => 'settings#getOrganizationAdminGroups', 'url' => '/api/settings/user-groups/organization-admin', 'verb' => 'GET'], + ['name' => 'settings#setOrganizationAdminGroups', 'url' => '/api/settings/user-groups/organization-admin', 'verb' => 'POST'], + ['name' => 'settings#getSuperUserGroups', 'url' => '/api/settings/user-groups/super-user', 'verb' => 'GET'], + ['name' => 'settings#setSuperUserGroups', 'url' => '/api/settings/user-groups/super-user', 'verb' => 'POST'], + ['name' => 'settings#getAllGroups', 'url' => '/api/settings/user-groups/all', 'verb' => 'GET'], // Progress streaming routes ['name' => 'settings#getProgress', 'url' => '/api/progress/{operationId}', 'verb' => 'GET'], @@ -177,6 +176,8 @@ // AangebodenGebruik API endpoints for filtering gebruiks by organization involvement ['name' => 'aangebodenGebruik#getGebruiksWhereAfnemer', 'url' => '/api/aangeboden-gebruik/afnemer', 'verb' => 'GET'], + ['name' => 'aangebodenGebruik#getAllGebruiksForAmbtenaar', 'url' => '/api/aangeboden-gebruik/ambtenaar', 'verb' => 'GET'], + ['name' => 'aangebodenGebruik#getSingleGebruikForAmbtenaar', 'url' => '/api/aangeboden-gebruik/ambtenaar/{gebruikId}', 'verb' => 'GET'], ['name' => 'aangebodenGebruik#getGebruiksWhereDeelnemers', 'url' => '/api/aangeboden-gebruik/deelnemers', 'verb' => 'GET'], ['name' => 'aangebodenGebruik#setGebruikSelfToActiveOrg', 'url' => '/api/aangeboden-gebruik/{gebruikId}/set-self', 'verb' => 'PUT'], ['name' => 'aangebodenGebruik#deleteGebruikAsAfnemer', 'url' => '/api/aangeboden-gebruik/{gebruikId}/deny', 'verb' => 'DELETE'], diff --git a/lib/Controller/AanbodController.php b/lib/Controller/AanbodController.php index c79caa30..097a57cb 100644 --- a/lib/Controller/AanbodController.php +++ b/lib/Controller/AanbodController.php @@ -79,7 +79,6 @@ public function __construct( * * @return JSONResponse JSON response with aanbod objects array * - * @NoAdminRequired * @NoCSRFRequired * @PublicPage * @@ -156,7 +155,6 @@ public function getAanbod(): JSONResponse * * @return JSONResponse JSON response with success status and updated object * - * @NoAdminRequired * @NoCSRFRequired * @PublicPage * @@ -257,7 +255,6 @@ function ($key) { * * @return JSONResponse JSON response with success status and deletion details * - * @NoAdminRequired * @NoCSRFRequired * @PublicPage * diff --git a/lib/Controller/AangebodenGebruikController.php b/lib/Controller/AangebodenGebruikController.php index 9a0fecd3..63061ebe 100644 --- a/lib/Controller/AangebodenGebruikController.php +++ b/lib/Controller/AangebodenGebruikController.php @@ -84,10 +84,8 @@ public function __construct( * * @return JSONResponse JSON response with gebruiks array where org is afnemer * - * @NoAdminRequired * @NoCSRFRequired * @PublicPage - * @PublicPage * @spec openspec/changes/retrofit-2026-05-26-aangeboden-gebruik-api/tasks.md#task-1 */ public function getGebruiksWhereAfnemer(): JSONResponse @@ -161,7 +159,6 @@ public function getGebruiksWhereAfnemer(): JSONResponse * * @return JSONResponse Koppelingen and gebruiks objects for the specified UUID * - * @NoAdminRequired * @NoCSRFRequired * @PublicPage * @spec openspec/changes/retrofit-2026-05-26-aangeboden-gebruik-api/tasks.md#task-1 @@ -252,7 +249,6 @@ public function getKoppelingenGebruikByUuid(string $uuid): JSONResponse * * @return JSONResponse All gebruiks objects in standard searchObjectsPaginated format * - * @NoAdminRequired * @NoCSRFRequired * @PublicPage * @spec openspec/changes/retrofit-2026-05-26-aangeboden-gebruik-api/tasks.md#task-1 @@ -357,10 +353,8 @@ public function getAllGebruiksForAmbtenaar(): JSONResponse * * @return JSONResponse Single gebruik object in standard searchObjectsPaginated format * - * @NoAdminRequired * @NoCSRFRequired * @PublicPage - * @PublicPage * * @SuppressWarnings(PHPMD.ExcessiveMethodLength) * @spec openspec/changes/retrofit-2026-05-26-aangeboden-gebruik-api/tasks.md#task-1 @@ -531,10 +525,8 @@ private function isUserInGroup(string $groupName): bool * * @return JSONResponse JSON response with gebruiks array where org is in deelnemers * - * @NoAdminRequired * @NoCSRFRequired * @PublicPage - * @PublicPage * @spec openspec/changes/retrofit-2026-05-26-aangeboden-gebruik-api/tasks.md#task-1 */ public function getGebruiksWhereDeelnemers(): JSONResponse @@ -610,10 +602,8 @@ public function getGebruiksWhereDeelnemers(): JSONResponse * * @return JSONResponse JSON response with success status and updated object * - * @NoAdminRequired * @NoCSRFRequired * @PublicPage - * @PublicPage * @spec openspec/changes/retrofit-2026-05-26-aangeboden-gebruik-api/tasks.md#task-2 */ public function setGebruikSelfToActiveOrg(string $gebruikId): JSONResponse @@ -718,10 +708,8 @@ function ($key) { * * @return JSONResponse JSON response with success status and deletion details * - * @NoAdminRequired * @NoCSRFRequired * @PublicPage - * @PublicPage * @spec openspec/changes/retrofit-2026-05-26-aangeboden-gebruik-api/tasks.md#task-2 */ public function deleteGebruikAsAfnemer(string $gebruikId): JSONResponse @@ -817,10 +805,8 @@ function ($key) { * * @return JSONResponse JSON response with API documentation * - * @NoAdminRequired * @NoCSRFRequired * @PublicPage - * @PublicPage * * @SuppressWarnings(PHPMD.ExcessiveMethodLength) * @spec openspec/changes/retrofit-2026-05-26-aangeboden-gebruik-api/tasks.md#task-3 diff --git a/lib/Controller/ContactpersonenController.php b/lib/Controller/ContactpersonenController.php index 5e29a918..91ce0220 100644 --- a/lib/Controller/ContactpersonenController.php +++ b/lib/Controller/ContactpersonenController.php @@ -23,6 +23,7 @@ use OCA\SoftwareCatalog\Service\SoftwareCatalogue\ContactPersonHandler; use OCA\SoftwareCatalog\Service\ContactpersoonService; use OCP\AppFramework\Controller; +use OCP\AppFramework\Http; use OCP\AppFramework\Http\JSONResponse; use OCP\IRequest; use OCP\IUserManager; @@ -173,6 +174,10 @@ public function __construct( */ public function getContactpersonen(string $organisationId): JSONResponse { + if ($this->userSession->getUser() === null) { + return new JSONResponse(['message' => 'Not authenticated'], Http::STATUS_UNAUTHORIZED); + } + try { // Get object service. $objectService = \OC::$server->get('OCA\OpenRegister\Service\ObjectService'); @@ -269,6 +274,10 @@ function ($group) { */ public function convertToUser(string $contactpersoonId): JSONResponse { + if ($this->userSession->getUser() === null) { + return new JSONResponse(['message' => 'Not authenticated'], Http::STATUS_UNAUTHORIZED); + } + try { // Get object service. $objectService = \OC::$server->get('OCA\OpenRegister\Service\ObjectService'); @@ -495,6 +504,10 @@ public function convertToUser(string $contactpersoonId): JSONResponse */ public function changePassword(string $username, string $newPassword): JSONResponse { + if ($this->userSession->getUser() === null) { + return new JSONResponse(['message' => 'Not authenticated'], Http::STATUS_UNAUTHORIZED); + } + try { $user = $this->userManager->get($username); @@ -584,6 +597,10 @@ public function changePassword(string $username, string $newPassword): JSONRespo */ public function updateUserGroups(string $username, array $groups=[]): JSONResponse { + if ($this->userSession->getUser() === null) { + return new JSONResponse(['message' => 'Not authenticated'], Http::STATUS_UNAUTHORIZED); + } + try { $user = $this->userManager->get($username); @@ -708,6 +725,10 @@ function ($group) { */ public function getContactPersonsWithUserDetailsForOrganization(string $organizationUuid): JSONResponse { + if ($this->userSession->getUser() === null) { + return new JSONResponse(['message' => 'Not authenticated'], Http::STATUS_UNAUTHORIZED); + } + try { $this->logger->info( 'ContactpersonenController: Getting contact persons with user details for organization', @@ -800,6 +821,10 @@ public function getContactPersonsWithUserDetailsForOrganization(string $organiza */ public function getUserInfo(string $contactpersoonId): JSONResponse { + if ($this->userSession->getUser() === null) { + return new JSONResponse(['message' => 'Not authenticated'], Http::STATUS_UNAUTHORIZED); + } + try { $this->logger->info( 'ContactpersonenController: Getting user info for contactpersoon', @@ -927,6 +952,10 @@ public function getUserInfo(string $contactpersoonId): JSONResponse */ public function getAvailableGroups(): JSONResponse { + if ($this->userSession->getUser() === null) { + return new JSONResponse(['message' => 'Not authenticated'], Http::STATUS_UNAUTHORIZED); + } + try { $availableGroups = [ [ @@ -992,6 +1021,10 @@ public function getAvailableGroups(): JSONResponse */ public function disableUser(string $contactpersoonId): JSONResponse { + if ($this->userSession->getUser() === null) { + return new JSONResponse(['message' => 'Not authenticated'], Http::STATUS_UNAUTHORIZED); + } + try { // Delegate to service. $this->contactSvc->disableUserForContactpersoon($contactpersoonId); @@ -1000,7 +1033,7 @@ public function disableUser(string $contactpersoonId): JSONResponse 'User account disabled', [ 'contactpersoonId' => $contactpersoonId, - 'disabled_by' => $this->userSession->getUser()?->getUID(), + 'disabled_by' => $this->userSession->getUser()->getUID(), ] ); return new JSONResponse( @@ -1040,6 +1073,10 @@ public function disableUser(string $contactpersoonId): JSONResponse */ public function enableUser(string $contactpersoonId): JSONResponse { + if ($this->userSession->getUser() === null) { + return new JSONResponse(['message' => 'Not authenticated'], Http::STATUS_UNAUTHORIZED); + } + try { // Delegate to service. $this->contactSvc->enableUserForContactpersoon($contactpersoonId); @@ -1048,7 +1085,7 @@ public function enableUser(string $contactpersoonId): JSONResponse 'User account enabled', [ 'contactpersoonId' => $contactpersoonId, - 'enabled_by' => $this->userSession->getUser()?->getUID(), + 'enabled_by' => $this->userSession->getUser()->getUID(), ] ); return new JSONResponse( @@ -1086,6 +1123,10 @@ public function enableUser(string $contactpersoonId): JSONResponse */ public function testBulkUserInfo(): JSONResponse { + if ($this->userSession->getUser() === null) { + return new JSONResponse(['message' => 'Not authenticated'], Http::STATUS_UNAUTHORIZED); + } + try { $objectServiceAvail = 'null'; if ($this->contactSvc !== null) { @@ -1147,6 +1188,10 @@ public function testBulkUserInfo(): JSONResponse */ public function getBulkUserInfo(): JSONResponse { + if ($this->userSession->getUser() === null) { + return new JSONResponse(['message' => 'Not authenticated'], Http::STATUS_UNAUTHORIZED); + } + try { $input = json_decode(file_get_contents('php://input'), true); $contactpersoonIds = $input['contactpersoonIds'] ?? []; @@ -1213,18 +1258,13 @@ public function getBulkUserInfo(): JSONResponse */ public function getMe(): JSONResponse { + if ($this->userSession->getUser() === null) { + return new JSONResponse(['message' => 'Not authenticated'], Http::STATUS_UNAUTHORIZED); + } + try { // Get current user from session. $user = $this->userSession->getUser(); - if ($user === null) { - return new JSONResponse( - [ - 'success' => false, - 'message' => 'Not authenticated', - ], - 401 - ); - } $userId = $user->getUID(); $userEmail = $user->getEMailAddress() ?? $userId; diff --git a/lib/Controller/DashboardController.php b/lib/Controller/DashboardController.php index 42caddb7..76969764 100644 --- a/lib/Controller/DashboardController.php +++ b/lib/Controller/DashboardController.php @@ -14,9 +14,11 @@ namespace OCA\SoftwareCatalog\Controller; use OCP\AppFramework\Controller; +use OCP\AppFramework\Http; use OCP\AppFramework\Http\TemplateResponse; use OCP\AppFramework\Http\JSONResponse; use OCP\IRequest; +use OCP\IUserSession; use OCP\AppFramework\Http\ContentSecurityPolicy; class DashboardController extends Controller @@ -24,11 +26,15 @@ class DashboardController extends Controller /** * Constructor for DashboardController. * - * @param string $appName The app name - * @param IRequest $request The request object + * @param string $appName The app name + * @param IRequest $request The request object + * @param IUserSession $userSession The user session */ - public function __construct($appName, IRequest $request) - { + public function __construct( + $appName, + IRequest $request, + private readonly IUserSession $userSession, + ) { parent::__construct(appName: $appName, request: $request); }//end __construct() @@ -80,6 +86,10 @@ public function page(?string $getParameter): TemplateResponse */ public function index(): JSONResponse { + if ($this->userSession->getUser() === null) { + return new JSONResponse(['message' => 'Not authenticated'], Http::STATUS_UNAUTHORIZED); + } + try { $results = ['results' => []]; return new JSONResponse($results); diff --git a/lib/Controller/GebruikController.php b/lib/Controller/GebruikController.php index c3442bc5..d1fdb66b 100644 --- a/lib/Controller/GebruikController.php +++ b/lib/Controller/GebruikController.php @@ -23,6 +23,7 @@ use OCA\SoftwareCatalog\Service\GebruikService; use OCP\AppFramework\Controller; use OCP\AppFramework\Http\JSONResponse; +use OCP\AppFramework\Http; use OCP\IConfig; use OCP\IGroup; use OCP\IGroupManager; @@ -72,7 +73,6 @@ public function __construct( * For a gebruik-beheerder, returns all gebruiken. * For an aanbod-beheerder, returns gebruiken of applications of the user's organization. * - * @NoAdminRequired * @NoCSRFRequired * @PublicPage * @@ -150,9 +150,8 @@ public function getGebruikenForDeelnemer(): JSONResponse { $user = $this->userSession->getUser(); - // Return empty results for non-logged-in users to prevent unnecessary errors. if ($user === null) { - return new JSONResponse($this->getEmptyResult()); + return new JSONResponse(['message' => 'Not authenticated'], Http::STATUS_UNAUTHORIZED); } $orgUuid = $this->config->getUserValue(userId: $user->getUID(), appName: 'core', key: 'organisation'); diff --git a/lib/Controller/SettingsController.php b/lib/Controller/SettingsController.php index 8c918d0f..00687ba9 100644 --- a/lib/Controller/SettingsController.php +++ b/lib/Controller/SettingsController.php @@ -22,6 +22,7 @@ use OCP\IAppConfig; use OCP\AppFramework\Controller; +use OCP\AppFramework\Http; use OCP\AppFramework\Http\TemplateResponse; use OCP\AppFramework\Http\JSONResponse; use OCP\AppFramework\Http\Response; @@ -149,6 +150,10 @@ public function getConfigurationService(): ?ConfigurationService */ public function index(): JSONResponse { + if ($this->userSession->getUser() === null) { + return new JSONResponse(['message' => 'Not authenticated'], Http::STATUS_UNAUTHORIZED); + } + try { $user = $this->userSession->getUser(); $isAdmin = $user !== null && $this->groupManager->isAdmin($user->getUID()); @@ -510,6 +515,10 @@ public function initialize(): JSONResponse */ public function status(): JSONResponse { + if ($this->userSession->getUser() === null) { + return new JSONResponse(['message' => 'Not authenticated'], Http::STATUS_UNAUTHORIZED); + } + try { $this->logger->debug('SettingsController: Getting configuration status'); @@ -608,6 +617,10 @@ public function autoConfigure(): JSONResponse */ public function stats(): JSONResponse { + if ($this->userSession->getUser() === null) { + return new JSONResponse(['message' => 'Not authenticated'], Http::STATUS_UNAUTHORIZED); + } + try { $statistics = $this->settingsService->getObjectCountsStatistics(); return new JSONResponse( @@ -645,6 +658,10 @@ public function stats(): JSONResponse */ public function debug(): JSONResponse { + if ($this->userSession->getUser() === null) { + return new JSONResponse(['message' => 'Not authenticated'], Http::STATUS_UNAUTHORIZED); + } + try { $debugInfo = $this->settingsService->getDebugInfo(); return new JSONResponse($debugInfo); @@ -719,6 +736,10 @@ public function sendTestEmail(): JSONResponse */ public function getSyncStatus(int $minutesBack=10): JSONResponse { + if ($this->userSession->getUser() === null) { + return new JSONResponse(['message' => 'Not authenticated'], Http::STATUS_UNAUTHORIZED); + } + $status = $this->orgSyncSvc->getSyncStatusWithErrorHandling($minutesBack); return new JSONResponse($status); }//end getSyncStatus() @@ -798,6 +819,10 @@ public function performSync(int $minutesBack=0): JSONResponse */ public function heartbeat(): JSONResponse { + if ($this->userSession->getUser() === null) { + return new JSONResponse(['message' => 'Not authenticated'], Http::STATUS_UNAUTHORIZED); + } + try { $timestamp = $this->request->getParam('timestamp', time() * 1000); @@ -848,6 +873,10 @@ public function heartbeat(): JSONResponse */ public function getVersionInfo(): JSONResponse { + if ($this->userSession->getUser() === null) { + return new JSONResponse(['message' => 'Not authenticated'], Http::STATUS_UNAUTHORIZED); + } + try { $this->logger->info('SettingsController: Getting version information'); $data = $this->settingsService->getVersionInfo(); @@ -1146,6 +1175,10 @@ public function consolidatedAutoConfigure(): JSONResponse */ public function getProgress(string $operationId): JSONResponse { + if ($this->userSession->getUser() === null) { + return new JSONResponse(['message' => 'Not authenticated'], Http::STATUS_UNAUTHORIZED); + } + try { $progress = $this->progressTracker->getProgress($operationId); @@ -1323,6 +1356,10 @@ public function render(): string */ public function importArchiMate(): JSONResponse { + if ($this->userSession->getUser() === null) { + return new JSONResponse(['message' => 'Not authenticated'], Http::STATUS_UNAUTHORIZED); + } + try { // Increase memory limit for large imports. ini_set('memory_limit', '4096M'); @@ -1503,6 +1540,10 @@ public function importArchiMate(): JSONResponse */ public function exportArchiMate(): Response { + if ($this->userSession->getUser() === null) { + return new JSONResponse(['message' => 'Not authenticated'], Http::STATUS_UNAUTHORIZED); + } + try { // Get JSON data from request parameters or body. $rawInput = file_get_contents('php://input'); @@ -1622,6 +1663,10 @@ public function render(): string */ public function exportOrgArchiMate(string $organizationUuid): Response { + if ($this->userSession->getUser() === null) { + return new JSONResponse(['message' => 'Not authenticated'], Http::STATUS_UNAUTHORIZED); + } + try { // Read boolean query parameters. $modules = $this->request->getParam('modules', 'true') === 'true'; @@ -1726,6 +1771,10 @@ public function render(): string */ public function downloadArchiMate(string $fileName): Response { + if ($this->userSession->getUser() === null) { + return new JSONResponse(['message' => 'Not authenticated'], Http::STATUS_UNAUTHORIZED); + } + try { // Security: validate filename to prevent path traversal. if (strpos(haystack: $fileName, needle: '..') !== false @@ -1821,6 +1870,10 @@ public function downloadArchiMate(string $fileName): Response */ public function testEmailConnection(): JSONResponse { + if ($this->userSession->getUser() === null) { + return new JSONResponse(['message' => 'Not authenticated'], Http::STATUS_UNAUTHORIZED); + } + $this->logger->info('SoftwareCatalog: Email connection test endpoint called'); try { @@ -1883,6 +1936,10 @@ public function testEmailConnection(): JSONResponse */ public function getEmailSettings(): JSONResponse { + if ($this->userSession->getUser() === null) { + return new JSONResponse(['message' => 'Not authenticated'], Http::STATUS_UNAUTHORIZED); + } + try { $emailSettings = $this->settingsService->getEmailSettings(); @@ -1920,6 +1977,10 @@ public function getEmailSettings(): JSONResponse */ public function updateEmailSettings(): JSONResponse { + if ($this->userSession->getUser() === null) { + return new JSONResponse(['message' => 'Not authenticated'], Http::STATUS_UNAUTHORIZED); + } + try { $data = $this->request->getParams(); $emailSettings = $data['emailSettings'] ?? $data; @@ -1966,6 +2027,10 @@ public function updateEmailSettings(): JSONResponse */ public function getEmailTemplates(): JSONResponse { + if ($this->userSession->getUser() === null) { + return new JSONResponse(['message' => 'Not authenticated'], Http::STATUS_UNAUTHORIZED); + } + try { // Delegate all business logic to service. $templates = $this->settingsService->getAllEmailTemplates(); @@ -2006,6 +2071,10 @@ public function getEmailTemplates(): JSONResponse */ public function getEmailTemplate(string $templateName): JSONResponse { + if ($this->userSession->getUser() === null) { + return new JSONResponse(['message' => 'Not authenticated'], Http::STATUS_UNAUTHORIZED); + } + try { $template = $this->settingsService->getEmailTemplate($templateName); @@ -2046,6 +2115,10 @@ public function getEmailTemplate(string $templateName): JSONResponse */ public function updateEmailTemplate(string $templateName): JSONResponse { + if ($this->userSession->getUser() === null) { + return new JSONResponse(['message' => 'Not authenticated'], Http::STATUS_UNAUTHORIZED); + } + try { $data = $this->request->getParams(); $templateContent = $data['template'] ?? $data['content'] ?? ''; @@ -2106,6 +2179,10 @@ public function updateEmailTemplate(string $templateName): JSONResponse */ public function getEmailTemplateDefault(string $templateName): JSONResponse { + if ($this->userSession->getUser() === null) { + return new JSONResponse(['message' => 'Not authenticated'], Http::STATUS_UNAUTHORIZED); + } + try { $defaultTemplate = $this->settingsService->getDefaultEmailTemplate($templateName); @@ -2146,6 +2223,10 @@ public function getEmailTemplateDefault(string $templateName): JSONResponse */ public function getEmailTemplateVariables(string $templateName): JSONResponse { + if ($this->userSession->getUser() === null) { + return new JSONResponse(['message' => 'Not authenticated'], Http::STATUS_UNAUTHORIZED); + } + try { $variables = $this->settingsService->getEmailTemplateVariables($templateName); @@ -2188,6 +2269,10 @@ public function getEmailTemplateVariables(string $templateName): JSONResponse */ public function getGenericUserGroups(): JSONResponse { + if ($this->userSession->getUser() === null) { + return new JSONResponse(['message' => 'Not authenticated'], Http::STATUS_UNAUTHORIZED); + } + try { $groups = $this->settingsService->getGenericUserGroups(); @@ -2225,6 +2310,10 @@ public function getGenericUserGroups(): JSONResponse */ public function setGenericUserGroups(): JSONResponse { + if ($this->userSession->getUser() === null) { + return new JSONResponse(['message' => 'Not authenticated'], Http::STATUS_UNAUTHORIZED); + } + try { $data = $this->request->getParams(); $groups = $data['groups'] ?? []; @@ -2268,6 +2357,10 @@ public function setGenericUserGroups(): JSONResponse */ public function getOrganizationAdminGroups(): JSONResponse { + if ($this->userSession->getUser() === null) { + return new JSONResponse(['message' => 'Not authenticated'], Http::STATUS_UNAUTHORIZED); + } + try { $groups = $this->settingsService->getOrganizationAdminGroups(); @@ -2305,6 +2398,10 @@ public function getOrganizationAdminGroups(): JSONResponse */ public function setOrganizationAdminGroups(): JSONResponse { + if ($this->userSession->getUser() === null) { + return new JSONResponse(['message' => 'Not authenticated'], Http::STATUS_UNAUTHORIZED); + } + try { $data = $this->request->getParams(); $groups = $data['groups'] ?? []; @@ -2348,6 +2445,10 @@ public function setOrganizationAdminGroups(): JSONResponse */ public function getSuperUserGroups(): JSONResponse { + if ($this->userSession->getUser() === null) { + return new JSONResponse(['message' => 'Not authenticated'], Http::STATUS_UNAUTHORIZED); + } + try { $groups = $this->settingsService->getSuperUserGroups(); @@ -2385,6 +2486,10 @@ public function getSuperUserGroups(): JSONResponse */ public function setSuperUserGroups(): JSONResponse { + if ($this->userSession->getUser() === null) { + return new JSONResponse(['message' => 'Not authenticated'], Http::STATUS_UNAUTHORIZED); + } + try { $data = $this->request->getParams(); $groups = $data['groups'] ?? []; @@ -2428,6 +2533,10 @@ public function setSuperUserGroups(): JSONResponse */ public function getAllGroups(): JSONResponse { + if ($this->userSession->getUser() === null) { + return new JSONResponse(['message' => 'Not authenticated'], Http::STATUS_UNAUTHORIZED); + } + try { $allGroups = $this->settingsService->getAllGroups(); @@ -2469,6 +2578,10 @@ public function getAllGroups(): JSONResponse */ public function clearArchiMateImportStatus(): JSONResponse { + if ($this->userSession->getUser() === null) { + return new JSONResponse(['message' => 'Not authenticated'], Http::STATUS_UNAUTHORIZED); + } + try { $result = $this->settingsService->clearArchiMateImportStatus(); @@ -2509,6 +2622,10 @@ public function clearArchiMateImportStatus(): JSONResponse */ public function killArchiMateImport(): JSONResponse { + if ($this->userSession->getUser() === null) { + return new JSONResponse(['message' => 'Not authenticated'], Http::STATUS_UNAUTHORIZED); + } + try { $result = $this->settingsService->killArchiMateImport(); @@ -2548,6 +2665,10 @@ public function killArchiMateImport(): JSONResponse */ public function cancelArchiMateImport(): JSONResponse { + if ($this->userSession->getUser() === null) { + return new JSONResponse(['message' => 'Not authenticated'], Http::STATUS_UNAUTHORIZED); + } + try { $result = $this->settingsService->cancelArchiMateImport(); @@ -2590,6 +2711,10 @@ public function cancelArchiMateImport(): JSONResponse */ public function clearArchiMateExportStatus(): JSONResponse { + if ($this->userSession->getUser() === null) { + return new JSONResponse(['message' => 'Not authenticated'], Http::STATUS_UNAUTHORIZED); + } + try { $this->settingsService->clearArchiMateExportStatus(); @@ -2631,6 +2756,10 @@ public function clearArchiMateExportStatus(): JSONResponse */ public function testArchiMateRoundTrip(): JSONResponse { + if ($this->userSession->getUser() === null) { + return new JSONResponse(['message' => 'Not authenticated'], Http::STATUS_UNAUTHORIZED); + } + try { $this->logger->info('SoftwareCatalog: ArchiMate round-trip test started'); @@ -2682,6 +2811,10 @@ public function testArchiMateRoundTrip(): JSONResponse */ public function getArchiMateSettings(): JSONResponse { + if ($this->userSession->getUser() === null) { + return new JSONResponse(['message' => 'Not authenticated'], Http::STATUS_UNAUTHORIZED); + } + try { $archimateStatus = $this->settingsService->getArchiMateStatus(); @@ -2720,6 +2853,10 @@ public function getArchiMateSettings(): JSONResponse */ public function getObjectCounts(): JSONResponse { + if ($this->userSession->getUser() === null) { + return new JSONResponse(['message' => 'Not authenticated'], Http::STATUS_UNAUTHORIZED); + } + try { $objectCounts = $this->settingsService->getObjectCountsStatistics(); @@ -2761,6 +2898,10 @@ public function getObjectCounts(): JSONResponse */ public function getArchiMateConfig(): JSONResponse { + if ($this->userSession->getUser() === null) { + return new JSONResponse(['message' => 'Not authenticated'], Http::STATUS_UNAUTHORIZED); + } + try { $config = $this->settingsService->getArchiMateConfig(); @@ -2825,6 +2966,10 @@ public function updateArchiMateConfig(): JSONResponse */ public function getEmailConfig(): JSONResponse { + if ($this->userSession->getUser() === null) { + return new JSONResponse(['message' => 'Not authenticated'], Http::STATUS_UNAUTHORIZED); + } + try { $config = $this->settingsService->getEmailConfigFocused(); @@ -2889,6 +3034,10 @@ public function updateEmailConfig(): JSONResponse */ public function getAmefConfig(): JSONResponse { + if ($this->userSession->getUser() === null) { + return new JSONResponse(['message' => 'Not authenticated'], Http::STATUS_UNAUTHORIZED); + } + try { $config = $this->settingsService->getAmefConfigFocused(); @@ -2953,6 +3102,10 @@ public function updateAmefConfig(): JSONResponse */ public function getVoorzieningenConfig(): JSONResponse { + if ($this->userSession->getUser() === null) { + return new JSONResponse(['message' => 'Not authenticated'], Http::STATUS_UNAUTHORIZED); + } + try { $config = $this->settingsService->getVoorzieningenConfigFocused(); @@ -3017,6 +3170,10 @@ public function updateVoorzieningenConfig(): JSONResponse */ public function getObjectsCounts(): JSONResponse { + if ($this->userSession->getUser() === null) { + return new JSONResponse(['message' => 'Not authenticated'], Http::STATUS_UNAUTHORIZED); + } + try { $counts = $this->settingsService->getObjectsCounts(); @@ -3049,6 +3206,10 @@ public function getObjectsCounts(): JSONResponse */ public function getObjectsStatistics(): JSONResponse { + if ($this->userSession->getUser() === null) { + return new JSONResponse(['message' => 'Not authenticated'], Http::STATUS_UNAUTHORIZED); + } + try { $statistics = $this->settingsService->getObjectsStatistics(); @@ -3081,6 +3242,10 @@ public function getObjectsStatistics(): JSONResponse */ public function getUserGroupsConfig(): JSONResponse { + if ($this->userSession->getUser() === null) { + return new JSONResponse(['message' => 'Not authenticated'], Http::STATUS_UNAUTHORIZED); + } + try { $config = $this->settingsService->getUserGroupsConfig(); @@ -3211,6 +3376,10 @@ private function getHttpStatusForErrorMessage(string $message): int */ public function syncOrganisations(): JSONResponse { + if ($this->userSession->getUser() === null) { + return new JSONResponse(['message' => 'Not authenticated'], Http::STATUS_UNAUTHORIZED); + } + try { $this->logger->info('SettingsController: Starting organisation sync via API'); @@ -3331,6 +3500,10 @@ public function bulkSyncStandards(): JSONResponse */ public function getCronjobConfig(): JSONResponse { + if ($this->userSession->getUser() === null) { + return new JSONResponse(['message' => 'Not authenticated'], Http::STATUS_UNAUTHORIZED); + } + try { $config = $this->settingsService->getCronjobConfig(); return new JSONResponse($config); @@ -3403,6 +3576,10 @@ public function updateCronjobConfig(): JSONResponse */ public function getCronjobUsers(): JSONResponse { + if ($this->userSession->getUser() === null) { + return new JSONResponse(['message' => 'Not authenticated'], Http::STATUS_UNAUTHORIZED); + } + try { $result = $this->settingsService->getAvailableUsersForCronjobs(); return new JSONResponse($result); @@ -3437,6 +3614,10 @@ public function getCronjobUsers(): JSONResponse */ public function getCronjobOrganisations(): JSONResponse { + if ($this->userSession->getUser() === null) { + return new JSONResponse(['message' => 'Not authenticated'], Http::STATUS_UNAUTHORIZED); + } + try { $result = $this->settingsService->getAvailableOrganisationsForCronjobs(); return new JSONResponse($result); diff --git a/lib/Controller/ViewController.php b/lib/Controller/ViewController.php index 0d677532..4b723151 100644 --- a/lib/Controller/ViewController.php +++ b/lib/Controller/ViewController.php @@ -69,7 +69,6 @@ public function __construct( * - include_gebruik (bool): Include usage data in view nodes * - include_deelnames_gebruik (bool): Include participation usage data in view nodes * - * @NoAdminRequired * @NoCSRFRequired * @PublicPage * @@ -145,7 +144,6 @@ public function getAllViews(): JSONResponse * * @param string $viewId The view identifier * - * @NoAdminRequired * @NoCSRFRequired * @PublicPage * @@ -305,7 +303,6 @@ private function parseBooleanParam($value): bool * * API Endpoint: GET /api/views/docs * - * @NoAdminRequired * @NoCSRFRequired * @PublicPage * diff --git a/lib/Service/ArchiMateService.php b/lib/Service/ArchiMateService.php index 299be980..f92faa00 100644 --- a/lib/Service/ArchiMateService.php +++ b/lib/Service/ArchiMateService.php @@ -2038,7 +2038,7 @@ public function isExportInProgress(): bool * * @return bool True if any operation is in progress */ - public function isOperationInProgress(): bool + private function isOperationInProgress(): bool { return $this->isImportInProgress() || $this->isExportInProgress(); }//end isOperationInProgress() diff --git a/lib/Service/SoftwareCatalogueService.php b/lib/Service/SoftwareCatalogueService.php index 1cdaf7eb..41f219c4 100644 --- a/lib/Service/SoftwareCatalogueService.php +++ b/lib/Service/SoftwareCatalogueService.php @@ -3322,15 +3322,16 @@ public function syncContactPersonUsernamesWithOrganization(string $organizationU }//end syncContactPersonUsernamesWithOrganization() /** - * Ensures a contact person's username is in their organization's users array - * This method is called when a contact person is created or updated + * Ensures a contact person's username is in their organization's users array. + * NOTE: Dead method — retained only as implementation reference until the sync + * pipeline invocation point is wired; not called from any live code path. + * @SuppressWarnings(PHPMD.UnusedPrivateMethod) * * @param object $contactPersonObject The contact person object * * @return void - * @spec openspec/changes/retrofit-2026-05-26-softwarecatalogue-orchestration/tasks.md#task-1 */ - public function ensureContactPersonInOrganization(object $contactPersonObject): void + private function ensureContactPersonInOrganization(object $contactPersonObject): void { $contactData = $contactPersonObject->getObject(); $email = $contactData['email'] ?? null; diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 611709c9..bd296285 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -41,6 +41,16 @@ parameters: count: 3 path: lib/Controller/AangebodenGebruikController.php + - + message: "#^Property OCA\\\\SoftwareCatalog\\\\Controller\\\\AangebodenGebruikController\\:\\:\\$groupManager is never read, only written\\.$#" + count: 1 + path: lib/Controller/AangebodenGebruikController.php + + - + message: "#^Undefined variable: \\$groupManager$#" + count: 1 + path: lib/Controller/AangebodenGebruikController.php + - message: "#^Property OCA\\\\SoftwareCatalog\\\\Controller\\\\ContactpersonenController\\:\\:\\$secureRandom is never read, only written\\.$#" count: 1 @@ -371,6 +381,11 @@ parameters: count: 1 path: lib/Service/ArchiMateService.php + - + message: "#^Method OCA\\\\SoftwareCatalog\\\\Service\\\\ArchiMateService\\:\\:isOperationInProgress\\(\\) is unused\\.$#" + count: 1 + path: lib/Service/ArchiMateService.php + - message: "#^Method OCA\\\\SoftwareCatalog\\\\Service\\\\ArchiMateService\\:\\:getAmefRegisterId\\(\\) never returns int so it can be removed from the return type\\.$#" count: 1 @@ -581,6 +596,16 @@ parameters: count: 1 path: lib/Service/OrganizationSyncService.php + - + message: "#^Offset 'organisatie' on array\\{username: string\\} on left side of \\?\\? does not exist\\.$#" + count: 1 + path: lib/Service/OrganizationSyncService.php + + - + message: "#^Cannot unset offset 'organisatie' on array\\{username: string\\}\\.$#" + count: 1 + path: lib/Service/OrganizationSyncService.php + - message: "#^Comparison operation \"\\>\" between 105 and 0 is always true\\.$#" count: 1 @@ -741,6 +766,11 @@ parameters: count: 1 path: lib/Service/SoftwareCatalogueService.php + - + message: "#^Method OCA\\\\SoftwareCatalog\\\\Service\\\\SoftwareCatalogueService\\:\\:ensureContactPersonInOrganization\\(\\) is unused\\.$#" + count: 1 + path: lib/Service/SoftwareCatalogueService.php + - message: "#^Offset 'roles' on array\\{\\} on left side of \\?\\? does not exist\\.$#" count: 1 @@ -775,3 +805,8 @@ parameters: message: "#^Property OCA\\\\SoftwareCatalog\\\\Settings\\\\SoftwareCatalogAdmin\\:\\:\\$l10n is never read, only written\\.$#" count: 1 path: lib/Settings/SoftwareCatalogAdmin.php + + - + message: "#^Property OCA\\\\SoftwareCatalog\\\\Settings\\\\SoftwareCatalogAdmin\\:\\:\\$config is never read, only written\\.$#" + count: 1 + path: lib/Settings/SoftwareCatalogAdmin.php