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:
@@ -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({
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -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");
|
||||
},
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user