1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-10 21:33:27 +00:00
Files
browser/libs/common/src/vault/models/domain/login.spec.ts
Shane Melton 9f0a565241 [PM-25682] Migrate CipherView and subviews to be TS strict compliant (#16463)
* [PM-25682] Remove ts-strict-ignore from Vault view models and update types to be strict

* [PM-25682] Ignore ViewEncryptableKeys error for old decrypt methods

* [PM-25682] Add null/undefined as possible types for isNull* and other helpers that include null checks internally

* [PM-25682] Use patchValue instead of setValue which does not support undefined values

* [PM-25682] Add type assertions and other misc. null checks where necessary

* [PM-25682] Fix importers specs

* [PM-25682] Cleanup card view/details

* [PM-25682] Fix cipher view hasAttachment helper

* [PM-25682] Cleanup unecessary null assignments in notification.background.spec.ts

* [PM-25682] Ensure linkedId is undefined instead of null

* [PM-25682] Cleanup misc typing errors

* [PM-25682] Make the CipherId required

* [PM-25682] Undo CipherId assertions

* [PM-25682] Undo brand initial value change

* [PM-25682] Update SshKeyView

* [PM-25682] Add constructor to Fido2CredentialView

* [PM-25682] Prettier

* [PM-25682] Fix strict type warnings after merge with main

* [PM-25682] Cleanup cipher view spec

* [PM-25682] Cleanup new type warnings after merge

* [PM-25682] Undo removed eslint-disable-next-line comment

* [PM-25682] Fix flaky test

* [PM-25682] Use satisfies instead of as for Fido2CredentialAutofillView
2025-10-07 11:40:57 -04:00

290 lines
10 KiB
TypeScript

import { MockProxy, mock } from "jest-mock-extended";
import { mockEnc, mockFromJson } from "../../../../spec";
import { EncryptedString, EncString } from "../../../key-management/crypto/models/enc-string";
import { UriMatchStrategy } from "../../../models/domain/domain-service";
import { LoginData } from "../../models/data/login.data";
import { Login } from "../../models/domain/login";
import { LoginUri } from "../../models/domain/login-uri";
import { LoginUriView } from "../../models/view/login-uri.view";
import { Fido2CredentialApi } from "../api/fido2-credential.api";
import { Fido2CredentialData } from "../data/fido2-credential.data";
import { Fido2CredentialView } from "../view/fido2-credential.view";
import { Fido2Credential } from "./fido2-credential";
describe("Login DTO", () => {
it("Convert from empty LoginData", () => {
const data = new LoginData();
const login = new Login(data);
expect(login).toEqual({
passwordRevisionDate: null,
autofillOnPageLoad: undefined,
username: null,
password: null,
totp: null,
});
});
it("Convert from full LoginData", () => {
const fido2CredentialData = initializeFido2Credential(new Fido2CredentialData());
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: [fido2CredentialData],
};
const login = new Login(data);
expect(login).toEqual({
passwordRevisionDate: new Date("2022-01-31T12:00:00.000Z"),
autofillOnPageLoad: false,
username: { encryptedString: "username", encryptionType: 0 },
password: { encryptedString: "password", encryptionType: 0 },
totp: { encryptedString: "123", encryptionType: 0 },
uris: [
{
match: 0,
uri: { encryptedString: "uri", encryptionType: 0 },
uriChecksum: { encryptedString: "checksum", encryptionType: 0 },
},
],
fido2Credentials: [encryptFido2Credential(fido2CredentialData)],
});
});
it("Initialize without LoginData", () => {
const login = new Login();
expect(login).toEqual({});
});
describe("decrypt", () => {
let loginUri: MockProxy<LoginUri>;
const loginUriView = new LoginUriView();
const decryptedFido2Credential = Symbol();
const login = Object.assign(new Login(), {
username: mockEnc("encrypted username"),
password: mockEnc("encrypted password"),
passwordRevisionDate: new Date("2022-01-31T12:00:00.000Z"),
totp: mockEnc("encrypted totp"),
autofillOnPageLoad: true,
fido2Credentials: [{ decrypt: jest.fn().mockReturnValue(decryptedFido2Credential) } as any],
});
const expectedView = {
username: "encrypted username",
password: "encrypted password",
passwordRevisionDate: new Date("2022-01-31T12:00:00.000Z"),
totp: "encrypted totp",
uris: [
{
_uri: "decrypted uri",
},
],
autofillOnPageLoad: true,
fido2Credentials: [decryptedFido2Credential],
};
beforeEach(() => {
loginUri = mock();
loginUriView.uri = "decrypted uri";
});
it("should decrypt to a view", async () => {
loginUri.decrypt.mockResolvedValue(loginUriView);
loginUri.validateChecksum.mockResolvedValue(true);
login.uris = [loginUri];
const loginView = await login.decrypt(null, true);
expect(loginView).toEqual(expectedView);
});
it("should ignore uris that fail checksum", async () => {
loginUri.decrypt.mockResolvedValue(loginUriView);
loginUri.validateChecksum
.mockResolvedValueOnce(false)
.mockResolvedValueOnce(false)
.mockResolvedValueOnce(true);
login.uris = [loginUri, loginUri, loginUri];
const loginView = await login.decrypt(null, false);
expect(loginView).toEqual(expectedView);
});
});
it("Converts from LoginData and back", () => {
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 loginData = login.toLoginData();
expect(loginData).toEqual(data);
});
describe("fromJSON", () => {
it("initializes nested objects", () => {
jest.spyOn(EncString, "fromJSON").mockImplementation(mockFromJson);
jest.spyOn(LoginUri, "fromJSON").mockImplementation(mockFromJson);
const passwordRevisionDate = new Date("2022-01-31T12:00:00.000Z");
const fido2CreationDate = new Date("2023-01-01T12:00:00.000Z");
const actual = Login.fromJSON({
uris: ["loginUri1", "loginUri2"] as any,
username: "myUsername" as EncryptedString,
password: "myPassword" as EncryptedString,
passwordRevisionDate: passwordRevisionDate.toISOString(),
totp: "myTotp" as EncryptedString,
// NOTE: `as any` is here until we migrate to Nx: https://bitwarden.atlassian.net/browse/PM-6493
fido2Credentials: [
{
credentialId: "keyId" as EncryptedString,
keyType: "keyType" as EncryptedString,
keyAlgorithm: "keyAlgorithm" as EncryptedString,
keyCurve: "keyCurve" as EncryptedString,
keyValue: "keyValue" as EncryptedString,
rpId: "rpId" as EncryptedString,
userHandle: "userHandle" as EncryptedString,
userName: "userName" as EncryptedString,
counter: "counter" as EncryptedString,
rpName: "rpName" as EncryptedString,
userDisplayName: "userDisplayName" as EncryptedString,
discoverable: "discoverable" as EncryptedString,
creationDate: fido2CreationDate.toISOString(),
},
] as any,
});
expect(actual).toEqual({
uris: ["loginUri1_fromJSON", "loginUri2_fromJSON"] as any,
username: "myUsername_fromJSON",
password: "myPassword_fromJSON",
passwordRevisionDate: passwordRevisionDate,
totp: "myTotp_fromJSON",
fido2Credentials: [
{
credentialId: "keyId_fromJSON",
keyType: "keyType_fromJSON",
keyAlgorithm: "keyAlgorithm_fromJSON",
keyCurve: "keyCurve_fromJSON",
keyValue: "keyValue_fromJSON",
rpId: "rpId_fromJSON",
userHandle: "userHandle_fromJSON",
userName: "userName_fromJSON",
counter: "counter_fromJSON",
rpName: "rpName_fromJSON",
userDisplayName: "userDisplayName_fromJSON",
discoverable: "discoverable_fromJSON",
creationDate: fido2CreationDate,
},
],
});
expect(actual).toBeInstanceOf(Login);
});
it("returns null if object is null", () => {
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;
function initializeFido2Credential<T extends Fido2CredentialLike>(key: T): T {
key.credentialId = "credentialId";
key.keyType = "public-key";
key.keyAlgorithm = "ECDSA";
key.keyCurve = "P-256";
key.keyValue = "keyValue";
key.rpId = "rpId";
key.userHandle = "userHandle";
key.userName = "userName";
key.counter = "counter";
key.rpName = "rpName";
key.userDisplayName = "userDisplayName";
key.discoverable = "discoverable";
key.creationDate = "2023-01-01T12:00:00.000Z";
return key;
}
function encryptFido2Credential(key: Fido2CredentialLike): Fido2Credential {
const encrypted = new Fido2Credential();
encrypted.credentialId = { encryptedString: key.credentialId, encryptionType: 0 } as EncString;
encrypted.keyType = { encryptedString: key.keyType, encryptionType: 0 } as EncString;
encrypted.keyAlgorithm = { encryptedString: key.keyAlgorithm, encryptionType: 0 } as EncString;
encrypted.keyCurve = { encryptedString: key.keyCurve, encryptionType: 0 } as EncString;
encrypted.keyValue = { encryptedString: key.keyValue, encryptionType: 0 } as EncString;
encrypted.rpId = { encryptedString: key.rpId, encryptionType: 0 } as EncString;
encrypted.userHandle = { encryptedString: key.userHandle, encryptionType: 0 } as EncString;
encrypted.userName = { encryptedString: key.userName, encryptionType: 0 } as EncString;
encrypted.counter = { encryptedString: key.counter, encryptionType: 0 } as EncString;
encrypted.rpName = { encryptedString: key.rpName, encryptionType: 0 } as EncString;
encrypted.userDisplayName = {
encryptedString: key.userDisplayName,
encryptionType: 0,
} as EncString;
encrypted.discoverable = { encryptedString: key.discoverable, encryptionType: 0 } as EncString;
// not encrypted
encrypted.creationDate = new Date(key.creationDate);
return encrypted;
}