feat(auth): migrate knowledge_base + tenants + settings to TenantId (PMS-139 sweep batch 4) #163

Merged
longjacksonle merged 1 commit from feat/pms-139-sweep-kb-tenants-settings into main 2026-06-11 14:03:08 +02:00

What

Batch 4 of the PMS-139 typed-tenant sweep: migrate knowledge_base, tenants, and settings from bare Uuid tenant ids to the TenantId newtype.

How

  • knowledge_base, settings - standard sweeps: handlers use u.tenant() (TenantScoped, now imported in each routes.rs); every public service method takes tenant_id: TenantId. Transparent newtype -> SQL binds and %tenant_id tracing unchanged.
  • tenants - cross-tenant super-admin surface: handlers address an arbitrary path tenant, so the role/ownership guard stays a user.tenant_id vs path Uuid comparison and the post-guard service call wraps the path id with TenantId::from_trusted(...) (5 tenant-service + 2 delegated settings-service sites). create_tenant mints a new id and copy_default_config seeds it from the hardcoded default tenant; neither is a claim, so the seed helper keeps Uuid and create_tenant bridges its minted id through from_trusted at the single get_tenant readback. The two audit_write sites unwrap with tenant_id.get() transitionally.
  • middleware - settings::is_module_enabled feeds the RequireModuleEnabled gate in auth/middleware.rs; that caller now derives the tenant via TenantScoped::tenant(&user).
  • portal (out of scope) - the portal KB feed calls a migrated knowledge_base method with its verified contact-JWT tenant; bridged through from_trusted + SAFETY note, since portal runs on contact sessions (not CurrentUser) and gets its own pass later.
  • tests - tests/knowledge_base.rs wraps its literal tenant id with from_trusted.

Verification

  • cargo check --all-targets, cargo clippy --all-targets: clean.
  • cargo test --test knowledge_base (10), --test tenants (1), --test notifications_backfill (5): pass.
  • cargo test --doc: compile_fail newtype guard holds (2 pass / 3 ignored).
  • tests/settings.rs: 8 pass, 2 fail (disabled_module_returns_404_on_route_access, enabled_module_response_unchanged). Both are the pre-existing billing-404 baseline failures - they reproduce identically on clean main (verified via git stash + checkout), unrelated to this change.

Progress: 11 of ~17 modules migrated. ~6 remain; the audit_write / notifications::dispatch / TicketService hubs are swept last (flipping them removes every transitional .get()).

🤖 Generated with Claude Code

## What Batch 4 of the PMS-139 typed-tenant sweep: migrate `knowledge_base`, `tenants`, and `settings` from bare `Uuid` tenant ids to the `TenantId` newtype. ## How - **knowledge_base, settings** - standard sweeps: handlers use `u.tenant()` (`TenantScoped`, now imported in each `routes.rs`); every public service method takes `tenant_id: TenantId`. Transparent newtype -> SQL binds and `%tenant_id` tracing unchanged. - **tenants** - cross-tenant super-admin surface: handlers address an arbitrary path tenant, so the role/ownership guard stays a `user.tenant_id` vs path `Uuid` comparison and the post-guard service call wraps the path id with `TenantId::from_trusted(...)` (5 tenant-service + 2 delegated settings-service sites). `create_tenant` mints a new id and `copy_default_config` seeds it from the hardcoded default tenant; neither is a claim, so the seed helper keeps `Uuid` and `create_tenant` bridges its minted id through `from_trusted` at the single `get_tenant` readback. The two `audit_write` sites unwrap with `tenant_id.get()` transitionally. - **middleware** - `settings::is_module_enabled` feeds the `RequireModuleEnabled` gate in `auth/middleware.rs`; that caller now derives the tenant via `TenantScoped::tenant(&user)`. - **portal (out of scope)** - the portal KB feed calls a migrated `knowledge_base` method with its verified contact-JWT tenant; bridged through `from_trusted` + SAFETY note, since portal runs on contact sessions (not `CurrentUser`) and gets its own pass later. - **tests** - `tests/knowledge_base.rs` wraps its literal tenant id with `from_trusted`. ## Verification - `cargo check --all-targets`, `cargo clippy --all-targets`: clean. - `cargo test --test knowledge_base` (10), `--test tenants` (1), `--test notifications_backfill` (5): pass. - `cargo test --doc`: `compile_fail` newtype guard holds (2 pass / 3 ignored). - `tests/settings.rs`: 8 pass, 2 fail (`disabled_module_returns_404_on_route_access`, `enabled_module_response_unchanged`). Both are the pre-existing billing-404 baseline failures - they reproduce identically on clean `main` (verified via `git stash` + checkout), unrelated to this change. Progress: 11 of ~17 modules migrated. ~6 remain; the `audit_write` / `notifications::dispatch` / `TicketService` hubs are swept last (flipping them removes every transitional `.get()`). 🤖 Generated with [Claude Code](https://claude.com/claude-code)
feat(auth): migrate knowledge_base + tenants + settings to TenantId (PMS-139 sweep batch 4)
Some checks failed
Check / fmt + clippy + compile + tests (pull_request) Failing after 15s
Create release / Create release from merged PR (pull_request) Has been skipped
E2E (staging) / Playwright against staging (pull_request) Successful in 44s
Build OCI container / Build and push mokosh-api image (push) Successful in 3m47s
0a1006d657
Sweep batch 4 of the typed-tenant rollout. `knowledge_base` and `settings` are standard sweeps - handlers derive scope via `u.tenant()` (`TenantScoped`), every public service method takes `tenant_id: TenantId`. The transparent newtype binds/decodes/Displays as its inner `Uuid`, so SQL binds and `%tenant_id` tracing are untouched.

`tenants` is the cross-tenant case: its super-admin handlers address an arbitrary path-supplied tenant rather than the caller's own claim, so the role/ownership guard stays a bare `user.tenant_id` vs path `Uuid` comparison and the post-guard service call wraps the path id with `TenantId::from_trusted(...)`. The same two module-config endpoints that delegate to `SettingsService` follow suit. `create_tenant` mints a brand-new tenant id and `copy_default_config` seeds it from the hardcoded default tenant; neither id is a claim, so the seed helper keeps its `Uuid` and `create_tenant` bridges its minted id through `from_trusted` at the one `get_tenant` readback. The two `audit_write` calls in `tenants` unwrap with `tenant_id.get()` transitionally (audit hub sweeps last).

`settings::is_module_enabled` is consumed by the `RequireModuleEnabled` gate in `auth/middleware.rs`; that caller now derives the tenant via `TenantScoped::tenant(&user)` instead of reading the `user.tenant_id` field.

The out-of-scope portal KB feed calls a migrated `knowledge_base` method with its verified contact-JWT tenant; it bridges through `from_trusted` with a SAFETY note, since portal runs on contact sessions (not `CurrentUser`) and gets its own scoping pass later.

Direct-construction test `tests/knowledge_base.rs` wraps its literal tenant id with `TenantId::from_trusted(...)`. The two pre-existing `tests/settings.rs` billing-404 failures reproduce identically on clean main (verified by stash + checkout) and are unrelated to this change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
longjacksonle deleted branch feat/pms-139-sweep-kb-tenants-settings 2026-06-11 14:03:09 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
psa-systems/mokosh-server!163
No description provided.