Skip to content

[PM-42605] refactor: wire PostBulkCollectionAccess into CollectionAuthorizationService - #8268

Draft
r-tome wants to merge 1 commit into
ac/pm-12473/collection-user-authorization-servicefrom
ac/pm-42605/collection-bulk-access-authorization
Draft

[PM-42605] refactor: wire PostBulkCollectionAccess into CollectionAuthorizationService#8268
r-tome wants to merge 1 commit into
ac/pm-12473/collection-user-authorization-servicefrom
ac/pm-42605/collection-bulk-access-authorization

Conversation

@r-tome

@r-tome r-tome commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

🎟️ Tracking

https://bitwarden.atlassian.net/browse/PM-42605

Stacked on #8211 (PM-12473). Review that one first — this PR only adds a caller.

📔 Objective

CollectionsController.PostBulkCollectionAccess was the last place in
CollectionsController still using BulkCollectionAuthorizationHandler for
collection access. This PR points it at ICollectionAuthorizationService,
which #8211 added, behind the same pm-35160-authorization-services flag.

The old call passed both ModifyUserAccess and ModifyGroupAccess to a
single AuthorizeAsync, so both operations had to succeed across the whole
collection set. The flag-on path intersects AuthorizeModifyUserAccessManyAsync
and AuthorizeModifyGroupAccessManyAsync, which is the same all-or-nothing
decision. This also gives those two methods their first caller.

Three things stay as they are:

  • The count pre-check. It rejects unknown, cross-organization and duplicate
    collection ids with a 404. ICollectionAuthorizationService leaves such ids
    out of its result rather than failing on them, so removing this check would
    turn a rejection into a silent ignore.
  • The collection fetch. BulkAddCollectionAccessCommand.AddAccessAsync
    takes the Collection entities and needs them for event logging and for its
    default-collection check. The authorization service resolves the same ids
    again, so this request reads collections twice. That is the cost of an
    authorization layer that fetches its own data, instead of threading
    pre-fetched entities into it — the pattern rejected on [PM-12473] feat: Add CollectionUser authorization handler and delta access endpoint #8075.
  • The 404 on an empty request. This one needs an explicit guard. The old
    handler fails an empty resource list, while the service returns an empty
    result, and "every id is authorized" is vacuously true over an empty list.
    Without the guard the flag-on path would fail open and reach the command.

No behavior change is expected on either path.

📸 Screenshots

N/A

…ionService behind a flag

The endpoint authorized ModifyUserAccess and ModifyGroupAccess in one
IAuthorizationService call, so both operations had to succeed for the whole
collection set. Behind pm-35160-authorization-services it now intersects
AuthorizeModifyUserAccessManyAsync and AuthorizeModifyGroupAccessManyAsync,
which is the same all-or-nothing decision.

Three things stay as they were. The count pre-check still rejects unknown,
cross-organization and duplicate ids with a 404, because the service omits
those ids rather than failing on them. The collection entities are still
fetched, because BulkAddCollectionAccessCommand needs them for event logging
and for its default-collection check. An empty request still returns a 404,
which needs an explicit guard: the legacy handler fails an empty resource
list, while the service returns an empty result that must not be read as
authorized.
@r-tome r-tome added the ai-review Request a Claude code review label Aug 27, 2026
@github-actions

github-actions Bot commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

🤖 Bitwarden Claude Code Review

Overall Assessment: APPROVE

Reviewed the PostBulkCollectionAccess migration from BulkCollectionAuthorizationHandler to ICollectionAuthorizationService behind the pm-35160-authorization-services flag, plus the four new controller tests. The flag-on path is authorization-equivalent to the old one: ASP.NET's multi-requirement AuthorizeAsync requires both ModifyUserAccess and ModifyGroupAccess to succeed across the whole resource set, which the intersection of AuthorizeModifyUserAccessManyAsync and AuthorizeModifyGroupAccessManyAsync reproduces, and the org-wide/per-collection/provider-bypass rules in CollectionRules line up with the handler's CanUpdateUserAccessAsync/CanUpdateGroupAccessAsync branches. The fail-open risk called out in the description is closed correctly — the empty-collection guard returns false before the service is consulted, and PostBulkCollectionAccess_WithNewAuthorizationEnabled_NoCollections_ThrowsNotFound asserts neither authorization method is reached. The retained count pre-check still rejects unknown, cross-organization, and duplicate collection ids with a 404 before authorization runs, so the handler's BadRequestException for mixed-organization resources stays unreachable on both paths.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ai-review Request a Claude code review

1 participant