1
0
mirror of https://github.com/bitwarden/browser.git synced 2024-12-03 13:33:32 +01:00

[PM-14990] Add password prompt for ssh key import (#12105)

* Add password prompt for ssh key import

* Remove empty line

* Convert to switch statement
This commit is contained in:
Bernd Schoolmann 2024-11-27 08:29:36 -08:00 committed by GitHub
parent 2767851925
commit f79141c421
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 147 additions and 30 deletions

View File

@ -207,6 +207,21 @@
"sshKeyGenerated": {
"message": "A new SSH key was generated"
},
"sshKeyWrongPassword": {
"message": "The password you entered is incorrect."
},
"importSshKey": {
"message": "Import"
},
"confirmSshKeyPassword": {
"message": "Confirm password"
},
"enterSshKeyPasswordDesc": {
"message": "Enter the password for the SSH key."
},
"enterSshKeyPassword": {
"message": "Enter password"
},
"sshAgentUnlockRequired": {
"message": "Please unlock your vault to approve the SSH key request."
},
@ -1752,10 +1767,10 @@
"deleteAccountWarning": {
"message": "Deleting your account is permanent. It cannot be undone."
},
"cannotDeleteAccount":{
"cannotDeleteAccount": {
"message": "Cannot delete account"
},
"cannotDeleteAccountDesc":{
"cannotDeleteAccountDesc": {
"message": "This action cannot be completed because your account is owned by an organization. Contact your organization administrator for additional details."
},
"accountDeleted": {

View File

@ -2,6 +2,7 @@ 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";
@ -22,6 +23,7 @@ import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folde
import { CipherType } from "@bitwarden/common/vault/enums";
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";
const BroadcasterSubscriptionId = "AddEditComponent";
@ -170,42 +172,64 @@ export class AddEditComponent extends BaseAddEditComponent implements OnInit, On
}
}
async importSshKeyFromClipboard() {
async importSshKeyFromClipboard(password: string = "") {
const key = await this.platformUtilsService.readFromClipboard();
const parsedKey = await ipc.platform.sshAgent.importKey(key, "");
if (parsedKey == null || parsedKey.status === sshAgent.SshKeyImportStatus.ParsingError) {
const parsedKey = await ipc.platform.sshAgent.importKey(key, password);
if (parsedKey == null) {
this.toastService.showToast({
variant: "error",
title: "",
message: this.i18nService.t("invalidSshKey"),
});
return;
} else if (parsedKey.status === sshAgent.SshKeyImportStatus.UnsupportedKeyType) {
this.toastService.showToast({
variant: "error",
title: "",
message: this.i18nService.t("sshKeyTypeUnsupported"),
});
} else if (
parsedKey.status === sshAgent.SshKeyImportStatus.PasswordRequired ||
parsedKey.status === sshAgent.SshKeyImportStatus.WrongPassword
) {
this.toastService.showToast({
variant: "error",
title: "",
message: this.i18nService.t("sshKeyPasswordUnsupported"),
});
return;
} else {
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"),
});
}
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();
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);
}
async typeChange() {

View File

@ -1,3 +1,4 @@
export * from "./import-error-dialog.component";
export * from "./import-success-dialog.component";
export * from "./file-password-prompt.component";
export * from "./sshkey-password-prompt.component";

View File

@ -0,0 +1,31 @@
<form [formGroup]="formGroup" [bitSubmit]="submit">
<bit-dialog>
<span bitDialogTitle>
{{ "enterSshKeyPassword" | i18n }}
</span>
<div bitDialogContent>
{{ "enterSshKeyPasswordDesc" | i18n }}
<bit-form-field class="tw-mt-6">
<bit-label>{{ "confirmSshKeyPassword" | i18n }}</bit-label>
<input
bitInput
type="password"
formControlName="sshKeyPassword"
appAutofocus
appInputVerbatim
/>
<button type="button" bitSuffix bitIconButton bitPasswordInputToggle></button>
</bit-form-field>
</div>
<ng-container bitDialogFooter>
<button bitButton buttonType="primary" type="submit">
<span>{{ "importSshKey" | i18n }}</span>
</button>
<button bitButton bitDialogClose buttonType="secondary" type="button">
<span>{{ "cancel" | i18n }}</span>
</button>
</ng-container>
</bit-dialog>
</form>

View File

@ -0,0 +1,46 @@
import { DialogRef } from "@angular/cdk/dialog";
import { CommonModule } from "@angular/common";
import { Component } from "@angular/core";
import { FormBuilder, ReactiveFormsModule, Validators } from "@angular/forms";
import { JslibModule } from "@bitwarden/angular/jslib.module";
import {
AsyncActionsModule,
ButtonModule,
DialogModule,
FormFieldModule,
IconButtonModule,
} from "@bitwarden/components";
@Component({
templateUrl: "sshkey-password-prompt.component.html",
standalone: true,
imports: [
CommonModule,
JslibModule,
DialogModule,
FormFieldModule,
AsyncActionsModule,
ButtonModule,
IconButtonModule,
ReactiveFormsModule,
],
})
export class SshKeyPasswordPromptComponent {
protected formGroup = this.formBuilder.group({
sshKeyPassword: ["", Validators.required],
});
constructor(
public dialogRef: DialogRef,
protected formBuilder: FormBuilder,
) {}
submit = () => {
this.formGroup.markAsTouched();
if (!this.formGroup.valid) {
return;
}
this.dialogRef.close(this.formGroup.value.sshKeyPassword);
};
}