feat(pam): explain request-gated access when viewing a governed collection - #22553
feat(pam): explain request-gated access when viewing a governed collection#22553maxkpower wants to merge 5 commits into
Conversation
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## pam/collections-gated-indicator #22553 +/- ##
===================================================================
+ Coverage 54.54% 54.55% +0.01%
===================================================================
Files 4382 4385 +3
Lines 138868 138890 +22
Branches 21917 21916 -1
===================================================================
+ Hits 75750 75778 +28
+ Misses 57619 57613 -6
Partials 5499 5499 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
🤖 Bitwarden Claude Code ReviewOverall Assessment: APPROVE Re-reviewed at head Code Review DetailsNo findings. The earlier pass's ♻️ DEBT finding — the indicator and helper TSDoc claiming the collection-row badge and the collection-dialog callout also route through |
9ff7385 to
b7b9a14
Compare
The helper has two callers, the sidebar lock and the vault banner. The collection-dialog callout reads GovernedCollectionsService and rulesGoverningCollection directly, and the collection-row badge reads the collection's own hasEnabledAccessRule, so neither is kept in step by a change to the helper. The module guide said otherwise in one place and contradicted itself in another.
…d check `gatedCollection` counts rules rather than naming them, so the reason it cannot take the collection-row badge's `hasEnabledAccessRule` shortcut is not "it needs the rules themselves" - it is that neither caller has a usable flag. The sidebar's nodes are rebuilt through `new CollectionView(...)`, which resets the flag to `false`, and the banner is handed ids alone.
b7b9a14 to
dbcd240
Compare
gbubemismith
left a comment
There was a problem hiding this comment.
@maxkpower I just left one comment, every other thing looks good. Nice job!
| * vault never depends on the feature library that implements it. Unprovided, the vault is | ||
| * unchanged. | ||
| */ | ||
| export const VAULT_GATED_COLLECTION_BANNER = new SafeInjectionToken<Type<unknown>>( |
There was a problem hiding this comment.
🎨 Using Type<Unknown> here defeats the purpose of SafeInjectionToken. The tokens value is a compile time check on the provider. From comments I think we can do this
| export const VAULT_GATED_COLLECTION_BANNER = new SafeInjectionToken<Type<unknown>>( | |
| export interface VaultGatedCollectionBanner { | |
| readonly organizationId: InputSignal<OrganizationId | undefined>; | |
| readonly collectionId: InputSignal<CollectionId | undefined>; | |
| } | |
| export const VAULT_GATED_COLLECTION_BANNER = new SafeInjectionToken<VaultGatedCollectionBanner>( | |
| "VaultGatedCollectionBanner", | |
| ); |
That way in vault.component.ts we can have something like this
protected readonly gatedCollectionBanner: Type<VaultGatedCollectionBanner> | null = inject(
VAULT_GATED_COLLECTION_BANNER,
{ optional: true },
);
🎟️ Tracking
https://bitwarden.atlassian.net/browse/PM-40088
📔 Objective
A member filtering to a gated collection saw request-gated rows with no collection-level explanation.
Adds a banner above the item list stating the restriction, and lifts the shared gating check into
services/gated-collection.tsso the sidebar indicator, this banner and the collection-dialog calloutmake one claim from one source.
Icon is
bwi-lock-encrypted, matching the sidebar indicator.📸 Screenshots
cipher-view banner.
organization root shows governed and ungated rows mixed, with the per-row column as the only signal.
GovernedCollectionsServiceis root-provided so four surfaces share one cached per-org rules read.That cache's invalidation is not addressed here: a rule enabled in another tab is not reflected until
the read refreshes.
hasEnabledAccessRule, this surface and the sidebar indicatorcould drop
GovernedCollectionsServiceentirely. Not done here; it is a design change to twomilestones rather than part of this one.