mirror of
https://github.com/bitwarden/browser
synced 2025-12-25 20:53:22 +00:00
PM-10393 SSH keys (#10825)
* [PM-10395] Add new item type ssh key (#10360) * Implement ssh-key cipher type * Fix linting * Fix edit and view components for ssh-keys on desktop * Fix tests * Remove ssh key type references * Remove add ssh key option * Fix typo * Add tests * [PM-10399] Add ssh key import export for bitwarden json (#10529) * Add ssh key import export for bitwarden json * Remove key type from ssh key export * [PM-10406] Add privatekey publickey and fingerprint to both add-edit and view co… (#11046) * Add privatekey publickey and fingerprint to both add-edit and view components * Remove wrong a11y title * Fix testid * [PM-10098] SSH Agent & SSH Key creation for Bitwarden Desktop (#10293) * Add ssh agent, generator & import * Move ssh agent code to bitwarden-russh crate * Remove generator component * Cleanup * Cleanup * Remove left over sshGenerator reference * Cleanup * Add documentation to sshkeyimportstatus * Fix outdated variable name * Update apps/desktop/src/platform/preload.ts Co-authored-by: Andreas Coroiu <acoroiu@bitwarden.com> * Rename renderersshagent * Rename MainSshAgentService * Improve clarity of 'id' variables being used * Improve clarity of 'id' variables being used * Update apps/desktop/src/vault/app/vault/add-edit.component.html Co-authored-by: Andreas Coroiu <acoroiu@bitwarden.com> * Fix outdated cipher/messageid names * Rename SSH to Ssh * Make agent syncing more reactive * Move constants to top of class * Make sshkey cipher filtering clearer * Add stricter equality check on ssh key unlock * Fix build and messages * Fix incorrect featureflag name * Replace anonymous async function with switchmap pipe * Fix build * Update apps/desktop/desktop_native/napi/src/lib.rs Co-authored-by: Andreas Coroiu <acoroiu@bitwarden.com> * Revert incorrectly renamed 'Ssh' usages to SSH * Run cargo fmt * Clean up ssh agent sock path logic * Cleanup and split to platform specific files * Small cleanup * Pull out generator and importer into core * Rename renderersshagentservice to sshagentservice * Rename cipheruuid to cipher_id * Drop ssh dependencies from napi crate * Clean up windows build * Small cleanup * Small cleanup * Cleanup * Add rxjs pipeline for agent services * [PM-12555] Pkcs8 sshkey import & general ssh key import tests (#11048) * Add pkcs8 import and tests * Add key type unsupported error * Remove unsupported formats * Remove code for unsupported formats * Fix encrypted pkcs8 import * Add ed25519 pkcs8 unencrypted test file * SSH agent rxjs tweaks (#11148) * feat: rewrite sshagent.signrequest as purely observable * feat: fail the request when unlock times out * chore: clean up, add some clarifying comments * chore: remove unused dependency * fix: result `undefined` crashing in NAPI -> Rust * Allow concurrent SSH requests in rust * Remove unwraps * Cleanup and add init service init call * Fix windows * Fix timeout behavior on locked vault --------- Co-authored-by: Andreas Coroiu <acoroiu@bitwarden.com> * Fix libc dependency being duplicated * fix SSH casing (#11840) * Move ssh agent behind feature flag (#11841) * Move ssh agent behind feature flag * Add separate flag for ssh agent * [PM-14215] fix unsupported key type error message (#11788) * Fix error message for import of unsupported ssh keys * Use triple equals in add-edit component for ssh keys --------- Co-authored-by: Andreas Coroiu <acoroiu@bitwarden.com> Co-authored-by: aj-bw <81774843+aj-bw@users.noreply.github.com>
This commit is contained in:
@@ -152,6 +152,15 @@
|
||||
"copyLicenseNumber": {
|
||||
"message": "Copy license number"
|
||||
},
|
||||
"copyPrivateKey": {
|
||||
"message": "Copy private key"
|
||||
},
|
||||
"copyPublicKey": {
|
||||
"message": "Copy public key"
|
||||
},
|
||||
"copyFingerprint": {
|
||||
"message": "Copy fingerprint"
|
||||
},
|
||||
"copyCustomField": {
|
||||
"message": "Copy $FIELD$",
|
||||
"placeholders": {
|
||||
@@ -1764,6 +1773,9 @@
|
||||
"typeIdentity": {
|
||||
"message": "Identity"
|
||||
},
|
||||
"typeSshKey": {
|
||||
"message": "SSH key"
|
||||
},
|
||||
"newItemHeader": {
|
||||
"message": "New $TYPE$",
|
||||
"placeholders": {
|
||||
@@ -4593,6 +4605,30 @@
|
||||
"enterprisePolicyRequirementsApplied": {
|
||||
"message": "Enterprise policy requirements have been applied to this setting"
|
||||
},
|
||||
"sshPrivateKey": {
|
||||
"message": "Private key"
|
||||
},
|
||||
"sshPublicKey": {
|
||||
"message": "Public key"
|
||||
},
|
||||
"sshFingerprint": {
|
||||
"message": "Fingerprint"
|
||||
},
|
||||
"sshKeyAlgorithm": {
|
||||
"message": "Key type"
|
||||
},
|
||||
"sshKeyAlgorithmED25519": {
|
||||
"message": "ED25519"
|
||||
},
|
||||
"sshKeyAlgorithmRSA2048": {
|
||||
"message": "RSA 2048-Bit"
|
||||
},
|
||||
"sshKeyAlgorithmRSA3072": {
|
||||
"message": "RSA 3072-Bit"
|
||||
},
|
||||
"sshKeyAlgorithmRSA4096": {
|
||||
"message": "RSA 4096-Bit"
|
||||
},
|
||||
"retry": {
|
||||
"message": "Retry"
|
||||
},
|
||||
|
||||
@@ -331,6 +331,8 @@ export class AddEditV2Component implements OnInit {
|
||||
return this.i18nService.t(partOne, this.i18nService.t("typeIdentity").toLocaleLowerCase());
|
||||
case CipherType.SecureNote:
|
||||
return this.i18nService.t(partOne, this.i18nService.t("note").toLocaleLowerCase());
|
||||
case CipherType.SshKey:
|
||||
return this.i18nService.t(partOne, this.i18nService.t("typeSshKey").toLocaleLowerCase());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,3 +88,27 @@
|
||||
[cipher]="cipher"
|
||||
></button>
|
||||
</bit-item-action>
|
||||
|
||||
<bit-item-action *ngIf="cipher.type === CipherType.SshKey">
|
||||
<button
|
||||
type="button"
|
||||
bitIconButton="bwi-clone"
|
||||
size="small"
|
||||
[appA11yTitle]="
|
||||
hasSshKeyValues ? ('copyInfoTitle' | i18n: cipher.name) : ('noValuesToCopy' | i18n)
|
||||
"
|
||||
[disabled]="!hasSshKeyValues"
|
||||
[bitMenuTriggerFor]="sshKeyOptions"
|
||||
></button>
|
||||
<bit-menu #sshKeyOptions>
|
||||
<button type="button" bitMenuItem appCopyField="privateKey" [cipher]="cipher">
|
||||
{{ "copyPrivateKey" | i18n }}
|
||||
</button>
|
||||
<button type="button" bitMenuItem appCopyField="publicKey" [cipher]="cipher">
|
||||
{{ "copyPublicKey" | i18n }}
|
||||
</button>
|
||||
<button type="button" bitMenuItem appCopyField="keyFingerprint" [cipher]="cipher">
|
||||
{{ "copyFingerprint" | i18n }}
|
||||
</button>
|
||||
</bit-menu>
|
||||
</bit-item-action>
|
||||
|
||||
@@ -48,5 +48,13 @@ export class ItemCopyActionsComponent {
|
||||
return !!this.cipher.notes;
|
||||
}
|
||||
|
||||
get hasSshKeyValues() {
|
||||
return (
|
||||
!!this.cipher.sshKey.privateKey ||
|
||||
!!this.cipher.sshKey.publicKey ||
|
||||
!!this.cipher.sshKey.keyFingerprint
|
||||
);
|
||||
}
|
||||
|
||||
constructor() {}
|
||||
}
|
||||
|
||||
@@ -131,6 +131,8 @@ export class ViewV2Component {
|
||||
);
|
||||
case CipherType.SecureNote:
|
||||
return this.i18nService.t("viewItemHeader", this.i18nService.t("note").toLowerCase());
|
||||
case CipherType.SshKey:
|
||||
return this.i18nService.t("viewItemHeader", this.i18nService.t("typeSshkey").toLowerCase());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -529,6 +529,26 @@
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- SshKey -->
|
||||
<div *ngIf="cipher.sshKey">
|
||||
<div class="box-content-row" *ngIf="cipher.sshKey.privateKey" style="overflow: hidden">
|
||||
<span class="row-label"> {{ "sshPrivateKey" | i18n }}</span>
|
||||
{{ cipher.sshKey.privateKey }}
|
||||
</div>
|
||||
<div class="box-content-row" *ngIf="cipher.sshKey.publicKey" style="overflow: hidden">
|
||||
<span class="row-label"> {{ "sshPublicKey" | i18n }}</span>
|
||||
{{ cipher.sshKey.publicKey }}
|
||||
</div>
|
||||
<div
|
||||
class="box-content-row"
|
||||
*ngIf="cipher.sshKey.keyFingerprint"
|
||||
style="overflow: hidden"
|
||||
>
|
||||
<span class="row-label"> {{ "sshKeyFingerprint" | i18n }}</span>
|
||||
{{ cipher.sshKey.keyFingerprint }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box" *ngIf="cipher.type === cipherType.Login">
|
||||
|
||||
@@ -114,6 +114,19 @@
|
||||
<span class="row-sub-label">{{ typeCounts.get(cipherType.SecureNote) || 0 }}</span>
|
||||
<span><i class="bwi bwi-angle-right bwi-lg row-sub-icon"></i></span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="box-content-row"
|
||||
appStopClick
|
||||
(click)="selectType(cipherType.SshKey)"
|
||||
>
|
||||
<div class="row-main">
|
||||
<div class="icon"><i class="bwi bwi-fw bwi-lg bwi-key"></i></div>
|
||||
<span class="text">{{ "typeSshKey" | i18n }}</span>
|
||||
</div>
|
||||
<span class="row-sub-label">{{ typeCounts.get(cipherType.SshKey) || 0 }}</span>
|
||||
<span><i class="bwi bwi-angle-right bwi-lg row-sub-icon"></i></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box list" *ngIf="nestedFolders?.length">
|
||||
|
||||
@@ -106,6 +106,9 @@ export class VaultItemsComponent extends BaseVaultItemsComponent implements OnIn
|
||||
case CipherType.SecureNote:
|
||||
this.groupingTitle = this.i18nService.t("secureNotes");
|
||||
break;
|
||||
case CipherType.SshKey:
|
||||
this.groupingTitle = this.i18nService.t("sshKeys");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -429,6 +429,39 @@
|
||||
<div *ngIf="cipher.identity.country">{{ cipher.identity.country }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- SshKey -->
|
||||
<div *ngIf="cipher.sshKey">
|
||||
<div class="box-content-row" *ngIf="cipher.sshKey.privateKey" style="overflow: hidden">
|
||||
<span
|
||||
class="row-label draggable"
|
||||
draggable="true"
|
||||
(dragstart)="setTextDataOnDrag($event, cipher.sshKey.privateKey)"
|
||||
>
|
||||
{{ "sshPrivateKey" | i18n }}
|
||||
</span>
|
||||
<div [innerText]="cipher.sshKey.maskedPrivateKey" class="monospaced"></div>
|
||||
</div>
|
||||
<div class="box-content-row" *ngIf="cipher.sshKey.publicKey" style="overflow: hidden">
|
||||
<span
|
||||
class="row-label draggable"
|
||||
draggable="true"
|
||||
(dragstart)="setTextDataOnDrag($event, cipher.sshKey.publicKey)"
|
||||
>
|
||||
{{ "sshPublicKey" | i18n }}</span
|
||||
>
|
||||
{{ cipher.sshKey.publicKey }}
|
||||
</div>
|
||||
<div class="box-content-row" *ngIf="cipher.sshKey.keyFingerprint" style="overflow: hidden">
|
||||
<span
|
||||
class="row-label draggable"
|
||||
draggable="true"
|
||||
(dragstart)="setTextDataOnDrag($event, cipher.sshKey.keyFingerprint)"
|
||||
>
|
||||
{{ "sshFingerprint" | i18n }}</span
|
||||
>
|
||||
{{ cipher.sshKey.keyFingerprint }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box" *ngIf="cipher.login && cipher.login.hasUris">
|
||||
|
||||
@@ -202,6 +202,7 @@ describe("VaultPopupItemsService", () => {
|
||||
[CipherType.Card]: 2,
|
||||
[CipherType.Identity]: 3,
|
||||
[CipherType.SecureNote]: 4,
|
||||
[CipherType.SshKey]: 5,
|
||||
};
|
||||
|
||||
// Assume all ciphers are autofill ciphers to test sorting
|
||||
|
||||
@@ -277,6 +277,7 @@ export class VaultPopupItemsService {
|
||||
[CipherType.Card]: 2,
|
||||
[CipherType.Identity]: 3,
|
||||
[CipherType.SecureNote]: 4,
|
||||
[CipherType.SshKey]: 5,
|
||||
};
|
||||
|
||||
// Compare types first
|
||||
|
||||
@@ -97,6 +97,7 @@ describe("VaultPopupListFiltersService", () => {
|
||||
CipherType.Card,
|
||||
CipherType.Identity,
|
||||
CipherType.SecureNote,
|
||||
CipherType.SshKey,
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -163,6 +163,11 @@ export class VaultPopupListFiltersService {
|
||||
label: this.i18nService.t("note"),
|
||||
icon: "bwi-sticky-note",
|
||||
},
|
||||
{
|
||||
value: CipherType.SshKey,
|
||||
label: this.i18nService.t("typeSshKey"),
|
||||
icon: "bwi-key",
|
||||
},
|
||||
];
|
||||
|
||||
/** Resets `filterForm` to the original state */
|
||||
|
||||
Reference in New Issue
Block a user