mirror of
https://github.com/bitwarden/browser
synced 2025-12-18 01:03:35 +00:00
[PM-12423] Migrate Cipher Decryption to Use SDK (#14206)
* Created mappings for client domain object to SDK * Add abstract decrypt observable * Added todo for future consideration * Added implementation to cipher service * Added adapter and unit tests * Created cipher encryption abstraction and service * Register cipher encryption service * Added tests for the cipher encryption service * changed signature * Updated feature flag name * added new function to be used for decrypting ciphers * Added new encryptedKey field * added new function to be used for decrypting ciphers * Manually set fields * Added encrypted key in attachment view * Fixed test * Updated references to use decrypt with feature flag * Added dependency * updated package.json * lint fix * fixed tests * Fixed small mapping issues * Fixed test * Added function to decrypt fido2 key value * Added function to decrypt fido2 key value and updated test * updated to use sdk function without prociding the key * updated localdata sdk type change * decrypt attachment content using sdk * Fixed dependency issues * updated package.json * Refactored service to handle getting decrypted buffer using the legacy and sdk implementations * updated services and component to use refactored version * Updated decryptCiphersWithSdk to use decryptManyLegacy for batch decryption, ensuring the SDK is only called once per batch * Fixed merge conflicts * Fixed merge conflicts * Fixed merge conflicts * Fixed lint issues * Moved getDecryptedAttachmentBuffer to cipher service * Moved getDecryptedAttachmentBuffer to cipher service * ensure CipherView properties are null instead of undefined * Fixed test * ensure AttachmentView properties are null instead of undefined * Linked ticket in comment * removed unused orgKey
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
import { Jsonify } from "type-fest";
|
||||
|
||||
import { CipherPermissions as SdkCipherPermissions } from "@bitwarden/sdk-internal";
|
||||
|
||||
import { BaseResponse } from "../../../models/response/base.response";
|
||||
|
||||
export class CipherPermissionsApi extends BaseResponse {
|
||||
@@ -18,4 +20,19 @@ export class CipherPermissionsApi extends BaseResponse {
|
||||
static fromJSON(obj: Jsonify<CipherPermissionsApi>) {
|
||||
return Object.assign(new CipherPermissionsApi(), obj);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the SDK CipherPermissionsApi to a CipherPermissionsApi.
|
||||
*/
|
||||
static fromSdkCipherPermissions(obj: SdkCipherPermissions): CipherPermissionsApi | undefined {
|
||||
if (!obj) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const permissions = new CipherPermissionsApi();
|
||||
permissions.delete = obj.delete;
|
||||
permissions.restore = obj.restore;
|
||||
|
||||
return permissions;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ export class CipherData {
|
||||
passwordHistory?: PasswordHistoryData[];
|
||||
collectionIds?: string[];
|
||||
creationDate: string;
|
||||
deletedDate: string;
|
||||
deletedDate: string | null;
|
||||
reprompt: CipherRepromptType;
|
||||
key: string;
|
||||
|
||||
|
||||
@@ -153,4 +153,21 @@ describe("Attachment", () => {
|
||||
expect(Attachment.fromJSON(null)).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe("toSdkAttachment", () => {
|
||||
it("should map to SDK Attachment", () => {
|
||||
const attachment = new Attachment(data);
|
||||
|
||||
const sdkAttachment = attachment.toSdkAttachment();
|
||||
|
||||
expect(sdkAttachment).toEqual({
|
||||
id: "id",
|
||||
url: "url",
|
||||
size: "1100",
|
||||
sizeName: "1.1 KB",
|
||||
fileName: "fileName",
|
||||
key: "key",
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
// @ts-strict-ignore
|
||||
import { Jsonify } from "type-fest";
|
||||
|
||||
import { Attachment as SdkAttachment } from "@bitwarden/sdk-internal";
|
||||
|
||||
import { Utils } from "../../../platform/misc/utils";
|
||||
import Domain from "../../../platform/models/domain/domain-base";
|
||||
import { EncString } from "../../../platform/models/domain/enc-string";
|
||||
@@ -113,4 +115,20 @@ export class Attachment extends Domain {
|
||||
fileName,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps to SDK Attachment
|
||||
*
|
||||
* @returns {SdkAttachment} - The SDK Attachment object
|
||||
*/
|
||||
toSdkAttachment(): SdkAttachment {
|
||||
return {
|
||||
id: this.id,
|
||||
url: this.url,
|
||||
size: this.size,
|
||||
sizeName: this.sizeName,
|
||||
fileName: this.fileName?.toJSON(),
|
||||
key: this.key?.toJSON(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,4 +99,21 @@ describe("Card", () => {
|
||||
expect(Card.fromJSON(null)).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe("toSdkCard", () => {
|
||||
it("should map to SDK Card", () => {
|
||||
const card = new Card(data);
|
||||
|
||||
const sdkCard = card.toSdkCard();
|
||||
|
||||
expect(sdkCard).toEqual({
|
||||
cardholderName: "encHolder",
|
||||
brand: "encBrand",
|
||||
number: "encNumber",
|
||||
expMonth: "encMonth",
|
||||
expYear: "encYear",
|
||||
code: "encCode",
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
// @ts-strict-ignore
|
||||
import { Jsonify } from "type-fest";
|
||||
|
||||
import { Card as SdkCard } from "@bitwarden/sdk-internal";
|
||||
|
||||
import Domain from "../../../platform/models/domain/domain-base";
|
||||
import { EncString } from "../../../platform/models/domain/enc-string";
|
||||
import { SymmetricCryptoKey } from "../../../platform/models/domain/symmetric-crypto-key";
|
||||
@@ -85,4 +87,20 @@ export class Card extends Domain {
|
||||
code,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps Card to SDK format.
|
||||
*
|
||||
* @returns {SdkCard} The SDK card object.
|
||||
*/
|
||||
toSdkCard(): SdkCard {
|
||||
return {
|
||||
cardholderName: this.cardholderName?.toJSON(),
|
||||
brand: this.brand?.toJSON(),
|
||||
number: this.number?.toJSON(),
|
||||
expMonth: this.expMonth?.toJSON(),
|
||||
expYear: this.expYear?.toJSON(),
|
||||
code: this.code?.toJSON(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,12 @@ import { Jsonify } from "type-fest";
|
||||
|
||||
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
|
||||
import { KeyService } from "@bitwarden/key-management";
|
||||
import {
|
||||
CipherType as SdkCipherType,
|
||||
UriMatchType,
|
||||
CipherRepromptType as SdkCipherRepromptType,
|
||||
LoginLinkedIdType,
|
||||
} from "@bitwarden/sdk-internal";
|
||||
|
||||
import { makeStaticByteArray, mockEnc, mockFromJson } from "../../../../spec/utils";
|
||||
import { EncryptService } from "../../../key-management/crypto/abstractions/encrypt.service";
|
||||
@@ -12,7 +18,7 @@ import { ContainerService } from "../../../platform/services/container.service";
|
||||
import { InitializerKey } from "../../../platform/services/cryptography/initializer-key";
|
||||
import { UserId } from "../../../types/guid";
|
||||
import { CipherService } from "../../abstractions/cipher.service";
|
||||
import { FieldType, SecureNoteType } from "../../enums";
|
||||
import { FieldType, LoginLinkedId, SecureNoteType } from "../../enums";
|
||||
import { CipherRepromptType } from "../../enums/cipher-reprompt-type";
|
||||
import { CipherType } from "../../enums/cipher-type";
|
||||
import { CipherData } from "../../models/data/cipher.data";
|
||||
@@ -770,6 +776,165 @@ describe("Cipher DTO", () => {
|
||||
expect(Cipher.fromJSON(null)).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe("toSdkCipher", () => {
|
||||
it("should map to SDK Cipher", () => {
|
||||
const lastUsedDate = new Date("2025-04-15T12:00:00.000Z").getTime();
|
||||
const lastLaunched = new Date("2025-04-15T12:00:00.000Z").getTime();
|
||||
|
||||
const cipherData: CipherData = {
|
||||
id: "id",
|
||||
organizationId: "orgId",
|
||||
folderId: "folderId",
|
||||
edit: true,
|
||||
permissions: new CipherPermissionsApi(),
|
||||
viewPassword: true,
|
||||
organizationUseTotp: true,
|
||||
favorite: false,
|
||||
revisionDate: "2022-01-31T12:00:00.000Z",
|
||||
type: CipherType.Login,
|
||||
name: "EncryptedString",
|
||||
notes: "EncryptedString",
|
||||
creationDate: "2022-01-01T12:00:00.000Z",
|
||||
deletedDate: null,
|
||||
reprompt: CipherRepromptType.None,
|
||||
key: "EncryptedString",
|
||||
login: {
|
||||
uris: [
|
||||
{
|
||||
uri: "EncryptedString",
|
||||
uriChecksum: "EncryptedString",
|
||||
match: UriMatchStrategy.Domain,
|
||||
},
|
||||
],
|
||||
username: "EncryptedString",
|
||||
password: "EncryptedString",
|
||||
passwordRevisionDate: "2022-01-31T12:00:00.000Z",
|
||||
totp: "EncryptedString",
|
||||
autofillOnPageLoad: false,
|
||||
},
|
||||
passwordHistory: [
|
||||
{ password: "EncryptedString", lastUsedDate: "2022-01-31T12:00:00.000Z" },
|
||||
],
|
||||
attachments: [
|
||||
{
|
||||
id: "a1",
|
||||
url: "url",
|
||||
size: "1100",
|
||||
sizeName: "1.1 KB",
|
||||
fileName: "file",
|
||||
key: "EncKey",
|
||||
},
|
||||
{
|
||||
id: "a2",
|
||||
url: "url",
|
||||
size: "1100",
|
||||
sizeName: "1.1 KB",
|
||||
fileName: "file",
|
||||
key: "EncKey",
|
||||
},
|
||||
],
|
||||
fields: [
|
||||
{
|
||||
name: "EncryptedString",
|
||||
value: "EncryptedString",
|
||||
type: FieldType.Linked,
|
||||
linkedId: LoginLinkedId.Username,
|
||||
},
|
||||
{
|
||||
name: "EncryptedString",
|
||||
value: "EncryptedString",
|
||||
type: FieldType.Linked,
|
||||
linkedId: LoginLinkedId.Password,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const cipher = new Cipher(cipherData, { lastUsedDate, lastLaunched });
|
||||
const sdkCipher = cipher.toSdkCipher();
|
||||
|
||||
expect(sdkCipher).toEqual({
|
||||
id: "id",
|
||||
organizationId: "orgId",
|
||||
folderId: "folderId",
|
||||
collectionIds: [],
|
||||
key: "EncryptedString",
|
||||
name: "EncryptedString",
|
||||
notes: "EncryptedString",
|
||||
type: SdkCipherType.Login,
|
||||
login: {
|
||||
username: "EncryptedString",
|
||||
password: "EncryptedString",
|
||||
passwordRevisionDate: "2022-01-31T12:00:00.000Z",
|
||||
uris: [
|
||||
{
|
||||
uri: "EncryptedString",
|
||||
uriChecksum: "EncryptedString",
|
||||
match: UriMatchType.Domain,
|
||||
},
|
||||
],
|
||||
totp: "EncryptedString",
|
||||
autofillOnPageLoad: false,
|
||||
fido2Credentials: undefined,
|
||||
},
|
||||
identity: undefined,
|
||||
card: undefined,
|
||||
secureNote: undefined,
|
||||
sshKey: undefined,
|
||||
favorite: false,
|
||||
reprompt: SdkCipherRepromptType.None,
|
||||
organizationUseTotp: true,
|
||||
edit: true,
|
||||
permissions: new CipherPermissionsApi(),
|
||||
viewPassword: true,
|
||||
localData: {
|
||||
lastUsedDate: "2025-04-15T12:00:00.000Z",
|
||||
lastLaunched: "2025-04-15T12:00:00.000Z",
|
||||
},
|
||||
attachments: [
|
||||
{
|
||||
id: "a1",
|
||||
url: "url",
|
||||
size: "1100",
|
||||
sizeName: "1.1 KB",
|
||||
fileName: "file",
|
||||
key: "EncKey",
|
||||
},
|
||||
{
|
||||
id: "a2",
|
||||
url: "url",
|
||||
size: "1100",
|
||||
sizeName: "1.1 KB",
|
||||
fileName: "file",
|
||||
key: "EncKey",
|
||||
},
|
||||
],
|
||||
fields: [
|
||||
{
|
||||
name: "EncryptedString",
|
||||
value: "EncryptedString",
|
||||
type: FieldType.Linked,
|
||||
linkedId: LoginLinkedIdType.Username,
|
||||
},
|
||||
{
|
||||
name: "EncryptedString",
|
||||
value: "EncryptedString",
|
||||
type: FieldType.Linked,
|
||||
linkedId: LoginLinkedIdType.Password,
|
||||
},
|
||||
],
|
||||
passwordHistory: [
|
||||
{
|
||||
password: "EncryptedString",
|
||||
lastUsedDate: "2022-01-31T12:00:00.000Z",
|
||||
},
|
||||
],
|
||||
creationDate: "2022-01-01T12:00:00.000Z",
|
||||
deletedDate: undefined,
|
||||
revisionDate: "2022-01-31T12:00:00.000Z",
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
const mockUserId = "TestUserId" as UserId;
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
// @ts-strict-ignore
|
||||
import { Jsonify } from "type-fest";
|
||||
|
||||
import { Cipher as SdkCipher } from "@bitwarden/sdk-internal";
|
||||
|
||||
import { Decryptable } from "../../../platform/interfaces/decryptable.interface";
|
||||
import { Utils } from "../../../platform/misc/utils";
|
||||
import Domain from "../../../platform/models/domain/domain-base";
|
||||
@@ -330,4 +332,72 @@ export class Cipher extends Domain implements Decryptable<CipherView> {
|
||||
|
||||
return domain;
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps Cipher to SDK format.
|
||||
*
|
||||
* @returns {SdkCipher} The SDK cipher object.
|
||||
*/
|
||||
toSdkCipher(): SdkCipher {
|
||||
const sdkCipher: SdkCipher = {
|
||||
id: this.id,
|
||||
organizationId: this.organizationId,
|
||||
folderId: this.folderId,
|
||||
collectionIds: this.collectionIds || [],
|
||||
key: this.key?.toJSON(),
|
||||
name: this.name.toJSON(),
|
||||
notes: this.notes?.toJSON(),
|
||||
type: this.type,
|
||||
favorite: this.favorite,
|
||||
organizationUseTotp: this.organizationUseTotp,
|
||||
edit: this.edit,
|
||||
permissions: this.permissions,
|
||||
viewPassword: this.viewPassword,
|
||||
localData: this.localData
|
||||
? {
|
||||
lastUsedDate: this.localData.lastUsedDate
|
||||
? new Date(this.localData.lastUsedDate).toISOString()
|
||||
: undefined,
|
||||
lastLaunched: this.localData.lastLaunched
|
||||
? new Date(this.localData.lastLaunched).toISOString()
|
||||
: undefined,
|
||||
}
|
||||
: undefined,
|
||||
attachments: this.attachments?.map((a) => a.toSdkAttachment()),
|
||||
fields: this.fields?.map((f) => f.toSdkField()),
|
||||
passwordHistory: this.passwordHistory?.map((ph) => ph.toSdkPasswordHistory()),
|
||||
revisionDate: this.revisionDate?.toISOString(),
|
||||
creationDate: this.creationDate?.toISOString(),
|
||||
deletedDate: this.deletedDate?.toISOString(),
|
||||
reprompt: this.reprompt,
|
||||
// Initialize all cipher-type-specific properties as undefined
|
||||
login: undefined,
|
||||
identity: undefined,
|
||||
card: undefined,
|
||||
secureNote: undefined,
|
||||
sshKey: undefined,
|
||||
};
|
||||
|
||||
switch (this.type) {
|
||||
case CipherType.Login:
|
||||
sdkCipher.login = this.login.toSdkLogin();
|
||||
break;
|
||||
case CipherType.SecureNote:
|
||||
sdkCipher.secureNote = this.secureNote.toSdkSecureNote();
|
||||
break;
|
||||
case CipherType.Card:
|
||||
sdkCipher.card = this.card.toSdkCard();
|
||||
break;
|
||||
case CipherType.Identity:
|
||||
sdkCipher.identity = this.identity.toSdkIdentity();
|
||||
break;
|
||||
case CipherType.SshKey:
|
||||
sdkCipher.sshKey = this.sshKey.toSdkSshKey();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return sdkCipher;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,6 +167,45 @@ describe("Fido2Credential", () => {
|
||||
expect(Fido2Credential.fromJSON(null)).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe("SDK Fido2Credential Mapping", () => {
|
||||
it("should map to SDK Fido2Credential", () => {
|
||||
const data: Fido2CredentialData = {
|
||||
credentialId: "credentialId",
|
||||
keyType: "public-key",
|
||||
keyAlgorithm: "ECDSA",
|
||||
keyCurve: "P-256",
|
||||
keyValue: "keyValue",
|
||||
rpId: "rpId",
|
||||
userHandle: "userHandle",
|
||||
userName: "userName",
|
||||
counter: "2",
|
||||
rpName: "rpName",
|
||||
userDisplayName: "userDisplayName",
|
||||
discoverable: "discoverable",
|
||||
creationDate: mockDate.toISOString(),
|
||||
};
|
||||
|
||||
const credential = new Fido2Credential(data);
|
||||
const sdkCredential = credential.toSdkFido2Credential();
|
||||
|
||||
expect(sdkCredential).toEqual({
|
||||
credentialId: "credentialId",
|
||||
keyType: "public-key",
|
||||
keyAlgorithm: "ECDSA",
|
||||
keyCurve: "P-256",
|
||||
keyValue: "keyValue",
|
||||
rpId: "rpId",
|
||||
userHandle: "userHandle",
|
||||
userName: "userName",
|
||||
counter: "2",
|
||||
rpName: "rpName",
|
||||
userDisplayName: "userDisplayName",
|
||||
discoverable: "discoverable",
|
||||
creationDate: mockDate.toISOString(),
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function createEncryptedEncString(s: string): EncString {
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
// @ts-strict-ignore
|
||||
import { Jsonify } from "type-fest";
|
||||
|
||||
import { Fido2Credential as SdkFido2Credential } from "@bitwarden/sdk-internal";
|
||||
|
||||
import Domain from "../../../platform/models/domain/domain-base";
|
||||
import { EncString } from "../../../platform/models/domain/enc-string";
|
||||
import { SymmetricCryptoKey } from "../../../platform/models/domain/symmetric-crypto-key";
|
||||
@@ -148,4 +150,27 @@ export class Fido2Credential extends Domain {
|
||||
creationDate,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps Fido2Credential to SDK format.
|
||||
*
|
||||
* @returns {SdkFido2Credential} The SDK Fido2Credential object.
|
||||
*/
|
||||
toSdkFido2Credential(): SdkFido2Credential {
|
||||
return {
|
||||
credentialId: this.credentialId?.toJSON(),
|
||||
keyType: this.keyType.toJSON(),
|
||||
keyAlgorithm: this.keyAlgorithm.toJSON(),
|
||||
keyCurve: this.keyCurve.toJSON(),
|
||||
keyValue: this.keyValue.toJSON(),
|
||||
rpId: this.rpId.toJSON(),
|
||||
userHandle: this.userHandle.toJSON(),
|
||||
userName: this.userName.toJSON(),
|
||||
counter: this.counter.toJSON(),
|
||||
rpName: this.rpName?.toJSON(),
|
||||
userDisplayName: this.userDisplayName?.toJSON(),
|
||||
discoverable: this.discoverable?.toJSON(),
|
||||
creationDate: this.creationDate.toISOString(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { mockEnc, mockFromJson } from "../../../../spec";
|
||||
import { EncryptedString, EncString } from "../../../platform/models/domain/enc-string";
|
||||
import { FieldType } from "../../enums";
|
||||
import { CardLinkedId, FieldType, IdentityLinkedId, LoginLinkedId } from "../../enums";
|
||||
import { FieldData } from "../../models/data/field.data";
|
||||
import { Field } from "../../models/domain/field";
|
||||
|
||||
@@ -82,4 +82,26 @@ describe("Field", () => {
|
||||
expect(Field.fromJSON(null)).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe("SDK Field Mapping", () => {
|
||||
it("should map to SDK Field", () => {
|
||||
// Test Login LinkedId
|
||||
const loginField = new Field(data);
|
||||
loginField.type = FieldType.Linked;
|
||||
loginField.linkedId = LoginLinkedId.Username;
|
||||
expect(loginField.toSdkField().linkedId).toBe(100);
|
||||
|
||||
// Test Card LinkedId
|
||||
const cardField = new Field(data);
|
||||
cardField.type = FieldType.Linked;
|
||||
cardField.linkedId = CardLinkedId.Number;
|
||||
expect(cardField.toSdkField().linkedId).toBe(305);
|
||||
|
||||
// Test Identity LinkedId
|
||||
const identityField = new Field(data);
|
||||
identityField.type = FieldType.Linked;
|
||||
identityField.linkedId = IdentityLinkedId.LicenseNumber;
|
||||
expect(identityField.toSdkField().linkedId).toBe(415);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
// @ts-strict-ignore
|
||||
import { Jsonify } from "type-fest";
|
||||
|
||||
import { Field as SdkField, LinkedIdType as SdkLinkedIdType } from "@bitwarden/sdk-internal";
|
||||
|
||||
import Domain from "../../../platform/models/domain/domain-base";
|
||||
import { EncString } from "../../../platform/models/domain/enc-string";
|
||||
import { SymmetricCryptoKey } from "../../../platform/models/domain/symmetric-crypto-key";
|
||||
@@ -73,4 +75,19 @@ export class Field extends Domain {
|
||||
value,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps Field to SDK format.
|
||||
*
|
||||
* @returns {SdkField} The SDK field object.
|
||||
*/
|
||||
toSdkField(): SdkField {
|
||||
return {
|
||||
name: this.name?.toJSON(),
|
||||
value: this.value?.toJSON(),
|
||||
type: this.type,
|
||||
// Safe type cast: client and SDK LinkedIdType enums have identical values
|
||||
linkedId: this.linkedId as unknown as SdkLinkedIdType,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -184,4 +184,32 @@ describe("Identity", () => {
|
||||
expect(Identity.fromJSON(null)).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe("toSdkIdentity", () => {
|
||||
it("returns the correct SDK Identity object", () => {
|
||||
const identity = new Identity(data);
|
||||
const sdkIdentity = identity.toSdkIdentity();
|
||||
|
||||
expect(sdkIdentity).toEqual({
|
||||
title: "enctitle",
|
||||
firstName: "encfirstName",
|
||||
middleName: "encmiddleName",
|
||||
lastName: "enclastName",
|
||||
address1: "encaddress1",
|
||||
address2: "encaddress2",
|
||||
address3: "encaddress3",
|
||||
city: "enccity",
|
||||
state: "encstate",
|
||||
postalCode: "encpostalCode",
|
||||
country: "enccountry",
|
||||
company: "enccompany",
|
||||
email: "encemail",
|
||||
phone: "encphone",
|
||||
ssn: "encssn",
|
||||
username: "encusername",
|
||||
passportNumber: "encpassportNumber",
|
||||
licenseNumber: "enclicenseNumber",
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
// @ts-strict-ignore
|
||||
import { Jsonify } from "type-fest";
|
||||
|
||||
import { Identity as SdkIdentity } from "@bitwarden/sdk-internal";
|
||||
|
||||
import Domain from "../../../platform/models/domain/domain-base";
|
||||
import { EncString } from "../../../platform/models/domain/enc-string";
|
||||
import { SymmetricCryptoKey } from "../../../platform/models/domain/symmetric-crypto-key";
|
||||
@@ -165,4 +167,32 @@ export class Identity extends Domain {
|
||||
licenseNumber,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps Identity to SDK format.
|
||||
*
|
||||
* @returns {SdkIdentity} The SDK identity object.
|
||||
*/
|
||||
toSdkIdentity(): SdkIdentity {
|
||||
return {
|
||||
title: this.title?.toJSON(),
|
||||
firstName: this.firstName?.toJSON(),
|
||||
middleName: this.middleName?.toJSON(),
|
||||
lastName: this.lastName?.toJSON(),
|
||||
address1: this.address1?.toJSON(),
|
||||
address2: this.address2?.toJSON(),
|
||||
address3: this.address3?.toJSON(),
|
||||
city: this.city?.toJSON(),
|
||||
state: this.state?.toJSON(),
|
||||
postalCode: this.postalCode?.toJSON(),
|
||||
country: this.country?.toJSON(),
|
||||
company: this.company?.toJSON(),
|
||||
email: this.email?.toJSON(),
|
||||
phone: this.phone?.toJSON(),
|
||||
ssn: this.ssn?.toJSON(),
|
||||
username: this.username?.toJSON(),
|
||||
passportNumber: this.passportNumber?.toJSON(),
|
||||
licenseNumber: this.licenseNumber?.toJSON(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { MockProxy, mock } from "jest-mock-extended";
|
||||
import { Jsonify } from "type-fest";
|
||||
|
||||
import { UriMatchType } from "@bitwarden/sdk-internal";
|
||||
|
||||
import { mockEnc, mockFromJson } from "../../../../spec";
|
||||
import { EncryptService } from "../../../key-management/crypto/abstractions/encrypt.service";
|
||||
import { UriMatchStrategy } from "../../../models/domain/domain-service";
|
||||
@@ -118,4 +120,17 @@ describe("LoginUri", () => {
|
||||
expect(LoginUri.fromJSON(null)).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe("SDK Login Uri Mapping", () => {
|
||||
it("should map to SDK login uri", () => {
|
||||
const loginUri = new LoginUri(data);
|
||||
const sdkLoginUri = loginUri.toSdkLoginUri();
|
||||
|
||||
expect(sdkLoginUri).toEqual({
|
||||
uri: "encUri",
|
||||
uriChecksum: "encUriChecksum",
|
||||
match: UriMatchType.Domain,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
// @ts-strict-ignore
|
||||
import { Jsonify } from "type-fest";
|
||||
|
||||
import { LoginUri as SdkLoginUri } from "@bitwarden/sdk-internal";
|
||||
|
||||
import { UriMatchStrategySetting } from "../../../models/domain/domain-service";
|
||||
import { Utils } from "../../../platform/misc/utils";
|
||||
import Domain from "../../../platform/models/domain/domain-base";
|
||||
@@ -87,4 +89,17 @@ export class LoginUri extends Domain {
|
||||
uriChecksum,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps LoginUri to SDK format.
|
||||
*
|
||||
* @returns {SdkLoginUri} The SDK login uri object.
|
||||
*/
|
||||
toSdkLoginUri(): SdkLoginUri {
|
||||
return {
|
||||
uri: this.uri.toJSON(),
|
||||
uriChecksum: this.uriChecksum.toJSON(),
|
||||
match: this.match,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -202,6 +202,54 @@ describe("Login DTO", () => {
|
||||
expect(Login.fromJSON(null)).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe("toSdkLogin", () => {
|
||||
it("should map to SDK login", () => {
|
||||
const data: LoginData = {
|
||||
uris: [{ uri: "uri", uriChecksum: "checksum", match: UriMatchStrategy.Domain }],
|
||||
username: "username",
|
||||
password: "password",
|
||||
passwordRevisionDate: "2022-01-31T12:00:00.000Z",
|
||||
totp: "123",
|
||||
autofillOnPageLoad: false,
|
||||
fido2Credentials: [initializeFido2Credential(new Fido2CredentialData())],
|
||||
};
|
||||
const login = new Login(data);
|
||||
const sdkLogin = login.toSdkLogin();
|
||||
|
||||
expect(sdkLogin).toEqual({
|
||||
username: "username",
|
||||
password: "password",
|
||||
passwordRevisionDate: "2022-01-31T12:00:00.000Z",
|
||||
uris: [
|
||||
{
|
||||
match: 0,
|
||||
uri: "uri",
|
||||
uriChecksum: "checksum",
|
||||
},
|
||||
],
|
||||
totp: "123",
|
||||
autofillOnPageLoad: false,
|
||||
fido2Credentials: [
|
||||
{
|
||||
credentialId: "credentialId",
|
||||
keyType: "public-key",
|
||||
keyAlgorithm: "ECDSA",
|
||||
keyCurve: "P-256",
|
||||
keyValue: "keyValue",
|
||||
rpId: "rpId",
|
||||
userHandle: "userHandle",
|
||||
userName: "userName",
|
||||
counter: "counter",
|
||||
rpName: "rpName",
|
||||
userDisplayName: "userDisplayName",
|
||||
discoverable: "discoverable",
|
||||
creationDate: "2023-01-01T12:00:00.000Z",
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
type Fido2CredentialLike = Fido2CredentialData | Fido2CredentialView | Fido2CredentialApi;
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
// @ts-strict-ignore
|
||||
import { Jsonify } from "type-fest";
|
||||
|
||||
import { Login as SdkLogin } from "@bitwarden/sdk-internal";
|
||||
|
||||
import Domain from "../../../platform/models/domain/domain-base";
|
||||
import { EncString } from "../../../platform/models/domain/enc-string";
|
||||
import { SymmetricCryptoKey } from "../../../platform/models/domain/symmetric-crypto-key";
|
||||
@@ -144,4 +146,21 @@ export class Login extends Domain {
|
||||
fido2Credentials,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps Login to SDK format.
|
||||
*
|
||||
* @returns {SdkLogin} The SDK login object.
|
||||
*/
|
||||
toSdkLogin(): SdkLogin {
|
||||
return {
|
||||
uris: this.uris?.map((u) => u.toSdkLoginUri()),
|
||||
username: this.username?.toJSON(),
|
||||
password: this.password?.toJSON(),
|
||||
passwordRevisionDate: this.passwordRevisionDate?.toISOString(),
|
||||
totp: this.totp?.toJSON(),
|
||||
autofillOnPageLoad: this.autofillOnPageLoad,
|
||||
fido2Credentials: this.fido2Credentials?.map((f) => f.toSdkFido2Credential()),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,4 +70,17 @@ describe("Password", () => {
|
||||
expect(Password.fromJSON(null)).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe("toSdkPasswordHistory", () => {
|
||||
it("returns the correct SDK PasswordHistory object", () => {
|
||||
const password = new Password(data);
|
||||
|
||||
const sdkPasswordHistory = password.toSdkPasswordHistory();
|
||||
|
||||
expect(sdkPasswordHistory).toEqual({
|
||||
password: "encPassword",
|
||||
lastUsedDate: new Date("2022-01-31T12:00:00.000Z").toISOString(),
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
// @ts-strict-ignore
|
||||
import { Jsonify } from "type-fest";
|
||||
|
||||
import { PasswordHistory } from "@bitwarden/sdk-internal";
|
||||
|
||||
import Domain from "../../../platform/models/domain/domain-base";
|
||||
import { EncString } from "../../../platform/models/domain/enc-string";
|
||||
import { SymmetricCryptoKey } from "../../../platform/models/domain/symmetric-crypto-key";
|
||||
@@ -57,4 +59,16 @@ export class Password extends Domain {
|
||||
lastUsedDate,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps Password to SDK format.
|
||||
*
|
||||
* @returns {PasswordHistory} The SDK password history object.
|
||||
*/
|
||||
toSdkPasswordHistory(): PasswordHistory {
|
||||
return {
|
||||
password: this.password.toJSON(),
|
||||
lastUsedDate: this.lastUsedDate.toISOString(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,4 +50,17 @@ describe("SecureNote", () => {
|
||||
expect(SecureNote.fromJSON(null)).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe("toSdkSecureNote", () => {
|
||||
it("returns the correct SDK SecureNote object", () => {
|
||||
const secureNote = new SecureNote();
|
||||
secureNote.type = SecureNoteType.Generic;
|
||||
|
||||
const sdkSecureNote = secureNote.toSdkSecureNote();
|
||||
|
||||
expect(sdkSecureNote).toEqual({
|
||||
type: 0,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
// @ts-strict-ignore
|
||||
import { Jsonify } from "type-fest";
|
||||
|
||||
import { SecureNote as SdkSecureNote } from "@bitwarden/sdk-internal";
|
||||
|
||||
import Domain from "../../../platform/models/domain/domain-base";
|
||||
import { SymmetricCryptoKey } from "../../../platform/models/domain/symmetric-crypto-key";
|
||||
import { SecureNoteType } from "../../enums";
|
||||
@@ -41,4 +43,15 @@ export class SecureNote extends Domain {
|
||||
|
||||
return Object.assign(new SecureNote(), obj);
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps Secure note to SDK format.
|
||||
*
|
||||
* @returns {SdkSecureNote} The SDK secure note object.
|
||||
*/
|
||||
toSdkSecureNote(): SdkSecureNote {
|
||||
return {
|
||||
type: this.type,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,4 +64,17 @@ describe("Sshkey", () => {
|
||||
expect(SshKey.fromJSON(null)).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe("toSdkSshKey", () => {
|
||||
it("returns the correct SDK SshKey object", () => {
|
||||
const sshKey = new SshKey(data);
|
||||
const sdkSshKey = sshKey.toSdkSshKey();
|
||||
|
||||
expect(sdkSshKey).toEqual({
|
||||
privateKey: "privateKey",
|
||||
publicKey: "publicKey",
|
||||
fingerprint: "keyFingerprint",
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
// @ts-strict-ignore
|
||||
import { Jsonify } from "type-fest";
|
||||
|
||||
import { SshKey as SdkSshKey } from "@bitwarden/sdk-internal";
|
||||
|
||||
import Domain from "../../../platform/models/domain/domain-base";
|
||||
import { EncString } from "../../../platform/models/domain/enc-string";
|
||||
import { SymmetricCryptoKey } from "../../../platform/models/domain/symmetric-crypto-key";
|
||||
@@ -70,4 +72,17 @@ export class SshKey extends Domain {
|
||||
keyFingerprint,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps SSH key to SDK format.
|
||||
*
|
||||
* @returns {SdkSshKey} The SDK SSH key object.
|
||||
*/
|
||||
toSdkSshKey(): SdkSshKey {
|
||||
return {
|
||||
privateKey: this.privateKey.toJSON(),
|
||||
publicKey: this.publicKey.toJSON(),
|
||||
fingerprint: this.keyFingerprint.toJSON(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import { AttachmentView as SdkAttachmentView } from "@bitwarden/sdk-internal";
|
||||
|
||||
import { mockFromJson } from "../../../../spec";
|
||||
import { EncString } from "../../../platform/models/domain/enc-string";
|
||||
import { SymmetricCryptoKey } from "../../../platform/models/domain/symmetric-crypto-key";
|
||||
|
||||
import { AttachmentView } from "./attachment.view";
|
||||
@@ -15,4 +18,56 @@ describe("AttachmentView", () => {
|
||||
|
||||
expect(actual.key).toEqual("encKeyB64_fromJSON");
|
||||
});
|
||||
|
||||
describe("fromSdkAttachmentView", () => {
|
||||
it("should return undefined when the input is null", () => {
|
||||
const result = AttachmentView.fromSdkAttachmentView(null as unknown as any);
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
|
||||
it("should return an AttachmentView from an SdkAttachmentView", () => {
|
||||
const sdkAttachmentView = {
|
||||
id: "id",
|
||||
url: "url",
|
||||
size: "size",
|
||||
sizeName: "sizeName",
|
||||
fileName: "fileName",
|
||||
key: "encKeyB64_fromString",
|
||||
} as SdkAttachmentView;
|
||||
|
||||
const result = AttachmentView.fromSdkAttachmentView(sdkAttachmentView);
|
||||
|
||||
expect(result).toMatchObject({
|
||||
id: "id",
|
||||
url: "url",
|
||||
size: "size",
|
||||
sizeName: "sizeName",
|
||||
fileName: "fileName",
|
||||
key: null,
|
||||
encryptedKey: new EncString(sdkAttachmentView.key as string),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("toSdkAttachmentView", () => {
|
||||
it("should convert AttachmentView to SdkAttachmentView", () => {
|
||||
const attachmentView = new AttachmentView();
|
||||
attachmentView.id = "id";
|
||||
attachmentView.url = "url";
|
||||
attachmentView.size = "size";
|
||||
attachmentView.sizeName = "sizeName";
|
||||
attachmentView.fileName = "fileName";
|
||||
attachmentView.encryptedKey = new EncString("encKeyB64");
|
||||
|
||||
const result = attachmentView.toSdkAttachmentView();
|
||||
expect(result).toEqual({
|
||||
id: "id",
|
||||
url: "url",
|
||||
size: "size",
|
||||
sizeName: "sizeName",
|
||||
fileName: "fileName",
|
||||
key: "encKeyB64",
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,7 +2,10 @@
|
||||
// @ts-strict-ignore
|
||||
import { Jsonify } from "type-fest";
|
||||
|
||||
import { AttachmentView as SdkAttachmentView } from "@bitwarden/sdk-internal";
|
||||
|
||||
import { View } from "../../../models/view/view";
|
||||
import { EncString } from "../../../platform/models/domain/enc-string";
|
||||
import { SymmetricCryptoKey } from "../../../platform/models/domain/symmetric-crypto-key";
|
||||
import { Attachment } from "../domain/attachment";
|
||||
|
||||
@@ -13,6 +16,10 @@ export class AttachmentView implements View {
|
||||
sizeName: string = null;
|
||||
fileName: string = null;
|
||||
key: SymmetricCryptoKey = null;
|
||||
/**
|
||||
* The SDK returns an encrypted key for the attachment.
|
||||
*/
|
||||
encryptedKey: EncString | undefined;
|
||||
|
||||
constructor(a?: Attachment) {
|
||||
if (!a) {
|
||||
@@ -40,4 +47,37 @@ export class AttachmentView implements View {
|
||||
const key = obj.key == null ? null : SymmetricCryptoKey.fromJSON(obj.key);
|
||||
return Object.assign(new AttachmentView(), obj, { key: key });
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the AttachmentView to a SDK AttachmentView.
|
||||
*/
|
||||
toSdkAttachmentView(): SdkAttachmentView {
|
||||
return {
|
||||
id: this.id,
|
||||
url: this.url,
|
||||
size: this.size,
|
||||
sizeName: this.sizeName,
|
||||
fileName: this.fileName,
|
||||
key: this.encryptedKey?.toJSON(),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the SDK AttachmentView to a AttachmentView.
|
||||
*/
|
||||
static fromSdkAttachmentView(obj: SdkAttachmentView): AttachmentView | undefined {
|
||||
if (!obj) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const view = new AttachmentView();
|
||||
view.id = obj.id ?? null;
|
||||
view.url = obj.url ?? null;
|
||||
view.size = obj.size ?? null;
|
||||
view.sizeName = obj.sizeName ?? null;
|
||||
view.fileName = obj.fileName ?? null;
|
||||
view.encryptedKey = new EncString(obj.key);
|
||||
|
||||
return view;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
// @ts-strict-ignore
|
||||
import { Jsonify } from "type-fest";
|
||||
|
||||
import { CardView as SdkCardView } from "@bitwarden/sdk-internal";
|
||||
|
||||
import { normalizeExpiryYearFormat } from "../../../autofill/utils";
|
||||
import { CardLinkedId as LinkedId } from "../../enums";
|
||||
import { linkedFieldOption } from "../../linked-field-option.decorator";
|
||||
@@ -146,4 +148,15 @@ export class CardView extends ItemView {
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an SDK CardView to a CardView.
|
||||
*/
|
||||
static fromSdkCardView(obj: SdkCardView): CardView | undefined {
|
||||
if (obj == null) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return Object.assign(new CardView(), obj);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,16 @@
|
||||
import { mockFromJson } from "../../../../spec";
|
||||
import {
|
||||
CipherView as SdkCipherView,
|
||||
CipherType as SdkCipherType,
|
||||
CipherRepromptType as SdkCipherRepromptType,
|
||||
AttachmentView as SdkAttachmentView,
|
||||
LoginUriView as SdkLoginUriView,
|
||||
LoginView as SdkLoginView,
|
||||
FieldView as SdkFieldView,
|
||||
FieldType as SdkFieldType,
|
||||
} from "@bitwarden/sdk-internal";
|
||||
|
||||
import { mockFromJson, mockFromSdk } from "../../../../spec";
|
||||
import { CipherRepromptType } from "../../enums";
|
||||
import { CipherType } from "../../enums/cipher-type";
|
||||
|
||||
import { AttachmentView } from "./attachment.view";
|
||||
@@ -9,6 +21,7 @@ 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";
|
||||
|
||||
jest.mock("../../models/view/login.view");
|
||||
jest.mock("../../models/view/attachment.view");
|
||||
@@ -73,4 +86,121 @@ describe("CipherView", () => {
|
||||
expect(actual).toMatchObject(expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe("fromSdkCipherView", () => {
|
||||
let sdkCipherView: SdkCipherView;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.spyOn(CardView, "fromSdkCardView").mockImplementation(mockFromSdk);
|
||||
jest.spyOn(IdentityView, "fromSdkIdentityView").mockImplementation(mockFromSdk);
|
||||
jest.spyOn(LoginView, "fromSdkLoginView").mockImplementation(mockFromSdk);
|
||||
jest.spyOn(SecureNoteView, "fromSdkSecureNoteView").mockImplementation(mockFromSdk);
|
||||
jest.spyOn(SshKeyView, "fromSdkSshKeyView").mockImplementation(mockFromSdk);
|
||||
jest.spyOn(AttachmentView, "fromSdkAttachmentView").mockImplementation(mockFromSdk);
|
||||
jest.spyOn(FieldView, "fromSdkFieldView").mockImplementation(mockFromSdk);
|
||||
|
||||
sdkCipherView = {
|
||||
id: "id",
|
||||
organizationId: "orgId",
|
||||
folderId: "folderId",
|
||||
collectionIds: ["collectionId"],
|
||||
key: undefined,
|
||||
name: "name",
|
||||
notes: undefined,
|
||||
type: SdkCipherType.Login,
|
||||
favorite: true,
|
||||
edit: true,
|
||||
reprompt: SdkCipherRepromptType.None,
|
||||
organizationUseTotp: false,
|
||||
viewPassword: true,
|
||||
localData: undefined,
|
||||
permissions: undefined,
|
||||
attachments: [{ id: "attachmentId", url: "attachmentUrl" } as SdkAttachmentView],
|
||||
login: {
|
||||
username: "username",
|
||||
password: "password",
|
||||
uris: [{ uri: "bitwarden.com" } as SdkLoginUriView],
|
||||
totp: "totp",
|
||||
autofillOnPageLoad: true,
|
||||
} as SdkLoginView,
|
||||
identity: undefined,
|
||||
card: undefined,
|
||||
secureNote: undefined,
|
||||
sshKey: undefined,
|
||||
fields: [
|
||||
{
|
||||
name: "fieldName",
|
||||
value: "fieldValue",
|
||||
type: SdkFieldType.Linked,
|
||||
linkedId: 100,
|
||||
} as SdkFieldView,
|
||||
],
|
||||
passwordHistory: undefined,
|
||||
creationDate: "2022-01-01T12:00:00.000Z",
|
||||
revisionDate: "2022-01-02T12:00:00.000Z",
|
||||
deletedDate: undefined,
|
||||
};
|
||||
});
|
||||
|
||||
it("returns undefined when input is null", () => {
|
||||
expect(CipherView.fromSdkCipherView(null as unknown as SdkCipherView)).toBeUndefined();
|
||||
});
|
||||
|
||||
it("maps properties correctly", () => {
|
||||
const result = CipherView.fromSdkCipherView(sdkCipherView);
|
||||
|
||||
expect(result).toMatchObject({
|
||||
id: "id",
|
||||
organizationId: "orgId",
|
||||
folderId: "folderId",
|
||||
collectionIds: ["collectionId"],
|
||||
name: "name",
|
||||
notes: null,
|
||||
type: CipherType.Login,
|
||||
favorite: true,
|
||||
edit: true,
|
||||
reprompt: CipherRepromptType.None,
|
||||
organizationUseTotp: false,
|
||||
viewPassword: true,
|
||||
localData: undefined,
|
||||
permissions: undefined,
|
||||
attachments: [
|
||||
{
|
||||
id: "attachmentId",
|
||||
url: "attachmentUrl",
|
||||
__fromSdk: true,
|
||||
},
|
||||
],
|
||||
login: {
|
||||
username: "username",
|
||||
password: "password",
|
||||
uris: [
|
||||
{
|
||||
uri: "bitwarden.com",
|
||||
},
|
||||
],
|
||||
totp: "totp",
|
||||
autofillOnPageLoad: true,
|
||||
__fromSdk: true,
|
||||
},
|
||||
identity: new IdentityView(),
|
||||
card: new CardView(),
|
||||
secureNote: new SecureNoteView(),
|
||||
sshKey: new SshKeyView(),
|
||||
fields: [
|
||||
{
|
||||
name: "fieldName",
|
||||
value: "fieldValue",
|
||||
type: SdkFieldType.Linked,
|
||||
linkedId: 100,
|
||||
__fromSdk: true,
|
||||
},
|
||||
],
|
||||
passwordHistory: null,
|
||||
creationDate: new Date("2022-01-01T12:00:00.000Z"),
|
||||
revisionDate: new Date("2022-01-02T12:00:00.000Z"),
|
||||
deletedDate: null,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { CipherView as SdkCipherView } from "@bitwarden/sdk-internal";
|
||||
|
||||
import { View } from "../../../models/view/view";
|
||||
import { InitializerMetadata } from "../../../platform/interfaces/initializer-metadata.interface";
|
||||
import { InitializerKey } from "../../../platform/services/cryptography/initializer-key";
|
||||
@@ -110,7 +112,7 @@ export class CipherView implements View, InitializerMetadata {
|
||||
get hasOldAttachments(): boolean {
|
||||
if (this.hasAttachments) {
|
||||
for (let i = 0; i < this.attachments.length; i++) {
|
||||
if (this.attachments[i].key == null) {
|
||||
if (this.attachments[i].key == null && this.attachments[i].encryptedKey == null) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -222,4 +224,68 @@ export class CipherView implements View, InitializerMetadata {
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a CipherView from the SDK CipherView.
|
||||
*/
|
||||
static fromSdkCipherView(obj: SdkCipherView): CipherView | undefined {
|
||||
if (obj == null) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const cipherView = new CipherView();
|
||||
cipherView.id = obj.id ?? null;
|
||||
cipherView.organizationId = obj.organizationId ?? null;
|
||||
cipherView.folderId = obj.folderId ?? null;
|
||||
cipherView.name = obj.name;
|
||||
cipherView.notes = obj.notes ?? null;
|
||||
cipherView.type = obj.type;
|
||||
cipherView.favorite = obj.favorite;
|
||||
cipherView.organizationUseTotp = obj.organizationUseTotp;
|
||||
cipherView.permissions = CipherPermissionsApi.fromSdkCipherPermissions(obj.permissions);
|
||||
cipherView.edit = obj.edit;
|
||||
cipherView.viewPassword = obj.viewPassword;
|
||||
cipherView.localData = obj.localData
|
||||
? {
|
||||
lastUsedDate: obj.localData.lastUsedDate
|
||||
? new Date(obj.localData.lastUsedDate).getTime()
|
||||
: undefined,
|
||||
lastLaunched: obj.localData.lastLaunched
|
||||
? new Date(obj.localData.lastLaunched).getTime()
|
||||
: undefined,
|
||||
}
|
||||
: undefined;
|
||||
cipherView.attachments =
|
||||
obj.attachments?.map((a) => AttachmentView.fromSdkAttachmentView(a)) ?? null;
|
||||
cipherView.fields = obj.fields?.map((f) => FieldView.fromSdkFieldView(f)) ?? null;
|
||||
cipherView.passwordHistory =
|
||||
obj.passwordHistory?.map((ph) => PasswordHistoryView.fromSdkPasswordHistoryView(ph)) ?? null;
|
||||
cipherView.collectionIds = obj.collectionIds ?? null;
|
||||
cipherView.revisionDate = obj.revisionDate == null ? null : new Date(obj.revisionDate);
|
||||
cipherView.creationDate = obj.creationDate == null ? null : new Date(obj.creationDate);
|
||||
cipherView.deletedDate = obj.deletedDate == null ? null : new Date(obj.deletedDate);
|
||||
cipherView.reprompt = obj.reprompt ?? CipherRepromptType.None;
|
||||
|
||||
switch (obj.type) {
|
||||
case CipherType.Card:
|
||||
cipherView.card = CardView.fromSdkCardView(obj.card);
|
||||
break;
|
||||
case CipherType.Identity:
|
||||
cipherView.identity = IdentityView.fromSdkIdentityView(obj.identity);
|
||||
break;
|
||||
case CipherType.Login:
|
||||
cipherView.login = LoginView.fromSdkLoginView(obj.login);
|
||||
break;
|
||||
case CipherType.SecureNote:
|
||||
cipherView.secureNote = SecureNoteView.fromSdkSecureNoteView(obj.secureNote);
|
||||
break;
|
||||
case CipherType.SshKey:
|
||||
cipherView.sshKey = SshKeyView.fromSdkSshKeyView(obj.sshKey);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return cipherView;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
// @ts-strict-ignore
|
||||
import { Jsonify } from "type-fest";
|
||||
|
||||
import { Fido2CredentialView as SdkFido2CredentialView } from "@bitwarden/sdk-internal";
|
||||
|
||||
import { ItemView } from "./item.view";
|
||||
|
||||
export class Fido2CredentialView extends ItemView {
|
||||
@@ -29,4 +31,29 @@ export class Fido2CredentialView extends ItemView {
|
||||
creationDate,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the SDK Fido2CredentialView to a Fido2CredentialView.
|
||||
*/
|
||||
static fromSdkFido2CredentialView(obj: SdkFido2CredentialView): Fido2CredentialView | undefined {
|
||||
if (!obj) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const view = new Fido2CredentialView();
|
||||
view.credentialId = obj.credentialId;
|
||||
view.keyType = obj.keyType as "public-key";
|
||||
view.keyAlgorithm = obj.keyAlgorithm as "ECDSA";
|
||||
view.keyCurve = obj.keyCurve as "P-256";
|
||||
view.rpId = obj.rpId;
|
||||
view.userHandle = obj.userHandle;
|
||||
view.userName = obj.userName;
|
||||
view.counter = parseInt(obj.counter);
|
||||
view.rpName = obj.rpName;
|
||||
view.userDisplayName = obj.userDisplayName;
|
||||
view.discoverable = obj.discoverable?.toLowerCase() === "true" ? true : false;
|
||||
view.creationDate = obj.creationDate ? new Date(obj.creationDate) : null;
|
||||
|
||||
return view;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
// @ts-strict-ignore
|
||||
import { Jsonify } from "type-fest";
|
||||
|
||||
import { FieldView as SdkFieldView } from "@bitwarden/sdk-internal";
|
||||
|
||||
import { View } from "../../../models/view/view";
|
||||
import { FieldType, LinkedIdType } from "../../enums";
|
||||
import { Field } from "../domain/field";
|
||||
@@ -31,4 +33,21 @@ export class FieldView implements View {
|
||||
static fromJSON(obj: Partial<Jsonify<FieldView>>): FieldView {
|
||||
return Object.assign(new FieldView(), obj);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the SDK FieldView to a FieldView.
|
||||
*/
|
||||
static fromSdkFieldView(obj: SdkFieldView): FieldView | undefined {
|
||||
if (!obj) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const view = new FieldView();
|
||||
view.name = obj.name;
|
||||
view.value = obj.value;
|
||||
view.type = obj.type;
|
||||
view.linkedId = obj.linkedId as unknown as LinkedIdType;
|
||||
|
||||
return view;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
// @ts-strict-ignore
|
||||
import { Jsonify } from "type-fest";
|
||||
|
||||
import { IdentityView as SdkIdentityView } from "@bitwarden/sdk-internal";
|
||||
|
||||
import { Utils } from "../../../platform/misc/utils";
|
||||
import { IdentityLinkedId as LinkedId } from "../../enums";
|
||||
import { linkedFieldOption } from "../../linked-field-option.decorator";
|
||||
@@ -158,4 +160,15 @@ export class IdentityView extends ItemView {
|
||||
static fromJSON(obj: Partial<Jsonify<IdentityView>>): IdentityView {
|
||||
return Object.assign(new IdentityView(), obj);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the SDK IdentityView to an IdentityView.
|
||||
*/
|
||||
static fromSdkIdentityView(obj: SdkIdentityView): IdentityView | undefined {
|
||||
if (obj == null) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return Object.assign(new IdentityView(), obj);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { LoginUriView as SdkLoginUriView, UriMatchType } from "@bitwarden/sdk-internal";
|
||||
|
||||
import { UriMatchStrategy, UriMatchStrategySetting } from "../../../models/domain/domain-service";
|
||||
import { Utils } from "../../../platform/misc/utils";
|
||||
|
||||
@@ -184,6 +186,26 @@ describe("LoginUriView", () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("fromSdkLoginUriView", () => {
|
||||
it("should return undefined when the input is null", () => {
|
||||
const result = LoginUriView.fromSdkLoginUriView(null as unknown as SdkLoginUriView);
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
|
||||
it("should create a LoginUriView from a SdkLoginUriView", () => {
|
||||
const sdkLoginUriView = {
|
||||
uri: "https://example.com",
|
||||
match: UriMatchType.Host,
|
||||
} as SdkLoginUriView;
|
||||
|
||||
const loginUriView = LoginUriView.fromSdkLoginUriView(sdkLoginUriView);
|
||||
|
||||
expect(loginUriView).toBeInstanceOf(LoginUriView);
|
||||
expect(loginUriView!.uri).toBe(sdkLoginUriView.uri);
|
||||
expect(loginUriView!.match).toBe(sdkLoginUriView.match);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function uriFactory(match: UriMatchStrategySetting, uri: string) {
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
// @ts-strict-ignore
|
||||
import { Jsonify } from "type-fest";
|
||||
|
||||
import { LoginUriView as SdkLoginUriView } from "@bitwarden/sdk-internal";
|
||||
|
||||
import { UriMatchStrategy, UriMatchStrategySetting } from "../../../models/domain/domain-service";
|
||||
import { View } from "../../../models/view/view";
|
||||
import { SafeUrls } from "../../../platform/misc/safe-urls";
|
||||
@@ -112,6 +114,21 @@ export class LoginUriView implements View {
|
||||
return Object.assign(new LoginUriView(), obj);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a LoginUriView object from the SDK to a LoginUriView object.
|
||||
*/
|
||||
static fromSdkLoginUriView(obj: SdkLoginUriView): LoginUriView | undefined {
|
||||
if (obj == null) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const view = new LoginUriView();
|
||||
view.uri = obj.uri;
|
||||
view.match = obj.match;
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
matchesUri(
|
||||
targetUri: string,
|
||||
equivalentDomains: Set<string>,
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import { mockFromJson } from "../../../../spec";
|
||||
import { LoginView as SdkLoginView } from "@bitwarden/sdk-internal";
|
||||
|
||||
import { mockFromJson, mockFromSdk } from "../../../../spec";
|
||||
|
||||
import { LoginUriView } from "./login-uri.view";
|
||||
import { LoginView } from "./login.view";
|
||||
@@ -25,4 +27,35 @@ describe("LoginView", () => {
|
||||
uris: ["uri1_fromJSON", "uri2_fromJSON", "uri3_fromJSON"],
|
||||
});
|
||||
});
|
||||
|
||||
describe("fromSdkLoginView", () => {
|
||||
it("should return undefined when the input is null", () => {
|
||||
const result = LoginView.fromSdkLoginView(null as unknown as SdkLoginView);
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
|
||||
it("should return a LoginView from an SdkLoginView", () => {
|
||||
jest.spyOn(LoginUriView, "fromSdkLoginUriView").mockImplementation(mockFromSdk);
|
||||
|
||||
const sdkLoginView = {
|
||||
username: "username",
|
||||
password: "password",
|
||||
passwordRevisionDate: "2025-01-01T01:06:40.441Z",
|
||||
uris: [{ uri: "bitwarden.com" } as any],
|
||||
totp: "totp",
|
||||
autofillOnPageLoad: true,
|
||||
} as SdkLoginView;
|
||||
|
||||
const result = LoginView.fromSdkLoginView(sdkLoginView);
|
||||
|
||||
expect(result).toMatchObject({
|
||||
username: "username",
|
||||
password: "password",
|
||||
passwordRevisionDate: new Date("2025-01-01T01:06:40.441Z"),
|
||||
uris: [expect.objectContaining({ uri: "bitwarden.com", __fromSdk: true })],
|
||||
totp: "totp",
|
||||
autofillOnPageLoad: true,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { LoginView as SdkLoginView } from "@bitwarden/sdk-internal";
|
||||
|
||||
import { UriMatchStrategySetting } from "../../../models/domain/domain-service";
|
||||
import { Utils } from "../../../platform/misc/utils";
|
||||
import { DeepJsonify } from "../../../types/deep-jsonify";
|
||||
@@ -100,4 +102,27 @@ export class LoginView extends ItemView {
|
||||
fido2Credentials,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the SDK LoginView to a LoginView.
|
||||
*
|
||||
* Note: FIDO2 credentials remain encrypted at this stage.
|
||||
* Unlike other fields that are decrypted as part of the LoginView, the SDK maintains
|
||||
* the FIDO2 credentials in encrypted form. We can decrypt them later using a separate
|
||||
* call to client.vault().ciphers().decrypt_fido2_credentials().
|
||||
*/
|
||||
static fromSdkLoginView(obj: SdkLoginView): LoginView | undefined {
|
||||
if (obj == null) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const passwordRevisionDate =
|
||||
obj.passwordRevisionDate == null ? null : new Date(obj.passwordRevisionDate);
|
||||
const uris = obj.uris?.map((uri) => LoginUriView.fromSdkLoginUriView(uri));
|
||||
|
||||
return Object.assign(new LoginView(), obj, {
|
||||
passwordRevisionDate,
|
||||
uris,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { PasswordHistoryView as SdkPasswordHistoryView } from "@bitwarden/sdk-internal";
|
||||
|
||||
import { PasswordHistoryView } from "./password-history.view";
|
||||
|
||||
describe("PasswordHistoryView", () => {
|
||||
@@ -10,4 +12,25 @@ describe("PasswordHistoryView", () => {
|
||||
|
||||
expect(actual.lastUsedDate).toEqual(lastUsedDate);
|
||||
});
|
||||
|
||||
describe("fromSdkPasswordHistoryView", () => {
|
||||
it("should return undefined when the input is null", () => {
|
||||
const result = PasswordHistoryView.fromSdkPasswordHistoryView(null as unknown as any);
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
|
||||
it("should return a PasswordHistoryView from an SdkPasswordHistoryView", () => {
|
||||
const sdkPasswordHistoryView = {
|
||||
password: "password",
|
||||
lastUsedDate: "2023-10-01T00:00:00Z",
|
||||
} as SdkPasswordHistoryView;
|
||||
|
||||
const result = PasswordHistoryView.fromSdkPasswordHistoryView(sdkPasswordHistoryView);
|
||||
|
||||
expect(result).toMatchObject({
|
||||
password: "password",
|
||||
lastUsedDate: new Date("2023-10-01T00:00:00Z"),
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
// @ts-strict-ignore
|
||||
import { Jsonify } from "type-fest";
|
||||
|
||||
import { PasswordHistoryView as SdkPasswordHistoryView } from "@bitwarden/sdk-internal";
|
||||
|
||||
import { View } from "../../../models/view/view";
|
||||
import { Password } from "../domain/password";
|
||||
|
||||
@@ -24,4 +26,19 @@ export class PasswordHistoryView implements View {
|
||||
lastUsedDate: lastUsedDate,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the SDK PasswordHistoryView to a PasswordHistoryView.
|
||||
*/
|
||||
static fromSdkPasswordHistoryView(obj: SdkPasswordHistoryView): PasswordHistoryView | undefined {
|
||||
if (!obj) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const view = new PasswordHistoryView();
|
||||
view.password = obj.password;
|
||||
view.lastUsedDate = obj.lastUsedDate == null ? null : new Date(obj.lastUsedDate);
|
||||
|
||||
return view;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
// @ts-strict-ignore
|
||||
import { Jsonify } from "type-fest";
|
||||
|
||||
import { SecureNoteView as SdkSecureNoteView } from "@bitwarden/sdk-internal";
|
||||
|
||||
import { SecureNoteType } from "../../enums";
|
||||
import { SecureNote } from "../domain/secure-note";
|
||||
|
||||
@@ -26,4 +28,15 @@ export class SecureNoteView extends ItemView {
|
||||
static fromJSON(obj: Partial<Jsonify<SecureNoteView>>): SecureNoteView {
|
||||
return Object.assign(new SecureNoteView(), obj);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the SDK SecureNoteView to a SecureNoteView.
|
||||
*/
|
||||
static fromSdkSecureNoteView(obj: SdkSecureNoteView): SecureNoteView | undefined {
|
||||
if (!obj) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return Object.assign(new SecureNoteView(), obj);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
// @ts-strict-ignore
|
||||
import { Jsonify } from "type-fest";
|
||||
|
||||
import { SshKeyView as SdkSshKeyView } from "@bitwarden/sdk-internal";
|
||||
|
||||
import { SshKey } from "../domain/ssh-key";
|
||||
|
||||
import { ItemView } from "./item.view";
|
||||
@@ -44,4 +46,19 @@ export class SshKeyView extends ItemView {
|
||||
static fromJSON(obj: Partial<Jsonify<SshKeyView>>): SshKeyView {
|
||||
return Object.assign(new SshKeyView(), obj);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the SDK SshKeyView to a SshKeyView.
|
||||
*/
|
||||
static fromSdkSshKeyView(obj: SdkSshKeyView): SshKeyView | undefined {
|
||||
if (!obj) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const keyFingerprint = obj.fingerprint;
|
||||
|
||||
return Object.assign(new SshKeyView(), obj, {
|
||||
keyFingerprint,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user