docs+ui(security): state plainly that node ACLs are not enforced (§8.1, #520)

deferment.md §8.1 asked for a decision: wire IPermissionEvaluator into the node
manager, or say plainly that ACLs are not enforced. Decision: the latter. The
evaluator stays in the tree; #520 tracks the wire-up.

Verifying the claim turned up four things worse than the audit recorded:

- docs/security.md, not ReadWriteOperations.md, was the highest-risk doc. It
  claimed an anonymous session is "default-denie[d] any node a session has no
  ACL grant for". The opposite holds: anonymous can Browse/Read/Subscribe/
  HistoryRead the entire address space, and is refused writes and alarm acks
  only because it carries no roles.
- Three of five documented data-plane role strings do nothing.
  OpcUaDataPlaneRoles declares exactly WriteOperate and AlarmAck; ReadOnly,
  WriteTune and WriteConfigure are compared against nowhere in src/. The doc
  called all five "exact, case-insensitive, and code-true".
- ReadWriteOperations.md was fiction beyond the ACL claims. OnReadValue has
  zero occurrences in src/ — the documented read path did not exist. Reads
  never reach a driver: the node manager is push-model and the SDK serves a
  Read from the cached pushed value. WriteAuthzPolicy, _sourceByFullRef,
  _writeIdempotentByFullRef and IRoleBearer are likewise zero-hit, and
  CapabilityInvoker is not referenced by the OpcUaServer project at all. This
  file is rewritten rather than bannered.
- v2-release-readiness.md claimed a whole enforcement layer shipped —
  FilterBrowseReferences, GateCallMethodRequests, MapCallOperation,
  AuthorizationBootstrap, Node:Authorization:* keys. None exist. Whatever
  landed on the v2 branch did not survive into the shipped tree.

The UI change is the operative one: ClusterAcls.razor and AclEdit.razor now
warn that a grant is saved and shipped in every deployment artifact but never
evaluated, so an operator cannot author a deny rule believing it works.

deferment.md gains a §9 execution log tracking §8 remediation.
This commit is contained in:
Joseph Doherty
2026-07-27 18:36:43 -04:00
parent e08855fb9d
commit 53ede679c3
7 changed files with 336 additions and 93 deletions
+18
View File
@@ -1,5 +1,23 @@
# OPC UA Client Authorization (ACL Design) — OtOpcUa v2
> ⚠️ **NEVER WIRED (verified against source 2026-07-27).** This is a design document for work that was
> only half-built. The trie, the builder, the cache and the evaluator all exist in
> `src/Core/ZB.MOM.WW.OtOpcUa.Core/Authorization/` and carry 30 passing unit tests — but
> `IPermissionEvaluator.Authorize` has **zero production call sites**, nothing registers it in DI, and
> `ZB.MOM.WW.OtOpcUa.OpcUaServer.csproj` does not reference the project it lives in. `NodeAcl` rows are
> authorable in the Admin UI and are snapshotted into every deployment artifact, but the node side never
> deserializes them.
>
> **Nothing described below is enforced.** For what actually gates OPC UA operations today, see
> [`docs/security.md`](../security.md) § Data-Plane Authorization and
> [`docs/ReadWriteOperations.md`](../ReadWriteOperations.md). Four blockers stand between this design
> and a wire-up (Raw-realm nodes have no `NodeScope` representation; the session carries roles, not LDAP
> groups; `PermissionTrieBuilder`'s `scopePaths` has no producer; no session lifecycle exists) — they
> are recorded in `deferment.md` §3.1.
>
> Note also that the `SystemPlatform` hierarchy kind used throughout was retired with the v3
> dual-namespace address space.
>
> **Status**: DRAFT — closes corrections-doc finding B1 (namespace / equipment-subtree ACLs not yet modeled in the data path).
>
> **Branch**: `v2`