Skip to content

fix(account): tighten password reset and admin-role assignment - #8063

Merged
renemadsen merged 1 commit into
stablefrom
fix/account-password-reset-rules
Sep 13, 2026
Merged

renemadsen merged 1 commit into
stablefrom
fix/account-password-reset-rules

Conversation

@renemadsen

Copy link
Copy Markdown
Member

Tightens who can set another account's password and who can grant the admin role.

Changes

  • Setting another account's password now requires a declared policy: administrator, users_update or device_users_update. Then these rules apply to the target account:
    • An unknown address is refused.
    • Only user 1 may reset user 1.
    • A non-administrator can't reset an administrator, or their own account, through this route.
    • A holder of device_users_update alone can reset only a property worker whose permissions are a subset of their own.
    • A request without a principal is refused.
  • The anonymous admin password reset is removed, together with its client route and component.
  • Admin role. Only an administrator can create an administrator or promote an account to administrator. A non-administrator editing their own account can no longer set a password or change their security group there.
  • Users page. "Set password" is shown only to users who can update users.

Tests

  • AccountPoliciesTests, AccountServiceTests and AdminServiceRoleGuardTests.

🤖 Generated with Claude Code

https://claude.ai/code/session_014Wuv2gtqY9BLMLWBZiBM2Y

Setting another account's password now requires a declared policy: an
administrator, or a holder of users_update or device_users_update. Rules on
the target then apply: an unknown address is refused, only user 1 may reset
user 1, a non-administrator may not reset an administrator or their own
account through this route, and a holder of device_users_update alone may
reset only a property worker whose permissions are a subset of their own.
A request without a principal is refused.

The anonymous admin password reset is removed, together with its client
route and component.

Only an administrator may now create an administrator or promote an
account to administrator, and a non-administrator editing their own account
may no longer set a password or change their security group there.

The Set password action on the users page is shown only to users who may
update users.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014Wuv2gtqY9BLMLWBZiBM2Y
Copilot AI lite review requested due to automatic review settings September 13, 2026 04:34

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.

🟡 Changes recommended

Two moderate authorization issues remain in AccountService and AdminService; an unused dependency also remains.

Get a fresh assessment by requesting another Copilot review.

Pull request overview

This PR tightens password-reset and administrator-role authorization across the API and Angular client.

Changes:

  • Adds policy- and target-based password-reset restrictions.
  • Restricts administrator role assignment and self-account edits.
  • Removes the anonymous admin reset flow and updates the UI, localization, and tests.
File summaries
File Summary
eFormAPI/eFormAPI.Web/Startup.cs Registers worker account lookup.
eFormAPI/eFormAPI.Web/Services/WorkerAccountLookup.cs Identifies active property workers.
eFormAPI/eFormAPI.Web/Services/AdminService.cs Guards administrator-role assignment and self-edits. Moderate finding: multiple memberships can bypass the intended group-change guard.
eFormAPI/eFormAPI.Web/Services/AccountService.cs Enforces password-reset target rules. Moderate finding: unauthenticated principals are not rejected reliably; nit: unused IDbOptions<ApplicationSettings> remains.
eFormAPI/eFormAPI.Web/Resources/SharedResource.resx Adds English authorization messages.
eFormAPI/eFormAPI.Web/Resources/SharedResource.da.resx Adds Danish authorization messages.
eFormAPI/eFormAPI.Web/Hosting/Security/AuthServiceCollectionExtensions.cs Registers the password-reset policy.
eFormAPI/eFormAPI.Web/Hosting/Security/AccountPolicies.cs Defines password-reset authorization rules.
eFormAPI/eFormAPI.Web/Controllers/AccountController.cs Applies the policy and removes the anonymous reset route.
eFormAPI/eFormAPI.Web/Abstractions/IWorkerAccountLookup.cs Adds the worker lookup abstraction.
eFormAPI/eFormAPI.Web/Abstractions/IAccountService.cs Removes the deleted reset contract.
eFormAPI/eFormAPI.Web.Integration.Tests/Services/AdminServiceRoleGuardTests.cs Tests role and self-edit guards.
eFormAPI/eFormAPI.Web.Integration.Tests/Services/AccountServiceTests.cs Tests password-reset restrictions.
eFormAPI/eFormAPI.Web.Integration.Tests/Security/AccountPoliciesTests.cs Tests policy registration and behavior.
eform-client/src/app/modules/auth/components/index.ts Removes the deleted component export.
eform-client/src/app/modules/auth/components/auth/reset-admin-password/reset-admin-password.component.ts Removes the obsolete component.
eform-client/src/app/modules/auth/components/auth/reset-admin-password/reset-admin-password.component.html Removes the obsolete template.
eform-client/src/app/modules/auth/components/auth/auth.component.ts Removes obsolete route handling.
eform-client/src/app/modules/auth/auth.routing.ts Removes the obsolete client route.
eform-client/src/app/modules/auth/auth.module.ts Removes the obsolete module declaration.
eform-client/src/app/modules/account-management/components/users/users-page/users-page.component.html Restricts password-action visibility.
eform-client/src/app/components/app.component.ts Removes the obsolete route exception.
eform-client/src/app/common/services/auth/auth.service.ts Removes the obsolete API method.
Review details

Suppressed comments (2)

eFormAPI/eFormAPI.Web/Services/AccountService.cs:72

  • The anonymous reset method was the only consumer of appSettings, but this constructor still injects IDbOptions<ApplicationSettings> even though AccountService no longer uses it. Please remove this dependency (and its now-unused using) so the service does not retain an unnecessary coupling or an unread primary-constructor parameter.
    IEmailService emailService,
    IHttpContextAccessor httpContextAccessor,
    IWorkerAccountLookup workerAccountLookup,
    IClaimsService claimsService)

eFormAPI/eFormAPI.Web/Services/AccountService.cs:213

  • HttpContext.User is normally a non-null anonymous ClaimsPrincipal even when the request has no authenticated identity. With this null-only check, a service call under such a context can reach the device-user branch and reset a live worker (especially if the target has no claims), so the fail-closed rule is not actually enforced. Check Identity?.IsAuthenticated as well.
        var caller = httpContextAccessor.HttpContext?.User;
        if (caller == null)
        {
  • Files reviewed: 23/23 changed files
  • Comments generated: 1
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +375 to +376
var changingGroup = userRegisterModel.GroupId.HasValue
&& userRegisterModel.GroupId != currentGroupId;
@renemadsen
renemadsen merged commit e7e95c4 into stable Sep 13, 2026
30 of 35 checks passed
renemadsen added a commit that referenced this pull request Sep 13, 2026
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