Multi-tenancy hardening: explicit companyId on all typed repository methods
All typed repository methods that previously relied solely on global query filters now require an explicit companyId parameter, providing defense-in- depth so IgnoreQueryFilters calls cannot leak cross-tenant data. - IBillRepository/BillRepository: GetForIndexAsync, LoadForViewAsync, LoadForEditAsync, GetLastBillNumberAsync, GetLastPaymentNumberAsync, GetForDateRangeAsync all scoped to companyId - IJobRepository/JobRepository: LoadForDetailsAsync, LoadForEditAsync, LoadForStatusChangeAsync, GetChangeHistoryAsync, LoadForTemplateSnapshotAsync, GetReworkJobCountAsync - IQuoteRepository/QuoteRepository: LoadForDetailsAsync, GetChangeHistoryAsync, GetItemsWithCoatsAsync - IInvoiceRepository/InvoiceRepository: LoadForViewAsync - ICustomerRepository/CustomerRepository: LoadForDetailsAsync - INotificationLogRepository/NotificationLogRepository: all 6 FK methods - BillsController: ITenantContext injected, all call sites updated - AccountingExportController, InvoicesController, JobsController, JobTemplatesController, QuotesController: call sites updated Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -15,10 +15,10 @@ public class QuoteRepository : Repository<Quote>, IQuoteRepository
|
||||
public QuoteRepository(ApplicationDbContext context) : base(context) { }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<Quote?> LoadForDetailsAsync(int id)
|
||||
public async Task<Quote?> LoadForDetailsAsync(int id, int companyId)
|
||||
{
|
||||
var quote = await _context.Quotes
|
||||
.Where(q => q.Id == id && !q.IsDeleted)
|
||||
.Where(q => q.Id == id && q.CompanyId == companyId && !q.IsDeleted)
|
||||
.Include(q => q.Customer)
|
||||
.Include(q => q.PreparedBy)
|
||||
.Include(q => q.QuoteStatus)
|
||||
@@ -32,7 +32,7 @@ public class QuoteRepository : Repository<Quote>, IQuoteRepository
|
||||
// QuoteItems with nested coats and prep services loaded separately to avoid
|
||||
// cartesian explosion from multiple collection includes in a single query.
|
||||
quote.QuoteItems = await _context.QuoteItems
|
||||
.Where(qi => qi.QuoteId == id && !qi.IsDeleted)
|
||||
.Where(qi => qi.QuoteId == id && qi.CompanyId == companyId && !qi.IsDeleted)
|
||||
.Include(qi => qi.Coats)
|
||||
.ThenInclude(c => c.InventoryItem)
|
||||
.Include(qi => qi.Coats)
|
||||
@@ -58,10 +58,10 @@ public class QuoteRepository : Repository<Quote>, IQuoteRepository
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<List<QuoteChangeHistory>> GetChangeHistoryAsync(int quoteId)
|
||||
public async Task<List<QuoteChangeHistory>> GetChangeHistoryAsync(int quoteId, int companyId)
|
||||
{
|
||||
return await _context.QuoteChangeHistories
|
||||
.Where(h => h.QuoteId == quoteId && !h.IsDeleted)
|
||||
.Where(h => h.QuoteId == quoteId && h.CompanyId == companyId && !h.IsDeleted)
|
||||
.Include(h => h.ChangedBy)
|
||||
.OrderByDescending(h => h.ChangedAt)
|
||||
.AsNoTracking()
|
||||
@@ -83,10 +83,10 @@ public class QuoteRepository : Repository<Quote>, IQuoteRepository
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<List<QuoteItem>> GetItemsWithCoatsAsync(int quoteId)
|
||||
public async Task<List<QuoteItem>> GetItemsWithCoatsAsync(int quoteId, int companyId)
|
||||
{
|
||||
return await _context.QuoteItems
|
||||
.Where(qi => qi.QuoteId == quoteId && !qi.IsDeleted)
|
||||
.Where(qi => qi.QuoteId == quoteId && qi.CompanyId == companyId && !qi.IsDeleted)
|
||||
.Include(qi => qi.Coats)
|
||||
.ThenInclude(c => c.InventoryItem)
|
||||
.Include(qi => qi.Coats)
|
||||
|
||||
Reference in New Issue
Block a user