Merge fix/826-folder-event-acl

This commit is contained in:
Joseph Doherty
2026-11-11 02:25:44 -05:00
14 changed files with 984 additions and 37 deletions
+31 -2
View File
@@ -600,9 +600,13 @@ and Write under old and new names until the address space re-points them. An id
folder / virtual tag / condition, a probe, a NodeDeleted model change) is decided only by a `Cluster` or `uns`
`Namespace` grant — so narrow grantees do not receive NodeDeleted for removed opaque-id nodes, and a removed id can
never match an unrelated narrow grant that shares its name; a four-segment (reference-shaped) id keeps its split.
Identifiers containing NUL are refused. Server-object (`i=2253`) delivery of a
Identifiers containing NUL are refused. A UNS variable the reference pass wired to its raw node (the `Organizes`
edge) is placed as a reference variable whatever its id spells (Gitea #826 — the id rule alone made an equipment's
references folder-owned when an Area was named exactly like that equipment's id, losing their `Tag`-grant match).
Server-object (`i=2253`) delivery of a
**scripted** condition is now decided on the condition alone (its folders add nothing in steady state and would
leak a moved alarm to its old owner). So a key holding only an `Equipment` grant can navigate to its equipment,
leak a moved alarm to its old owner). Direct event items on a folder get the same per-event decision (Gitea #826,
below). So a key holding only an `Equipment` grant can navigate to its equipment,
read its alarm children, receive its scripted alarm events through the Server object and Acknowledge / Confirm /
AddComment with the EventId it received. Fail-closed throughout: an unplaceable node is denied under every grant.
| HistoryRead (all four variants) | **nothing** | `HistoryRead` on each node's scope, decided **per node in the batch** |
@@ -709,6 +713,31 @@ Behaviours worth knowing before enabling it:
condition) runs on removal, rebuild and Server-object subscribe/delete. A deploy of N alarms under a
live Server-object item is therefore N bookkeeping steps, not N²/2 under the node-manager lock.
**Direct (per-folder) event items are filtered per event too** (Gitea #826). An item on an equipment or raw
device folder is fed by the SDK's own per-notifier fan-out (`MonitoredNode2`), which the #586 dispatcher never
touches — so after a deployment moved a scripted alarm (or dropped the reference projecting a native alarm into
an equipment), the OLD folder's subscribers kept receiving it, EventId included, until the rebuild landed, and
indefinitely after a failed apply (commands were already denied). With the flag **on**, each event to such an
item now also needs, against the CURRENT state:
- the event's condition to be the **live** instance (a torn-down one is withheld);
- the folder to still be a legitimate notifier of it — a node it hangs beneath, or a folder it is wired into
**that the current generation still projects its raw tag into** (`DataPlaneAuthoritySnapshot.TagReferencingEquipment`,
`RawPath → EquipmentIds`, decoded from the artifact's `UnsTagReferences` by the same resolver as the grants);
- and the condition to pass exactly the Server-object rule above for the item's session.
The generation check also narrows the **Server-object** rule: a wired folder whose reference the generation
dropped no longer admits the native alarm there either. **`ConditionRefresh`** on a folder item is filtered the
same way. The hook is `INodeManager3.ValidateEventRolePermissions`, which the node manager re-implements (the
base method is not virtual); the SDK memoizes that verdict per `(item, event type, SourceNode)`, so the node
manager clears each notifier's cache whole (sessions and orphaned, session-less subscriptions alike — an orphaned
subscription must not queue a moved alarm's events for a later `TransferSubscriptions`) on every new generation or
authorizer, on each `AclRevalidationTimer` tick, and at the end of every apply, and not at all while enforcement is
off. A verdict is decided on the condition currently live under the event's NodeId, so a replaced instance's late
event cannot leave a deny behind for its successor. Only alarm-bearing raw tags are carried in the reference map. Withheld events are not replayed and the client is not told; restoring the reference or
grant resumes the feed on the next event. ⚠️ A native alarm reaches an equipment folder's items under
enforcement only while the installed generation carries that reference — a hand-built authority (tests) must
supply `tagReferencingEquipment`. Inert with the flag off.
**Model-change and audit events on the Server object are gated too** (Gitea #588). The node manager's
own server-wide events — `GeneralModelChangeEvent` (NodeAdded / NodeDeleted / DataTypeChanged) and the
`AuditWriteUpdateEvent` of a rejected device write — used to go straight to `Server.ReportEvent`, where