feat(dashboard): GroupToTag / UntaggedSessionVisibility config (SEC-25)
Groundwork for the per-session dashboard event ACL (docs/plans/2026-07-10-dashboard-session-acl-tst15.md 3.2): a dashboard group can now grant visibility tags, and untagged sessions default to AdminOnly. Enforcement lands with the EventsHub ACL; nothing consumes the grant yet. GroupToTag is deliberately uncoupled from GroupToRole - a group may appear in either map, both, or neither - and is validated for shape only. Tags gate dashboard event visibility, never data access.
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
namespace ZB.MOM.WW.MxGateway.Server.Dashboard;
|
||||
|
||||
/// <summary>
|
||||
/// Single source of truth for mapping a user's LDAP groups to the dashboard
|
||||
/// visibility tags they are granted (<c>MxGateway:Dashboard:GroupToTag</c>).
|
||||
/// Sibling of <see cref="DashboardGroupRoleMapping"/> and deliberately follows
|
||||
/// the same group-matching rules (full DN first, leading-RDN fallback,
|
||||
/// case-insensitive) so operators write one kind of group key for both maps.
|
||||
/// Tags gate dashboard event VISIBILITY only; they are never a data-access
|
||||
/// constraint.
|
||||
/// </summary>
|
||||
internal static class DashboardGroupTagMapping
|
||||
{
|
||||
/// <summary>
|
||||
/// Maps the user's LDAP groups to the union of the tags those groups grant.
|
||||
/// A group with no entry in the map contributes nothing; duplicate tags
|
||||
/// across groups collapse (case-insensitively). Returns an empty set when no
|
||||
/// group matches — an empty grant, which the ACL treats as "sees no tagged
|
||||
/// session".
|
||||
/// </summary>
|
||||
/// <param name="groups">The collection of LDAP groups the user belongs to.</param>
|
||||
/// <param name="groupToTag">The mapping from group names to granted tags.</param>
|
||||
/// <returns>The distinct tags granted across all of the user's groups.</returns>
|
||||
internal static IReadOnlySet<string> MapGroupsToTags(
|
||||
IEnumerable<string> groups,
|
||||
IReadOnlyDictionary<string, string[]> groupToTag)
|
||||
{
|
||||
HashSet<string> tags = new(StringComparer.OrdinalIgnoreCase);
|
||||
if (groupToTag.Count == 0)
|
||||
{
|
||||
return tags;
|
||||
}
|
||||
|
||||
foreach (string group in groups)
|
||||
{
|
||||
string normalizedGroup = group.Trim();
|
||||
|
||||
if (!groupToTag.TryGetValue(normalizedGroup, out string[]? granted)
|
||||
&& !groupToTag.TryGetValue(
|
||||
DashboardGroupRoleMapping.ExtractFirstRdnValue(normalizedGroup),
|
||||
out granted))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (granted is null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (string tag in granted)
|
||||
{
|
||||
tags.Add(tag);
|
||||
}
|
||||
}
|
||||
|
||||
return tags;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user