Skip to content

feat(settings): tenant self-service settings, validate TENANT scope_id (#382) [stack 10/11] - #394

Merged
antosubash merged 17 commits into
tenancy/09-file-storagefrom
tenancy/10-settings
Oct 1, 2026
Merged

antosubash merged 17 commits into
tenancy/09-file-storagefrom
tenancy/10-settings

Conversation

@antosubash

Copy link
Copy Markdown
Owner

Closes #382 (which replaces #368). Stack 10/11 of the tenancy-adoption series (base: #393).

What

  • Overridable keys. SettingDefinition gets tenant_overridable and an optional check hook. Raising ValueError gives 422 and LookupError gives 404. The hook runs before every TENANT-scope write. Setting keeps its explicit (scope, scope_id, key) key and has no mixin.
  • Self-service routes: GET /api/settings/tenant/current plus GET/PUT/DELETE /api/settings/tenant/current/{key}.
    • They act on request.state.tenant_id only, never on a tenant id from the URL.
    • They accept only overridable keys.
    • With no active tenant they return 403.
    • The new settings.tenant.edit permission guards them. It is mapped onto tenant:owner and tenant:admin; members get 403.
  • Platform routes check TENANT scope_id with tenant_exists. Unknown ids get 404 on GET and PUT and 422 on POST. DELETE stays unvalidated so leftover rows can still be cleaned up.
  • tenants.settings.manage is retired in favour of the single settings.tenant.edit. The old permission was owner-only and nothing checked it.
  • Invalidation: each write publishes on settings.values after commit, keyed "<tenant>|<key>".
  • New page: /tenants/settings ("Organisation settings"). For each key it shows the platform value, the organisation's override and the effective value.

Tests

  • 22 in test_tenant_settings.py, 3 for the page and 2 JS tests.
  • make test-py: 3444 passed. make test-js: 465 passed. make lint and make doctor are clean.

Follow-ups

  • The /admin/settings form PUT path doesn't run check for TENANT rows; the JSON API does.
  • Setting descriptions and error details shown to tenants aren't translated yet.

https://claude.ai/code/session_01F8RiTBUJQnZmSq56qReZeV

…dation (#382)

- SettingDefinition gains tenant_overridable and an optional check hook
  (ValueError -> 422, LookupError -> 404) run on every TENANT-scope write.
- GET /api/settings/tenant/current (+ GET/PUT/DELETE .../{key}) act on
  request.state.tenant_id only, overridable keys only, guarded by the new
  settings.tenant.edit permission mapped onto tenant:owner/tenant:admin;
  403 with no active tenant, 422 for non-overridable keys.
- Platform routes validate a TENANT scope_id via tenant_exists: 404 on
  GET/PUT, 422 for a create body / store form; DELETE stays unvalidated.
- SYSTEM/TENANT writes publish a per-(tenant, key) notice on the
  settings.values invalidation channel after commit.
- tenants: retire the unused tenants.settings.manage permission; add the
  /tenants/settings page for owners/admins.

Claude-Session: https://claude.ai/code/session_01F8RiTBUJQnZmSq56qReZeV
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Oct 1, 2026 •

Copy link
Copy Markdown

Deploying simple-module-python with  Cloudflare Pages  Cloudflare Pages

Latest commit: ae88c53
Status: ✅  Deploy successful!
Preview URL: https://6f22a911.simple-module-python.pages.dev
Branch Preview URL: https://tenancy-10-settings.simple-module-python.pages.dev

View logs

…(review of #382)

PUT /admin/settings/{id} wrote a TENANT row's new value without the
tenant/key check the store form and the JSON routes run. Extract
tenant_update_error (row's own scope/tenant/key; skipped for
description-only or unchanged values) so views.py stays under the cap.

Claude-Session: https://claude.ai/code/session_01F8RiTBUJQnZmSq56qReZeV
…where (ship review)

SettingDefinition.clear_via names the route that owns clearing a key (an
upload route that reaps the stored file). The self-service, platform tenant,
by-id and admin-form deletes answer 422 pointing there while the tenant
exists; rows of deleted tenants stay clearable by an operator.

Claude-Session: https://claude.ai/code/session_01F8RiTBUJQnZmSq56qReZeV
…cope (ship review r2)

The managed-key guard was a per-route Depends that only covered TENANT rows,
so SYSTEM/USER rows of a clear_via key could be deleted generically and
orphan the stored file, and a direct service call bypassed it. It now lives in
SettingService.delete/delete_scoped (single row read): any scope raises
ManagedKeyError, mapped to 422 in the API and to a translated toast in the
Inertia store screen. Owners pass as_owner=True; a deleted tenant's leftover
TENANT row stays deletable. Listing queries split into _listing.py for the
300-line cap.

Claude-Session: https://claude.ai/code/session_01F8RiTBUJQnZmSq56qReZeV
… i18n keys (ship review r2)

Move the testable-packages helper next to the other module-settings prop
code and commit the regenerated i18n catalog for the new delete keys.

Claude-Session: https://claude.ai/code/session_01F8RiTBUJQnZmSq56qReZeV
@antosubash
antosubash added this pull request to stack #397 October 1, 2026 16:14
@antosubash
antosubash marked this pull request as ready for review October 1, 2026 16:17
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 1, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
🔒 Security Review ✅ Completed 2026-10-01T16:23:36.572314Z f1327d2 Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@antosubash
antosubash merged commit 1f176ee into main Oct 1, 2026
16 of 25 checks passed
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.

settings: current-tenant settings write surface for tenant owners, validate TENANT scope_id

1 participant