188 lines
14 KiB
Markdown
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 && 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 && 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 && 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>
|