case 4712 - customer contacts reach only their own customer's work orders

Step 2 of 4712 (security review F13). The customer work-order rights checked
only tag-based feature switches, never which customer a work order belongs to.
New UserBiz.CustomerContactOwnsCustomerAsync (own customer, or for a head
office contact a customer of its head office), used by:
- workorder/customer/{id}: another customer's work order is not found (404);
- customer work order report (-100): exactly one work order, which must be the
  contact's (the renderer used every id it was given);
- work order attachment list and download for contacts; other attachment types
  are refused to contacts (the customer screens show them only on work orders).

Checked: other customer's work order 404 (was 200); report on another's or on
two work orders not found; own customer report still accepted (202).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-24 13:52:31 -07:00
parent 2b1adaa496
commit bd4494abac
6 changed files with 48 additions and 11 deletions

View File

@@ -157,11 +157,16 @@ namespace AyaNova.Api.Controllers
//Is this a customer user attempting to view a wo attachments??
var userType = UserTypeFromContext.Type(HttpContext.Items);
if (ayaType == AyaType.WorkOrder && (userType == UserType.Customer || userType == UserType.HeadOffice))
//case 4712: customer contacts see attachments only on their own customer's work orders
if (userType == UserType.Customer || userType == UserType.HeadOffice)
{
if (ayaType != AyaType.WorkOrder)
return StatusCode(403, new ApiNotAuthorizedResponse());
var wo = await ct.WorkOrder.AsNoTracking().Where(x => x.Id == ayaId).Select(x => new { x.Tags, x.CustomerId }).FirstOrDefaultAsync();
if (wo == null || !await UserBiz.CustomerContactOwnsCustomerAsync(ct, UserIdFromContext.Id(HttpContext.Items), wo.CustomerId))
return StatusCode(403, new ApiNotAuthorizedResponse());
//check if allowed
var woTags = await ct.WorkOrder.AsNoTracking().Where(x => x.Id == ayaId).Select(x => x.Tags).FirstOrDefaultAsync();
var custUserRights = await UserBiz.CustomerUserEffectiveRightsAsync(UserIdFromContext.Id(HttpContext.Items), woTags);
var custUserRights = await UserBiz.CustomerUserEffectiveRightsAsync(UserIdFromContext.Id(HttpContext.Items), wo.Tags);
if (!custUserRights.ThisWOCanAttachments)
return StatusCode(403, new ApiNotAuthorizedResponse());
}
@@ -553,11 +558,16 @@ namespace AyaNova.Api.Controllers
//is this allowed?
//Is this a customer user attempting to view a wo attachments??
if (dbObject.AttachToAType == AyaType.WorkOrder && (DownloadUser.UserType == UserType.Customer || DownloadUser.UserType == UserType.HeadOffice))
//case 4712: customer contacts download attachments only from their own customer's work orders
if (DownloadUser.UserType == UserType.Customer || DownloadUser.UserType == UserType.HeadOffice)
{
if (dbObject.AttachToAType != AyaType.WorkOrder)
return StatusCode(403, new ApiNotAuthorizedResponse());
var wo = await ct.WorkOrder.AsNoTracking().Where(x => x.Id == dbObject.AttachToObjectId).Select(x => new { x.Tags, x.CustomerId }).FirstOrDefaultAsync();
if (wo == null || !await UserBiz.CustomerContactOwnsCustomerAsync(ct, DownloadUser.Id, wo.CustomerId))
return StatusCode(403, new ApiNotAuthorizedResponse());
//check if allowed
var woTags = await ct.WorkOrder.AsNoTracking().Where(x => x.Id == dbObject.AttachToObjectId).Select(x => x.Tags).FirstOrDefaultAsync();
var custUserRights = await UserBiz.CustomerUserEffectiveRightsAsync(DownloadUser.Id, woTags);
var custUserRights = await UserBiz.CustomerUserEffectiveRightsAsync(DownloadUser.Id, wo.Tags);
if (!custUserRights.ThisWOCanAttachments)
return StatusCode(403, new ApiNotAuthorizedResponse());
}