1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-26 09:33:22 +00:00

[PM-24748][PM-24072] Chromium importer (#16100)

* Add importer dummy lib, add cargo deps for win/mac

* Add Chromium importer source from bitwarden/password-access

* Mod crypto is no more

* Expose some Chromium importer functions via NAPI, replace home with home_dir crate

* Add Chromium importer to the main <-> renderer IPC, export all functions from Rust

* Add password and notes fields to the imported logins

* Fix windows to use homedir instead of home

* Return success/failure results

* Import from account logins and join

* Linux v10 support

* Use mod util on Windows

* Use mod util on macOS

* Refactor to move shared code into chromium.rs

* Fix windows

* Fix Linux as well

* Linux v11 support for Chrome/Gnome, everything is async now

* Support multiple browsers on Linux v11

* Move oo7 to Linux

* Fix Windows

* Fix macOS

* Add support for Brave browser in Linux configuration

* Add support for Opera browser in Linux configuration

* Fix Edge and add Arc on macOS

* Add Opera on macOS

* Add support for Vivaldi browser in macOS configuration

* Add support for Chromium browser in macOS configuration

* Fix Edge on Windows

* Add Opera on Windows

* Add Vivaldi on windows

* Add Chromium to supported browsers on Windows

* stub out UI options for chromium direct import

* call IPC funcs from import-desktop

* add notes to chrome csv importer

* remove (csv) from import tool names and format item names as hostnames

* Add ABE/v20 encryption support

* ABE/v20 architecture description

* Add a build step to produce admin.exe and service.exe

* Add Windows v20/ABE configuration functionality to specify the full path to the admin.exe and service.exe. Use ipc.platform.chromiumImporter.configureWindowsCryptoService to configure the Chromium importer on Windows.

* rename ARCHITECTURE.md to README.md

* aligns with guidance from architecture re: in-repository documentation.
* also fixes a failing lint.

* cargo fmt

* cargo clippy fix

* Declare feature flag for using chromium importer

* Linter fix after executing npm run prettier

* Use feature flag to guard the use of the chromium importer

* Added temporary logging to further debug, why the Angular change detection isn't working as expected

* introduce importer metadata; host metadata from service; includes tests

* fix cli build

* Register autotype module in lib.rs
introduce by a bad merge

* Fix web build

* Fix issue with loaders being undefined and the feature flag turned off

* Add missing Chromium support when selecting chromecsv

* debugging

* remove chromium support from chromecsv metadata

* fix default loader selection

* [PM-24753] cargo lib file (#16090)

* Add new modules

* Fix chromium importer

* Fix compile bugs for toolchain

* remove importer folder

* remove IPC code

* undo setting change

* clippy fixes

* cargo fmt

* clippy fixes

* clippy fixes

* clippy fixes

* clippy fixes

* lint fix

* fix release build

* Add files in CODEOWNERS

* Create tools owned preload.ts

* Move chromium-importer.service under tools-ownership

* Fix typeError
When accessing the Chromium direct import options the file button is hidden, so trying to access it's values will fail

* Fix tools owned preload

* Remove dead code and redundant truncation

* Remove configureWindowsCryptoService function/methods

* Clean up cargo files

* Fix unused async

* Update apps/desktop/desktop_native/bitwarden_chromium_importer/Cargo.toml

Co-authored-by: Oscar Hinton <Hinton@users.noreply.github.com>

* Fix napi deps

* fix lints

* format

* fix linux lint

* fix windows lints

* format

* fix missing `?`

* fix a different missing `?`

---------

Co-authored-by: Dmitry Yakimenko <detunized@gmail.com>
Co-authored-by: Kyle Spearrin <kyle.spearrin@gmail.com>
Co-authored-by: Daniel James Smith <djsmith85@users.noreply.github.com>
Co-authored-by:  Audrey  <ajensen@bitwarden.com>
Co-authored-by:  Audrey  <audrey@audreyality.com>
Co-authored-by: adudek-bw <adudek@bitwarden.com>
Co-authored-by: Oscar Hinton <Hinton@users.noreply.github.com>
This commit is contained in:
Daniel James Smith
2025-09-04 11:21:57 +02:00
committed by GitHub
parent b957a0c28f
commit 66f5700a75
46 changed files with 2436 additions and 90 deletions

View File

@@ -4,6 +4,7 @@ import { CommonModule } from "@angular/common";
import {
AfterViewInit,
Component,
DestroyRef,
EventEmitter,
Inject,
Input,
@@ -13,17 +14,23 @@ import {
Output,
ViewChild,
} from "@angular/core";
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
import { FormBuilder, ReactiveFormsModule, Validators } from "@angular/forms";
import * as JSZip from "jszip";
import { Observable, Subject, lastValueFrom, combineLatest, firstValueFrom } from "rxjs";
import {
Observable,
Subject,
lastValueFrom,
combineLatest,
firstValueFrom,
BehaviorSubject,
} from "rxjs";
import { combineLatestWith, filter, map, switchMap, takeUntil } from "rxjs/operators";
// This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop.
// eslint-disable-next-line no-restricted-imports
import { CollectionService, CollectionView } from "@bitwarden/admin-console/common";
import { JslibModule } from "@bitwarden/angular/jslib.module";
import { safeProvider, SafeProvider } from "@bitwarden/angular/platform/utils/safe-provider";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import {
getOrganizationById,
OrganizationService,
@@ -34,14 +41,10 @@ import { Organization } from "@bitwarden/common/admin-console/models/domain/orga
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { getUserId } from "@bitwarden/common/auth/services/account.service";
import { ClientType } from "@bitwarden/common/enums";
import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service";
import { PinServiceAbstraction } from "@bitwarden/common/key-management/pin/pin.service.abstraction";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { SdkService } from "@bitwarden/common/platform/abstractions/sdk/sdk.service";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction";
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
import { FolderView } from "@bitwarden/common/vault/models/view/folder.view";
@@ -62,49 +65,20 @@ import {
ToastService,
LinkModule,
} from "@bitwarden/components";
import { KeyService } from "@bitwarden/key-management";
import { ImporterMetadata, DataLoader, Loader, Instructions } from "../metadata";
import { ImportOption, ImportResult, ImportType } from "../models";
import {
ImportApiService,
ImportApiServiceAbstraction,
ImportCollectionServiceAbstraction,
ImportService,
ImportServiceAbstraction,
} from "../services";
import { ImportCollectionServiceAbstraction, ImportServiceAbstraction } from "../services";
import { ImportChromeComponent } from "./chrome";
import {
FilePasswordPromptComponent,
ImportErrorDialogComponent,
ImportSuccessDialogComponent,
} from "./dialog";
import { ImporterProviders } from "./importer-providers";
import { ImportLastPassComponent } from "./lastpass";
const safeProviders: SafeProvider[] = [
safeProvider({
provide: ImportApiServiceAbstraction,
useClass: ImportApiService,
deps: [ApiService],
}),
safeProvider({
provide: ImportServiceAbstraction,
useClass: ImportService,
deps: [
CipherService,
FolderService,
ImportApiServiceAbstraction,
I18nService,
CollectionService,
KeyService,
EncryptService,
PinServiceAbstraction,
AccountService,
SdkService,
RestrictedItemTypesService,
],
}),
];
@Component({
selector: "tools-import",
templateUrl: "import.component.html",
@@ -118,6 +92,7 @@ const safeProviders: SafeProvider[] = [
SelectModule,
CalloutModule,
ReactiveFormsModule,
ImportChromeComponent,
ImportLastPassComponent,
RadioButtonModule,
CardComponent,
@@ -125,7 +100,7 @@ const safeProviders: SafeProvider[] = [
SectionComponent,
LinkModule,
],
providers: safeProviders,
providers: ImporterProviders,
})
export class ImportComponent implements OnInit, OnDestroy, AfterViewInit {
featuredImportOptions: ImportOption[];
@@ -160,6 +135,12 @@ export class ImportComponent implements OnInit, OnDestroy, AfterViewInit {
});
}
@Input()
onLoadProfilesFromBrowser: (browser: string) => Promise<any[]>;
@Input()
onImportFromBrowser: (browser: string, profile: string) => Promise<any[]>;
protected organization: Organization;
protected destroy$ = new Subject<void>();
@@ -184,6 +165,8 @@ export class ImportComponent implements OnInit, OnDestroy, AfterViewInit {
fileContents: [],
file: [],
lastPassType: ["direct" as "csv" | "direct"],
// FIXME: once the flag is disabled this should initialize to `Strategy.browser`
chromiumLoader: [Loader.file as DataLoader],
});
@ViewChild(BitSubmitDirective)
@@ -208,6 +191,26 @@ export class ImportComponent implements OnInit, OnDestroy, AfterViewInit {
});
}
private importer$ = new BehaviorSubject<ImporterMetadata | undefined>(undefined);
/** emits `true` when the chromium instruction block should be visible. */
protected readonly showChromiumInstructions$ = this.importer$.pipe(
map((importer) => importer?.instructions === Instructions.chromium),
);
/** emits `true` when direct browser import is available. */
// FIXME: use the capabilities list to populate `chromiumLoader` and replace the explicit
// strategy check with a check for multiple loaders
protected readonly browserImporterAvailable$ = this.importer$.pipe(
map((importer) => (importer?.loaders ?? []).includes(Loader.chromium)),
);
/** emits `true` when the chromium loader is selected. */
protected readonly showChromiumOptions$ =
this.formGroup.controls.chromiumLoader.valueChanges.pipe(
map((chromiumLoader) => chromiumLoader === Loader.chromium),
);
constructor(
protected i18nService: I18nService,
protected importService: ImportServiceAbstraction,
@@ -226,6 +229,7 @@ export class ImportComponent implements OnInit, OnDestroy, AfterViewInit {
protected toastService: ToastService,
protected accountService: AccountService,
private restrictedItemTypesService: RestrictedItemTypesService,
private destroyRef: DestroyRef,
) {}
protected get importBlockedByPolicy(): boolean {
@@ -246,6 +250,23 @@ export class ImportComponent implements OnInit, OnDestroy, AfterViewInit {
async ngOnInit() {
this.setImportOptions();
this.importService
.metadata$(this.formGroup.controls.format.valueChanges)
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe({
next: (importer) => {
this.importer$.next(importer);
// when an importer is defined, the loader needs to be set to a value from
// its list.
const loader = importer.loaders.includes(Loader.chromium)
? Loader.chromium
: importer.loaders?.[0];
this.formGroup.controls.chromiumLoader.setValue(loader ?? Loader.file);
},
error: (err: unknown) => this.logService.error("an error occurred", err),
});
if (this.organizationId) {
await this.handleOrganizationImportInit();
} else {
@@ -578,7 +599,7 @@ export class ImportComponent implements OnInit, OnDestroy, AfterViewInit {
private async setImportContents(): Promise<string> {
const fileEl = document.getElementById("import_input_file") as HTMLInputElement;
const files = fileEl.files;
const files = fileEl?.files;
let fileContents = this.formGroup.controls.fileContents.value;
if (files != null && files.length > 0) {