mirror of
https://github.com/bitwarden/browser
synced 2026-02-13 06:54:07 +00:00
* [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>
207 lines
5.9 KiB
TypeScript
207 lines
5.9 KiB
TypeScript
import { View } from "../../../models/view/view";
|
|
import { InitializerMetadata } from "../../../platform/interfaces/initializer-metadata.interface";
|
|
import { InitializerKey } from "../../../platform/services/cryptography/initializer-key";
|
|
import { DeepJsonify } from "../../../types/deep-jsonify";
|
|
import { CipherType, LinkedIdType } from "../../enums";
|
|
import { CipherRepromptType } from "../../enums/cipher-reprompt-type";
|
|
import { LocalData } from "../data/local.data";
|
|
import { Cipher } from "../domain/cipher";
|
|
|
|
import { AttachmentView } from "./attachment.view";
|
|
import { CardView } from "./card.view";
|
|
import { FieldView } from "./field.view";
|
|
import { IdentityView } from "./identity.view";
|
|
import { LoginView } from "./login.view";
|
|
import { PasswordHistoryView } from "./password-history.view";
|
|
import { SecureNoteView } from "./secure-note.view";
|
|
import { SshKeyView } from "./ssh-key.view";
|
|
|
|
export class CipherView implements View, InitializerMetadata {
|
|
readonly initializerKey = InitializerKey.CipherView;
|
|
|
|
id: string = null;
|
|
organizationId: string = null;
|
|
folderId: string = null;
|
|
name: string = null;
|
|
notes: string = null;
|
|
type: CipherType = null;
|
|
favorite = false;
|
|
organizationUseTotp = false;
|
|
edit = false;
|
|
viewPassword = true;
|
|
localData: LocalData;
|
|
login = new LoginView();
|
|
identity = new IdentityView();
|
|
card = new CardView();
|
|
secureNote = new SecureNoteView();
|
|
sshKey = new SshKeyView();
|
|
attachments: AttachmentView[] = null;
|
|
fields: FieldView[] = null;
|
|
passwordHistory: PasswordHistoryView[] = null;
|
|
collectionIds: string[] = null;
|
|
revisionDate: Date = null;
|
|
creationDate: Date = null;
|
|
deletedDate: Date = null;
|
|
reprompt: CipherRepromptType = CipherRepromptType.None;
|
|
|
|
constructor(c?: Cipher) {
|
|
if (!c) {
|
|
return;
|
|
}
|
|
|
|
this.id = c.id;
|
|
this.organizationId = c.organizationId;
|
|
this.folderId = c.folderId;
|
|
this.favorite = c.favorite;
|
|
this.organizationUseTotp = c.organizationUseTotp;
|
|
this.edit = c.edit;
|
|
this.viewPassword = c.viewPassword;
|
|
this.type = c.type;
|
|
this.localData = c.localData;
|
|
this.collectionIds = c.collectionIds;
|
|
this.revisionDate = c.revisionDate;
|
|
this.creationDate = c.creationDate;
|
|
this.deletedDate = c.deletedDate;
|
|
// Old locally stored ciphers might have reprompt == null. If so set it to None.
|
|
this.reprompt = c.reprompt ?? CipherRepromptType.None;
|
|
}
|
|
|
|
private get item() {
|
|
switch (this.type) {
|
|
case CipherType.Login:
|
|
return this.login;
|
|
case CipherType.SecureNote:
|
|
return this.secureNote;
|
|
case CipherType.Card:
|
|
return this.card;
|
|
case CipherType.Identity:
|
|
return this.identity;
|
|
case CipherType.SshKey:
|
|
return this.sshKey;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
get subTitle(): string {
|
|
return this.item?.subTitle;
|
|
}
|
|
|
|
get hasPasswordHistory(): boolean {
|
|
return this.passwordHistory && this.passwordHistory.length > 0;
|
|
}
|
|
|
|
get hasAttachments(): boolean {
|
|
return this.attachments && this.attachments.length > 0;
|
|
}
|
|
|
|
get hasOldAttachments(): boolean {
|
|
if (this.hasAttachments) {
|
|
for (let i = 0; i < this.attachments.length; i++) {
|
|
if (this.attachments[i].key == null) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
get hasFields(): boolean {
|
|
return this.fields && this.fields.length > 0;
|
|
}
|
|
|
|
get passwordRevisionDisplayDate(): Date {
|
|
if (this.type !== CipherType.Login || this.login == null) {
|
|
return null;
|
|
} else if (this.login.password == null || this.login.password === "") {
|
|
return null;
|
|
}
|
|
return this.login.passwordRevisionDate;
|
|
}
|
|
|
|
get isDeleted(): boolean {
|
|
return this.deletedDate != null;
|
|
}
|
|
|
|
get linkedFieldOptions() {
|
|
return this.item?.linkedFieldOptions;
|
|
}
|
|
|
|
get isUnassigned(): boolean {
|
|
return (
|
|
this.organizationId != null && (this.collectionIds == null || this.collectionIds.length === 0)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Determines if the cipher can be launched in a new browser tab.
|
|
*/
|
|
get canLaunch(): boolean {
|
|
return this.type === CipherType.Login && this.login.canLaunch;
|
|
}
|
|
|
|
linkedFieldValue(id: LinkedIdType) {
|
|
const linkedFieldOption = this.linkedFieldOptions?.get(id);
|
|
if (linkedFieldOption == null) {
|
|
return null;
|
|
}
|
|
|
|
const item = this.item;
|
|
return this.item[linkedFieldOption.propertyKey as keyof typeof item];
|
|
}
|
|
|
|
linkedFieldI18nKey(id: LinkedIdType): string {
|
|
return this.linkedFieldOptions.get(id)?.i18nKey;
|
|
}
|
|
|
|
// This is used as a marker to indicate that the cipher view object still has its prototype
|
|
toJSON() {
|
|
return this;
|
|
}
|
|
|
|
static fromJSON(obj: Partial<DeepJsonify<CipherView>>): CipherView {
|
|
if (obj == null) {
|
|
return null;
|
|
}
|
|
|
|
const view = new CipherView();
|
|
const revisionDate = obj.revisionDate == null ? null : new Date(obj.revisionDate);
|
|
const deletedDate = obj.deletedDate == null ? null : new Date(obj.deletedDate);
|
|
const attachments = obj.attachments?.map((a: any) => AttachmentView.fromJSON(a));
|
|
const fields = obj.fields?.map((f: any) => FieldView.fromJSON(f));
|
|
const passwordHistory = obj.passwordHistory?.map((ph: any) => PasswordHistoryView.fromJSON(ph));
|
|
|
|
Object.assign(view, obj, {
|
|
revisionDate: revisionDate,
|
|
deletedDate: deletedDate,
|
|
attachments: attachments,
|
|
fields: fields,
|
|
passwordHistory: passwordHistory,
|
|
});
|
|
|
|
switch (obj.type) {
|
|
case CipherType.Card:
|
|
view.card = CardView.fromJSON(obj.card);
|
|
break;
|
|
case CipherType.Identity:
|
|
view.identity = IdentityView.fromJSON(obj.identity);
|
|
break;
|
|
case CipherType.Login:
|
|
view.login = LoginView.fromJSON(obj.login);
|
|
break;
|
|
case CipherType.SecureNote:
|
|
view.secureNote = SecureNoteView.fromJSON(obj.secureNote);
|
|
break;
|
|
case CipherType.SshKey:
|
|
view.sshKey = SshKeyView.fromJSON(obj.sshKey);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return view;
|
|
}
|
|
}
|