feat(authz): remove SystemPlatform scope + permission-trie walk (Galaxy resolves via Equipment)

This commit is contained in:
Joseph Doherty
2026-06-12 21:54:28 -04:00
parent 95be607a07
commit b4b7cd7d0f
4 changed files with 35 additions and 101 deletions
@@ -33,16 +33,6 @@ public sealed class PermissionTrieTests
Kind = NodeHierarchyKind.Equipment,
};
private static NodeScope GalaxyTag(string cluster, string ns, string[] folders, string tag) =>
new()
{
ClusterId = cluster,
NamespaceId = ns,
FolderSegments = folders,
TagId = tag,
Kind = NodeHierarchyKind.SystemPlatform,
};
/// <summary>Verifies cluster-level grant cascades to every tag.</summary>
[Fact]
public void ClusterLevelGrant_Cascades_ToEveryTag()
@@ -111,72 +101,48 @@ public sealed class PermissionTrieTests
matches.ShouldBeEmpty();
}
/// <summary>Verifies Galaxy folder segment grant does not leak to sibling folder.</summary>
/// <summary>
/// Galaxy is now a standard Equipment-kind driver: Galaxy points are ordinary equipment
/// tags, so a grant on a Galaxy equipment resolves via the Equipment walk and must not leak
/// to a sibling Galaxy equipment.
/// </summary>
[Fact]
public void Galaxy_FolderSegment_Grant_DoesNotLeak_To_Sibling_Folder()
public void Galaxy_EquipmentScope_Grant_DoesNotLeak_To_Sibling_Equipment()
{
var paths = new Dictionary<string, NodeAclPath>(StringComparer.OrdinalIgnoreCase)
{
["folder-A"] = new(new[] { "ns-gal", "folder-A" }),
["gal-eq-A"] = new(new[] { "ns-gal", "area1", "line1", "gal-eq-A" }),
};
var rows = new[] { Row("cn=ops", NodeAclScopeKind.Equipment, "folder-A", NodePermissions.Read) };
var rows = new[] { Row("cn=ops", NodeAclScopeKind.Equipment, "gal-eq-A", NodePermissions.Read) };
var trie = PermissionTrieBuilder.Build("c1", 1, rows, paths);
var matchA = trie.CollectMatches(GalaxyTag("c1", "ns-gal", ["folder-A"], "tag1"), ["cn=ops"]);
var matchB = trie.CollectMatches(GalaxyTag("c1", "ns-gal", ["folder-B"], "tag1"), ["cn=ops"]);
var matchA = trie.CollectMatches(EquipmentTag("c1", "ns-gal", "area1", "line1", "gal-eq-A", "tag1"), ["cn=ops"]);
var matchB = trie.CollectMatches(EquipmentTag("c1", "ns-gal", "area1", "line1", "gal-eq-B", "tag1"), ["cn=ops"]);
matchA.Count.ShouldBe(1);
matchB.ShouldBeEmpty();
matchB.ShouldBeEmpty("grant at gal-eq-A must not apply to sibling gal-eq-B");
}
/// <summary>
/// Core-003 regression: grants matched during the SystemPlatform (Galaxy) folder walk must
/// report <see cref="NodeAclScopeKind.FolderSegment"/> in <see cref="MatchedGrant.Scope"/>,
/// not <see cref="NodeAclScopeKind.Equipment"/>. This distinguishes Galaxy folder grants
/// from UNS Equipment grants in the audit trail and Admin UI "Probe this permission" panel.
/// A grant on a Galaxy equipment tag is reported with its true Equipment-walk structural
/// scope (Equipment), not a Galaxy-specific scope — Galaxy now flows through the same
/// Equipment permission walk as every other equipment tag.
/// </summary>
[Fact]
public void Galaxy_FolderSegment_Grant_Reports_FolderSegment_Scope_Not_Equipment()
public void Galaxy_EquipmentScope_Grant_Reports_Equipment_Scope()
{
var paths = new Dictionary<string, NodeAclPath>(StringComparer.OrdinalIgnoreCase)
{
["section-X"] = new(new[] { "ns-gal", "section-X" }),
["gal-eq"] = new(new[] { "ns-gal", "area1", "line1", "gal-eq" }),
};
var rows = new[] { Row("cn=ops", NodeAclScopeKind.Equipment, "section-X", NodePermissions.Read) };
var rows = new[] { Row("cn=ops", NodeAclScopeKind.Equipment, "gal-eq", NodePermissions.Read) };
var trie = PermissionTrieBuilder.Build("c1", 1, rows, paths);
var matches = trie.CollectMatches(GalaxyTag("c1", "ns-gal", ["section-X"], "tag1"), ["cn=ops"]);
var matches = trie.CollectMatches(EquipmentTag("c1", "ns-gal", "area1", "line1", "gal-eq", "tag1"), ["cn=ops"]);
matches.Count.ShouldBe(1);
matches[0].Scope.ShouldBe(NodeAclScopeKind.FolderSegment,
"the trie walk reports the structural level where the grant was found — FolderSegment for Galaxy, not Equipment");
}
/// <summary>Verifies Galaxy deep folder path all segments report folder segment scope.</summary>
[Fact]
public void Galaxy_DeepFolderPath_AllSegments_Report_FolderSegment_Scope()
{
// A three-level folder path — each visited level that carries a grant must report FolderSegment.
var paths = new Dictionary<string, NodeAclPath>(StringComparer.OrdinalIgnoreCase)
{
["area"] = new(new[] { "ns-gal", "area" }),
["area/line"] = new(new[] { "ns-gal", "area", "line" }),
["area/line/cell"] = new(new[] { "ns-gal", "area", "line", "cell" }),
};
var rows = new[]
{
Row("cn=ops", NodeAclScopeKind.Equipment, "area", NodePermissions.Read),
Row("cn=ops", NodeAclScopeKind.Equipment, "area/line", NodePermissions.Read),
Row("cn=ops", NodeAclScopeKind.Equipment, "area/line/cell", NodePermissions.Read),
};
var trie = PermissionTrieBuilder.Build("c1", 1, rows, paths);
var matches = trie.CollectMatches(
GalaxyTag("c1", "ns-gal", ["area", "line", "cell"], "tag1"), ["cn=ops"]);
matches.Count.ShouldBe(3, "one match per folder level visited");
matches.ShouldAllBe(m => m.Scope == NodeAclScopeKind.FolderSegment,
"every matched folder level must report FolderSegment, never Equipment");
matches[0].Scope.ShouldBe(NodeAclScopeKind.Equipment,
"Galaxy equipment grants resolve via the Equipment walk and report Equipment scope");
}
/// <summary>Verifies cross-cluster grant does not leak.</summary>