Skip to content

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

Open
sven-bitwarden wants to merge 2 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 2 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

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 65.77%. Comparing base (aaed234) to head (c964952).
⚠️ Report is 15 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #7859      +/-   ##
==========================================
- Coverage   65.83%   65.77%   -0.06%     
==========================================
  Files        2214     2220       +6     
  Lines       98091    98092       +1     
  Branches     8856     8849       -7     
==========================================
- Hits        64574    64520      -54     
- Misses      31300    31360      +60     
+ Partials     2217     2212       -5     

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

@sonarqubecloud

Copy link
Copy Markdown

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

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.

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

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.

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

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