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:
@@ -67,4 +67,20 @@ public sealed class DashboardOptions
|
||||
/// Users with no matching group are rejected at login.
|
||||
/// </summary>
|
||||
public Dictionary<string, string> GroupToRole { get; init; } = new(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
/// <summary>
|
||||
/// LDAP group → dashboard visibility tags. A dashboard user's granted tag set
|
||||
/// is the union over the groups they belong to; a session is observable on the
|
||||
/// events hub when its tags intersect that grant. Independent of
|
||||
/// <see cref="GroupToRole"/> — a group may appear in either map, both, or
|
||||
/// neither. Visibility only: tags never gate data access.
|
||||
/// </summary>
|
||||
public Dictionary<string, string[]> GroupToTag { get; init; } = new(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
/// <summary>
|
||||
/// Who may observe a session whose owning API key carries no dashboard tags.
|
||||
/// Defaults to <see cref="Configuration.UntaggedSessionVisibility.AdminOnly"/>
|
||||
/// so an upgrade tightens rather than loosens.
|
||||
/// </summary>
|
||||
public UntaggedSessionVisibility UntaggedSessionVisibility { get; init; } = UntaggedSessionVisibility.AdminOnly;
|
||||
}
|
||||
|
||||
@@ -410,6 +410,36 @@ public sealed class GatewayOptionsValidator : OptionsValidatorBase<GatewayOption
|
||||
}
|
||||
}
|
||||
|
||||
// GroupToTag is validated for shape only, and independently of GroupToRole:
|
||||
// a group may grant a role, a tag, both, or neither. An empty map is legal —
|
||||
// it yields Viewers with no tag grant, which (under the default AdminOnly)
|
||||
// means they observe no session's events. That is the fail-closed posture.
|
||||
foreach (KeyValuePair<string, string[]> entry in options.GroupToTag)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(entry.Key))
|
||||
{
|
||||
builder.Add("MxGateway:Dashboard:GroupToTag keys (LDAP group names) must be non-blank.");
|
||||
}
|
||||
|
||||
if (entry.Value is null)
|
||||
{
|
||||
builder.Add($"MxGateway:Dashboard:GroupToTag['{entry.Key}'] must be a list of tags, not null.");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Array.Exists(entry.Value, string.IsNullOrWhiteSpace))
|
||||
{
|
||||
builder.Add($"MxGateway:Dashboard:GroupToTag['{entry.Key}'] tags must be non-blank.");
|
||||
}
|
||||
}
|
||||
|
||||
if (!Enum.IsDefined(options.UntaggedSessionVisibility))
|
||||
{
|
||||
builder.Add(
|
||||
$"MxGateway:Dashboard:UntaggedSessionVisibility must be '{nameof(UntaggedSessionVisibility.AdminOnly)}' "
|
||||
+ $"or '{nameof(UntaggedSessionVisibility.AllViewers)}'.");
|
||||
}
|
||||
|
||||
AddIfNotPositive(
|
||||
options.SnapshotIntervalMilliseconds,
|
||||
"MxGateway:Dashboard:SnapshotIntervalMilliseconds must be greater than zero.",
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
namespace ZB.MOM.WW.MxGateway.Server.Configuration;
|
||||
|
||||
/// <summary>
|
||||
/// Who may observe the dashboard event stream of a session that carries no
|
||||
/// dashboard tags. Tags gate dashboard event VISIBILITY only; they never widen
|
||||
/// or narrow data access.
|
||||
/// </summary>
|
||||
public enum UntaggedSessionVisibility
|
||||
{
|
||||
/// <summary>
|
||||
/// Default. An untagged session is visible only to a dashboard Administrator.
|
||||
/// Fails closed: a deployment that has not populated
|
||||
/// <see cref="DashboardOptions.GroupToTag"/> shows Viewers nothing.
|
||||
/// </summary>
|
||||
AdminOnly,
|
||||
|
||||
/// <summary>
|
||||
/// An untagged session is visible to every dashboard Viewer. Opt-in for a
|
||||
/// genuinely single-tenant deployment that wants the pre-ACL behaviour.
|
||||
/// </summary>
|
||||
AllViewers
|
||||
}
|
||||
Reference in New Issue
Block a user