case 4712 - notification subscriptions are the caller's own; object events only to subscribers who may read the record type
Step 6 of 4712 (security review F17). A subscription kept the UserId sent by the client, and object events (created, modified, deleted, age) were matched by record type only, so a user could get record names of types it may not read, delivered to any address. Now a subscription is created for the caller and an update cannot move it to another user, unless the caller administers users; and object events skip subscribers without read rights on the record type. Checked: Service's subscription asked for SuperUser is saved as its own; with "customer created" subscriptions, Inventory (no read on customers) got 0 events, Service 1. Full suite: 109 passed, 5 skipped, the same 2 known failures. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -72,6 +72,13 @@ namespace AyaNova.Biz
|
||||
// PROCESS STANDARD CREATE NOTIFICATION
|
||||
//
|
||||
//
|
||||
//case 4712: object events (created, modified, deleted, age) go only to subscribers with read rights on that type
|
||||
private static async Task<bool> SubscriberMayReadAsync(AyContext ct, long userId, AyaType aType)
|
||||
{
|
||||
var roles = await ct.User.AsNoTracking().Where(z => z.Id == userId).Select(z => z.Roles).FirstOrDefaultAsync();
|
||||
return AyaNova.Api.ControllerHelpers.Authorized.HasReadFullRole(roles, aType);
|
||||
}
|
||||
|
||||
public static async Task ProcessStandardObjectCreatedEvents(ICoreBizObjectModel newObject, AyContext ct)
|
||||
{
|
||||
//CREATED SUBSCRIPTIONS
|
||||
@@ -81,6 +88,8 @@ namespace AyaNova.Biz
|
||||
{
|
||||
//not for inactive users
|
||||
if (!await UserBiz.UserIsActive(sub.UserId)) continue;
|
||||
//case 4712: not for users who may not read this type of record
|
||||
if (!await SubscriberMayReadAsync(ct, sub.UserId, newObject.AyaType)) continue;
|
||||
if (ObjectHasAllSubscriptionTags(newObject.Tags, sub.Tags))
|
||||
{
|
||||
NotifyEvent n = new NotifyEvent()
|
||||
@@ -107,6 +116,8 @@ namespace AyaNova.Biz
|
||||
{
|
||||
//not for inactive users
|
||||
if (!await UserBiz.UserIsActive(sub.UserId)) continue;
|
||||
//case 4712: not for users who may not read this type of record
|
||||
if (!await SubscriberMayReadAsync(ct, sub.UserId, newObject.AyaType)) continue;
|
||||
if (ObjectHasAllSubscriptionTags(newObject.Tags, sub.Tags))
|
||||
{
|
||||
//Note: age is set by advance notice which is consulted by CoreJobNotify in it's run so the deliver date is not required here only the reference EventDate to check for deliver
|
||||
@@ -145,6 +156,8 @@ namespace AyaNova.Biz
|
||||
{
|
||||
//not for inactive users
|
||||
if (!await UserBiz.UserIsActive(sub.UserId)) continue;
|
||||
//case 4712: not for users who may not read this type of record
|
||||
if (!await SubscriberMayReadAsync(ct, sub.UserId, newObject.AyaType)) continue;
|
||||
if (ObjectHasAllSubscriptionTags(newObject.Tags, sub.Tags))
|
||||
{
|
||||
NotifyEvent n = new NotifyEvent()
|
||||
@@ -185,6 +198,8 @@ namespace AyaNova.Biz
|
||||
{
|
||||
//not for inactive users
|
||||
if (!await UserBiz.UserIsActive(sub.UserId)) continue;
|
||||
//case 4712: not for users who may not read this type of record
|
||||
if (!await SubscriberMayReadAsync(ct, sub.UserId, bizObject.AyaType)) continue;
|
||||
if (ObjectHasAllSubscriptionTags(bizObject.Tags, sub.Tags))
|
||||
{
|
||||
//TODO: On deliver should point to history event log record or take from there and insert into delivery message?
|
||||
|
||||
@@ -38,6 +38,9 @@ namespace AyaNova.Biz
|
||||
//
|
||||
internal async Task<NotifySubscription> CreateAsync(NotifySubscription newObject)
|
||||
{
|
||||
//case 4712: a subscription is the caller's own unless the caller administers users
|
||||
if (!AyaNova.Api.ControllerHelpers.Authorized.HasModifyRole(CurrentUserRoles, AyaType.User))
|
||||
newObject.UserId = UserId;
|
||||
await ValidateAsync(newObject);
|
||||
if (HasErrors)
|
||||
return null;
|
||||
@@ -78,6 +81,9 @@ namespace AyaNova.Biz
|
||||
AddError(ApiErrorCode.NOT_FOUND, "id");
|
||||
return null;
|
||||
}
|
||||
//case 4712: an update cannot move a subscription to another user unless the caller administers users
|
||||
if (!AyaNova.Api.ControllerHelpers.Authorized.HasModifyRole(CurrentUserRoles, AyaType.User))
|
||||
putObject.UserId = dbObject.UserId;
|
||||
if (dbObject.Concurrency != putObject.Concurrency)
|
||||
{
|
||||
AddError(ApiErrorCode.CONCURRENCY_CONFLICT);
|
||||
|
||||
Reference in New Issue
Block a user