-
Notifications
You must be signed in to change notification settings - Fork 1.5k
[PM-26636] - Auto Confirm Org User Command #6488
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
|
|
|
|
||
| if (userId is null || userId.Value == Guid.Empty) | ||
| { | ||
| throw new UnauthorizedAccessException(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could possibly be an attribute? Just something to think about
|
|
||
|
|
||
| [Authorize<ManageUsersRequirement>] | ||
| [HttpPost("{id:guid}/auto-confirm")] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I remember a discussion about this https://bitwarden.slack.com/archives/C02LPDKPML6/p1641389870010400?thread_ts=1641386236.008400&cid=C02LPDKPML6
| switch (result.AsError) | ||
| { | ||
| case OrganizationNotFound organizationNotFound: | ||
| throw new NotFoundException(organizationNotFound.Message); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How does the return message look like?
| errorMessage = "Resource not found."; |
Should we throw an exception on the controller or return NotFound()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah. I see now. Well that's not great.
Should we allow for the overriding of that not found message? Or is that something that needs to be run by architecture?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think its wise to run by architecture
...izationFeatures/OrganizationUsers/Interfaces/IAutomaticallyConfirmOrganizationUserCommand.cs
Show resolved
Hide resolved
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #6488 +/- ##
===========================================
- Coverage 51.86% 13.24% -38.62%
===========================================
Files 1901 1141 -760
Lines 84051 49886 -34165
Branches 7501 3888 -3613
===========================================
- Hits 43594 6608 -36986
- Misses 38763 43158 +4395
+ Partials 1694 120 -1574 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
| [FromRoute] Guid id, | ||
| [FromBody] OrganizationUserConfirmRequestModel model) | ||
| { | ||
| if (!_featureService.IsEnabled(FeatureFlagKeys.AutomaticConfirmUsers)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be replaced with [RequireFeature(FeatureFlagKeys.AutomaticConfirmUsers)]
|
|
||
| var successfulConfirmation = await organizationUserRepository.ConfirmOrganizationUserAsync(validatedRequest.OrganizationUser); | ||
|
|
||
| if (!successfulConfirmation) return new None(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would add a comment here to explain that the only reason this would not be successful is if the user is already confirmed. Otherwise it seems like silent failure.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's also worth explaining here that this is idempotent and designed to handle multiple simultaneous requests from logged in clients.
| { | ||
| try | ||
| { | ||
| if (!featureService.IsEnabled(FeatureFlagKeys.CreateDefaultLocation) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Extract all these conditions into a private method
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Filename is misspelled
|
|
||
| if (result is { IsError: true }) | ||
| { | ||
| return result.AsError switch |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The Billing team has a helper method in BaseBillingController.Handle, we should do something similar
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should add unit tests for this command
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice work!
| /// <summary> | ||
| /// Command to automatically confirm an organization user. | ||
| /// </summary> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
More context around what autoconfirm is would be helpful - <remarks> is good for this. e.g.
The autoconfirm feature sends push notifications to eligible logged in client apps to automatically confirm OrganizationUsers without manual intervention by administrators. When a client app receives the push notification, it performs the key exchange and sends an autoconfirm request to the server. This command is used to process those requests. It should not be used to confirm users in any other context.
I think a README in the AutoConfirmUser folder would also be useful to document the feature in more detail. I'll raise it in the dev channel.
| { | ||
| var requestData = await RetrieveDataAsync(request); | ||
|
|
||
| if (requestData.IsError) return requestData.AsError; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here, I got you these: { and }
(Please avoid single line if statements.)
| await collectionRepository.CreateAsync( | ||
| new Collection | ||
| { | ||
| OrganizationId = request.Organization.Id, | ||
| Name = request.DefaultUserCollectionName, | ||
| Type = CollectionType.DefaultUserCollection | ||
| }, | ||
| groups: null, | ||
| [new CollectionAccessSelection | ||
| { | ||
| Id = request.OrganizationUser.Id, | ||
| Manage = true | ||
| }]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💭 (non-blocking) Thoughts on making this an idempotent repository method as well? e.g. CreateDefaultCollectionAsync. Given that we are creating it in multiple places and creating default collections properly is quite important. (not this PR though)
|
|
||
| if (organization.IsError) return organization.AsError; | ||
|
|
||
| return new AutomaticallyConfirmOrganizationUserRequestData |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suggest that it's clearer to call this the ValidationRequest; it's an enriched DTO required by the validator. The overall flow of data then looks like: API request -> Command Request -> Validation Request -> Validation Result -> Command Result -> API response... which is nice and symmetrical. The Data suffix is not very clear by comparison. (it's not a data model)
|
|
||
| var successfulConfirmation = await organizationUserRepository.ConfirmOrganizationUserAsync(validatedRequest.OrganizationUser); | ||
|
|
||
| if (!successfulConfirmation) return new None(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's also worth explaining here that this is idempotent and designed to handle multiple simultaneous requests from logged in clients.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👏 thank you for moving this. I should've used the v2 convention to begin with.
| var input = await inputTask; | ||
| if (input.IsError) return input.AsError; | ||
| return await next(input.AsSuccess); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could this use Match to take advantage of the OneOf library utils? (same for the other util methods)
| /// <summary> | ||
| /// Enables the Organization Auto Confirm policy for the specified organization. | ||
| /// </summary> | ||
| public static async Task EnableOrganizationAutoConfirmPolicyAsync<T>( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think a generic EnablePolicyAsync(PolicyType policyType) method would be helpful, otherwise we're going to end up with a helper method for every policy.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Controller tests are going to get very long; thoughts on making separate files per set of functionality? e.g. autoconfirm endpoints would have their own test file. I did this with unit tests for OrganizationUserControllerPutTests.
| } | ||
|
|
||
| [Fact] | ||
| public async Task AutoConfirm_WhenOwnerInvitesValidUser_ThenShouldReturnNoContent() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Test name: we're testing acceptance, not invitation.
This test should also check the db to verify the user has moved into the correct state (that is, they've actually been confirmed).
|
Also... enjoy your merge errors 😎 |


🎟️ Tracking
PM-26636
📔 Objective
TODO
📸 Screenshots
⏰ Reminders before review
🦮 Reviewer guidelines
:+1:) or similar for great changes:memo:) or ℹ️ (:information_source:) for notes or general info:question:) for questions:thinking:) or 💭 (:thought_balloon:) for more open inquiry that's not quite a confirmed issue and could potentially benefit from discussion:art:) for suggestions / improvements:x:) or:warning:) for more significant problems or concerns needing attention:seedling:) or ♻️ (:recycle:) for future improvements or indications of technical debt:pick:) for minor or nitpick changes