Skip to content

feat(adr-023): ActionAuthService skeleton + repair step + empty seed + unit tests#21

Open
rubenvdlinde wants to merge 1 commit into
developmentfrom
feat/adr-023-action-authorization
Open

feat(adr-023): ActionAuthService skeleton + repair step + empty seed + unit tests#21
rubenvdlinde wants to merge 1 commit into
developmentfrom
feat/adr-023-action-authorization

Conversation

@rubenvdlinde
Copy link
Copy Markdown
Contributor

Ports the ADR-023 action-authorization pattern from decidesk's reference implementation to the template so every new app picks it up for free.

What's included

  • lib/Service/ActionAuthService.php — the service (verbatim from decidesk@88844d0, namespace adjusted to OCA\AppTemplate).

    • requireAction(IUser, string) — throws OCSForbiddenException
    • can(IUser, string): bool — non-throwing variant
    • getAllowedGroups / getMatrix / setMatrix / getActions
    • Admin always passes (break-glass). Default-deny on missing or malformed matrix. Matrix stored in IAppConfig under actions key.
  • lib/Repair/InitializeActions.php — seeds lib/actions.seed.json on install; preserves admin-customized matrix on upgrade.

  • lib/actions.seed.json — empty actions object (template ships with no actions; implementers add one entry per requireAction() call site as the app grows).

  • tests/Unit/Service/ActionAuthServiceTest.php — 8 unit tests covering the contract (admin always passes, default-deny, group intersection, malformed JSON fallback, etc.).

  • appinfo/info.xmlInitializeActions registered on install + post-migration.

  • openspec/architecture/adr-023-action-authorization.md — ADR local to the app for reference.

How implementers use it

use OCA\AppTemplate\Service\ActionAuthService;
use OCP\AppFramework\OCS\OCSForbiddenException;

#[NoAdminRequired]
public function publish(int $id): JSONResponse {
    $user = $this->userSession->getUser();
    if ($user === null) {
        return new JSONResponse(['error' => 'Not authenticated'], 401);
    }
    try {
        $this->actionAuth->requireAction($user, 'item.publish');
    } catch (OCSForbiddenException $e) {
        return new JSONResponse(['error' => $e->getMessage()], 403);
    }
    // … do the work
}

Add "item.publish": ["admin"] to lib/actions.seed.json. Admin broadens via a settings UI (not included here — follow-up can port decidesk's SettingsController matrix endpoints).

Not included (follow-up material)

  • Settings-page UI to edit the matrix. Decidesk has get/setActions on SettingsController; left out of this initial port to keep the diff surgical.
  • Vue components for the matrix editor.

…+ 8 unit tests

Ports the ADR-023 action-authorization pattern from decidesk's reference
implementation (decidesk@88844d0) to the template so every new app picks
it up for free.

What's included:

* lib/Service/ActionAuthService.php — verbatim service:
  - requireAction (throws OCSForbiddenException)
  - can (non-throwing bool variant)
  - getAllowedGroups / getMatrix / setMatrix / getActions
  - Admin always passes (break-glass)
  - Default-deny on missing or malformed matrix
  - Matrix stored in IAppConfig under 'actions' key

* lib/Repair/InitializeActions.php — seeds lib/actions.seed.json on
  install; preserves admin-customized matrix on upgrade

* lib/actions.seed.json — empty "actions" object (template ships
  with no actions; implementers add one entry per requireAction()
  call site as the app grows)

* tests/Unit/Service/ActionAuthServiceTest.php — 8 unit tests
  covering the contract:
  1. Admin always passes
  2. Default-deny on missing matrix entry
  3. Admin-only matrix entry blocks non-admin
  4. Group-matching non-admin passes
  5. Admin in entry doesn't auto-pass non-admin
  6. can() returns bool
  7. Malformed matrix JSON falls back to deny
  8. getAllowedGroups defaults to ['admin']

* appinfo/info.xml — registered Repair\InitializeActions on install
  + post-migration

* openspec/architecture/adr-023-action-authorization.md — ADR copied
  from hydra for the app's local reference

Implementers declare an action name in their controller (e.g.
'item.publish'), add it to actions.seed.json with ['admin'] default,
then call $this->actionAuth->requireAction($user, 'item.publish')
wrapped in try/catch for OCSForbiddenException. Admin broadens the
group list via a settings UI (not included in this template port — a
follow-up change can port decidesk's SettingsController matrix API).

@SPEC openspec/architecture/adr-023-action-authorization.md
@github-actions
Copy link
Copy Markdown
Contributor

Quality Report — ConductionNL/nextcloud-app-template @ a9683a0

Check PHP Vue Security License Tests
lint
phpcs
phpmd
psalm
phpstan
phpmetrics
eslint
stylelint
composer ✅ 100/100
npm ✅ 215/215
PHPUnit ⏭️
Newman ⏭️
Playwright ⏭️

Quality workflow — 2026-04-23 17:43 UTC

Download the full PDF report from the workflow artifacts.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant