fix(core): resolve Medium code-review finding (Core-003)
Add FolderSegment member to NodeAclScopeKind; update WalkSystemPlatform to report NodeAclScopeKind.FolderSegment (not Equipment) for each visited Galaxy folder level, so MatchedGrant.Scope in AuthorizationDecision.Provenance correctly distinguishes Galaxy folder grants from UNS Equipment grants in the audit trail and Admin UI diagnostics. Three regression tests added to PermissionTrieTests. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -125,6 +125,55 @@ public sealed class PermissionTrieTests
|
||||
matchB.ShouldBeEmpty();
|
||||
}
|
||||
|
||||
/// <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.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Galaxy_FolderSegment_Grant_Reports_FolderSegment_Scope_Not_Equipment()
|
||||
{
|
||||
var paths = new Dictionary<string, NodeAclPath>(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
["section-X"] = new(new[] { "ns-gal", "section-X" }),
|
||||
};
|
||||
var rows = new[] { Row("cn=ops", NodeAclScopeKind.Equipment, "section-X", NodePermissions.Read) };
|
||||
var trie = PermissionTrieBuilder.Build("c1", 1, rows, paths);
|
||||
|
||||
var matches = trie.CollectMatches(GalaxyTag("c1", "ns-gal", ["section-X"], "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");
|
||||
}
|
||||
|
||||
[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");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CrossCluster_Grant_DoesNotLeak()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user