merge: SQL browser/probe review findings resolved (Driver.Sql-009, Driver.Sql.Browser-001/-002/-003)
This commit is contained in:
+12
-3
@@ -190,14 +190,23 @@ commit writes as `rowSelector.topByTimestamp` ("read the newest row"). Before th
|
||||
a selector-less blob that `SqlEquipmentTagParser` and the authoring gate both refuse, so no
|
||||
browse-committed Sql tag was ever readable.
|
||||
|
||||
Two properties of the choice are deliberate:
|
||||
Three properties of the choice are deliberate:
|
||||
|
||||
- **Candidacy is by declared TYPE, never by column name.** The session asks
|
||||
`ISqlDialect.MapColumnType` for `DriverDataType.DateTime` — the same metadata the browse tree already
|
||||
renders per column. A name heuristic (`*_ts`, `*time*`) would happily order a wide-row read by a
|
||||
*string* column called `timestamp`, binding the tag to whichever row sorts last lexically.
|
||||
- **The tie-break is first in catalog order** (every dialect's column query orders by ordinal
|
||||
position), so the pick is stable across calls, connections and servers. On a
|
||||
- **Candidates are RANKED: a time-bearing type beats a date-only one.** `MapColumnType` reports
|
||||
`DriverDataType.DateTime` for SQL Server's and PostgreSQL's `date` too, and ordering a single-row read
|
||||
by a date-only column ties for *every row written on the same day* — "the newest row" quietly becomes
|
||||
storage order among the ties, and the reader cannot warn about it because the server has already
|
||||
discarded the others. So a `datetime2`/`timestamptz`/… column wins over a `date` one wherever both
|
||||
exist, regardless of declaration order. A date-only column is still stated when it is **all** the table
|
||||
has, so such a table stays bindable by browse rather than becoming un-committable.
|
||||
**Oracle's `DATE` is exempt** — it carries a time-of-day component (which is why `OracleDialect` maps
|
||||
it faithfully rather than as a widening), so it is not demoted there.
|
||||
- **The tie-break within a rank is first in catalog order** (every dialect's column query orders by
|
||||
ordinal position), so the pick is stable across calls, connections and servers. On a
|
||||
`CreatedUtc`/`ModifiedUtc` pair it takes the earlier-declared one. It is a **visible, editable
|
||||
default**: the typed editor shows the selector and the operator can change it — to the other
|
||||
timestamp, or to a `whereColumn`/`whereValue` pair entirely.
|
||||
|
||||
@@ -719,6 +719,38 @@ Every routable AdminUI page carries an explicit **named-policy** `@attribute [Au
|
||||
|
||||
In v2 the authentication + authorization stack is wired centrally by `AddOtOpcUaAuth` (`src/Server/ZB.MOM.WW.OtOpcUa.Security/ServiceCollectionExtensions.cs`), which also installs a `FallbackPolicy` that requires an authenticated user. Razor pages gate inline with the named-policy attribute above, e.g. `@attribute [Authorize(Policy = AdminUiPolicies.ConfigEditor)]`. Nav-menu sections hide via `<AuthorizeView>`.
|
||||
|
||||
#### Address-picker browse is an outbound-connect capability (trust boundary)
|
||||
|
||||
Worth stating plainly, because "browse a configured database" reads weaker than what it is: the AdminUI's
|
||||
live address pickers **dial a target named in the browse form, from the AdminUI host, before anything is
|
||||
persisted.** The `Sql` picker is the sharpest case — `SqlDriverBrowser.OpenAsync`
|
||||
(`src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Sql.Browser/SqlDriverBrowser.cs`) accepts an **ad-hoc
|
||||
`connectionString` literal** an operator pastes for a database that has no `connectionStringRef` yet, and
|
||||
hands it to the ADO.NET provider verbatim. That is an arbitrary host and port plus whatever the provider's
|
||||
keyword set allows (`Microsoft.Data.SqlClient` alone offers `AttachDbFilename`, `Failover Partner`,
|
||||
`Enlist`). The OPC UA client and MQTT pickers have the same shape against their own endpoint fields.
|
||||
|
||||
**The control is the page policy, and it is admin-grade.** The only surfaces that reach a browse session
|
||||
are `/raw` and `/uns`, both `@attribute [Authorize(Policy = AdminUiPolicies.ConfigEditor)]` — satisfied by
|
||||
`Administrator` or `Designer` only. There is **no HTTP endpoint** for browse: `IBrowserSessionService` is a
|
||||
scoped service consumed from those pages' Blazor circuits, so it inherits the page's policy and the
|
||||
`FallbackPolicy` beneath it, and `PageAuthorizationGuardTests` fails the build if either page's attribute
|
||||
drifts. A `Viewer` cannot open a picker; `AdminUiPolicies.DriverOperator` gates only the *live-ops* actions
|
||||
inside one (Sparkplug rebirth), never the connect.
|
||||
|
||||
Supporting hygiene, none of which is the control:
|
||||
|
||||
- The pasted literal is **never persisted** — `SqlDriverConfigDto` has no such property, so the driver
|
||||
factory would drop it; the picker composes a session-only document and the browser keeps no field.
|
||||
- A literal supplied alongside a `connectionStringRef` **logs a warning** naming the shadowed ref (never
|
||||
any connection text), so the override is visible rather than silent.
|
||||
- Every failure on the open path is **redacted** (`SqlDriverBrowser.Sanitize`), and the provider's own
|
||||
connection-string-parser message is withheld outright because it quotes connection-string text verbatim.
|
||||
- `NodeEdit.razor` warns against pasting a literal into a *saved* `DriverConfig`, which would persist it.
|
||||
|
||||
If a deployment needs the browse surface narrower than "anyone who may author config", the lever is the
|
||||
page policy (and the LDAP group→role mapping behind it), not the browser.
|
||||
|
||||
### Role grant source
|
||||
|
||||
Admin reads `LdapGroupRoleMapping` rows from the Config DB (`src/Core/ZB.MOM.WW.OtOpcUa.Configuration/Entities/LdapGroupRoleMapping.cs`) — the same pattern as the data-plane `NodeAcl` but scoped to Admin roles + (optionally) one cluster for multi-site fleets (a system-wide row, `IsSystemWide = true`, stacks additively with cluster-scoped rows). The `RoleGrants.razor` page lets `Administrator`s edit these mappings without leaving the UI.
|
||||
|
||||
Reference in New Issue
Block a user