This commit is contained in:
2026-08-21 11:17:55 +03:00
parent f14dc633ae
commit 61ec566f63
11 changed files with 403 additions and 28 deletions
@@ -1,5 +1,10 @@
using Esiur.Core;
using Esiur.Data;
using Esiur.Data.Types;
using Esiur.Protocol;
using Esiur.Resource;
using Esiur.Security.Authority;
using Esiur.Security.Permissions;
namespace Esiur.Tests.Unit.Integration;
@@ -36,10 +41,68 @@ public class AttachmentSecurityTests
Assert.Equal(1, cluster.Connection.ResourceAttachRequestCount);
}
[Fact]
public async Task PropertyNotifications_RespectPerPropertyReadPermission()
{
PropertyBroadcastResource? source = null;
await using var cluster = await IntegrationCluster.StartAsync(async warehouse =>
{
var permissions = new PropertyBroadcastPermissions();
warehouse.RegisterManager(permissions);
source = await warehouse.Put("sys/property-broadcast", new PropertyBroadcastResource());
source.Instance!.Managers.Add(permissions);
}).WaitAsync(TimeSpan.FromSeconds(10));
var remote = (EpResource)await cluster.Connection.Get("sys/property-broadcast");
var received = new List<string>();
remote.Instance.PropertyModified += info => received.Add(info.PropertyDef.Name);
source!.Secret = 7;
source.Visible = 9;
var deadline = DateTime.UtcNow + TimeSpan.FromSeconds(3);
while (!received.Contains(nameof(PropertyBroadcastResource.Visible)) &&
DateTime.UtcNow < deadline)
await Task.Delay(20);
Assert.Contains(nameof(PropertyBroadcastResource.Visible), received);
Assert.DoesNotContain(nameof(PropertyBroadcastResource.Secret), received);
}
static Task<IntegrationCluster> StartCluster()
=> IntegrationCluster.StartAsync(async warehouse =>
{
await warehouse.Put("sys/first", new RateLimitedResource());
await warehouse.Put("sys/second", new RateLimitedResource());
});
sealed class PropertyBroadcastPermissions : IPermissionsManager
{
public Map<string, object> Settings { get; } = new();
public Ruling Applicable(
IResource resource,
Session session,
ActionType action,
MemberDef member,
object inquirer = null!)
{
if (action == ActionType.Attach)
return Ruling.Allowed;
if (action == ActionType.GetProperty)
return member?.Name == nameof(PropertyBroadcastResource.Secret)
? Ruling.Denied
: Ruling.Allowed;
return Ruling.DontCare;
}
public bool Initialize(Map<string, object> settings, IResource resource) => true;
}
}
[Resource]
public partial class PropertyBroadcastResource
{
[Export] int secret;
[Export] int visible;
}