docs(acl): record the shared-root-folder browse-transit mapping
CLAUDE.md's "Browse needs ancestor visibility" bullet and docs/security.md's ancestor-visibility bullet now name the topmost ancestor: the single shared root folder (s=OtOpcUa, raw namespace) that both realm trees hang under, its mapping onto the trie's zero-segment realm-root scope, the either-realm transit (it fronts both trees), and the realm qualification. Also corrects the adjacent security.md bullet that claimed "the two namespace root folders remain visible" — there is one anchor, not one per namespace, and what an ungranted session sees is the ns=0 Objects listing of it rather than a browse into it. Flagged rather than silently left, since it contradicts the mechanism the new bullet documents.
This commit is contained in:
@@ -801,7 +801,13 @@ Two things to know before relying on any of this:
|
||||
grant deep in the tree is reached by browsing the nodes above it, and the trie inherits downward
|
||||
only, so `PermissionTrieNode.BrowseTransitPrincipals` marks the path. Planting a real `Browse` grant
|
||||
on each ancestor instead would be collected by the additive downward walk and leak `Browse` onto
|
||||
every sibling subtree beneath that ancestor.
|
||||
every sibling subtree beneath that ancestor. ⚠️ **The topmost ancestor is the single shared root
|
||||
folder** (`s=OtOpcUa`, raw namespace) that BOTH realm trees hang under — its identifier is a display
|
||||
name, not a path, so `DataPlaneAuthorizer.BuildScope` maps it onto the trie's **zero-segment
|
||||
realm-root scope** (`V3NodeIds.SharedRootFolderId`, realm-qualified: in the UNS namespace that
|
||||
identifier is an ordinary Area), and its Browse considers transit in **either** realm because it
|
||||
fronts both trees. Before this every narrow Browse grant was unreachable by navigation — the Browse
|
||||
gate denies the whole call when its source node is denied, so clients were stopped at the front door.
|
||||
- **Revocation reaches live subscriptions** (stage 6): every other operation re-asks per request, but a
|
||||
subscription is authorized once and then streams — and streaming is how machine consumers actually
|
||||
read this server. `DataPlaneAuthorityState.Changed` fires `RevalidateMonitoredItems`. ⚠️ A denied
|
||||
|
||||
+14
-3
@@ -427,12 +427,23 @@ Behaviours worth knowing before enabling it:
|
||||
what this necessarily discloses — the *names* of the folders on the path to a granted node are visible
|
||||
to whoever holds that grant. It is conferred only by grants that actually carry `Browse`, and it
|
||||
confers nothing but `Browse`.
|
||||
- **…including the shared root folder every browse starts at.** Both realm trees hang under a single
|
||||
anchor folder (`s=OtOpcUa`, minted in the **raw** namespace by `CreateAddressSpace`; top-level raw
|
||||
folders and top-level UNS areas alike parent under it). Its identifier is a display name rather than
|
||||
a path, so splitting it produced a segment no grant path can contain, and the Browse gate refuses the
|
||||
*whole* call when its source node is denied — every narrow `Browse` grant was therefore unreachable
|
||||
by navigation, denied at the front door. The gate maps that one identifier onto the trie's
|
||||
zero-segment **realm-root** scope, and because the anchor fronts both trees its browse considers
|
||||
transit in **either** realm: a principal whose only grant lives in the UNS tree still enters through
|
||||
this raw-namespace node. The mapping is realm-qualified — in the UNS namespace `s=OtOpcUa` is an
|
||||
ordinary Area an operator happened to name that way and is evaluated as the path it is.
|
||||
- **A denied node reports `BadUserAccessDenied` rather than `BadNodeIdUnknown`**, because the gate runs
|
||||
before node resolution. That is the information-hiding direction — error codes can no longer be used
|
||||
to enumerate the address space — but it does mean "access denied" is not evidence the node exists.
|
||||
- **The two namespace root folders remain visible** to any admitted session. They are referenced from
|
||||
the ns=0 `Objects` folder, whose browse the SDK's own node manager serves; a session with no grants
|
||||
sees the two roots and is refused on entry.
|
||||
- **The shared root folder remains *listed*** for any admitted session. It is referenced from the ns=0
|
||||
`Objects` folder, whose browse the SDK's own node manager serves, so a session with no grants sees the
|
||||
anchor in that listing — and is refused the moment it tries to browse *into* it (the transit above is
|
||||
what a granted session enters on). There is one anchor, not one per namespace.
|
||||
|
||||
#### Cost
|
||||
|
||||
|
||||
Reference in New Issue
Block a user