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:
parent
2767851925
commit
f79141c421
@ -207,6 +207,21 @@
|
|||||||
"sshKeyGenerated": {
|
"sshKeyGenerated": {
|
||||||
"message": "A new SSH key was generated"
|
"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": {
|
"sshAgentUnlockRequired": {
|
||||||
"message": "Please unlock your vault to approve the SSH key request."
|
"message": "Please unlock your vault to approve the SSH key request."
|
||||||
},
|
},
|
||||||
@ -1752,10 +1767,10 @@
|
|||||||
"deleteAccountWarning": {
|
"deleteAccountWarning": {
|
||||||
"message": "Deleting your account is permanent. It cannot be undone."
|
"message": "Deleting your account is permanent. It cannot be undone."
|
||||||
},
|
},
|
||||||
"cannotDeleteAccount":{
|
"cannotDeleteAccount": {
|
||||||
"message": "Cannot delete account"
|
"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."
|
"message": "This action cannot be completed because your account is owned by an organization. Contact your organization administrator for additional details."
|
||||||
},
|
},
|
||||||
"accountDeleted": {
|
"accountDeleted": {
|
||||||
|
@ -2,6 +2,7 @@ import { DatePipe } from "@angular/common";
|
|||||||
import { Component, NgZone, OnChanges, OnDestroy, OnInit, ViewChild } from "@angular/core";
|
import { Component, NgZone, OnChanges, OnDestroy, OnInit, ViewChild } from "@angular/core";
|
||||||
import { NgForm } from "@angular/forms";
|
import { NgForm } from "@angular/forms";
|
||||||
import { sshagent as sshAgent } from "desktop_native/napi";
|
import { sshagent as sshAgent } from "desktop_native/napi";
|
||||||
|
import { lastValueFrom } from "rxjs";
|
||||||
|
|
||||||
import { CollectionService } from "@bitwarden/admin-console/common";
|
import { CollectionService } from "@bitwarden/admin-console/common";
|
||||||
import { AddEditComponent as BaseAddEditComponent } from "@bitwarden/angular/vault/components/add-edit.component";
|
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 { CipherType } from "@bitwarden/common/vault/enums";
|
||||||
import { CipherAuthorizationService } from "@bitwarden/common/vault/services/cipher-authorization.service";
|
import { CipherAuthorizationService } from "@bitwarden/common/vault/services/cipher-authorization.service";
|
||||||
import { DialogService, ToastService } from "@bitwarden/components";
|
import { DialogService, ToastService } from "@bitwarden/components";
|
||||||
|
import { SshKeyPasswordPromptComponent } from "@bitwarden/importer/ui";
|
||||||
import { PasswordRepromptService } from "@bitwarden/vault";
|
import { PasswordRepromptService } from "@bitwarden/vault";
|
||||||
|
|
||||||
const BroadcasterSubscriptionId = "AddEditComponent";
|
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 key = await this.platformUtilsService.readFromClipboard();
|
||||||
const parsedKey = await ipc.platform.sshAgent.importKey(key, "");
|
const parsedKey = await ipc.platform.sshAgent.importKey(key, password);
|
||||||
if (parsedKey == null || parsedKey.status === sshAgent.SshKeyImportStatus.ParsingError) {
|
if (parsedKey == null) {
|
||||||
this.toastService.showToast({
|
this.toastService.showToast({
|
||||||
variant: "error",
|
variant: "error",
|
||||||
title: "",
|
title: "",
|
||||||
message: this.i18nService.t("invalidSshKey"),
|
message: this.i18nService.t("invalidSshKey"),
|
||||||
});
|
});
|
||||||
return;
|
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() {
|
async typeChange() {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
export * from "./import-error-dialog.component";
|
export * from "./import-error-dialog.component";
|
||||||
export * from "./import-success-dialog.component";
|
export * from "./import-success-dialog.component";
|
||||||
export * from "./file-password-prompt.component";
|
export * from "./file-password-prompt.component";
|
||||||
|
export * from "./sshkey-password-prompt.component";
|
||||||
|
@ -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>
|
@ -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);
|
||||||
|
};
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user