Skip to content

[PM-38923] feat: Add v2 UpdateOrganizationUser command with role-escalation validation#7919

Draft
jrmccannon wants to merge 4 commits into
jmccannon/ac/pm-38927-org-user-role-validationfrom
jmccannon/ac/pm-38923-update-orguser-v2
Draft

[PM-38923] feat: Add v2 UpdateOrganizationUser command with role-escalation validation#7919
jrmccannon wants to merge 4 commits into
jmccannon/ac/pm-38927-org-user-role-validationfrom
jmccannon/ac/pm-38923-update-orguser-v2

Conversation

@jrmccannon

@jrmccannon jrmccannon commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

🎟️ Tracking

PM-38923 (epic PM-28365 — "Manual single-email change for members without passwords")

Note

This PR is based on #7876 (PM-38927) and targets that branch, since it builds on the new IOrganizationUserValidationService. It should be re-based onto main once #7876 merges. The diff will shrink to just the commits below once the base lands.

📔 Objective

Refactors OrganizationUsersController.Put and the v1 UpdateOrganizationUserCommand into a v2 command + validator (returning CommandResult / ValidationResult<T>), mirroring the RevokeUser v2 pattern. The v2 path is gated behind the pm-28365-change-member-email-no-mp feature flag and stays off by default.

Key points:

  • Validator ports v1's request validation (invite-first, free-org admin, self-add-to-collection, collection/group existence, custom-permissions plan, confirmed-owner, Manage mutual-exclusivity).
  • Escalating-role guard — the previously-stubbed release blocker — now delegates to the shared IOrganizationUserValidationService.CanManage rules from [PM-38927] - Extract Organziation User Role Validation #7876, checking both the member's current role and the requested role so an existing higher-ranked member can't be modified from below. v1's specific error messages are preserved by mapping the denial (OnlyOwnersCanManageOwners / CustomUsersCannotManageAdminsOrOwners).
  • Zero-knowledge / boundaries — Core stays free of ICurrentContext / ClaimsPrincipal; the controller passes the acting user's membership (role + permissions) and the IActingUser as plain inputs. Collection resource authorization (ModifyUserAccess) stays in the controller.
  • Also fixes the Put collection-auth unit tests to mock the bulk ModifyUserAccess authorization call.

Test coverage

  • Core: v2 validator (incl. 7 escalation cases) + command tests.
  • Api: v2-path routing tests, and the repaired v1-path Put collection-auth tests (42/42 in the two controller test classes).

📸 Screenshots

…lation validation

Refactor the OrganizationUsersController.Put flow into a v2 command + validator
(returning CommandResult / ValidationResult<T>), gated behind the
pm-28365-change-member-email-no-mp feature flag. The validator ports v1's request
validation and adds the escalating-role guard by delegating to the shared
IOrganizationUserValidationService.CanManage rules (PM-38927), while preserving
v1's specific error messages. The controller supplies the acting user's membership
(role + permissions) as a plain field so Core stays free of ICurrentContext.
@jrmccannon jrmccannon added the t:feature Change Type - Feature Development label Jul 2, 2026
@codecov

codecov Bot commented Jul 2, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 87.05882% with 33 lines in your changes missing coverage. Please review.
✅ Project coverage is 61.42%. Comparing base (ac100b9) to head (c750fcb).

Files with missing lines Patch % Lines
...s/UpdateUser/v2/UpdateOrganizationUserValidator.cs 83.48% 9 Missing and 9 partials ⚠️
...ers/UpdateUser/v2/UpdateOrganizationUserCommand.cs 83.67% 6 Missing and 2 partials ⚠️
...Console/Controllers/OrganizationUsersController.cs 88.33% 5 Missing and 2 partials ⚠️
Additional details and impacted files
@@                                Coverage Diff                                 @@
##           jmccannon/ac/pm-38927-org-user-role-validation    #7919      +/-   ##
==================================================================================
+ Coverage                                           61.36%   61.42%   +0.05%     
==================================================================================
  Files                                                2238     2242       +4     
  Lines                                               98563    98793     +230     
  Branches                                             8914     8948      +34     
==================================================================================
+ Hits                                                60482    60682     +200     
- Misses                                              35944    35962      +18     
- Partials                                             2137     2149      +12     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

… UpdateOrganizationUser

Pass the API-loaded collections into the v2 validation request so the validator
validates existence without re-querying. The validator now rejects default user
collections with a new CannotAssignDefaultCollection error instead of silently
filtering them, and the controller excludes current-access defaults from the
preserved read-only set.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

t:feature Change Type - Feature Development

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant