64 lines
2.6 KiB
C#
64 lines
2.6 KiB
C#
using Microsoft.EntityFrameworkCore;
|
|
using ZB.MOM.WW.OtOpcUa.Configuration;
|
|
using ZB.MOM.WW.OtOpcUa.Configuration.Entities;
|
|
using ZB.MOM.WW.OtOpcUa.Configuration.Enums;
|
|
using ZB.MOM.WW.OtOpcUa.Core.Authorization;
|
|
|
|
namespace ZB.MOM.WW.OtOpcUa.Admin.Services;
|
|
|
|
/// <summary>
|
|
/// Runs an ad-hoc permission probe against a draft or published generation's NodeAcl rows —
|
|
/// "if LDAP group X asks for permission Y on node Z, would the trie grant it, and which
|
|
/// rows contributed?" Powers the AclsTab "Probe this permission" form per the #196 sub-slice.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Thin wrapper over <see cref="PermissionTrieBuilder"/> + <see cref="PermissionTrie.CollectMatches"/> —
|
|
/// the same code path the Server's dispatch layer uses at request time, so a probe result
|
|
/// is guaranteed to match what the live server would decide. The probe is read-only + has
|
|
/// no side effects; failing probes do NOT generate audit log rows.
|
|
/// </remarks>
|
|
public sealed class PermissionProbeService(OtOpcUaConfigDbContext db)
|
|
{
|
|
/// <summary>
|
|
/// Evaluate <paramref name="required"/> against the NodeAcl rows of
|
|
/// <paramref name="generationId"/> for a request by <paramref name="ldapGroup"/> at
|
|
/// <paramref name="scope"/>. Returns whether the permission would be granted + the list
|
|
/// of matching grants so the UI can show *why*.
|
|
/// </summary>
|
|
public async Task<PermissionProbeResult> ProbeAsync(
|
|
long generationId,
|
|
string ldapGroup,
|
|
NodeScope scope,
|
|
NodePermissions required,
|
|
CancellationToken ct)
|
|
{
|
|
ArgumentException.ThrowIfNullOrWhiteSpace(ldapGroup);
|
|
ArgumentNullException.ThrowIfNull(scope);
|
|
|
|
var rows = await db.NodeAcls.AsNoTracking()
|
|
.Where(a => a.GenerationId == generationId && a.ClusterId == scope.ClusterId)
|
|
.ToListAsync(ct).ConfigureAwait(false);
|
|
|
|
var trie = PermissionTrieBuilder.Build(scope.ClusterId, generationId, rows);
|
|
var matches = trie.CollectMatches(scope, [ldapGroup]);
|
|
|
|
var effective = NodePermissions.None;
|
|
foreach (var m in matches)
|
|
effective |= m.PermissionFlags;
|
|
|
|
var granted = (effective & required) == required;
|
|
return new PermissionProbeResult(
|
|
Granted: granted,
|
|
Required: required,
|
|
Effective: effective,
|
|
Matches: matches);
|
|
}
|
|
}
|
|
|
|
/// <summary>Outcome of a <see cref="PermissionProbeService.ProbeAsync"/> call.</summary>
|
|
public sealed record PermissionProbeResult(
|
|
bool Granted,
|
|
NodePermissions Required,
|
|
NodePermissions Effective,
|
|
IReadOnlyList<MatchedGrant> Matches);
|