5.5 KiB
ADR 0001: Work-order media uses single-org scope (board-aligned)
Status
Proposed — awaiting approval (2026-08-05).
This ADR is not self-accepted by the PR author. Approval is required from:
- CODEOWNERS team
@Sea-Haven-Industries/internal-dev(see.github/CODEOWNERS) - SH-116 product owner
Until approved (or until real tenant enforcement lands), this document records a pending exception request against the hard rule below — it does not waive the rule on its own.
Context
SH-116 requires that cross-tenant, unauthorized, and out-of-scope media access
be rejected without metadata disclosure. The repository hard rule
(ARCHITECTURE_AND_CODE_QUALITY.md §2) requires server-derived tenant
scope: filtering by tenant/customer/owner comes from the authenticated
principal and the data layer, never from client-supplied body/query as the
source of truth.
Verified against the current codebase:
- There is no
TenantId/CustomerIdcolumn onWorkOrder,WorkOrderAttachments, orApplicationUser. WorkOrder.Customeris free-text (nvarchar), not a FK.- JWT issuance (
AuthenticationService.GetToken) emits onlyName,NameIdentifier,Jti, andRole— no tenant/customer claim. - Board, search, and detail already treat the deployment as a single
organization via
ApplyBaseScope(non-deleted, non-template).
Introducing a multi-tenant key requires product modeling plus a schema
migration, which is out of scope for the SH-116 media contract and blocked by
AGENTS.md (no migrations / product behavior without explicit instruction).
Decision (proposed)
Until real tenant enforcement exists, work-order media authorization matches the board:
- Organization boundary =
ApplyBaseScopein the data layer (istemplate != trueand not deleted). Lookups outside that set resolve as missing →NotFound/ null (no disclosure). - Authorization at service entry from claims: staff roles
(
Admin,Manager,Dispatcher,Supervisor) may read/mutate any in-org work order; roleUser(technician) only whenWorkOrder.AssignTo == actorId; delete remains staff-only. - Scope is never taken from request body or query as the trust source;
actorIdand roles come from the authenticated principal.
This is not a substitute for SH-116 cross-tenant isolation. It is the interim behavior while the exception is under review or real tenant keys land.
Accepted risk (while Proposed / if Accepted)
Any authenticated staff principal who knows a numeric work-order id can
read or mutate media for that work order, provided it passes ApplyBaseScope.
There is no server-derived tenant/customer boundary separating staff access
across customers. Deleted / template / missing ids are not a cross-tenant
test; they only prove the ApplyBaseScope filter.
Consequences
- Tests for deleted, template, and missing work-order ids cover
ApplyBaseScoperejection only — they must not be labeled as SH-116 cross-tenant coverage. - Staff org-wide access by numeric id remains intentional and aligned with the board for this interim state.
- Merge of PR #47 that relies on this ADR requires either:
- formal approval of this ADR by the approvers listed in Status, or
- landing of real tenant enforcement (see below).
Path to real enforcement
Candidate design for the deferred multi-tenant work (tracked in the linked Jira ticket):
- Tenant key — reuse the existing
Accountsentity (CRM customer) as the customer boundary; addWorkOrder.AccountId(FK) andApplicationUser.AccountId(or an equivalent membership table). - Claim — emit a server-derived
account_id(or equivalent) claim inAuthenticationService.GetTokenfrom the authenticated user’s account membership; never accept account id from body/query as trust source. - Data filter — extend
ApplyBaseScope(or a sibling filter) so board, detail, search, and media loads restrict by the claim-derived account scope; staff may still be broader if product defines org-wide roles, but that must be an explicit claims rule, not “any numeric id”. - Backfill — map free-text
WorkOrder.Customerstrings toAccountsrows where possible; unresolved rows need a product decision (block, orphan bucket, or manual remapping). - Tests — add true cross-tenant rejection tests (staff/tech of account A
cannot read or mutate media of a work order owned by account B) with stable
NotFound/Forbiddenand no metadata disclosure.
Excepted rule
Hard rule: server-derived tenant scope
(ARCHITECTURE_AND_CODE_QUALITY.md §2).
Requested clarification while this ADR is Proposed/Accepted: in the work-order
domain, the interim server-derived scope key is the organization boundary
enforced by ApplyBaseScope plus claims-derived role/assignee — not a
TenantId/CustomerId column. Absence of a multi-tenant key is a temporary
product/architecture gap until superseded by the path above.
Review / expiry
Re-review by 2027-02-04, or earlier if product introduces
TenantId/CustomerId/AccountId on work orders or JWT claims, or when the
linked multi-tenant ticket closes.
References
- SH-116 — Completion document: fields + media categorization
- SH-221 — Server-derived tenant/customer scope for Work Order domain (deferred enforcement)
- PR that relies on this ADR: Sea-Haven-Industries/shoc-backend#47
WorkOrderBoardQueryFilters.ApplyBaseScopeWorkOrderMediaAuthorizationARCHITECTURE_AND_CODE_QUALITY.md§2, §10REVIEW_AND_PR_FRAMEWORK.md§7, §8