Scope all controller account lookups by CompanyId (defense-in-depth sweep)
Completes the read-path defense-in-depth pass flagged in the accounting audit:
every Accounts lookup in a controller now carries an explicit CompanyId predicate,
matching the standing rule in CLAUDE.md ("every FindAsync/GetAllAsync must include
an explicit CompanyId"). ~19 lookups across 12 controllers:
- Tier 1 (write-path): AccountsController duplicate account-number check (Create/Edit)
- Tier 2 (dropdowns/lists): Accounts (Index/year-end/parent), BankReconciliations,
Bills (bank list + receipt scan + suggest), Budgets, CatalogItems, Expenses,
FixedAssets, Inventory, JournalEntries chart dropdown, Vendors
- Tier 3 (accountIds.Contains display maps): JournalEntries/Reports/VendorCredits
detail views, scoped via the in-scope entity's CompanyId for uniformity
companyId source per controller: _tenantContext where available, else the in-scope
entity's CompanyId, else the current user. Build clean; 291 unit tests pass.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -118,13 +118,17 @@ Verification of O3+O4: `dotnet build` clean; `dotnet test tests/PowderCoating.Un
|
||||
|
||||
---
|
||||
|
||||
## Remaining (non-O1–O4) — known lower-risk follow-up
|
||||
Pure **read-path dropdown/display** account lookups still rely on the global tenant filter (correct for
|
||||
non-SuperAdmin; only a SuperAdmin acting inside a company could see another tenant's accounts in a picker).
|
||||
These are app-wide, not specific to the accounting posting path, e.g. `VendorCreditsController`
|
||||
PopulateDropdowns/Details, `BillsController` PopulateDropdowns and Index/edit account lists,
|
||||
`ExpensesController` filter + categorization pickers. Tracked here so they aren't mistaken for a regression;
|
||||
fold into a broader defense-in-depth pass if/when desired.
|
||||
## Read-path account lookup sweep (Tier 1–3) — **RESOLVED**
|
||||
Completed the app-wide defense-in-depth pass: every `Accounts` lookup in a controller now carries an
|
||||
explicit `CompanyId` predicate, matching the standing rule in CLAUDE.md. ~19 lookups across 12 controllers:
|
||||
- **Tier 1 (write-path validation):** `AccountsController` duplicate account-number check on Create/Edit.
|
||||
- **Tier 2 (dropdowns/lists):** `AccountsController` (Index/year-end/parent dropdown), `BankReconciliations`,
|
||||
`Bills` (bank list + receipt-scan + suggest), `Budgets`, `CatalogItems`, `Expenses`, `FixedAssets`,
|
||||
`Inventory`, `JournalEntries` (chart dropdown), `Vendors`.
|
||||
- **Tier 3 (`accountIds.Contains` display maps):** `JournalEntries` Details, `Reports` budget vs actual,
|
||||
`VendorCredits` Details — scoped via the in-scope entity's `CompanyId` for uniformity.
|
||||
`companyId` source per controller: `_tenantContext.GetCurrentCompanyId()` where available, else the
|
||||
in-scope entity's `CompanyId`, else `_userManager` current user. Build clean; 291 unit tests pass.
|
||||
|
||||
## Status
|
||||
All audit findings **O1–O4 are resolved** on `dev`. Original audit numbering #1–3/#5/#6/#8 remains
|
||||
|
||||
Reference in New Issue
Block a user