Skip to content

Commit

Permalink
Enable import into managed collections within Password Manager (#13288)
Browse files Browse the repository at this point in the history
Members of an org that lacked the canAccessImport permission were not able to import into an organization. With the introduction of flexible collections, Bitwarden would like to enable members of an organization to import into collections they manage

Co-authored-by: Daniel James Smith <[email protected]>
  • Loading branch information
djsmith85 and djsmith85 committed Feb 13, 2025
1 parent 28c6706 commit 8c339ea
Showing 1 changed file with 30 additions and 20 deletions.
50 changes: 30 additions & 20 deletions libs/importer/src/components/import.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ import {
} from "@angular/core";
import { FormBuilder, ReactiveFormsModule, Validators } from "@angular/forms";
import * as JSZip from "jszip";
import { concat, Observable, Subject, lastValueFrom, combineLatest, firstValueFrom } from "rxjs";
import { filter, map, switchMap, takeUntil } from "rxjs/operators";
import { Observable, Subject, lastValueFrom, combineLatest, firstValueFrom } from "rxjs";
import { combineLatestWith, filter, map, switchMap, takeUntil } from "rxjs/operators";

import { CollectionService, CollectionView } from "@bitwarden/admin-console/common";
import { JslibModule } from "@bitwarden/angular/jslib.module";
Expand Down Expand Up @@ -239,11 +239,10 @@ export class ImportComponent implements OnInit, OnDestroy, AfterViewInit {
async ngOnInit() {
this.setImportOptions();

await this.initializeOrganizations();
if (this.organizationId && (await this.canAccessImport(this.organizationId))) {
this.handleOrganizationImportInit();
if (this.organizationId) {
await this.handleOrganizationImportInit();
} else {
this.handleImportInit();
await this.handleImportInit();
}

this.formGroup.controls.format.valueChanges
Expand All @@ -255,7 +254,19 @@ export class ImportComponent implements OnInit, OnDestroy, AfterViewInit {
await this.handlePolicies();
}

private handleOrganizationImportInit() {
private async handleOrganizationImportInit() {
const userId = await firstValueFrom(getUserId(this.accountService.activeAccount$));
this.organizations$ = this.organizationService
.memberOrganizations$(userId)
.pipe(
map((orgs) =>
orgs.filter(
(org) =>
org.id == this.organizationId && (org.canAccessImport || org.canCreateNewCollections),
),
),
);

this.formGroup.controls.vaultSelector.patchValue(this.organizationId);
this.formGroup.controls.vaultSelector.disable();

Expand All @@ -268,7 +279,7 @@ export class ImportComponent implements OnInit, OnDestroy, AfterViewInit {
this.isFromAC = true;
}

private handleImportInit() {
private async handleImportInit() {
// Filter out the no folder-item from folderViews$
this.folders$ = this.activeUserId$.pipe(
switchMap((userId) => {
Expand All @@ -279,6 +290,17 @@ export class ImportComponent implements OnInit, OnDestroy, AfterViewInit {

this.formGroup.controls.targetSelector.disable();

// Retrieve all organizations a user is a member of and has collections they can manage
const userId = await firstValueFrom(getUserId(this.accountService.activeAccount$));
this.organizations$ = this.organizationService.memberOrganizations$(userId).pipe(
combineLatestWith(this.collectionService.decryptedCollections$),
map(([organizations, collections]) =>
organizations
.filter((org) => collections.some((c) => c.organizationId === org.id && c.manage))
.sort(Utils.getSortFunction(this.i18nService, "name")),
),
);

combineLatest([this.formGroup.controls.vaultSelector.valueChanges, this.organizations$])
.pipe(takeUntil(this.destroy$))
.subscribe(([value, organizations]) => {
Expand All @@ -303,18 +325,6 @@ export class ImportComponent implements OnInit, OnDestroy, AfterViewInit {
this.formGroup.controls.vaultSelector.setValue("myVault");
}

private async initializeOrganizations() {
const userId = await firstValueFrom(getUserId(this.accountService.activeAccount$));
this.organizations$ = concat(
this.organizationService.memberOrganizations$(userId).pipe(
// Import is an alternative way to create collections during onboarding, so import from Password Manager
// is available to any user who can create collections in the organization.
map((orgs) => orgs.filter((org) => org.canAccessImport || org.canCreateNewCollections)),
map((orgs) => orgs.sort(Utils.getSortFunction(this.i18nService, "name"))),
),
);
}

private async handlePolicies() {
combineLatest([
this.policyService.policyAppliesToActiveUser$(PolicyType.PersonalOwnership),
Expand Down

0 comments on commit 8c339ea

Please sign in to comment.