Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion lib/Controller/AanbodController.php
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,16 @@ public function __construct(
*
* @return JSONResponse JSON response with aanbod objects array
*
* REQ-009 requires an authenticated caller, and the body enforces it.
* The PublicPage annotation told Nextcloud's middleware to admit
* anonymous requests anyway, so declared and enforced contract disagreed:
* every anonymous call was admitted, routed, and only then rejected by
* the controller. @NoAdminRequired is the annotation that matches —
* any logged-in user, rejected at the middleware. The in-body guard
* stays as deny-before-grant (REQ-001).
*
* @NoCSRFRequired
* @PublicPage
* @NoAdminRequired
*
* @spec openspec/specs/aanbod-listings/spec.md
* @spec openspec/specs/vendor-visibility-rbac/spec.md#requirement-the-aanbod-listing-endpoint-must-require-authentication-explicitly-not-implicitly-req-009
Expand Down
14 changes: 11 additions & 3 deletions lib/Controller/AangebodenGebruikController.php
Original file line number Diff line number Diff line change
Expand Up @@ -87,10 +87,18 @@ public function __construct(
*
* @return JSONResponse JSON response with gebruiks array where org is afnemer
*
* REQ-004 requires an authenticated caller, and the body enforces it.
* The PublicPage annotation told Nextcloud's middleware to admit
* anonymous requests anyway, so declared and enforced contract disagreed:
* every anonymous call was admitted, routed, and only then rejected by
* the controller. @NoAdminRequired is the annotation that matches —
* any logged-in user, rejected at the middleware. The in-body guard
* stays as deny-before-grant (REQ-001).
*
* @NoCSRFRequired
* @PublicPage
* @spec openspec/specs/aangeboden-gebruik-api/spec.md
* @spec openspec/specs/vendor-visibility-rbac/spec.md#requirement-the-offered-usage-afnemer-endpoint-must-require-authentication-explicitly-not-implicitly-req-004
* @NoAdminRequired
* @spec openspec/specs/aangeboden-gebruik-api/spec.md
* @spec openspec/specs/vendor-visibility-rbac/spec.md#requirement-the-offered-usage-afnemer-endpoint-must-require-authentication-explicitly-not-implicitly-req-004
*/
public function getGebruiksWhereAfnemer(): JSONResponse
{
Expand Down
62 changes: 51 additions & 11 deletions lib/Controller/SettingsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -2290,7 +2290,10 @@ public function getEmailTemplateVariables(string $templateName): JSONResponse
/**
* Get generic user groups
*
* @NoAdminRequired
* Admin-only: the body rejects a non-admin with 403, so the endpoint
* must not declare @NoAdminRequired. Its sibling setGenericUserGroups()
* already omits it.
*
* @NoCSRFRequired
*
* @return JSONResponse Generic user groups
Expand Down Expand Up @@ -2387,7 +2390,10 @@ public function setGenericUserGroups(): JSONResponse
/**
* Get organization admin groups
*
* @NoAdminRequired
* Admin-only: the body rejects a non-admin with 403, so the endpoint
* must not declare @NoAdminRequired. Its sibling
* setOrganizationAdminGroups() already omits it.
*
* @NoCSRFRequired
*
* @return JSONResponse Organization admin groups
Expand Down Expand Up @@ -2484,7 +2490,10 @@ public function setOrganizationAdminGroups(): JSONResponse
/**
* Get super user groups
*
* @NoAdminRequired
* Admin-only: the body rejects a non-admin with 403, so the endpoint
* must not declare @NoAdminRequired. Its sibling setSuperUserGroups()
* already omits it.
*
* @NoCSRFRequired
*
* @return JSONResponse Super user groups
Expand Down Expand Up @@ -2581,7 +2590,10 @@ public function setSuperUserGroups(): JSONResponse
/**
* Get all user groups
*
* @NoAdminRequired
* Admin-only: the body rejects a non-admin with 403, and the payload is
* the full group list of the instance — an enumeration surface. The
* endpoint must not declare @NoAdminRequired.
*
* @NoCSRFRequired
*
* @return JSONResponse All user groups
Expand Down Expand Up @@ -2631,7 +2643,9 @@ public function getAllGroups(): JSONResponse
/**
* Clear ArchiMate import status
*
* @NoAdminRequired
* Admin-only: the body rejects a non-admin with 403, and importArchiMate()
* — the operation whose status this clears — is already admin-only.
*
* @NoCSRFRequired
*
* @return JSONResponse Clear result
Expand Down Expand Up @@ -2682,9 +2696,11 @@ public function clearArchiMateImportStatus(): JSONResponse
*
* @deprecated Use cancelArchiMateImport() instead.
*
* @NoAdminRequired
* Admin-only: the body rejects a non-admin with 403, and importArchiMate()
* — the process this kills — is already admin-only.
*
* @NoCSRFRequired
* @spec openspec/specs/settings-admin-controller/spec.md
* @spec openspec/specs/settings-admin-controller/spec.md
*/
public function killArchiMateImport(): JSONResponse
{
Expand Down Expand Up @@ -2728,7 +2744,9 @@ public function killArchiMateImport(): JSONResponse
* Cancel a running ArchiMate import
* This combines force clearing and process killing for complete cancellation
*
* @NoAdminRequired
* Admin-only: the body rejects a non-admin with 403, and importArchiMate()
* — the process this cancels — is already admin-only.
*
* @NoCSRFRequired
*
* @return JSONResponse Cancellation result
Expand Down Expand Up @@ -2773,7 +2791,8 @@ public function cancelArchiMateImport(): JSONResponse
/**
* Clear ArchiMate export status
*
* @NoAdminRequired
* Admin-only: the body rejects a non-admin with 403.
*
* @NoCSRFRequired
*
* @return JSONResponse Clear result
Expand Down Expand Up @@ -3322,18 +3341,30 @@ public function getObjectsStatistics(): JSONResponse
/**
* Get user groups configuration only
*
* @NoAdminRequired
* This endpoint returns the union of getGenericUserGroups(),
* getOrganizationAdminGroups(), getSuperUserGroups() and getAllGroups().
* Each of those is exposed on its own route behind an explicit
* `isAdmin() === false -> 403` guard, so this aggregate MUST carry the
* same guard: without it any authenticated user reads through it the
* data the four dedicated routes refuse them, and the guards on those
* routes protect nothing.
*
* @NoCSRFRequired
*
* @return JSONResponse User groups configuration
* @spec openspec/specs/settings-admin-controller/spec.md
*/
public function getUserGroupsConfig(): JSONResponse
{
if ($this->userSession->getUser() === null) {
$currentUser = $this->userSession->getUser();
if ($currentUser === null) {
return new JSONResponse(['message' => 'Not authenticated'], Http::STATUS_UNAUTHORIZED);
}

if ($this->groupManager->isAdmin($currentUser->getUID()) === false) {
return new JSONResponse(['message' => 'Admin privileges required'], Http::STATUS_FORBIDDEN);
}

try {
$config = $this->settingsService->getUserGroupsConfig();

Expand Down Expand Up @@ -3365,6 +3396,15 @@ public function getUserGroupsConfig(): JSONResponse
*/
public function updateUserGroupsConfig(): JSONResponse
{
$currentUser = $this->userSession->getUser();
if ($currentUser === null) {
return new JSONResponse(['message' => 'Not authenticated'], Http::STATUS_UNAUTHORIZED);
}

if ($this->groupManager->isAdmin($currentUser->getUID()) === false) {
return new JSONResponse(['message' => 'Admin privileges required'], Http::STATUS_FORBIDDEN);
}

try {
$data = $this->request->getParams();
$result = $this->settingsService->updateUserGroupsConfig($data);
Expand Down
Loading
Loading