-
Notifications
You must be signed in to change notification settings - Fork 1.5k
[PM-25913] Fix owners unable to rename provider-managed organization #6599
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
[PM-25913] Fix owners unable to rename provider-managed organization #6599
Conversation
…r-msp-organization-owners-unable-to-rename-organization-linked-to-resale-provider
It is better for the command to fetch the org so that callers do not mistakenly use it as a general purpose update method; it is only to be used to update those properties in the request model. This reverts commit 9df75c4.
…r-msp-organization-owners-unable-to-rename-organization-linked-to-resale-provider
…r-msp-organization-owners-unable-to-rename-organization-linked-to-resale-provider
|
Claude finished @eliykat's task —— View job Code Review CompleteThis is a well-executed refactoring that successfully separates authorization from business logic, addressing the root cause where organization owners couldn't rename provider-managed organizations. Summary of Changes Since Last ReviewThis PR has been through multiple review cycles, with previous reviewers (cyprain-okeke and JimmyVo16) having approved. The latest commits addressed their feedback about using extension methods instead of modifying input parameters. Core architectural improvements:
FindingsFinding 1: Billing email trimming inconsistency Example scenario// User sends: { "BillingEmail": " " }
// Controller: requiresBillingPermission = true (requires EditSubscription)
// Extension: result is " ".ToLowerInvariant().Trim() = ""
// Organization ends up with empty billing emailThis may be intentional, but worth verifying the desired behavior. Finding 2: Missing validation for organization name length Finding 3: Authorization logic correctness
This properly allows owners of provider-managed orgs to rename, since name changes trigger Stripe updates but don't require billing permissions anymore. Well done. Good Practices Observed
SuggestionsFinding 4: Consider extracting authorization logic to a separate method Suggested refactoringprivate async Task<bool> IsAuthorizedToUpdate(Guid organizationId, OrganizationUpdateRequestModel model)
{
var requiresBillingPermission = model.BillingEmail is not null;
return requiresBillingPermission
? await _currentContext.EditSubscription(organizationId)
: await _currentContext.OrganizationOwner(organizationId);
}
public async Task<IResult> Put(Guid organizationId, [FromBody] OrganizationUpdateRequestModel model)
{
if (!await IsAuthorizedToUpdate(organizationId, model))
{
return TypedResults.Unauthorized();
}
// ...
}Finding 5: Self-host documentation could be more prominent Security & Compliance✅ Zero-knowledge principles maintained: Private keys remain encrypted Test CoverageExcellent coverage across:
Codecov report shows 98.21% patch coverage - the 2 missing lines are acceptable and likely in auto-generated code or edge cases. Action Items
This is high-quality work that successfully resolves the issue while improving code organization and test coverage. The separation of concerns is well-executed, and the nullable properties API is cleaner than the previous approach. The previous reviewers' feedback has been adequately addressed. |
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #6599 +/- ##
==========================================
+ Coverage 53.10% 53.25% +0.15%
==========================================
Files 1903 1906 +3
Lines 84877 84955 +78
Branches 7633 7639 +6
==========================================
+ Hits 45071 45247 +176
+ Misses 38055 37954 -101
- Partials 1751 1754 +3 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
cyprain-okeke
left a comment
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.
Looks Good for billing
…r-msp-organization-owners-unable-to-rename-organization-linked-to-resale-provider
| return organization; | ||
| } | ||
|
|
||
| private static void UpdateOrganizationDetails(Organization organization, UpdateOrganizationRequest request) |
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.
Not a dealbreaker, but I’m always uneasy about modifying input parameters. It isn’t obvious from looking at the method, and it can lead to race conditions.
Based on what you’re trying to do, I think using an extension method would make more sense here and provide more idiomatic meaning.
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.
With that said, the impact here is pretty small if the scope is only within this class.
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.
Done
| /// This is legacy code for old organizations that were not created with a public/private keypair. It is a soft | ||
| /// migration that will silently migrate organizations when they change their details. | ||
| /// </summary> | ||
| private static void UpdatePublicPrivateKeyPair(Organization organization, UpdateOrganizationRequest request) |
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.
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.
Done
| Organization organization, | ||
| SutProvider<OrganizationBillingService> sutProvider) | ||
| { | ||
| organization.Name = "Short name"; |
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.
Also non-blocker: same but with a different name for this case.
…rs-unable-to-rename-organization-linked-to-resale-provider
🎟️ Tracking
https://bitwarden.atlassian.net/browse/PM-25913
📔 Objective
Fix a bug where the owner of an organization managed by a provider could not update the organization name.
Root cause: changing an organization name triggers a Stripe update, which checks whether the owner has permission to manage billing. Owners cannot manage billing if the organization is managed by a provider (it's managed by the provider), so the change fails.
Solution: separate authorization checks from business logic. Previously this was tightly coupled because
shouldUpdateBillingwas evaluated in the controller and passed into the service method. With this PR, the service method is moved to a command, and the command logic is separated from authorization checks in the controller. This allows owners to change the name, even though this will trigger a Stripe update, and even though they do not have permission to make Stripe changes otherwise.Other changes: There was some accumulated tech debt here I kept tripping over:
OrganizationServicerather than a command, and was a mix of AC and Billing logic. AC logic has been moved into a command and Billing logic into one of their services (as discussed with @amorask-bitwarden)OrganizationsControllerdid not use SutProvider, requiring manual updates to dependencies every time. This has been updated.Web PR: bitwarden/clients#17482
📸 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