Skip to content

Commit 0d20584

Browse files
robgrameCopilot
andcommitted
feat(web): on-prem Web DEK auto-unlock + operator toggle (v1.9.0)
The on-prem Web portal now runs its own EncryptionReconcilerService, so admins clicking Reveal after a restart no longer see the "(encrypted - operator must unlock)" placeholder. The behavior is gated by a new DbEncryptionStates.WebAutoUnlockEnabled column (default true) with a UI toggle in Settings; the Worker always ignores the toggle so unattended scans never stall. Highlights: - EncryptionReconcilerService moved from Worker/Jobs to Core/Services/ Encryption and made options-driven (ProcessName, RespectWebAutoUnlockToggle, PollInterval). - New migration AddWebAutoUnlockEnabled (bit NOT NULL DEFAULT 1). - IDataKeyService.SetWebAutoUnlockEnabledAsync (Serializable tx + best-effort audit). - Settings page: status pills + Enable/Disable toggle + Lock now + Unlock now (inline passphrase form) + Refresh. Disabling the toggle also immediately locks the in-memory DEK. - DeviceDetail.razor now catches InvalidOperationException to eliminate the IsUnlocked->DecryptRowValue race when the reconciler locks on a cross-process fingerprint mismatch. - 7 new xUnit tests in EncryptionReconcilerServiceTests; 120/120 total. - CHANGELOG: [1.9.0] section above [Unreleased]. - Directory.Build.props: 1.8.0 -> 1.9.0. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent bfeff33 commit 0d20584

18 files changed

Lines changed: 1515 additions & 69 deletions

CHANGELOG.md

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,63 @@ follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) conventions and
66

77
## [Unreleased]
88

9+
## [1.9.0] — 2026-06-11 — Web DEK auto-unlock + operator toggle
10+
11+
### Added
12+
- **Web DEK auto-unlock**: the on-prem Web portal now runs an
13+
`EncryptionReconcilerService` of its own (previously Worker-only). Within
14+
~30s of startup the Web process unwraps the DPAPI-protected passphrase blob
15+
stored in `DbEncryptionStates.AutoUnlockPassphraseProtected` and unlocks the
16+
in-memory DEK, so admins clicking **Reveal** on a device no longer see the
17+
`(encrypted — operator must unlock)` placeholder after a restart.
18+
- **Operator toggle**: new
19+
`DbEncryptionStates.WebAutoUnlockEnabled` column (`bit NOT NULL DEFAULT 1`,
20+
migration `AddWebAutoUnlockEnabled`) gates the Web reconciler. Default `true`
21+
preserves backwards compatibility on upgrade. The Worker reconciler **ignores
22+
this flag** — unattended scans must always be able to write encrypted values.
23+
- **Settings page — "🔐 Database encryption — Web auto-unlock" fieldset**: live
24+
status pills (Lifecycle, DEK locked/unlocked + fingerprint hex, DPAPI blob
25+
present, toggle state) plus four admin actions:
26+
- **Enable / Disable Web auto-unlock** (audited as
27+
`DbEncryption.WebAutoUnlockToggleChanged`). Disabling also immediately locks
28+
the in-memory DEK so the toggle takes effect without a restart.
29+
- **🔒 Lock now** — zeroizes the DEK from this Web process
30+
(audited as `DbEncryption.ManualLock`).
31+
- **🔑 Unlock now…** — inline passphrase form for manual unlock when
32+
auto-unlock is disabled or the DPAPI blob is unavailable.
33+
- **↻ Refresh** — re-renders the live status.
34+
- `IDataKeyService.SetWebAutoUnlockEnabledAsync(bool, string operatorIdentity)`
35+
with Serializable transaction + best-effort audit (DB commit never reported
36+
as failed because of an audit write error).
37+
- 7 new xUnit tests in `EncryptionReconcilerServiceTests` covering the toggle
38+
gate, fingerprint mismatch, NotInitialized / SetupPending lifecycles,
39+
already-unlocked steady state, and the no-blob degraded path.
40+
41+
### Changed
42+
- `EncryptionReconcilerService` moved from `BitLockerKeyMonitor.Worker/Jobs/`
43+
to `BitLockerKeyMonitor.Core/Services/Encryption/` and made options-driven
44+
(`EncryptionReconcilerOptions { ProcessName, RespectWebAutoUnlockToggle,
45+
PollInterval }`). Worker registers with `RespectWebAutoUnlockToggle=false`,
46+
Web with `true`.
47+
48+
### Fixed
49+
- **Reveal race after DEK rotation**: `DeviceDetail.razor` now catches
50+
`InvalidOperationException` (in addition to `CryptographicException`) around
51+
`DecryptRowValue`, eliminating an unhandled-exception window where the
52+
reconciler could `Lock()` the DEK on a cross-process fingerprint mismatch
53+
between the `IsUnlocked` check and the decrypt call.
54+
- **Settings actions no longer lie about success**: audit failures in
55+
`SetWebAutoUnlockEnabledAsync` and the page-level `LockNowAsync` are now
56+
swallowed (logged at Warning) so a transient audit-store outage doesn't
57+
surface as "Toggle failed" / "Lock failed" after the security-relevant
58+
side effect has already been applied.
59+
60+
### Security
61+
- Honest comment on the manual unlock form: the passphrase transits the Blazor
62+
Server SignalR circuit and is held in component state on the server; managed
63+
strings cannot be zeroized. Operator browser + Web host are part of the
64+
trust boundary for this admin action.
65+
966
### Added
1067
- **On-demand scan trigger**: a "Run scan now" button on the admin **Settings**
1168
page lets administrators start an AD/Entra scan immediately without waiting for

Directory.Build.props

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@
66
row's Version column, and the published assembly stamps in one place.
77
-->
88
<PropertyGroup>
9-
<Version>1.8.0</Version>
10-
<AssemblyVersion>1.8.0.0</AssemblyVersion>
11-
<FileVersion>1.8.0.0</FileVersion>
12-
<InformationalVersion>1.8.0</InformationalVersion>
9+
<Version>1.9.0</Version>
10+
<AssemblyVersion>1.9.0.0</AssemblyVersion>
11+
<FileVersion>1.9.0.0</FileVersion>
12+
<InformationalVersion>1.9.0</InformationalVersion>
1313

1414
<Company>robgrame</Company>
1515
<Product>BitLocker Key Monitor</Product>

src/BitLockerKeyMonitor.Core/BitLockerKeyMonitor.Core.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
<PrivateAssets>all</PrivateAssets>
2626
</PackageReference>
2727
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="10.0.7" />
28+
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="10.0.8" />
2829
<PackageReference Include="Microsoft.Extensions.Http.Resilience" Version="10.6.0" />
2930
<PackageReference Include="Microsoft.Graph" Version="5.105.0" />
3031
<PackageReference Include="Microsoft.Identity.Client" Version="4.*" />

0 commit comments

Comments
 (0)