Merge fix/808-uns-folder-scope

This commit is contained in:
Joseph Doherty
2026-11-10 23:44:32 -05:00
18 changed files with 1603 additions and 75 deletions
+26 -2
View File
@@ -579,8 +579,32 @@ re-binds it. Fail-closed: an unbound condition (owner folder missing), a
removed/replaced instance, or an owner the generation cannot place is denied for **every** grant, `Cluster`
included — there is no realm-root or id-split fallback. The key, grant and generation are re-read on every call
(uncached). NodeIds and `AlarmCommand.AlarmId` are unchanged. With the flag **off** none of this is consulted.
⚠️ Read/Browse/Subscribe of UNS folders and scripted conditions, and Server-object event visibility, are still
decided on the NodeId's split, and UNS folder ids are opaque entity ids — Gitea #808.
**Where an opaque-id UNS node is placed for Read / Browse / Subscribe / HistoryRead (Gitea #808).** UNS Area /
Line / Equipment folders are minted under their opaque entity ids (`s=UA-…`, `s=UL-…`, `s=EQ-…`), an equipment's
virtual tags under `s=EQ-…/Name`, and scripted conditions plus their instance-scoped children under
`s=<ScriptedAlarmId>[/…]` — none of which is a grant path, so splitting them reached only `Cluster` / `uns` grants.
`DataPlaneAuthorizer` now asks the node manager (`ResolveUnsAclSubject`, attached when the authorizer is set) how
the node sits in the LIVE address space and places it against the CURRENT generation: a hierarchy folder at its
entity's path from the generation's `UnsFolderScopes` (built by the same `AclScopePathResolver` call that plants
`UnsArea`/`UnsLine`/`Equipment` grants), **only while the live folder-id ancestry equals the one the generation
deploys** (a moved equipment is denied under its old and new line's grants until the rebuild re-parents it); a
virtual tag / its sub-folder at its equipment's path plus a reserved segment; a scripted condition and its children
at the owning equipment's path plus that reserved segment, through exactly the #797 owner rule. The reserved segment
means only grants on the equipment or an ancestor confer them — a `Tag` grant beneath the equipment never does, and
its Browse transit does not expose the equipment's alarms or virtual tags. A scripted condition additionally requires
its owner folder's live ancestry to match the generation's (the folder rule). UNS reference variables
(`s=Area/Line/Equipment/Name`) keep their id as their path — but only while it equals their live equipment folder's
placement plus one name, so a moved or renamed equipment's variables (still on the old-name id) are denied for Read
and Write under old and new names until the address space re-points them. An id that no live node holds (a removed
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
**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,
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** |
| HistoryUpdate (insert / replace / update / delete) | role `HistoryUpdate` (from a `Cluster`-scoped grant) + key re-check | `HistoryUpdate` on that node's scope, decided **per node in the batch** |
| Read (every attribute, not just Value) | nothing | `Read` on that node's scope, decided per node in the batch |