case 4723 - direct email and direct notification for staff only, record rights

notify/direct-smtp: customer contacts refused; an address taken from a
Customer, HeadOffice, Vendor or User record needs read rights on that type
(a contact's user record: User or Customer rights). Staff may still give any
address (report scripts use it). The delivery-on check runs after these.
notify/direct-message: customer contacts and subcontractors refused, as the
client already hides it from them. F3 and F4 fixed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-24 18:30:08 -07:00
parent 52d66bcf10
commit f3ba5156c7
3 changed files with 32 additions and 9 deletions

View File

@@ -204,6 +204,10 @@ namespace AyaNova.Api.Controllers
if (!ModelState.IsValid)
return BadRequest(new ApiErrorResponse(ModelState));
//case 4723: staff only, as in the client (no customer contacts or subcontractors)
var userType = UserTypeFromContext.Type(HttpContext.Items);
if (userType == UserType.Customer || userType == UserType.HeadOffice || userType == UserType.ServiceContractor)
return StatusCode(403, new ApiNotAuthorizedResponse());
foreach (long l in notifyDirectMessage.Users)
{
@@ -247,11 +251,13 @@ namespace AyaNova.Api.Controllers
if (!ModelState.IsValid)
return BadRequest(new ApiErrorResponse(ModelState));
if (!ServerGlobalOpsSettingsCache.Notify.SmtpDeliveryActive)
return BadRequest(new ApiErrorResponse(ApiErrorCode.VALIDATION_MISSING_PROPERTY, null, "Email notifications are set to OFF at server, unable to send 'on request' type SMTP notification"));
//for now I'm allowing any authenticated user to call this route intentionally as the use case is for our own internal messaging from the Customer form only or for API users who want to send
//email when rendering a report (case 4310).
//Staff users may send to any address: the use case is our own messaging from the Customer form etc and
//API users and report scripts that send email when rendering a report (case 4310).
//case 4723: customer contacts may not use this route at all; an address taken from a record
//needs read rights on that record
var userType = UserTypeFromContext.Type(HttpContext.Items);
if (userType == UserType.Customer || userType == UserType.HeadOffice)
return StatusCode(403, new ApiNotAuthorizedResponse());
//validate incoming data
if (string.IsNullOrWhiteSpace(notifyDirectSMTP.Subject))
@@ -275,6 +281,8 @@ namespace AyaNova.Api.Controllers
return BadRequest(new ApiErrorResponse(ApiErrorCode.VALIDATION_MISSING_PROPERTY, "ToAddress", "No address or object type and id specified no where to send this"));
case AyaType.Customer:
{
if (!Authorized.HasReadFullRole(HttpContext.Items, AyaType.Customer))
return StatusCode(403, new ApiNotAuthorizedResponse());
var o = await ct.Customer.AsNoTracking().Where(x => x.Id == notifyDirectSMTP.ObjectId).Select(x => new { x.Name, x.EmailAddress, x.Active }).FirstOrDefaultAsync();
if (o == null)
{
@@ -289,6 +297,8 @@ namespace AyaNova.Api.Controllers
break;
case AyaType.HeadOffice:
{
if (!Authorized.HasReadFullRole(HttpContext.Items, AyaType.HeadOffice))
return StatusCode(403, new ApiNotAuthorizedResponse());
var o = await ct.HeadOffice.AsNoTracking().Where(x => x.Id == notifyDirectSMTP.ObjectId).Select(x => new { x.Name, x.EmailAddress, x.Active }).FirstOrDefaultAsync();
if (o == null)
{
@@ -303,6 +313,8 @@ namespace AyaNova.Api.Controllers
break;
case AyaType.Vendor:
{
if (!Authorized.HasReadFullRole(HttpContext.Items, AyaType.Vendor))
return StatusCode(403, new ApiNotAuthorizedResponse());
var o = await ct.Vendor.AsNoTracking().Where(x => x.Id == notifyDirectSMTP.ObjectId).Select(x => new { x.Name, x.EmailAddress, x.Active }).FirstOrDefaultAsync();
if (o == null)
{
@@ -317,11 +329,15 @@ namespace AyaNova.Api.Controllers
break;
case AyaType.User:
{
var o = await ct.User.Include(z => z.UserOptions).AsNoTracking().Where(x => x.Id == notifyDirectSMTP.ObjectId).Select(x => new { x.Name, x.UserOptions.EmailAddress, x.Active }).FirstOrDefaultAsync();
var o = await ct.User.Include(z => z.UserOptions).AsNoTracking().Where(x => x.Id == notifyDirectSMTP.ObjectId).Select(x => new { x.Name, x.UserOptions.EmailAddress, x.Active, x.UserType }).FirstOrDefaultAsync();
if (o == null)
{
return BadRequest(new ApiErrorResponse(ApiErrorCode.NOT_FOUND, "ObjectId"));
}
//a customer contact's user record is read with Customer rights, as on the Contact form
bool isContact = o.UserType == UserType.Customer || o.UserType == UserType.HeadOffice;
if (!Authorized.HasReadFullRole(HttpContext.Items, AyaType.User) && !(isContact && Authorized.HasReadFullRole(HttpContext.Items, AyaType.Customer)))
return StatusCode(403, new ApiNotAuthorizedResponse());
if (string.IsNullOrWhiteSpace(o.EmailAddress))
return BadRequest(new ApiErrorResponse(ApiErrorCode.VALIDATION_MISSING_PROPERTY, "EmailAddress", $"Vendor {o.Name} doesn't have an email address no where to send this"));
if (o.Active == false)
@@ -335,6 +351,10 @@ namespace AyaNova.Api.Controllers
}
}
if (!ServerGlobalOpsSettingsCache.Notify.SmtpDeliveryActive)
return BadRequest(new ApiErrorResponse(ApiErrorCode.VALIDATION_MISSING_PROPERTY, null, "Email notifications are set to OFF at server, unable to send 'on request' type SMTP notification"));
var UserId = UserIdFromContext.Id(HttpContext.Items);
IMailer m = AyaNova.Util.ServiceProviderProvider.Mailer;