docs(audit): Reservations.md — accuracy pass
STALE-STATUS / CODE-REALITY fixes: - Table row ReleasedAt/ReleasedBy: "FleetAdmin" → "Administrator" (AdminRole enum renamed in CanonicalizeAdminRoles migration). ReleasedBy now documents that it is the LDAP operator name passed as explicit @ReleasedBy param — not SUSER_SNAME() — per migration 20260522000001_AddReleasedByToReleaseExternalIdReservation. - §4 Release: "FleetAdmin" → "Administrator"; added @ReleasedBy required param requirement matching the updated stored-proc signature; replaced "SUSER_SNAME()" attribution claim with the correct explicit-param description. - §The Admin page: replaced entirely. Actual Reservations.razor uses bare [Authorize] (not [Authorize(Policy="FleetAdmin")] and not "CanPublish"). The page is a read-only flat list (no Active/Released split, no Release row action, no Release dialog). Redirected release-flow readers to docs/v2/admin-ui.md §"Release an external-ID reservation". Evidence: src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Pages/Reservations.razor:2 src/Core/ZB.MOM.WW.OtOpcUa.Configuration/Enums/AdminRole.cs:36 src/Server/ZB.MOM.WW.OtOpcUa.Security/ServiceCollectionExtensions.cs:130 src/Core/ZB.MOM.WW.OtOpcUa.Configuration/Migrations/20260522000001_AddReleasedByToReleaseExternalIdReservation.cs
This commit is contained in:
+17
-16
@@ -52,7 +52,7 @@ is refreshed, and they are eventually *released* — but never silently deleted.
|
||||
| `ClusterId` | The first cluster to publish the reservation. |
|
||||
| `FirstPublishedAt` / `FirstPublishedBy` | When and by whom the claim was first made. |
|
||||
| `LastPublishedAt` | Refreshed on every subsequent publish that re-asserts the same `(Kind, Value, EquipmentUuid)`. |
|
||||
| `ReleasedAt` / `ReleasedBy` / `ReleaseReason` | Non-null once a FleetAdmin explicitly releases the claim. A row with `ReleasedAt IS NULL` is *active*. |
|
||||
| `ReleasedAt` / `ReleasedBy` / `ReleaseReason` | Non-null once an Administrator explicitly releases the claim. `ReleasedBy` is the LDAP operator name (passed explicitly as `@ReleasedBy`; not `SUSER_SNAME()`). A row with `ReleasedAt IS NULL` is *active*. |
|
||||
|
||||
There is no foreign key from `EquipmentUuid` / `ClusterId` to their tables — by
|
||||
design, so a reservation survives the deletion or disabling of the equipment
|
||||
@@ -99,14 +99,16 @@ being disabled, the generation being superseded, or a rollback.
|
||||
|
||||
### 4. Release
|
||||
|
||||
Reusing an identifier for a **different** piece of equipment requires a
|
||||
FleetAdmin to explicitly release the existing claim. Release runs
|
||||
Reusing an identifier for a **different** piece of equipment requires an
|
||||
Administrator to explicitly release the existing claim. Release runs
|
||||
`sp_ReleaseExternalIdReservation`, which:
|
||||
|
||||
- Requires a non-empty **reason** — a hard audit invariant; the procedure
|
||||
raises an error without one.
|
||||
- Stamps `ReleasedAt`, `ReleasedBy` (`SUSER_SNAME()`), and `ReleaseReason`
|
||||
rather than deleting the row, so the history is preserved.
|
||||
- Requires a non-empty **`@ReleasedBy`** — the LDAP operator name supplied
|
||||
by the caller; the procedure raises an error without it.
|
||||
- Stamps `ReleasedAt`, `ReleasedBy` (the supplied operator name), and
|
||||
`ReleaseReason` rather than deleting the row, so the history is preserved.
|
||||
- Once released, the `(Kind, Value)` pair is free — a different
|
||||
`EquipmentUuid` can claim it on a future publish.
|
||||
|
||||
@@ -116,20 +118,19 @@ permanent for the life of the asset.
|
||||
|
||||
## The Admin page
|
||||
|
||||
`/reservations` (Admin UI) is the operator surface. It is **FleetAdmin-only**
|
||||
(the `CanPublish` policy).
|
||||
`/reservations` (Admin UI) is the operator surface. It requires authentication
|
||||
(`[Authorize]`) but is not restricted to a specific Admin UI role — any signed-in
|
||||
user can view it.
|
||||
|
||||
- **Active** table — every reservation with `ReleasedAt IS NULL`: kind, value,
|
||||
owning `EquipmentUuid`, cluster, and the first/last publish stamps. Each row
|
||||
has a **Release…** action.
|
||||
- **Released** table — the 100 most recently released reservations, with the
|
||||
releasing user and reason.
|
||||
- **Release dialog** — opened from an active row; it requires a reason before
|
||||
the Release button will submit, mirroring the procedure's audit invariant.
|
||||
The page is a **read-only flat list** of all `ExternalIdReservation` rows,
|
||||
ordered by Kind then Value. It shows Kind, Value, owning `EquipmentUuid`, and
|
||||
Cluster. There is no Active/Released split, no Release action, and no Release
|
||||
dialog on this page.
|
||||
|
||||
You cannot *create* a reservation from this page — reservations only ever come
|
||||
into existence as a side-effect of publishing a generation. The page is for
|
||||
inspection and for the release flow.
|
||||
into existence as a side-effect of publishing a generation. The release flow
|
||||
is described in `docs/v2/admin-ui.md` § "Release an external-ID reservation"
|
||||
and runs via `sp_ReleaseExternalIdReservation`.
|
||||
|
||||
## Related
|
||||
|
||||
|
||||
Reference in New Issue
Block a user