Skip to content

[PM-37954] Add Command for Provisioning Staged OrganizationUsers#7859

Open
sven-bitwarden wants to merge 7 commits into
mainfrom
ac/pm-37954/add-staged-provision-command
Open

[PM-37954] Add Command for Provisioning Staged OrganizationUsers#7859
sven-bitwarden wants to merge 7 commits into
mainfrom
ac/pm-37954/add-staged-provision-command

Conversation

@sven-bitwarden

@sven-bitwarden sven-bitwarden commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

🎟️ Tracking

PM-37954

📔 Objective

Staged users need merely a fraction of the validation present for invited users - they're lightweight placeholders. Their validation will take place when the are promoted from Staged -> Invited (far future), or Staged -> Accepted (via invite link).

@sven-bitwarden sven-bitwarden requested review from a team as code owners June 24, 2026 14:52
@sven-bitwarden sven-bitwarden changed the title initial pass of staged provisioning command [PM-37954] Add Command for Provisioning Staged OrganizationUsers Jun 24, 2026
@github-actions

github-actions Bot commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

🤖 Bitwarden Claude Code Review

Overall Assessment: APPROVE

Reviewed the new ProvisionStagedOrganizationUsersCommand and its interface, the OrganizationUser_Staged event type, DI registration, and the accompanying unit and integration tests. The command correctly creates seatless Staged members without invitation emails or seat autoscale, normalizes emails to lower-case, deduplicates case-insensitively within the batch, and skips emails already present in the organization. ID generation, in-place mutation by CreateManyAsync, and event attribution all line up with the tests. No security, correctness, or breaking-change concerns were found.

Code Review Details
  • ❓ : The comment on ProvisionStagedOrganizationUsersCommand.cs:44 is a dangling, incomplete sentence ("...only populated by the revoke flow when") — worth completing or removing for clarity.

No inline comments posted; the above is a minor, non-blocking note.

@codecov

codecov Bot commented Jun 24, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 93.02326% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 65.76%. Comparing base (aaed234) to head (ec9b919).
⚠️ Report is 25 commits behind head on main.

Files with missing lines Patch % Lines
...tagedUsers/CreateStagedOrganizationUsersRequest.cs 71.42% 2 Missing ⚠️
...tagedUsers/CreateStagedOrganizationUsersCommand.cs 97.14% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #7859      +/-   ##
==========================================
- Coverage   65.83%   65.76%   -0.07%     
==========================================
  Files        2214     2221       +7     
  Lines       98091    98102      +11     
  Branches     8856     8849       -7     
==========================================
- Hits        64574    64513      -61     
- Misses      31300    31375      +75     
+ Partials     2217     2214       -3     

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

Comment on lines +9 to +10
/// Staged users are placeholders created by automated directory sync (SCIM / Directory Connector): they do not
/// yet consume a seat, are not subject to organization policies, and have not been sent an invitation.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think these 2 lines belong on the enum definition rather than here - it is defining what a Staged user is. (While you're at it - can you add documentation to the other enum values as well?)

@sven-bitwarden sven-bitwarden Jun 29, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

They're already there (on the enum), but I did remove the extra details from this guy

Comment on lines +26 to +29
Task<ICollection<OrganizationUser>> ProvisionStagedOrganizationUsersAsync(
Organization organization,
IEnumerable<(string Email, string ExternalId)> users,
EventSystemUser eventSystemUser);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Command conventions (undocumented, sorry! working to fix this):

  • method should be called RunAsync
  • should use a dedicated request object
  • should return a v2 CommandResult

/// Staged users are placeholders created by automated directory sync (SCIM / Directory Connector): they do not
/// yet consume a seat, are not subject to organization policies, and have not been sent an invitation.
/// </summary>
public interface IProvisionStagedOrganizationUsersCommand

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

(non-blocking, not strongly held): sticking to CRUD verbs might be clearer than Provision. e.g. CreateStagedOrganizationUserCommand.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Can do 👍 not fussed one way or the other

Key = null,
Type = OrganizationUserType.User,
Status = OrganizationUserStatusType.Staged,
// StatusNew is intentionally left null - it is only populated by the revoke flow when

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

when? when??? :o

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

cliffhanger deleted: it just be populated by the revoke flow 😆

Comment on lines +18 to +20
// The command is a thin orchestration over the repository; event emission is covered by unit tests,
// so a no-op event service keeps this integration test focused on real database persistence.
var sut = new ProvisionStagedOrganizationUsersCommand(organizationUserRepository, new NoopEventService());

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This project is for integration testing against the database, so it should call repositories directly, not Core methods. Here you're not adding a new repository method, you're re-using CreateManyAsync, so no provision-specific db tests are required. That said, you could make sure that CreateManyAsync is tested.

When you add the api endpoint, you can also add api integration tests which will hit the whole stack.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I can totally add the API tests later and remove these, but it does feel like we're missing a capability to test commands. Repositories are generally the agnostic building blocks, but the parameters and order-of-operations validity (the broad business logic) comes from the commands

// Each row is actually persisted as a Staged member with the expected shape.
foreach (var created in result)
{
var persisted = await organizationUserRepository.GetByIdAsync(created.Id);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

There is a GetManyAsync method.

@sven-bitwarden sven-bitwarden requested a review from eliykat June 29, 2026 21:02
@sven-bitwarden sven-bitwarden added the t:feature Change Type - Feature Development label Jun 29, 2026
@sonarqubecloud

Copy link
Copy Markdown

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.

2 participants