Skip to content

feat(device-users): disable the login when a worker is resigned (#1282) - #1283

Merged
renemadsen merged 1 commit into
stablefrom
feat/1282-disable-login-on-resign
Sep 17, 2026
Merged

renemadsen merged 1 commit into
stablefrom
feat/1282-disable-login-on-resign

Conversation

@renemadsen

Copy link
Copy Markdown
Member

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).

Resigned has 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

UpdateDeviceUser writes IsActive = !Resigned — unconditionally, so reinstating restores access through the same path:

  1. The login it already resolves and updates — set alongside the account's other fields, where the existing UpdateAsync persists it.
  2. A login whose login work is skipped. skipLoginWork fires 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 with ExecuteUpdateUpdateAsync would 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.
  3. 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 — that account must not arrive enabled. Found in review; without it, resigning a worker with no email minted them a fresh, enabled login.

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 !resigned guard in the UI, so a resigned worker with an email and no login reaches RemovePasswordAsync(null).

Tests

Four in BackendConfigurationAssignmentWorkerServiceHelperTest, one extended state in SecurityGroupBackfillServiceTest:

  • resigning disables the login
  • reinstating re-enables it (with a real precondition, not a trusted default)
  • a login this plugin does not manage is disabled anyway — and its name, username and group membership are asserted untouched, so the skipLoginWork protection is pinned at the same time
  • a worker with no login who is resigned gets a login created disabled
  • the backfill sweep's worker-state matrix gains a resigned worker: it gets a row, and that row cannot sign in

This 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. EformUser and BaseDbContext are in the plugin loader's shared-types list, so EformUser resolves from the host's assemblies at runtime; the csproj bump is a compile-time requirement only. On an older host, IsActive is a missing member and the outer catch turns every device-user save into a generic DeviceUserCouldNotBeUpdated.

Scope

  • Blast radius is wider than the web UI: the plugin's gRPC services resolve the caller from an EformUser JWT, so disabling the account also cuts off flutter-eform and flutter-time. Intended for a resignation, but worth knowing.
  • Not immediate revocation: core issues 24h tokens and checks IsActive at issue and at refresh, not per request (Resigned/disabled accounts keep working sessions: nothing revokes a live token eform-angular-frontend#8071).
  • The historical population — ~370 accounts across 82 tenants already resigned — is a one-off script run by hand after deploy, deliberately not a startup job: a bulk lockout should not fire unattended on first boot.
  • The second resign path, TimeSettingService.UpdateAssignedSite in the time-planning plugin, is tracked separately and comes next.

🤖 Generated with Claude Code

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>
Copilot AI lite review requested due to automatic review settings September 17, 2026 13:35

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 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 UpdateDeviceUser to persist IsActive = !Resigned for existing logins (including skipLoginWork cases via ExecuteUpdateAsync) 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 IsActive column/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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants