case 4712 - customer contacts' pick lists limited to their own records
Step 4 of 4712 (security review F15). Pick lists checked only the Select role, which customer contacts have for most types, and took the columns from the request, so a contact could list every customer with phones and emails. For contacts now: the customer list holds only their own customer (head office contacts: their head office's customers); the unit list only their own customer's units (head office contacts: the chosen customer, which must be theirs; none until one is chosen); unit models as before; any other list 403. pick-list/single names only those records. New CustomerPickList variant for a single customer. Staff unchanged. Checked: the contact's customer list went from 72 customers to its own one; units only its own; vendors and users 403. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -75,6 +75,13 @@ namespace AyaNova.Api.Controllers
|
||||
return StatusCode(403, new ApiNotAuthorizedResponse());
|
||||
|
||||
|
||||
//case 4712: customer contacts get only the pick lists of the service request screen, limited to their own records
|
||||
var contactVariant = await ContactPickListVariantAsync(pickListParams.AyaType, pickListParams.ListVariant);
|
||||
if (contactVariant == CONTACT_DENIED)
|
||||
return StatusCode(403, new ApiNotAuthorizedResponse());
|
||||
if (contactVariant != null)
|
||||
pickListParams.ListVariant = contactVariant;
|
||||
|
||||
//Instantiate the business object handler
|
||||
PickListBiz biz = PickListBiz.GetBiz(ct, HttpContext);
|
||||
|
||||
@@ -122,6 +129,10 @@ namespace AyaNova.Api.Controllers
|
||||
if (!Authorized.HasSelectRole(HttpContext.Items, pickListSingleParams.AyaType))
|
||||
return StatusCode(403, new ApiNotAuthorizedResponse());
|
||||
|
||||
//case 4712: a customer contact gets names only of its own customers, their units, and unit models
|
||||
if (!await ContactMayNameRecordAsync(pickListSingleParams.AyaType, pickListSingleParams.Id))
|
||||
return StatusCode(403, new ApiNotAuthorizedResponse());
|
||||
|
||||
//Instantiate the business object handler
|
||||
PickListBiz biz = PickListBiz.GetBiz(ct, HttpContext);
|
||||
|
||||
@@ -276,9 +287,68 @@ namespace AyaNova.Api.Controllers
|
||||
}
|
||||
|
||||
|
||||
//case 4712: marker for a pick list a customer contact may not use
|
||||
private const string CONTACT_DENIED = "\u0000contact-denied";
|
||||
|
||||
//case 4712: for a customer contact, the variant that limits a pick list to its own records:
|
||||
//null when the caller is not a contact (request used as is), CONTACT_DENIED for any list the
|
||||
//customer screens do not use or a customer the contact does not own
|
||||
private async Task<string> ContactPickListVariantAsync(AyaType aType, string requestedVariant)
|
||||
{
|
||||
var userType = UserTypeFromContext.Type(HttpContext.Items);
|
||||
if (userType != UserType.Customer && userType != UserType.HeadOffice)
|
||||
return null;
|
||||
var userId = UserIdFromContext.Id(HttpContext.Items);
|
||||
var u = await ct.User.AsNoTracking().Where(x => x.Id == userId).Select(x => new { x.CustomerId, x.HeadOfficeId }).SingleOrDefaultAsync();
|
||||
long ownCustomerId = u?.CustomerId ?? 0;
|
||||
long ownHeadOfficeId = u?.HeadOfficeId ?? 0;
|
||||
switch (aType)
|
||||
{
|
||||
case AyaType.Customer:
|
||||
if (userType == UserType.Customer)
|
||||
return ownCustomerId == 0 ? CONTACT_DENIED : $"{ownCustomerId},{(int)AyaType.Customer}";
|
||||
return ownHeadOfficeId == 0 ? CONTACT_DENIED : $"{ownHeadOfficeId},{(int)AyaType.HeadOffice}";
|
||||
case AyaType.Unit:
|
||||
if (userType == UserType.Customer)
|
||||
return ownCustomerId == 0 ? CONTACT_DENIED : $"customerid:{ownCustomerId}";
|
||||
//head office contact: units of the customer chosen on the form, which must be one of its customers;
|
||||
//none until a customer is chosen
|
||||
var v = (requestedVariant ?? string.Empty).Split(':');
|
||||
long requestedCustomerId = 0;
|
||||
if (v.Length == 2 && v[0].Trim() == "customerid")
|
||||
long.TryParse(v[1].Trim(), System.Globalization.NumberStyles.Integer, System.Globalization.CultureInfo.InvariantCulture, out requestedCustomerId);
|
||||
if (requestedCustomerId == 0)
|
||||
return "customerid:-1";
|
||||
return await UserBiz.CustomerContactOwnsCustomerAsync(ct, userId, requestedCustomerId) ? $"customerid:{requestedCustomerId}" : CONTACT_DENIED;
|
||||
case AyaType.UnitModel:
|
||||
return requestedVariant;//unit models are not customer data
|
||||
default:
|
||||
return CONTACT_DENIED;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//case 4712: may the caller see this record's pick list name (always true for staff)
|
||||
private async Task<bool> ContactMayNameRecordAsync(AyaType aType, long id)
|
||||
{
|
||||
var userType = UserTypeFromContext.Type(HttpContext.Items);
|
||||
if (userType != UserType.Customer && userType != UserType.HeadOffice)
|
||||
return true;
|
||||
var userId = UserIdFromContext.Id(HttpContext.Items);
|
||||
switch (aType)
|
||||
{
|
||||
case AyaType.Customer:
|
||||
return id == 0 || await UserBiz.CustomerContactOwnsCustomerAsync(ct, userId, id);
|
||||
case AyaType.Unit:
|
||||
if (id == 0)
|
||||
return true;
|
||||
var unitCustomerId = await ct.Unit.AsNoTracking().Where(x => x.Id == id).Select(x => (long?)x.CustomerId).FirstOrDefaultAsync();
|
||||
return unitCustomerId != null && await UserBiz.CustomerContactOwnsCustomerAsync(ct, userId, (long)unitCustomerId);
|
||||
case AyaType.UnitModel:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}//eoc
|
||||
}//ens
|
||||
Reference in New Issue
Block a user