feat(device-users): disable the login when a worker is resigned (#1282) - #1283
Conversation
Resigned has been a visibility flag that no authentication code reads, so a resigned employee kept a working login. On one production tenant all four resigned workers still had usable accounts, one of them seven weeks on. Core now refuses IsActive = false at every credential surface (eform-angular-frontend #8075); this is the half that sets it. UpdateDeviceUser writes IsActive = !Resigned on the worker's login, so reinstating restores access through the same path. Three places needed it, not one: - the login it already resolves and updates, alongside the account's other fields - a login whose login work is skipped -- either because this plugin does not manage it or because its address is invalid but unchanged. skipLoginWork protects that account's name, locale and groups; whether it can sign in at all is a different question, and a resigned admin is the account you least want left open. Written with ExecuteUpdate, because UpdateAsync would re-run the address validation that caused the skip and fail for the legacy non-ASCII address population - the login the method CREATES when the worker had none: a worker can be resigned in the same save that first gives them an account, and it must not arrive enabled SecurityGroupBackfillService creates its logins disabled for a resigned worker rather than skipping them: skipping would re-open the HTTP 500 that sweep exists to prevent, since "Set password" has no resigned guard and would reach RemovePasswordAsync(null). Bumps BasePn to 10.0.35 and EformAngularFrontendBase to 10.0.39, which carry EformUser.IsActive and its column. Core must be deployed before this plugin: EformUser resolves from the host's assemblies, so an older host makes every device-user save fail. The historical population -- ~370 accounts across 82 tenants already resigned -- is handled by a one-off script run by hand after deploy, not by a startup job. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
There was a problem hiding this comment.
🔵 Needs a closer look
It changes authentication account state behavior (including a direct DB ExecuteUpdateAsync path) and has strict deployment-order requirements, so it should receive final human review focused on operational rollout risk.
Pull request overview
Implements the “write-side” of the resigned-worker login lockout for BackendConfiguration.Pn by ensuring EformUser.IsActive is set to !Resigned across the update, skip-login-work, and create-login paths, and by creating backfilled users disabled when their worker is resigned. This aligns the plugin’s worker lifecycle updates with core’s already-shipped login refusal behavior.
Changes:
- Update
UpdateDeviceUserto persistIsActive = !Resignedfor existing logins (includingskipLoginWorkcases viaExecuteUpdateAsync) and for newly created logins. - Update the security-group backfill sweep to create missing users with
IsActive = !worker.Resigned(rather than skipping resigned workers). - Bump base package dependencies to versions that include the
IsActivecolumn/property, and extend integration tests to cover resigned/reinstated behavior and backfill behavior.
File summaries
| File | Description |
|---|---|
| eFormAPI/Plugins/BackendConfiguration.Pn/BackendConfiguration.Pn/Services/SecurityGroupBackfillService/SecurityGroupBackfillService.cs | Backfill now creates logins for resigned workers in a disabled state (IsActive=false). |
| eFormAPI/Plugins/BackendConfiguration.Pn/BackendConfiguration.Pn/Infrastructure/Helpers/BackendConfigurationAssignmentWorkerServiceHelper.cs | UpdateDeviceUser now sets IsActive based on resignation across update/create and skip-login-work paths. |
| eFormAPI/Plugins/BackendConfiguration.Pn/BackendConfiguration.Pn/BackendConfiguration.Pn.csproj | Bumps Base/FrontendBase package versions to include IsActive support. |
| eFormAPI/Plugins/BackendConfiguration.Pn/BackendConfiguration.Pn.Integration.Test/SecurityGroupBackfillServiceTest.cs | Adds resigned-worker case to backfill worker-state matrix and asserts disabled login. |
| eFormAPI/Plugins/BackendConfiguration.Pn/BackendConfiguration.Pn.Integration.Test/BackendConfigurationAssignmentWorkerServiceHelperTest.cs | Adds integration tests for resigning/reinstating, unmanaged login handling, and “create disabled login when resigned” behavior. |
Review details
- Files reviewed: 5/5 changed files
- Comments generated: 0
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Closes #1282. Step 4 of microting/eform-angular-frontend#8072 — the half that sets the flag. Core already refuses it (microting/eform-angular-frontend#8075, merged).
Resignedhas been a visibility flag that no authentication code reads, so resigned employees kept working logins. Measured on one production tenant: all four resigned workers still had usable accounts, one seven weeks on.Three places needed the write, not one
UpdateDeviceUserwritesIsActive = !Resigned— unconditionally, so reinstating restores access through the same path:UpdateAsyncpersists it.skipLoginWorkfires for a login this plugin does not manage and for one whose address is invalid but unchanged. It protects that account's name, locale and groups; whether it can sign in at all is a different question, and a resigned admin is the account you least want left open. Written withExecuteUpdate—UpdateAsyncwould re-run the address validation that caused the skip and fail for the legacy non-ASCII address population the code comments already describe, silently leaving the account enabled.SecurityGroupBackfillService
Creates its logins disabled for a resigned worker rather than skipping them. Skipping was the first attempt and it re-opens the HTTP 500 that sweep exists to prevent: "Set password" has no
!resignedguard in the UI, so a resigned worker with an email and no login reachesRemovePasswordAsync(null).Tests
Four in
BackendConfigurationAssignmentWorkerServiceHelperTest, one extended state inSecurityGroupBackfillServiceTest:skipLoginWorkprotection is pinned at the same timeThis repo's suite runs in CI only (it needs a merged dev tree plus MariaDB and RabbitMQ), so unlike the base repo these were not watched red-then-green locally; both projects build clean.
Deploy ordering — not optional
Core must be deployed before this plugin.
EformUserandBaseDbContextare in the plugin loader's shared-types list, soEformUserresolves from the host's assemblies at runtime; the csproj bump is a compile-time requirement only. On an older host,IsActiveis a missing member and the outer catch turns every device-user save into a genericDeviceUserCouldNotBeUpdated.Scope
EformUserJWT, so disabling the account also cuts off flutter-eform and flutter-time. Intended for a resignation, but worth knowing.IsActiveat issue and at refresh, not per request (Resigned/disabled accounts keep working sessions: nothing revokes a live token eform-angular-frontend#8071).TimeSettingService.UpdateAssignedSitein the time-planning plugin, is tracked separately and comes next.🤖 Generated with Claude Code