mirror of
https://github.com/bitwarden/clients.git
synced 2026-07-01 21:10:49 +08:00
[PM-32765] [PM-32744] Default import destination to My Items when org data owner (#19242)
* [PM-32744] Default import destination to My Items when org data ownership policy is enforced * remove feature flag gating, simplify collection selector logic * additional code comments to explain import form behavior --------- Co-authored-by: vlad-trofimov <vlad@Vlads-MacBook-Pro.local> Co-authored-by: John Harrington <84741727+harr1424@users.noreply.github.com>
This commit is contained in:
parent
5d8adc08b3
commit
f0dbf65dd6
@ -348,8 +348,13 @@ export class ImportComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
this.isFromAC = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the import form for personal vault imports.
|
||||
* Sets up folder selection for personal vault and collection selection for organizations.
|
||||
* The targetSelector control dynamically switches between folders (personal vault) and collections (organization vault) based on the vaultSelector value.
|
||||
*/
|
||||
private async handleImportInit() {
|
||||
// Filter out the no folder-item from folderViews$
|
||||
// Set up observable for user's personal folders (excludes the special "no folder" item)
|
||||
this.folders$ = this.activeUserId$.pipe(
|
||||
switchMap((userId) => {
|
||||
return this.folderService.folderViews$(userId);
|
||||
@ -357,9 +362,10 @@ export class ImportComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
map((folders) => folders.filter((f) => !!f.id)),
|
||||
);
|
||||
|
||||
// Start with targetSelector disabled - it will be enabled when a vault destination is selected
|
||||
this.formGroup.controls.targetSelector.disable();
|
||||
|
||||
// Retrieve all organizations a user is a member of and has collections they can manage
|
||||
// Get organizations where the user can import (has manageable collections)
|
||||
const userId = await firstValueFrom(getUserId(this.accountService.activeAccount$));
|
||||
this.organizations$ = this.organizationService.memberOrganizations$(userId).pipe(
|
||||
combineLatestWith(this.collectionService.decryptedCollections$(userId)),
|
||||
@ -370,15 +376,18 @@ export class ImportComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
),
|
||||
);
|
||||
|
||||
// React to vault destination changes (personal vault vs organization selection)
|
||||
combineLatest([this.formGroup.controls.vaultSelector.valueChanges, this.organizations$])
|
||||
.pipe(takeUntil(this.destroy$))
|
||||
.subscribe(([value, organizations]) => {
|
||||
// Set organizationId for org imports, undefined for personal vault
|
||||
this.organizationId = value !== "myVault" ? value : undefined;
|
||||
|
||||
if (!this._importBlockedByPolicy) {
|
||||
this.formGroup.controls.targetSelector.enable();
|
||||
}
|
||||
// Enable targetSelector for both personal vault (folders) and org vault (collections)
|
||||
// Note: The template switches between showing folders vs collections based on organizationId
|
||||
this.formGroup.controls.targetSelector.enable();
|
||||
|
||||
// When an organization is selected, load its manageable collections
|
||||
if (value) {
|
||||
this.collections$ = this.collectionService
|
||||
.decryptedCollections$(userId)
|
||||
@ -391,31 +400,65 @@ export class ImportComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
// Set initial vault selector to personal vault
|
||||
this.formGroup.controls.vaultSelector.setValue("myVault");
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the "Enforce organization data ownership" policy enforcement.
|
||||
* When this policy is active, users cannot import to their personal vault and must
|
||||
* select an organization. This method:
|
||||
* 1. Forces the vault selector to the first available organization [there should only be 1, since My Items requires Single Org policy]
|
||||
* 2. Auto-selects the user's "My Items" collection as the default import destination
|
||||
* 3. Disables the entire form if the policy is active but no organizations are available
|
||||
*/
|
||||
private async handlePolicies() {
|
||||
combineLatest([
|
||||
const userId = await firstValueFrom(getUserId(this.accountService.activeAccount$));
|
||||
|
||||
// Create a shared observable combining policy status and available organizations
|
||||
// This is reused by two subscriptions below to avoid duplicating the combineLatest logic
|
||||
const policyAndOrgs$ = combineLatest([
|
||||
this.accountService.activeAccount$.pipe(
|
||||
getUserId,
|
||||
switchMap((userId) =>
|
||||
this.policyService.policyAppliesToUser$(PolicyType.OrganizationDataOwnership, userId),
|
||||
switchMap((uid) =>
|
||||
this.policyService.policyAppliesToUser$(PolicyType.OrganizationDataOwnership, uid),
|
||||
),
|
||||
),
|
||||
this.organizations$,
|
||||
])
|
||||
.pipe(takeUntil(this.destroy$))
|
||||
.subscribe(([policyApplies, orgs]) => {
|
||||
this._importBlockedByPolicy = policyApplies;
|
||||
if (policyApplies && orgs.length == 0) {
|
||||
this.formGroup.disable();
|
||||
}
|
||||
]);
|
||||
|
||||
// If there are orgs the user has access to import into set
|
||||
// the default value to the first org in the collection.
|
||||
if (policyApplies && orgs.length > 0) {
|
||||
this.formGroup.controls.vaultSelector.setValue(orgs[0].id);
|
||||
}
|
||||
// Subscription 1: Handle policy enforcement on vault selection
|
||||
policyAndOrgs$.pipe(takeUntil(this.destroy$)).subscribe(([policyApplies, orgs]) => {
|
||||
this._importBlockedByPolicy = policyApplies;
|
||||
|
||||
// If policy applies but user has no organizations they can import to, disable the form
|
||||
if (policyApplies && orgs.length == 0) {
|
||||
this.formGroup.disable();
|
||||
}
|
||||
|
||||
// If policy applies and user has organizations, force selection of the first org
|
||||
// (personal vault is hidden when policy is active)
|
||||
if (policyApplies && orgs.length > 0) {
|
||||
this.formGroup.controls.vaultSelector.setValue(orgs[0].id);
|
||||
}
|
||||
});
|
||||
|
||||
// Subscription 2: Auto-select "My Items" collection when the "Enforce organization data ownership" policy is active
|
||||
// It serves as the default landing place for imports, similar to the personal vault.
|
||||
policyAndOrgs$
|
||||
.pipe(
|
||||
filter(([policyApplies, orgs]) => policyApplies && orgs.length > 0),
|
||||
switchMap(([, orgs]) =>
|
||||
this.collectionService.defaultUserCollection$(userId, orgs[0].id as OrganizationId),
|
||||
),
|
||||
filter(Boolean),
|
||||
takeUntil(this.destroy$),
|
||||
)
|
||||
.subscribe((defaultCollection) => {
|
||||
// Set the targetSelector to the user's My Items collection
|
||||
// Users can still change this to a different collection if desired
|
||||
this.formGroup.controls.targetSelector.setValue(defaultCollection);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user