mirror of
https://github.com/bitwarden/browser.git
synced 2024-11-22 11:45:59 +01:00
[EC-281] Add de/serialization methods to CipherView objects (#2970)
This commit is contained in:
parent
ee0d87690b
commit
8626e1d4e6
@ -92,7 +92,7 @@ export class LocalBackedSessionStorageService extends AbstractStorageService {
|
||||
storedKey = await this.keyGenerationService.makeEphemeralKey();
|
||||
await this.setSessionEncKey(storedKey);
|
||||
}
|
||||
return SymmetricCryptoKey.initFromJson(
|
||||
return SymmetricCryptoKey.fromJSON(
|
||||
Object.create(SymmetricCryptoKey.prototype, Object.getOwnPropertyDescriptors(storedKey))
|
||||
);
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ import { Attachment } from "@bitwarden/common/models/domain/attachment";
|
||||
import { SymmetricCryptoKey } from "@bitwarden/common/models/domain/symmetricCryptoKey";
|
||||
import { ContainerService } from "@bitwarden/common/services/container.service";
|
||||
|
||||
import { makeStaticByteArray, mockEnc } from "../utils";
|
||||
import { makeStaticByteArray, mockEnc } from "../../utils";
|
||||
|
||||
describe("Attachment", () => {
|
||||
let data: AttachmentData;
|
@ -1,7 +1,7 @@
|
||||
import { CardData } from "@bitwarden/common/models/data/cardData";
|
||||
import { Card } from "@bitwarden/common/models/domain/card";
|
||||
|
||||
import { mockEnc } from "../utils";
|
||||
import { mockEnc } from "../../utils";
|
||||
|
||||
describe("Card", () => {
|
||||
let data: CardData;
|
@ -15,7 +15,7 @@ import { CardView } from "@bitwarden/common/models/view/cardView";
|
||||
import { IdentityView } from "@bitwarden/common/models/view/identityView";
|
||||
import { LoginView } from "@bitwarden/common/models/view/loginView";
|
||||
|
||||
import { mockEnc } from "../utils";
|
||||
import { mockEnc } from "../../utils";
|
||||
|
||||
describe("Cipher DTO", () => {
|
||||
it("Convert from empty CipherData", () => {
|
@ -1,7 +1,7 @@
|
||||
import { CollectionData } from "@bitwarden/common/models/data/collectionData";
|
||||
import { Collection } from "@bitwarden/common/models/domain/collection";
|
||||
|
||||
import { mockEnc } from "../utils";
|
||||
import { mockEnc } from "../../utils";
|
||||
|
||||
describe("Collection", () => {
|
||||
let data: CollectionData;
|
@ -2,7 +2,7 @@ import { FieldType } from "@bitwarden/common/enums/fieldType";
|
||||
import { FieldData } from "@bitwarden/common/models/data/fieldData";
|
||||
import { Field } from "@bitwarden/common/models/domain/field";
|
||||
|
||||
import { mockEnc } from "../utils";
|
||||
import { mockEnc } from "../../utils";
|
||||
|
||||
describe("Field", () => {
|
||||
let data: FieldData;
|
@ -1,7 +1,7 @@
|
||||
import { FolderData } from "@bitwarden/common/models/data/folderData";
|
||||
import { Folder } from "@bitwarden/common/models/domain/folder";
|
||||
|
||||
import { mockEnc } from "../utils";
|
||||
import { mockEnc } from "../../utils";
|
||||
|
||||
describe("Folder", () => {
|
||||
let data: FolderData;
|
@ -1,7 +1,7 @@
|
||||
import { IdentityData } from "@bitwarden/common/models/data/identityData";
|
||||
import { Identity } from "@bitwarden/common/models/domain/identity";
|
||||
|
||||
import { mockEnc } from "../utils";
|
||||
import { mockEnc } from "../../utils";
|
||||
|
||||
describe("Identity", () => {
|
||||
let data: IdentityData;
|
@ -6,7 +6,7 @@ import { Login } from "@bitwarden/common/models/domain/login";
|
||||
import { LoginUri } from "@bitwarden/common/models/domain/loginUri";
|
||||
import { LoginUriView } from "@bitwarden/common/models/view/loginUriView";
|
||||
|
||||
import { mockEnc } from "../utils";
|
||||
import { mockEnc } from "../../utils";
|
||||
|
||||
describe("Login DTO", () => {
|
||||
it("Convert from empty LoginData", () => {
|
@ -2,7 +2,7 @@ import { UriMatchType } from "@bitwarden/common/enums/uriMatchType";
|
||||
import { LoginUriData } from "@bitwarden/common/models/data/loginUriData";
|
||||
import { LoginUri } from "@bitwarden/common/models/domain/loginUri";
|
||||
|
||||
import { mockEnc } from "../utils";
|
||||
import { mockEnc } from "../../utils";
|
||||
|
||||
describe("LoginUri", () => {
|
||||
let data: LoginUriData;
|
@ -1,7 +1,7 @@
|
||||
import { PasswordHistoryData } from "@bitwarden/common/models/data/passwordHistoryData";
|
||||
import { Password } from "@bitwarden/common/models/domain/password";
|
||||
|
||||
import { mockEnc } from "../utils";
|
||||
import { mockEnc } from "../../utils";
|
||||
|
||||
describe("Password", () => {
|
||||
let data: PasswordHistoryData;
|
@ -8,7 +8,7 @@ import { Send } from "@bitwarden/common/models/domain/send";
|
||||
import { SendText } from "@bitwarden/common/models/domain/sendText";
|
||||
import { ContainerService } from "@bitwarden/common/services/container.service";
|
||||
|
||||
import { makeStaticByteArray, mockEnc } from "../utils";
|
||||
import { makeStaticByteArray, mockEnc } from "../../utils";
|
||||
|
||||
describe("Send", () => {
|
||||
let data: SendData;
|
@ -5,7 +5,7 @@ import { SendAccess } from "@bitwarden/common/models/domain/sendAccess";
|
||||
import { SendText } from "@bitwarden/common/models/domain/sendText";
|
||||
import { SendAccessResponse } from "@bitwarden/common/models/response/sendAccessResponse";
|
||||
|
||||
import { mockEnc } from "../utils";
|
||||
import { mockEnc } from "../../utils";
|
||||
|
||||
describe("SendAccess", () => {
|
||||
let request: SendAccessResponse;
|
@ -1,7 +1,7 @@
|
||||
import { SendFileData } from "@bitwarden/common/models/data/sendFileData";
|
||||
import { SendFile } from "@bitwarden/common/models/domain/sendFile";
|
||||
|
||||
import { mockEnc } from "../utils";
|
||||
import { mockEnc } from "../../utils";
|
||||
|
||||
describe("SendFile", () => {
|
||||
let data: SendFileData;
|
@ -1,7 +1,7 @@
|
||||
import { SendTextData } from "@bitwarden/common/models/data/sendTextData";
|
||||
import { SendText } from "@bitwarden/common/models/domain/sendText";
|
||||
|
||||
import { mockEnc } from "../utils";
|
||||
import { mockEnc } from "../../utils";
|
||||
|
||||
describe("SendText", () => {
|
||||
let data: SendTextData;
|
@ -1,7 +1,7 @@
|
||||
import { EncryptionType } from "@bitwarden/common/enums/encryptionType";
|
||||
import { SymmetricCryptoKey } from "@bitwarden/common/models/domain/symmetricCryptoKey";
|
||||
|
||||
import { makeStaticByteArray } from "../utils";
|
||||
import { makeStaticByteArray } from "../../utils";
|
||||
|
||||
describe("SymmetricCryptoKey", () => {
|
||||
it("errors if no key", () => {
|
||||
@ -66,4 +66,21 @@ describe("SymmetricCryptoKey", () => {
|
||||
expect(t).toThrowError("Unable to determine encType.");
|
||||
});
|
||||
});
|
||||
|
||||
it("toJSON creates object for serialization", () => {
|
||||
const key = new SymmetricCryptoKey(makeStaticByteArray(64).buffer);
|
||||
const actual = key.toJSON();
|
||||
|
||||
const expected = { keyB64: key.keyB64 };
|
||||
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
|
||||
it("fromJSON hydrates new object", () => {
|
||||
const expected = new SymmetricCryptoKey(makeStaticByteArray(64).buffer);
|
||||
const actual = SymmetricCryptoKey.fromJSON({ keyB64: expected.keyB64 });
|
||||
|
||||
expect(actual).toEqual(expected);
|
||||
expect(actual).toBeInstanceOf(SymmetricCryptoKey);
|
||||
});
|
||||
});
|
17
libs/common/spec/models/view/attachmentView.spec.ts
Normal file
17
libs/common/spec/models/view/attachmentView.spec.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import { SymmetricCryptoKey } from "@bitwarden/common/models/domain/symmetricCryptoKey";
|
||||
import { AttachmentView } from "@bitwarden/common/models/view/attachmentView";
|
||||
|
||||
jest.mock("@bitwarden/common/models/domain/symmetricCryptoKey");
|
||||
|
||||
describe("AttachmentView", () => {
|
||||
it("fromJSON initializes nested objects", () => {
|
||||
const mockFromJson = (stub: string) => stub + "_fromJSON";
|
||||
jest.spyOn(SymmetricCryptoKey, "fromJSON").mockImplementation(mockFromJson as any);
|
||||
|
||||
const actual = AttachmentView.fromJSON({
|
||||
key: "encKeyB64" as any,
|
||||
});
|
||||
|
||||
expect(actual.key).toEqual("encKeyB64_fromJSON");
|
||||
});
|
||||
});
|
76
libs/common/spec/models/view/cipherView.spec.ts
Normal file
76
libs/common/spec/models/view/cipherView.spec.ts
Normal file
@ -0,0 +1,76 @@
|
||||
import { CipherType } from "@bitwarden/common/enums/cipherType";
|
||||
import { AttachmentView } from "@bitwarden/common/models/view/attachmentView";
|
||||
import { CardView } from "@bitwarden/common/models/view/cardView";
|
||||
import { CipherView } from "@bitwarden/common/models/view/cipherView";
|
||||
import { FieldView } from "@bitwarden/common/models/view/fieldView";
|
||||
import { IdentityView } from "@bitwarden/common/models/view/identityView";
|
||||
import { LoginView } from "@bitwarden/common/models/view/loginView";
|
||||
import { PasswordHistoryView } from "@bitwarden/common/models/view/passwordHistoryView";
|
||||
import { SecureNoteView } from "@bitwarden/common/models/view/secureNoteView";
|
||||
|
||||
jest.mock("@bitwarden/common/models/view/loginView");
|
||||
jest.mock("@bitwarden/common/models/view/attachmentView");
|
||||
jest.mock("@bitwarden/common/models/view/fieldView");
|
||||
jest.mock("@bitwarden/common/models/view/passwordHistoryView");
|
||||
|
||||
describe("CipherView", () => {
|
||||
beforeEach(() => {
|
||||
(LoginView as any).mockClear();
|
||||
(AttachmentView as any).mockClear();
|
||||
(FieldView as any).mockClear();
|
||||
(PasswordHistoryView as any).mockClear();
|
||||
});
|
||||
|
||||
describe("fromJSON", () => {
|
||||
const mockFromJson = (stub: any) => (stub + "_fromJSON") as any;
|
||||
|
||||
it("initializes nested objects", () => {
|
||||
jest.spyOn(AttachmentView, "fromJSON").mockImplementation(mockFromJson);
|
||||
jest.spyOn(FieldView, "fromJSON").mockImplementation(mockFromJson);
|
||||
jest.spyOn(PasswordHistoryView, "fromJSON").mockImplementation(mockFromJson);
|
||||
|
||||
const revisionDate = new Date("2022-08-04T01:06:40.441Z");
|
||||
const deletedDate = new Date("2022-09-04T01:06:40.441Z");
|
||||
const actual = CipherView.fromJSON({
|
||||
revisionDate: revisionDate.toISOString(),
|
||||
deletedDate: deletedDate.toISOString(),
|
||||
attachments: ["attachment1", "attachment2"] as any,
|
||||
fields: ["field1", "field2"] as any,
|
||||
passwordHistory: ["ph1", "ph2", "ph3"] as any,
|
||||
});
|
||||
|
||||
const expected = {
|
||||
revisionDate: revisionDate,
|
||||
deletedDate: deletedDate,
|
||||
attachments: ["attachment1_fromJSON", "attachment2_fromJSON"],
|
||||
fields: ["field1_fromJSON", "field2_fromJSON"],
|
||||
passwordHistory: ["ph1_fromJSON", "ph2_fromJSON", "ph3_fromJSON"],
|
||||
};
|
||||
|
||||
expect(actual).toMatchObject(expected);
|
||||
});
|
||||
|
||||
test.each([
|
||||
// Test description, CipherType, expected output
|
||||
["LoginView", CipherType.Login, { login: "myLogin_fromJSON" }],
|
||||
["CardView", CipherType.Card, { card: "myCard_fromJSON" }],
|
||||
["IdentityView", CipherType.Identity, { identity: "myIdentity_fromJSON" }],
|
||||
["Secure Note", CipherType.SecureNote, { secureNote: "mySecureNote_fromJSON" }],
|
||||
])("initializes %s", (description: string, cipherType: CipherType, expected: any) => {
|
||||
jest.spyOn(LoginView, "fromJSON").mockImplementation(mockFromJson);
|
||||
jest.spyOn(IdentityView, "fromJSON").mockImplementation(mockFromJson);
|
||||
jest.spyOn(CardView, "fromJSON").mockImplementation(mockFromJson);
|
||||
jest.spyOn(SecureNoteView, "fromJSON").mockImplementation(mockFromJson);
|
||||
|
||||
const actual = CipherView.fromJSON({
|
||||
login: "myLogin",
|
||||
card: "myCard",
|
||||
identity: "myIdentity",
|
||||
secureNote: "mySecureNote",
|
||||
type: cipherType,
|
||||
} as any);
|
||||
|
||||
expect(actual).toMatchObject(expected);
|
||||
});
|
||||
});
|
||||
});
|
27
libs/common/spec/models/view/loginView.spec.ts
Normal file
27
libs/common/spec/models/view/loginView.spec.ts
Normal file
@ -0,0 +1,27 @@
|
||||
import { LoginUriView } from "@bitwarden/common/models/view/loginUriView";
|
||||
import { LoginView } from "@bitwarden/common/models/view/loginView";
|
||||
|
||||
jest.mock("@bitwarden/common/models/view/loginUriView");
|
||||
|
||||
describe("LoginView", () => {
|
||||
beforeEach(() => {
|
||||
(LoginUriView as any).mockClear();
|
||||
});
|
||||
|
||||
it("fromJSON initializes nested objects", () => {
|
||||
const mockFromJson = (stub: string) => stub + "_fromJSON";
|
||||
jest.spyOn(LoginUriView, "fromJSON").mockImplementation(mockFromJson as any);
|
||||
|
||||
const passwordRevisionDate = new Date();
|
||||
|
||||
const actual = LoginView.fromJSON({
|
||||
passwordRevisionDate: passwordRevisionDate.toISOString(),
|
||||
uris: ["uri1", "uri2", "uri3"] as any,
|
||||
});
|
||||
|
||||
expect(actual).toMatchObject({
|
||||
passwordRevisionDate: passwordRevisionDate,
|
||||
uris: ["uri1_fromJSON", "uri2_fromJSON", "uri3_fromJSON"],
|
||||
});
|
||||
});
|
||||
});
|
13
libs/common/spec/models/view/passwordHistoryView.spec.ts
Normal file
13
libs/common/spec/models/view/passwordHistoryView.spec.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import { PasswordHistoryView } from "@bitwarden/common/models/view/passwordHistoryView";
|
||||
|
||||
describe("PasswordHistoryView", () => {
|
||||
it("fromJSON initializes nested objects", () => {
|
||||
const lastUsedDate = new Date();
|
||||
|
||||
const actual = PasswordHistoryView.fromJSON({
|
||||
lastUsedDate: lastUsedDate.toISOString(),
|
||||
});
|
||||
|
||||
expect(actual.lastUsedDate).toEqual(lastUsedDate);
|
||||
});
|
||||
});
|
@ -1,5 +1,8 @@
|
||||
import { Jsonify } from "type-fest";
|
||||
|
||||
import { Utils } from "@bitwarden/common/misc/utils";
|
||||
|
||||
import { EncryptionType } from "../../enums/encryptionType";
|
||||
import { Utils } from "../../misc/utils";
|
||||
|
||||
export class SymmetricCryptoKey {
|
||||
key: ArrayBuffer;
|
||||
@ -55,21 +58,17 @@ export class SymmetricCryptoKey {
|
||||
}
|
||||
}
|
||||
|
||||
static initFromJson(jsonResult: SymmetricCryptoKey): SymmetricCryptoKey {
|
||||
if (jsonResult == null) {
|
||||
return jsonResult;
|
||||
toJSON() {
|
||||
// The whole object is constructed from the initial key, so just store the B64 key
|
||||
return { keyB64: this.keyB64 };
|
||||
}
|
||||
|
||||
static fromJSON(obj: Jsonify<SymmetricCryptoKey>): SymmetricCryptoKey {
|
||||
if (obj == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (jsonResult.keyB64 != null) {
|
||||
jsonResult.key = Utils.fromB64ToArray(jsonResult.keyB64).buffer;
|
||||
}
|
||||
if (jsonResult.encKeyB64 != null) {
|
||||
jsonResult.encKey = Utils.fromB64ToArray(jsonResult.encKeyB64).buffer;
|
||||
}
|
||||
if (jsonResult.macKeyB64 != null) {
|
||||
jsonResult.macKey = Utils.fromB64ToArray(jsonResult.macKeyB64).buffer;
|
||||
}
|
||||
|
||||
return jsonResult;
|
||||
const arrayBuffer = Utils.fromB64ToArray(obj.keyB64).buffer;
|
||||
return new SymmetricCryptoKey(arrayBuffer);
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
import { Jsonify } from "type-fest";
|
||||
|
||||
import { Attachment } from "../domain/attachment";
|
||||
import { SymmetricCryptoKey } from "../domain/symmetricCryptoKey";
|
||||
|
||||
@ -32,4 +34,9 @@ export class AttachmentView implements View {
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static fromJSON(obj: Partial<Jsonify<AttachmentView>>): AttachmentView {
|
||||
const key = obj.key == null ? null : SymmetricCryptoKey.fromJSON(obj.key);
|
||||
return Object.assign(new AttachmentView(), obj, { key: key });
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
import { Jsonify } from "type-fest";
|
||||
|
||||
import { CardLinkedId as LinkedId } from "../../enums/linkedIdType";
|
||||
import { linkedFieldOption } from "../../misc/linkedFieldOption.decorator";
|
||||
|
||||
@ -17,10 +19,6 @@ export class CardView extends ItemView {
|
||||
private _number: string = null;
|
||||
private _subTitle: string = null;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
get maskedCode(): string {
|
||||
return this.code != null ? "•".repeat(this.code.length) : null;
|
||||
}
|
||||
@ -79,4 +77,8 @@ export class CardView extends ItemView {
|
||||
private formatYear(year: string): string {
|
||||
return year.length === 2 ? "20" + year : year;
|
||||
}
|
||||
|
||||
static fromJSON(obj: Partial<Jsonify<CardView>>): CardView {
|
||||
return Object.assign(new CardView(), obj);
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
import { Jsonify } from "type-fest";
|
||||
|
||||
import { CipherRepromptType } from "../../enums/cipherRepromptType";
|
||||
import { CipherType } from "../../enums/cipherType";
|
||||
import { LinkedIdType } from "../../enums/linkedIdType";
|
||||
@ -131,4 +133,40 @@ export class CipherView implements View {
|
||||
linkedFieldI18nKey(id: LinkedIdType): string {
|
||||
return this.linkedFieldOptions.get(id)?.i18nKey;
|
||||
}
|
||||
|
||||
static fromJSON(obj: Partial<Jsonify<CipherView>>): CipherView {
|
||||
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;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return view;
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
import { Jsonify } from "type-fest";
|
||||
|
||||
import { FieldType } from "../../enums/fieldType";
|
||||
import { LinkedIdType } from "../../enums/linkedIdType";
|
||||
import { Field } from "../domain/field";
|
||||
@ -25,4 +27,8 @@ export class FieldView implements View {
|
||||
get maskedValue(): string {
|
||||
return this.value != null ? "••••••••" : null;
|
||||
}
|
||||
|
||||
static fromJSON(obj: Partial<Jsonify<FieldView>>): FieldView {
|
||||
return Object.assign(new FieldView(), obj);
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
import { Jsonify } from "type-fest";
|
||||
|
||||
import { IdentityLinkedId as LinkedId } from "../../enums/linkedIdType";
|
||||
import { linkedFieldOption } from "../../misc/linkedFieldOption.decorator";
|
||||
import { Utils } from "../../misc/utils";
|
||||
@ -139,4 +141,8 @@ export class IdentityView extends ItemView {
|
||||
addressPart2 += ", " + postalCode;
|
||||
return addressPart2;
|
||||
}
|
||||
|
||||
static fromJSON(obj: Partial<Jsonify<IdentityView>>): IdentityView {
|
||||
return Object.assign(new IdentityView(), obj);
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
import { Jsonify } from "type-fest";
|
||||
|
||||
import { UriMatchType } from "../../enums/uriMatchType";
|
||||
import { Utils } from "../../misc/utils";
|
||||
import { LoginUri } from "../domain/loginUri";
|
||||
@ -124,4 +126,8 @@ export class LoginUriView implements View {
|
||||
? "http://" + this.uri
|
||||
: this.uri;
|
||||
}
|
||||
|
||||
static fromJSON(obj: Partial<Jsonify<LoginUriView>>): LoginUriView {
|
||||
return Object.assign(new LoginUriView(), obj);
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
import { Jsonify } from "type-fest";
|
||||
|
||||
import { LoginLinkedId as LinkedId } from "../../enums/linkedIdType";
|
||||
import { linkedFieldOption } from "../../misc/linkedFieldOption.decorator";
|
||||
import { Utils } from "../../misc/utils";
|
||||
@ -60,4 +62,15 @@ export class LoginView extends ItemView {
|
||||
get hasUris(): boolean {
|
||||
return this.uris != null && this.uris.length > 0;
|
||||
}
|
||||
|
||||
static fromJSON(obj: Partial<Jsonify<LoginView>>): LoginView {
|
||||
const passwordRevisionDate =
|
||||
obj.passwordRevisionDate == null ? null : new Date(obj.passwordRevisionDate);
|
||||
const uris = obj.uris?.map((uri: any) => LoginUriView.fromJSON(uri));
|
||||
|
||||
return Object.assign(new LoginView(), obj, {
|
||||
passwordRevisionDate: passwordRevisionDate,
|
||||
uris: uris,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
import { Jsonify } from "type-fest";
|
||||
|
||||
import { Password } from "../domain/password";
|
||||
|
||||
import { View } from "./view";
|
||||
@ -13,4 +15,12 @@ export class PasswordHistoryView implements View {
|
||||
|
||||
this.lastUsedDate = ph.lastUsedDate;
|
||||
}
|
||||
|
||||
static fromJSON(obj: Partial<Jsonify<PasswordHistoryView>>): PasswordHistoryView {
|
||||
const lastUsedDate = obj.lastUsedDate == null ? null : new Date(obj.lastUsedDate);
|
||||
|
||||
return Object.assign(new PasswordHistoryView(), obj, {
|
||||
lastUsedDate: lastUsedDate,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
import { Jsonify } from "type-fest";
|
||||
|
||||
import { SecureNoteType } from "../../enums/secureNoteType";
|
||||
import { SecureNote } from "../domain/secureNote";
|
||||
|
||||
@ -18,4 +20,8 @@ export class SecureNoteView extends ItemView {
|
||||
get subTitle(): string {
|
||||
return null;
|
||||
}
|
||||
|
||||
static fromJSON(obj: Partial<Jsonify<SecureNoteView>>): SecureNoteView {
|
||||
return Object.assign(new SecureNoteView(), obj);
|
||||
}
|
||||
}
|
||||
|
@ -498,7 +498,7 @@ export class StateService<
|
||||
);
|
||||
}
|
||||
|
||||
@withPrototype(SymmetricCryptoKey, SymmetricCryptoKey.initFromJson)
|
||||
@withPrototype(SymmetricCryptoKey, SymmetricCryptoKey.fromJSON)
|
||||
async getCryptoMasterKey(options?: StorageOptions): Promise<SymmetricCryptoKey> {
|
||||
return (
|
||||
await this.getAccount(this.reconcileOptions(options, await this.defaultInMemoryOptions()))
|
||||
@ -661,7 +661,7 @@ export class StateService<
|
||||
);
|
||||
}
|
||||
|
||||
@withPrototype(SymmetricCryptoKey, SymmetricCryptoKey.initFromJson)
|
||||
@withPrototype(SymmetricCryptoKey, SymmetricCryptoKey.fromJSON)
|
||||
async getDecryptedCryptoSymmetricKey(options?: StorageOptions): Promise<SymmetricCryptoKey> {
|
||||
return (
|
||||
await this.getAccount(this.reconcileOptions(options, await this.defaultInMemoryOptions()))
|
||||
@ -682,7 +682,7 @@ export class StateService<
|
||||
);
|
||||
}
|
||||
|
||||
@withPrototypeForMap(SymmetricCryptoKey, SymmetricCryptoKey.initFromJson)
|
||||
@withPrototypeForMap(SymmetricCryptoKey, SymmetricCryptoKey.fromJSON)
|
||||
async getDecryptedOrganizationKeys(
|
||||
options?: StorageOptions
|
||||
): Promise<Map<string, SymmetricCryptoKey>> {
|
||||
@ -789,7 +789,7 @@ export class StateService<
|
||||
);
|
||||
}
|
||||
|
||||
@withPrototypeForMap(SymmetricCryptoKey, SymmetricCryptoKey.initFromJson)
|
||||
@withPrototypeForMap(SymmetricCryptoKey, SymmetricCryptoKey.fromJSON)
|
||||
async getDecryptedProviderKeys(
|
||||
options?: StorageOptions
|
||||
): Promise<Map<string, SymmetricCryptoKey>> {
|
||||
@ -2748,7 +2748,7 @@ export class StateService<
|
||||
|
||||
export function withPrototype<T>(
|
||||
constructor: new (...args: any[]) => T,
|
||||
converter: (input: T) => T = (i) => i
|
||||
converter: (input: any) => T = (i) => i
|
||||
): (
|
||||
target: any,
|
||||
propertyKey: string | symbol,
|
||||
@ -2784,7 +2784,7 @@ export function withPrototype<T>(
|
||||
|
||||
function withPrototypeForArrayMembers<T>(
|
||||
memberConstructor: new (...args: any[]) => T,
|
||||
memberConverter: (input: T) => T = (i) => i
|
||||
memberConverter: (input: any) => T = (i) => i
|
||||
): (
|
||||
target: any,
|
||||
propertyKey: string | symbol,
|
||||
@ -2832,7 +2832,7 @@ function withPrototypeForArrayMembers<T>(
|
||||
|
||||
function withPrototypeForObjectValues<T>(
|
||||
valuesConstructor: new (...args: any[]) => T,
|
||||
valuesConverter: (input: T) => T = (i) => i
|
||||
valuesConverter: (input: any) => T = (i) => i
|
||||
): (
|
||||
target: any,
|
||||
propertyKey: string | symbol,
|
||||
@ -2879,7 +2879,7 @@ function withPrototypeForObjectValues<T>(
|
||||
|
||||
function withPrototypeForMap<T>(
|
||||
valuesConstructor: new (...args: any[]) => T,
|
||||
valuesConverter: (input: T) => T = (i) => i
|
||||
valuesConverter: (input: any) => T = (i) => i
|
||||
): (
|
||||
target: any,
|
||||
propertyKey: string | symbol,
|
||||
|
89
package-lock.json
generated
89
package-lock.json
generated
@ -166,6 +166,7 @@
|
||||
"ts-jest": "^28.0.6",
|
||||
"ts-loader": "^9.2.5",
|
||||
"tsconfig-paths-webpack-plugin": "^3.5.2",
|
||||
"type-fest": "^2.18.0",
|
||||
"typescript": "4.6.4",
|
||||
"url": "^0.11.0",
|
||||
"util": "^0.12.4",
|
||||
@ -3965,6 +3966,18 @@
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/eslintrc/node_modules/type-fest": {
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
|
||||
"integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/@figspec/components": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@figspec/components/-/components-1.0.1.tgz",
|
||||
@ -15965,6 +15978,18 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/boxen/node_modules/type-fest": {
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
|
||||
"integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/bplist-parser": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.1.1.tgz",
|
||||
@ -20353,18 +20378,6 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/electron-store/node_modules/type-fest": {
|
||||
"version": "2.17.0",
|
||||
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.17.0.tgz",
|
||||
"integrity": "sha512-U+g3/JVXnOki1kLSc+xZGPRll3Ah9u2VIG6Sn9iH9YX6UkPERmt6O/0fIyTgsd2/whV0+gAaHAg8fz6sG1QzMA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=12.20"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/electron-to-chromium": {
|
||||
"version": "1.4.200",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.200.tgz",
|
||||
@ -21732,6 +21745,18 @@
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint/node_modules/type-fest": {
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
|
||||
"integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/espree": {
|
||||
"version": "9.3.2",
|
||||
"resolved": "https://registry.npmjs.org/espree/-/espree-9.3.2.tgz",
|
||||
@ -40504,12 +40529,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/type-fest": {
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
|
||||
"integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
|
||||
"version": "2.18.0",
|
||||
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.18.0.tgz",
|
||||
"integrity": "sha512-pRS+/yrW5TjPPHNOvxhbNZexr2bS63WjrMU8a+VzEBhUi9Tz1pZeD+vQz3ut0svZ46P+SRqMEPnJmk2XnvNzTw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
"node": ">=12.20"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
@ -45457,6 +45482,12 @@
|
||||
"requires": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
}
|
||||
},
|
||||
"type-fest": {
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
|
||||
"integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -54924,6 +54955,12 @@
|
||||
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz",
|
||||
"integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==",
|
||||
"dev": true
|
||||
},
|
||||
"type-fest": {
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
|
||||
"integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -58370,14 +58407,6 @@
|
||||
"requires": {
|
||||
"conf": "^10.1.2",
|
||||
"type-fest": "^2.12.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"type-fest": {
|
||||
"version": "2.17.0",
|
||||
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.17.0.tgz",
|
||||
"integrity": "sha512-U+g3/JVXnOki1kLSc+xZGPRll3Ah9u2VIG6Sn9iH9YX6UkPERmt6O/0fIyTgsd2/whV0+gAaHAg8fz6sG1QzMA==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"electron-to-chromium": {
|
||||
@ -59119,6 +59148,12 @@
|
||||
"requires": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
}
|
||||
},
|
||||
"type-fest": {
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
|
||||
"integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -73936,9 +73971,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"type-fest": {
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
|
||||
"integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
|
||||
"version": "2.18.0",
|
||||
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.18.0.tgz",
|
||||
"integrity": "sha512-pRS+/yrW5TjPPHNOvxhbNZexr2bS63WjrMU8a+VzEBhUi9Tz1pZeD+vQz3ut0svZ46P+SRqMEPnJmk2XnvNzTw==",
|
||||
"dev": true
|
||||
},
|
||||
"type-is": {
|
||||
|
@ -128,6 +128,7 @@
|
||||
"ts-jest": "^28.0.6",
|
||||
"ts-loader": "^9.2.5",
|
||||
"tsconfig-paths-webpack-plugin": "^3.5.2",
|
||||
"type-fest": "^2.18.0",
|
||||
"typescript": "4.6.4",
|
||||
"url": "^0.11.0",
|
||||
"util": "^0.12.4",
|
||||
|
Loading…
Reference in New Issue
Block a user