fix(alarms): refuse a keyed native ack with no Galaxy identity synchronously, from a cached key set (lmxopcua#765)
v2-ci / unit-tests (push) Waiting to run
v2-ci / unit-tests (push) Waiting to run
Item 3 of #765. A keyed (API-key) session whose key had no Galaxy ack identity got Good and then a revert, because whether the key has an identity lives in the Secrets store, which must not be read under the SDK lock. GalaxyAckIdentityDirectory (Driver.Galaxy) keeps a node-wide set of the key ids that have a galaxy-ack-user secret. It lists this node's Secrets store (metadata only, nothing decrypted) and is read lock-free. GalaxyDriver.GetAcknowledgeRefusal (IAlarmAcknowledgePreflight, published through NativeAlarmAckPreflightView as in #760) now refuses a keyed session whose key the directory reports Absent, when the driver has no fallback ack account. A refusal also asks for a refresh. Fails open: before the first refresh, once the snapshot is older than StaleAfter, and from a signalled identity-secret change until the next refresh, the lookup answers Unknown and the ack takes the old Good-then-revert path. Refresh: GalaxyAckIdentityRefreshService runs on driver nodes, every Drivers:Galaxy:AckIdentityCache:RefreshIntervalSeconds (default 30; StaleAfterSeconds 0 = three intervals; validated at start). It also refreshes early on secret changes. The Secrets library has no change event, so SecretsRegistration decorates ISecretCacheInvalidator, which is called by AdminUI /api-keys, /admin/secrets and the replication appliers. API-key changes need no event: a revoked key is already denied before the pre-check. Tests: Galaxy.Tests GalaxyAckIdentityDirectoryTests (16) and Host.Tests GalaxyAckIdentityCacheWiringTests (9). Seven separate mutations each turn at least one test red: pre-check ignores the directory, factory drops it, no invalidator decoration, no staleness check, no change-pending check, no refresh on refusal, loop never wakes early. Docs: Galaxy.md, AlarmTracking.md, Configuration.md, .claude/rules/alarms.md. Claude-Session: https://claude.ai/code/session_01GT67AEHd2Acv1YPwGfXEoV
This commit is contained in:
@@ -536,6 +536,21 @@ deploy that recreates the driver), not on `ReinitializeAsync`. The init log line
|
||||
`transport=Libplctag (node default)` when the pin is what selected the stack. There is **no AbLegacy
|
||||
counterpart**: an AbLegacy driver with no key always runs libplctag (Native is opt-in per driver).
|
||||
|
||||
### `Drivers:Galaxy:AckIdentityCache` (cached API-key → Galaxy ack identity set, #765)
|
||||
|
||||
- **Purpose:** the node-wide set of API key ids that have a Galaxy ack identity (a `galaxy-ack-user` secret),
|
||||
which the Galaxy driver's synchronous ack pre-check reads to refuse a keyed session without one
|
||||
(`BadUserAccessDenied`) instead of returning Good and reverting. See
|
||||
[`drivers/Galaxy.md` § Alarm acknowledge credential](drivers/Galaxy.md#alarm-acknowledge-credential).
|
||||
- **Options class:** `GalaxyAckIdentityCacheOptions` (`SectionName = "Drivers:Galaxy:AckIdentityCache"`) —
|
||||
`src/Server/ZB.MOM.WW.OtOpcUa.Host/Configuration/GalaxyAckIdentityCacheOptions.cs`; bound and validated
|
||||
in `Program.cs`. The directory and its refresh loop exist on driver-role nodes only.
|
||||
|
||||
| Key | Type | Default | Meaning |
|
||||
|---|---|---|---|
|
||||
| `RefreshIntervalSeconds` | int | `30` | How often the Secrets store is re-listed (1..3600). An in-process identity change or a refusal also triggers an early refresh. |
|
||||
| `StaleAfterSeconds` | int | `0` | Snapshot age after which keyed acks fall back to Good-then-revert (fail open). `0` = three refresh intervals; otherwise must exceed `RefreshIntervalSeconds`. A bad value fails host start. |
|
||||
|
||||
---
|
||||
|
||||
## Environment variables
|
||||
|
||||
Reference in New Issue
Block a user