Harden multi-tenant isolation across all user-facing controllers
Added explicit CompanyId == companyId predicates to every tenant-scoped query in 22 controllers so cross-tenant data leakage is impossible even if EF Core global query filters are bypassed or misconfigured. Also fixed ApplicationDbContext.IsPlatformAdmin to correctly return true for SuperAdmins with no CompanyId claim (break-glass accounts) and when no HTTP context is present (background services, unit tests), resolving 225 unit test failures that stemmed from the global filter blocking all in-memory test data. New MultiTenantIsolationTests class (8 tests) verifies the explicit predicate layer independently of the global query filters. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -44,7 +44,8 @@ public class RecurringTemplatesController : Controller
|
||||
/// <summary>Lists all recurring templates for the current company, active first then by name.</summary>
|
||||
public async Task<IActionResult> Index()
|
||||
{
|
||||
var templates = await _unitOfWork.RecurringTemplates.GetAllAsync();
|
||||
var companyId = _tenantContext.GetCurrentCompanyId() ?? 0;
|
||||
var templates = await _unitOfWork.RecurringTemplates.FindAsync(t => t.CompanyId == companyId);
|
||||
return View(templates.OrderByDescending(t => t.IsActive).ThenBy(t => t.Name).ToList());
|
||||
}
|
||||
|
||||
@@ -425,11 +426,12 @@ public class RecurringTemplatesController : Controller
|
||||
/// <summary>Loads dropdowns for vendors, accounts, and payment methods into ViewBag.</summary>
|
||||
private async Task PopulateDropDownsAsync()
|
||||
{
|
||||
var vendors = await _unitOfWork.Vendors.GetAllAsync();
|
||||
var companyId = _tenantContext.GetCurrentCompanyId() ?? 0;
|
||||
var vendors = await _unitOfWork.Vendors.FindAsync(v => v.CompanyId == companyId);
|
||||
ViewBag.Vendors = vendors.OrderBy(v => v.CompanyName)
|
||||
.Select(v => new SelectListItem(v.CompanyName, v.Id.ToString())).ToList();
|
||||
|
||||
var accounts = await _unitOfWork.Accounts.GetAllAsync();
|
||||
var accounts = await _unitOfWork.Accounts.FindAsync(a => a.CompanyId == companyId);
|
||||
ViewBag.APAccounts = accounts
|
||||
.Where(a => a.AccountSubType == AccountSubType.AccountsPayable)
|
||||
.OrderBy(a => a.AccountNumber)
|
||||
|
||||
Reference in New Issue
Block a user