1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-17 16:53:34 +00:00

[PM-16227] Move import to sdk and enable it in browser/web (#12479)

* Move import to sdk and enable it in browser/web

* Add uncomitted files

* Update package lock

* Fix prettier formatting

* Fix build

* Rewrite import logic

* Update ssh import logic for cipher form component

* Fix build on browser

* Break early in retry logic

* Fix build

* Fix build

* Fix build errors

* Update paste icons and throw error on wrong import

* Fix tests

* Fix build for cli

* Undo change to jest config

* Undo change to feature flag enum

* Remove unneeded lifetime

* Fix browser build

* Refactor control flow

* Fix i18n key and improve import behavior

* Remove for loop limit

* Clean up tests

* Remove unused code

* Update libs/vault/src/cipher-form/components/sshkey-section/sshkey-section.component.ts

Co-authored-by: SmithThe4th <gsmith@bitwarden.com>

* Move import logic to service and add tests

* Fix linting

* Remove erroneous includes

* Attempt to fix storybook

* Fix storybook, explicitly implement ssh-import-prompt service abstraction

* Fix eslint

* Update libs/importer/src/importers/bitwarden/bitwarden-json-importer.ts

Co-authored-by:  Audrey  <ajensen@bitwarden.com>

* Fix services module

* Remove ssh import sdk init code

* Add tests for errors

* Fix import

* Fix import

* Fix pkcs8 encrypted key not parsing

* Fix import button showing on web

---------

Co-authored-by: SmithThe4th <gsmith@bitwarden.com>
Co-authored-by:  Audrey  <ajensen@bitwarden.com>
This commit is contained in:
Bernd Schoolmann
2025-03-10 18:41:47 +01:00
committed by GitHub
parent 85a5aea897
commit 01f6fd7ee3
35 changed files with 428 additions and 621 deletions

View File

@@ -102,6 +102,7 @@ import {
BiometricsService,
} from "@bitwarden/key-management";
import { LockComponentService } from "@bitwarden/key-management-ui";
import { DefaultSshImportPromptService, SshImportPromptService } from "@bitwarden/vault";
import { DesktopLoginApprovalComponentService } from "../../auth/login/desktop-login-approval-component.service";
import { DesktopLoginComponentService } from "../../auth/login/desktop-login-component.service";
@@ -430,6 +431,11 @@ const safeProviders: SafeProvider[] = [
useClass: DesktopLoginApprovalComponentService,
deps: [I18nServiceAbstraction],
}),
safeProvider({
provide: SshImportPromptService,
useClass: DefaultSshImportPromptService,
deps: [DialogService, ToastService, PlatformUtilsServiceAbstraction, I18nServiceAbstraction],
}),
];
@NgModule({

View File

@@ -25,16 +25,6 @@ export class MainSshAgentService {
private logService: LogService,
private messagingService: MessagingService,
) {
ipcMain.handle(
"sshagent.importkey",
async (
event: any,
{ privateKey, password }: { privateKey: string; password?: string },
): Promise<sshagent.SshKeyImportResult> => {
return sshagent.importKey(privateKey, password);
},
);
ipcMain.handle("sshagent.init", async (event: any, message: any) => {
this.init();
});

View File

@@ -3532,9 +3532,6 @@
"unknownApplication": {
"message": "An application"
},
"sshKeyPasswordUnsupported": {
"message": "Importing password protected SSH keys is not yet supported"
},
"invalidSshKey": {
"message": "The SSH key is invalid"
},
@@ -3544,7 +3541,7 @@
"importSshKeyFromClipboard": {
"message": "Import key from clipboard"
},
"sshKeyPasted": {
"sshKeyImported": {
"message": "SSH key imported successfully"
},
"fileSavedToDevice": {

View File

@@ -1,4 +1,3 @@
import { sshagent as ssh } from "desktop_native/napi";
import { ipcRenderer } from "electron";
import { DeviceType } from "@bitwarden/common/enums";
@@ -64,13 +63,6 @@ const sshAgent = {
clearKeys: async () => {
return await ipcRenderer.invoke("sshagent.clearkeys");
},
importKey: async (key: string, password: string): Promise<ssh.SshKeyImportResult> => {
const res = await ipcRenderer.invoke("sshagent.importkey", {
privateKey: key,
password: password,
});
return res;
},
isLoaded(): Promise<boolean> {
return ipcRenderer.invoke("sshagent.isloaded");
},

View File

@@ -512,6 +512,15 @@
[ngClass]="{ 'bwi-eye': !showPrivateKey, 'bwi-eye-slash': showPrivateKey }"
></i>
</button>
<button
type="button"
class="row-btn"
appStopClick
appA11yTitle="{{ 'importSshKeyFromClipboard' | i18n }}"
(click)="importSshKeyFromClipboard()"
>
<i class="bwi bwi-lg bwi-paste" aria-hidden="true"></i>
</button>
</div>
</div>
<div class="box-content-row box-content-row-flex" appBoxRow>
@@ -559,16 +568,6 @@
</button>
</div>
</div>
<div class="box-content-row box-content-row-flex" appBoxRow>
<button
type="button"
class="row-btn"
appStopClick
(click)="importSshKeyFromClipboard()"
>
{{ "importSshKeyFromClipboard" | i18n }}
</button>
</div>
</div>
</div>
</div>

View File

@@ -3,8 +3,6 @@
import { DatePipe } from "@angular/common";
import { Component, NgZone, OnChanges, OnDestroy, OnInit, ViewChild } from "@angular/core";
import { NgForm } from "@angular/forms";
import { sshagent as sshAgent } from "desktop_native/napi";
import { lastValueFrom } from "rxjs";
import { CollectionService } from "@bitwarden/admin-console/common";
import { AddEditComponent as BaseAddEditComponent } from "@bitwarden/angular/vault/components/add-edit.component";
@@ -25,8 +23,7 @@ import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folde
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
import { CipherAuthorizationService } from "@bitwarden/common/vault/services/cipher-authorization.service";
import { DialogService, ToastService } from "@bitwarden/components";
import { SshKeyPasswordPromptComponent } from "@bitwarden/importer-ui";
import { PasswordRepromptService } from "@bitwarden/vault";
import { PasswordRepromptService, SshImportPromptService } from "@bitwarden/vault";
const BroadcasterSubscriptionId = "AddEditComponent";
@@ -60,6 +57,7 @@ export class AddEditComponent extends BaseAddEditComponent implements OnInit, On
toastService: ToastService,
cipherAuthorizationService: CipherAuthorizationService,
sdkService: SdkService,
sshImportPromptService: SshImportPromptService,
) {
super(
cipherService,
@@ -82,6 +80,7 @@ export class AddEditComponent extends BaseAddEditComponent implements OnInit, On
cipherAuthorizationService,
toastService,
sdkService,
sshImportPromptService,
);
}
@@ -159,69 +158,6 @@ export class AddEditComponent extends BaseAddEditComponent implements OnInit, On
this.cipher.revisionDate = cipher.revisionDate;
}
async importSshKeyFromClipboard(password: string = "") {
const key = await this.platformUtilsService.readFromClipboard();
const parsedKey = await ipc.platform.sshAgent.importKey(key, password);
if (parsedKey == null) {
this.toastService.showToast({
variant: "error",
title: "",
message: this.i18nService.t("invalidSshKey"),
});
return;
}
switch (parsedKey.status) {
case sshAgent.SshKeyImportStatus.ParsingError:
this.toastService.showToast({
variant: "error",
title: "",
message: this.i18nService.t("invalidSshKey"),
});
return;
case sshAgent.SshKeyImportStatus.UnsupportedKeyType:
this.toastService.showToast({
variant: "error",
title: "",
message: this.i18nService.t("sshKeyTypeUnsupported"),
});
return;
case sshAgent.SshKeyImportStatus.PasswordRequired:
case sshAgent.SshKeyImportStatus.WrongPassword:
if (password !== "") {
this.toastService.showToast({
variant: "error",
title: "",
message: this.i18nService.t("sshKeyWrongPassword"),
});
} else {
password = await this.getSshKeyPassword();
if (password === "") {
return;
}
await this.importSshKeyFromClipboard(password);
}
return;
default:
this.cipher.sshKey.privateKey = parsedKey.sshKey.privateKey;
this.cipher.sshKey.publicKey = parsedKey.sshKey.publicKey;
this.cipher.sshKey.keyFingerprint = parsedKey.sshKey.keyFingerprint;
this.toastService.showToast({
variant: "success",
title: "",
message: this.i18nService.t("sshKeyPasted"),
});
}
}
async getSshKeyPassword(): Promise<string> {
const dialog = this.dialogService.open<string>(SshKeyPasswordPromptComponent, {
ariaModal: true,
});
return await lastValueFrom(dialog.closed);
}
truncateString(value: string, length: number) {
return value.length > length ? value.substring(0, length) + "..." : value;
}