Files
summercms/.planning/phases/09-backend-admin-authentication-and-schema-pipeline/09-11-PLAN.md
2026-09-24 16:45:45 +02:00

188 lines
14 KiB
Markdown

---
phase: 09-backend-admin-authentication-and-schema-pipeline
plan: 11
type: execute
wave: 8
depends_on: [09-07, 09-08, 09-09, 09-10]
files_modified:
- cabana/registry.go
- cabana/navigation.go
- cabana/settings.go
- cabana/http.go
- ../fonoteka.go/plugins/golem15/fonoteka/admin_permissions.go
- ../fonoteka.go/plugins/golem15/fonoteka/admin_navigation.go
- ../fonoteka.go/plugins/golem15/fonoteka/admin_settings.go
- ../fonoteka.go/plugins/golem15/fonoteka/models/settings/fields.yaml
- ../fonoteka.go/plugins/golem15/fonoteka/admin_metadata_test.go
- ../fonoteka.go/plugins/golem15/fonoteka/admin_settings_test.go
autonomous: true
requirements: [AUTH-08, ADMIN-05]
estimate:
tokens: 30000
raw_tokens: 30000
tasks: 3
confidence: low
must_haves:
truths:
- "Per D-01/D-03, the developer role/superuser path receives all registered permissions while publisher receives only its explicit assignments; wildcard semantics remain deterministic and every controller/setting permission resolves from the registry."
- "Per D-18, navigation preserves the plugin's Winter IDs, labels, icons, ordering, and controller targets, removes entries the principal cannot use, and never exposes an unauthorized route or setting through metadata."
- "Per D-18, settings-list entries preserve code, label, description, category, icon, order, keywords, and permissions, are stable by order then code, and are filtered through the same backend principal permission set."
- "Per D-17, the Fonoteka singleton setting serves its localized schema and GET/PUT through Fill then Validate with `required: true` producing a 422; only compiled fillable fields can change."
- statement: "[flagged assumption ADMIN-05] Before a singleton row exists, GET is side-effect-free and returns exists: false plus the model's default-valued data; the first valid PUT creates it, and repeating an identical PUT is an idempotent success with unchanged persisted values."
verification: backstop
- "AUTH-08 identity no-change remains in force: all permission/navigation/settings evaluation uses the distinct backend_users principal and backend guard, never a generalized frontend user or add-alongside role."
artifacts:
- path: "../fonoteka.go/plugins/golem15/fonoteka/admin_permissions.go"
provides: "Complete exact plugin permission registry and role assignments"
- path: "cabana/navigation.go"
provides: "Permission-filtered stable navigation and settings-list metadata"
- path: "cabana/settings.go"
provides: "HasSettings registry plus singleton Fill/Validate GET/PUT service"
- path: "../fonoteka.go/plugins/golem15/fonoteka/models/settings/fields.yaml"
provides: "Fonoteka settings form schema"
key_links:
- from: "Fonoteka permission registry"
to: "backend role HasPermissions"
via: "exact registered codes plus wildcard/superuser evaluation"
- from: "cabana/navigation.go"
to: "controller/settings registry"
via: "permission-filtered target validation"
- from: "cabana/settings.go"
to: "lagoon.Fill and lagoon.Validate"
via: "compiled writable projection and singleton transaction"
prohibitions:
- "[flagged-unverified] Navigation and settings metadata must not reveal the existence, label, or target of entries the backend principal cannot access."
- "[flagged-unverified] Reading a missing settings singleton must not create a database row or mutate defaults."
---
## Phase Goal
**As a** backend administrator, **I want to** authenticate separately and manage resources described by Winter-shaped schemas, **so that** the administration surface stays permission-gated and reusable without coupling it to frontend users.
<objective>
Complete the backend permission catalog, permission-filtered navigation, and singleton settings capability.
Purpose: Make AUTH-08 authorization visible and coherent across discovery metadata and deliver ADMIN-05 without bypassing the schema/lifecycle pipeline.
Output: Exact plugin registries, stable filtered metadata endpoints, and permissioned singleton settings schema/read/write routes.
</objective>
<execution_context>
@/home/jin/.codex/gsd-core/workflows/execute-plan.md
@/home/jin/.codex/gsd-core/templates/summary.md
</execution_context>
<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/09-backend-admin-authentication-and-schema-pipeline/09-CONTEXT.md
@.planning/phases/09-backend-admin-authentication-and-schema-pipeline/09-RESEARCH.md
@.planning/phases/09-backend-admin-authentication-and-schema-pipeline/09-02-SUMMARY.md
@.planning/phases/09-backend-admin-authentication-and-schema-pipeline/09-07-SUMMARY.md
@.planning/phases/09-backend-admin-authentication-and-schema-pipeline/09-08-SUMMARY.md
@.planning/phases/09-backend-admin-authentication-and-schema-pipeline/09-09-SUMMARY.md
@.planning/phases/09-backend-admin-authentication-and-schema-pipeline/09-10-SUMMARY.md
@pact/capabilities.go
@lagoon/fill.go
@lagoon/validate.go
@../fonoteka.go/plugins/golem15/fonoteka/models/settings.go
</context>
## Artifacts this phase produces
- Complete Fonoteka backend permission declarations and publisher assignments
- `cabana.NavigationItem`, `cabana.SettingsEntry`, and filtered metadata response services
- `cabana.SettingsService` and D-09 `/settings` schema/GET/PUT routes
- `fonoteka.AdminSettings` registration plus embedded settings `fields.yaml`
- Assembled `TestAdminMetadata*` and `TestAdminSettings*` suites
<tasks>
<task type="auto" tdd="true">
<name>Task 1: Register exact permissions and validate every operation reference</name>
<files>cabana/registry.go, ../fonoteka.go/plugins/golem15/fonoteka/admin_permissions.go, ../fonoteka.go/plugins/golem15/fonoteka/admin_metadata_test.go</files>
<read_first>cabana/registry.go, pact/capabilities.go, bouncer/registry.go, bouncer/guard.go, /media/nvme/dev/golem15/fonoteka/plugins/golem15/fonoteka/Plugin.php, .planning/phases/09-backend-admin-authentication-and-schema-pipeline/09-CONTEXT.md</read_first>
<behavior>
- Test 1: every source Fonoteka permission code/label is registered exactly once, publisher assignments match the source, and developer/superuser wildcard grants all registered codes.
- Test 2: every controller operation, relation, navigation target, and setting names a registered permission; unknown or duplicate codes fail activation.
- Test 3: a frontend user identity with matching numeric ID/permissions cannot satisfy a backend permission check.
</behavior>
<action>Implement the complete D-03 permission contribution from the tracked plugin source and bind it to D-01's developer/publisher roles. Extend cabana activation validation so all controller operations, relations, navigation items, and settings reference registered codes and duplicate/unknown declarations stop boot. Keep evaluation on the backend `bouncer.Principal` established by the named backend guard, preserving exact and wildcard semantics; do not adapt frontend-user roles into the admin registry.</action>
<verify>
<automated>(cd ../fonoteka.go &amp;&amp; go test ./plugins/golem15/fonoteka -run '^TestAdminMetadata(Permissions|Publisher|Wildcard|RegistryReferences|RejectsFrontendPrincipal)$' -count=1)</automated>
<fails_when>The command exits non-zero, reports no matching test, a source permission/assignment is absent, duplicate/unknown references activate, wildcard/exact semantics drift, or a frontend identity passes backend authorization.</fails_when>
</verify>
<acceptance_criteria>The exact permission catalog is complete, referentially valid, role-assigned, and exclusively evaluated against the distinct backend identity.</acceptance_criteria>
<done>Every backend operation and metadata entry has a validated permission code with correct role behavior.</done>
</task>
<task type="auto" tdd="true">
<name>Task 2: Serve exact permission-filtered navigation and settings metadata</name>
<files>cabana/navigation.go, ../fonoteka.go/plugins/golem15/fonoteka/admin_navigation.go, ../fonoteka.go/plugins/golem15/fonoteka/admin_metadata_test.go</files>
<read_first>/media/nvme/dev/golem15/fonoteka/plugins/golem15/fonoteka/Plugin.php, ../fonoteka.go/plugins/golem15/fonoteka/admin_permissions.go, cabana/registry.go, bouncer/guard.go</read_first>
<behavior>
- Test 1: navigation/settings metadata exactly preserves registered labels/icons/order/targets/keywords and sorts by order then stable code.
- Test 2: developer sees all source entries, publisher sees only permitted entries, and a no-permission admin receives allocated empty arrays.
- Test 3: a metadata item with missing target or permission fails activation; responses never include denied item fields.
</behavior>
<action>Port D-18's exact `registerNavigation()` shape and provide typed HasNavigation/HasSettings registry consumption. Validate controller/setting targets and permissions at activation, then filter whole entries before serialization through `HasPermissions`; never serialize then mask selected fields. Preserve the declared plugin shape and stable order/code tie-break, use request-time localization, and return non-null empty arrays.</action>
<verify>
<automated>(cd ../fonoteka.go &amp;&amp; go test ./plugins/golem15/fonoteka -run '^TestAdminMetadata(Navigation|SettingsList|Filtering|StableOrder|RejectsInvalidTarget)$' -count=1)</automated>
<fails_when>The command exits non-zero, reports no matching test, source metadata differs, ordering is unstable, empty output is null, an invalid target activates, or any denied entry field appears in the response.</fails_when>
</verify>
<acceptance_criteria>Navigation and settings-list metadata are exact, stable, localized, referentially valid, and filtered as whole entries by backend permission.</acceptance_criteria>
<done>An admin discovers only the controllers and settings pages that they can actually open.</done>
</task>
<task type="auto" tdd="true">
<name>Task 3: Serve permissioned singleton settings through Fill and Validate</name>
<files>cabana/settings.go, cabana/http.go, ../fonoteka.go/plugins/golem15/fonoteka/admin_settings.go, ../fonoteka.go/plugins/golem15/fonoteka/models/settings/fields.yaml, ../fonoteka.go/plugins/golem15/fonoteka/admin_settings_test.go</files>
<read_first>../fonoteka.go/plugins/golem15/fonoteka/models/settings.go, /media/nvme/dev/golem15/fonoteka/plugins/golem15/fonoteka/models/settings/fields.yaml, lagoon/fill.go, lagoon/validate.go, cabana/form_schema.go, cabana/http.go</read_first>
<behavior>
- Test 1: schema/GET/PUT require the settings permission before model/database work and return the D-17 localized typed form contract.
- Test 2: missing GET creates no row and returns exists false/default data; valid first PUT creates one row; identical repeated PUT leaves one row with unchanged values.
- Test 3: unknown/protected fields are ignored/rejected by writable projection, required/modeled Rules failures return 422, and failed update rolls back the singleton.
</behavior>
<action>Implement the D-17 HasSettings registry/service and D-09 routes for schema, GET, and PUT. Register the existing typed Fonoteka settings model and a complete embedded form schema under code `fonoteka` with `golem15.fonoteka.manage_settings`. For the flagged ADMIN-05 edge assumption, make missing GET side-effect-free with `exists: false` and a default-valued safe DTO, then make PUT lock/find-or-create the singleton and apply compiled writable projection, lagoon.Fill, and lagoon.Validate in one transaction. Treat identical PUT as success without adding rows or changing timestamps solely due to replay.</action>
<verify>
<automated>(cd ../fonoteka.go &amp;&amp; go test ./plugins/golem15/fonoteka -run '^TestAdminSettings(Schema|PermissionOrder|MissingRead|Create|IdempotentUpdate|Projection|Validation|Rollback)$' -count=1)</automated>
<fails_when>The command exits non-zero, reports no matching test, PostgreSQL is skipped, permission follows model/database work, GET creates a row, repeated PUT creates/mutates again, a protected key changes, required/Rules validation is skipped, or failure commits state.</fails_when>
</verify>
<acceptance_criteria>ADMIN-05 has explicit missing/create/repeat semantics and all D-17 schema, permission, Fill, Validate, projection, singleton, and transaction guarantees.</acceptance_criteria>
<done>Fonoteka settings are discoverable only when permitted and safely readable/updatable as one validated singleton.</done>
</task>
</tasks>
<threat_model>
## Trust Boundaries
| Boundary | Description |
|----------|-------------|
| backend principal→metadata | Authorization determines which controller/setting existence can be disclosed |
| settings JSON→singleton row | Untrusted fields attempt to mutate persistent global configuration |
## STRIDE Threat Register
| Threat ID | Category | Component | Severity | Disposition | Mitigation Plan |
|-----------|----------|-----------|----------|-------------|-----------------|
| T-09-18 | Tampering / Elevation | settings PUT | high | mitigate | Permission first, compiled writable projection, Fill/Validate, singleton row lock, transaction rollback, and protected-field fixtures in Task 3. |
| T-09-19 | Information Disclosure | navigation/settings metadata | medium | mitigate | Filter whole entries before serialization, validate targets/permissions at activation, and test no-permission responses for field leakage. |
| T-09-SC | Tampering | npm/pip/cargo installs | high | mitigate | No npm/pip/cargo install occurs; existing Go dependencies only, so the package-legitimacy gate remains closed. |
</threat_model>
<verification>
Run `(cd ../fonoteka.go && go test ./plugins/golem15/fonoteka -run '^(TestAdminMetadata|TestAdminSettings)' -count=1)`; it fails on non-zero exit, zero matched tests, catalog/source drift, backend/frontend identity crossover, metadata disclosure, settings mass assignment, singleton replay drift, or validation/transaction bypass.
</verification>
<success_criteria>
- Permission declarations and publisher/developer behavior match the tracked plugin source and D-01/D-03.
- D-18 navigation and settings metadata are stable and filter denied entries before serialization.
- D-17 settings schema/GET/PUT use permission-first writable projection, Fill, Validate, and singleton transactions.
- The flagged ADMIN-05 missing/create/repeat assumption has executable assembled backstop tests.
</success_criteria>
<output>
Create `.planning/phases/09-backend-admin-authentication-and-schema-pipeline/09-11-SUMMARY.md` when done.
</output>