1
0
mirror of https://github.com/bitwarden/browser synced 2026-01-06 02:23:44 +00:00
Files
browser/libs/vault/src/services/default-ssh-import-prompt.service.ts
Bernd Schoolmann 01f6fd7ee3 [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>
2025-03-10 18:41:47 +01:00

110 lines
3.3 KiB
TypeScript

import { Injectable } from "@angular/core";
import { firstValueFrom } from "rxjs";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { SshKeyApi } from "@bitwarden/common/vault/models/api/ssh-key.api";
import { SshKeyData } from "@bitwarden/common/vault/models/data/ssh-key.data";
import { DialogService, ToastService } from "@bitwarden/components";
import { SshKeyPasswordPromptComponent } from "@bitwarden/importer-ui";
import { import_ssh_key, SshKeyImportError, SshKeyView } from "@bitwarden/sdk-internal";
import { SshImportPromptService } from "./ssh-import-prompt.service";
/**
* Used to import ssh keys and prompt for their password.
*/
@Injectable()
export class DefaultSshImportPromptService implements SshImportPromptService {
constructor(
private dialogService: DialogService,
private toastService: ToastService,
private platformUtilsService: PlatformUtilsService,
private i18nService: I18nService,
) {}
async importSshKeyFromClipboard(): Promise<SshKeyData | null> {
const key = await this.platformUtilsService.readFromClipboard();
let isPasswordProtectedSshKey = false;
let parsedKey: SshKeyView | null = null;
try {
parsedKey = import_ssh_key(key);
} catch (e) {
const error = e as SshKeyImportError;
if (error.variant === "PasswordRequired" || error.variant === "WrongPassword") {
isPasswordProtectedSshKey = true;
} else {
this.toastService.showToast({
variant: "error",
title: "",
message: this.i18nService.t(this.sshImportErrorVariantToI18nKey(error.variant)),
});
return null;
}
}
if (isPasswordProtectedSshKey) {
for (;;) {
const password = await this.getSshKeyPassword();
if (password === "" || password == null) {
return null;
}
try {
parsedKey = import_ssh_key(key, password);
break;
} catch (e) {
const error = e as SshKeyImportError;
if (error.variant !== "WrongPassword") {
this.toastService.showToast({
variant: "error",
title: "",
message: this.i18nService.t(this.sshImportErrorVariantToI18nKey(error.variant)),
});
return null;
}
}
}
}
this.toastService.showToast({
variant: "success",
title: "",
message: this.i18nService.t("sshKeyImported"),
});
return new SshKeyData(
new SshKeyApi({
privateKey: parsedKey!.privateKey,
publicKey: parsedKey!.publicKey,
keyFingerprint: parsedKey!.fingerprint,
}),
);
}
private sshImportErrorVariantToI18nKey(variant: string): string {
switch (variant) {
case "ParsingError":
return "invalidSshKey";
case "UnsupportedKeyType":
return "sshKeyTypeUnsupported";
case "PasswordRequired":
case "WrongPassword":
return "sshKeyWrongPassword";
default:
return "errorOccurred";
}
}
private async getSshKeyPassword(): Promise<string | undefined> {
const dialog = this.dialogService.open<string>(SshKeyPasswordPromptComponent, {
ariaModal: true,
});
return await firstValueFrom(dialog.closed);
}
}