feat(tenants): finish PMS-21 story (TenantScope extractor, feature-gate /tenants) #23

Closed
David wants to merge 0 commits from feat/tenants-story-pms-21 into main
Owner

Implements YouTrack story PMS-21 (tenants + multi-tenancy enforcement). One commit per sub-task.

Sub-tasks

  • PMS-22 (F5) module-config endpoints: already shipped in tenants/routes.rs (GET + PUT /api/v1/tenants/:tenant_id/modules/:module); closed administratively.
  • PMS-23 tenant scoping extractor: new TenantScope { tenant_id, user } FromRequestParts extractor that hard-binds tenant id to the authenticated caller's claim, so handlers stop copy-pasting user.tenant_id by hand. Existing RequireAuth callsites can migrate incrementally; new handlers should take scope: TenantScope.
  • PMS-24 gate /tenants behind multi-tenant feature: .nest("/tenants", ...) and TenantService::new are only built under #[cfg(feature = "multi-tenant")]. Verified both --features multi-tenant (default) and --no-default-features --features single-tenant,server compile clean.

Behaviour-visible changes

  • Multi-tenant build (default): no change. /api/v1/tenants/* continues to work.
  • Single-tenant build: /api/v1/tenants/* now returns the global fallback (HTML "this is an API endpoint") because the route is absent. Operators of single-tenant deployments no longer have a tenant-CRUD foot-gun.

Test plan

  • cargo check --bin mokosh-server clean (verified locally, both feature configurations).
  • Multi-tenant smoke: GET /api/v1/tenants/:id/modules/:module returns expected JSON for a known tenant + module.
  • Single-tenant smoke: rebuild with --no-default-features --features single-tenant,server; /api/v1/tenants/* 404s.

Closes #PMS-21

Implements YouTrack story PMS-21 (tenants + multi-tenancy enforcement). One commit per sub-task. ## Sub-tasks - PMS-22 (F5) module-config endpoints: already shipped in `tenants/routes.rs` (GET + PUT `/api/v1/tenants/:tenant_id/modules/:module`); closed administratively. - PMS-23 tenant scoping extractor: new `TenantScope { tenant_id, user }` `FromRequestParts` extractor that hard-binds tenant id to the authenticated caller's claim, so handlers stop copy-pasting `user.tenant_id` by hand. Existing `RequireAuth` callsites can migrate incrementally; new handlers should take `scope: TenantScope`. - PMS-24 gate /tenants behind multi-tenant feature: `.nest("/tenants", ...)` and `TenantService::new` are only built under `#[cfg(feature = "multi-tenant")]`. Verified both `--features multi-tenant` (default) and `--no-default-features --features single-tenant,server` compile clean. ## Behaviour-visible changes - Multi-tenant build (default): no change. `/api/v1/tenants/*` continues to work. - Single-tenant build: `/api/v1/tenants/*` now returns the global fallback (HTML "this is an API endpoint") because the route is absent. Operators of single-tenant deployments no longer have a tenant-CRUD foot-gun. ## Test plan - [ ] `cargo check --bin mokosh-server` clean (verified locally, both feature configurations). - [ ] Multi-tenant smoke: `GET /api/v1/tenants/:id/modules/:module` returns expected JSON for a known tenant + module. - [ ] Single-tenant smoke: rebuild with `--no-default-features --features single-tenant,server`; `/api/v1/tenants/*` 404s. Closes #PMS-21
Audit cross-cutting #8 / PMS-23: every service method takes `tenant_id: Uuid` as the first parameter, but until now there was nothing in the handler signature making the source of that id obvious. Handlers copied `user.tenant_id` by hand; a new handler that forgot would leak across tenants.

`TenantScope { tenant_id, user }` is a `FromRequestParts` extractor that hard-binds the tenant id to the authenticated caller's claim. New handlers should take a `scope: TenantScope` parameter and pass `scope.tenant_id` to the service; existing `RequireAuth` callsites can migrate incrementally. Handlers that need cross-tenant access (super-admin only) must take an additional path / query parameter and gate on role explicitly.

Re-exported alongside `RequireAuth` and the role-requirement family.

#PMS-23 State Done
feat(router): gate /api/v1/tenants behind the multi-tenant feature
All checks were successful
Create release / Create release from merged PR (pull_request) Has been skipped
c38cdeab5e
The `multi-tenant` / `single-tenant` cargo features have existed in `AppConfig::is_multi_tenant` since day one but were inert at the routing layer — both builds exposed `/api/v1/tenants/*`. In a single-tenant deployment that's a foot-gun: a CRUD endpoint that can spawn extra tenant rows breaks the deployment's load-bearing assumption.

Now `.nest("/tenants", ...)` is only added under `#[cfg(feature = "multi-tenant")]`, and `TenantService` is constructed only in that mode. Verified both `--features multi-tenant` (default) and `--no-default-features --features single-tenant,server` builds compile clean.

#PMS-24 State Done
vas2000-work closed this pull request 2026-05-21 02:42:54 +02:00
Some checks are pending
Create release / Create release from merged PR (pull_request) Has been skipped
Check / * (pull_request)
Required
E2E / * (pull_request)
Required

Pull request closed

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!23
No description provided.