Narrow UNS grants cannot browse/read/subscribe UNS folders or receive scripted-alarm Server-object events (opaque folder ids split as paths) #808
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Found while fixing #797 (by code reading; not live-gated).
AddressSpaceApplier.MaterialiseHierarchy mints UNS Area/Line/Equipment folders under their opaque entity ids (UnsAreaId / UnsLineId / EquipmentId, e.g. EQ-xxxxxxxxxxxx), while AclScopePathResolver plants UnsArea/UnsLine/Equipment grants at NAME paths [uns, Area, Line, Equipment]. DataPlaneAuthorizer.Evaluate/BuildScope splits the NodeId identifier, so with Security:Acl:Enabled:
#797 fixed only the Part 9 command gate for scripted conditions, via an explicit owner binding (live condition -> EquipmentId -> generation's EquipmentScopePaths). The same EquipmentId -> path lookup (DataPlaneAuthoritySnapshot.EquipmentScopePaths, extended to areas/lines) is the likely fix for folder ids and for scripted-condition event visibility. Tests must use production-shaped opaque folder ids, not the rig's synthetic EqA.
Confirmed by over-the-wire probe during the #797 review (commit
3d1b86b4, production-shaped UA-/UL-/EQ- opaque folders, condition s=scripted-eqa-hi, session with only an Equipment grant on its equipment):Consequence: #797's command-gate fix is necessary but not sufficient end-to-end. Acknowledge/Confirm/AddComment need the current EventId (a fabricated one returns BadEventIdUnknown), so a narrow principal still cannot ack its own scripted alarms in production. Shelve/Unshelve/Enable/Disable (no EventId) do work. The #797 wire tests obtain the EventId through a Cluster-grant observer session, so they do not cover this path. Also: the comment near OtOpcUaNodeManager.cs ~5270 ('an Equipment grant covers the UNS folder a native condition is wired into') is false in production for the same reason. And an Area whose name equals a scripted alarm id still gains Read/Subscribe on that condition (commands are now closed).
Fixed on master (merge b89f4580; commits
af5c9744+e39a1250, pushed in5bbe1e3c).The authority generation now carries
UnsFolderScopes(folder id → name path + folder-id chain, built byDeploymentArtifact.BuildUnsFolderScopeswith the same resolver that places grants).OtOpcUaNodeManager.ResolveUnsAclSubjectreports each UNS node's live placement, andDataPlaneAuthorizer.Evaluateplaces it against the generation before the decision cache. Covers UNS folders, virtual tags (s=EQ-…/Name, which were broken too), scripted conditions + children, reference variables, HistoryRead, folder event items, i=2253 delivery, Call and model-change filtering. Fail-closed: live ancestry must match the generation (a moved equipment/alarm is denied under old and new line grants until the rebuild); NUL ids rejected before the cache; unknown/removed opaque ids are decided only by realm-wide grants. Independent review found 4 gaps in the first commit, all fixed ine39a1250.Tests: new
UnsFolderScopeWireTests(opaque UA-/UL-/EQ- ids, incl. one artifact-driven test), each guard shown to be load-bearing. OpcUaServer.Tests 1141/1142 (1 skip), IntegrationTests 218/222 (4 skip), Runtime.Tests 1007/1010, Core.Tests 362.Live gate on docker-dev (
b89f4580, Security:Acl on): PASS on all steps. A key with only an Equipment grant oneqabrowsed only its own subtree, read its folder/alarm/vt/reference (siblingeqb: BadUserAccessDenied), received onlyeqa's alarm on i=2253 (sibling's fired first as a control, confirmed by a Cluster key), and acknowledged/confirmed with the received EventId (/alertsshowed the ack by the narrow key); acking the sibling was denied. Movingeqato a new line: the old-line grant lost access, the new-line grant gained it.ScadaBridge impact: no NodeId/wire change; with ACLs on, Area/Line/Equipment-scoped keys can now browse/read/subscribe/HistoryRead their subtree and ack/confirm/comment scripted and native alarms — a Cluster grant is no longer needed to ack. Narrow grantees no longer receive NodeDeleted model-change events for removed UNS folders/VTs/conditions.
Residuals: #826 (per-folder event items during the stale window).