mirror of
https://github.com/bitwarden/browser
synced 2025-12-12 06:13:38 +00:00
[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
This commit is contained in:
@@ -133,19 +133,11 @@ describe("NotificationBackground", () => {
|
|||||||
|
|
||||||
expect(cipherView.name).toEqual("example.com");
|
expect(cipherView.name).toEqual("example.com");
|
||||||
expect(cipherView.login).toEqual({
|
expect(cipherView.login).toEqual({
|
||||||
autofillOnPageLoad: null,
|
fido2Credentials: [],
|
||||||
fido2Credentials: null,
|
|
||||||
password: message.password,
|
password: message.password,
|
||||||
passwordRevisionDate: null,
|
|
||||||
totp: null,
|
|
||||||
uris: [
|
uris: [
|
||||||
{
|
{
|
||||||
_canLaunch: null,
|
|
||||||
_domain: null,
|
|
||||||
_host: null,
|
|
||||||
_hostname: null,
|
|
||||||
_uri: message.uri,
|
_uri: message.uri,
|
||||||
match: null,
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
username: message.username,
|
username: message.username,
|
||||||
|
|||||||
@@ -1271,7 +1271,6 @@ export default class NotificationBackground {
|
|||||||
cipherView.folderId = folderId;
|
cipherView.folderId = folderId;
|
||||||
cipherView.type = CipherType.Login;
|
cipherView.type = CipherType.Login;
|
||||||
cipherView.login = loginView;
|
cipherView.login = loginView;
|
||||||
cipherView.organizationId = null;
|
|
||||||
|
|
||||||
return cipherView;
|
return cipherView;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ import {
|
|||||||
CipherViewLike,
|
CipherViewLike,
|
||||||
CipherViewLikeUtils,
|
CipherViewLikeUtils,
|
||||||
} from "@bitwarden/common/vault/utils/cipher-view-like-utils";
|
} from "@bitwarden/common/vault/utils/cipher-view-like-utils";
|
||||||
|
import { filterOutNullish } from "@bitwarden/common/vault/utils/observable-utilities";
|
||||||
import {
|
import {
|
||||||
BadgeModule,
|
BadgeModule,
|
||||||
ButtonModule,
|
ButtonModule,
|
||||||
@@ -168,6 +169,7 @@ export class VaultV2Component<C extends CipherViewLike>
|
|||||||
|
|
||||||
private organizations$: Observable<Organization[]> = this.accountService.activeAccount$.pipe(
|
private organizations$: Observable<Organization[]> = this.accountService.activeAccount$.pipe(
|
||||||
map((a) => a?.id),
|
map((a) => a?.id),
|
||||||
|
filterOutNullish(),
|
||||||
switchMap((id) => this.organizationService.organizations$(id)),
|
switchMap((id) => this.organizationService.organizations$(id)),
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -319,7 +321,7 @@ export class VaultV2Component<C extends CipherViewLike>
|
|||||||
this.searchBarService.setPlaceholderText(this.i18nService.t("searchVault"));
|
this.searchBarService.setPlaceholderText(this.i18nService.t("searchVault"));
|
||||||
|
|
||||||
const authRequests = await firstValueFrom(
|
const authRequests = await firstValueFrom(
|
||||||
this.authRequestService.getLatestPendingAuthRequest$(),
|
this.authRequestService.getLatestPendingAuthRequest$()!,
|
||||||
);
|
);
|
||||||
if (authRequests != null) {
|
if (authRequests != null) {
|
||||||
this.messagingService.send("openLoginApproval", {
|
this.messagingService.send("openLoginApproval", {
|
||||||
|
|||||||
@@ -978,7 +978,7 @@ export class VaultComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
// Allow restore of an Unassigned Item
|
// Allow restore of an Unassigned Item
|
||||||
try {
|
try {
|
||||||
if (c.id == null) {
|
if (c.id == null || c.id === "") {
|
||||||
throw new Error("Cipher must have an Id to be restored");
|
throw new Error("Cipher must have an Id to be restored");
|
||||||
}
|
}
|
||||||
const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
|
const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
|
||||||
@@ -1211,7 +1211,7 @@ export class VaultComponent implements OnInit, OnDestroy {
|
|||||||
aType = "Password";
|
aType = "Password";
|
||||||
value = cipher.login.password;
|
value = cipher.login.password;
|
||||||
typeI18nKey = "password";
|
typeI18nKey = "password";
|
||||||
} else if (field === "totp") {
|
} else if (field === "totp" && cipher.login.totp != null) {
|
||||||
aType = "TOTP";
|
aType = "TOTP";
|
||||||
const totpResponse = await firstValueFrom(this.totpService.getCode$(cipher.login.totp));
|
const totpResponse = await firstValueFrom(this.totpService.getCode$(cipher.login.totp));
|
||||||
value = totpResponse.code;
|
value = totpResponse.code;
|
||||||
@@ -1232,7 +1232,7 @@ export class VaultComponent implements OnInit, OnDestroy {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!cipher.viewPassword) {
|
if (!cipher.viewPassword || value == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -89,6 +89,9 @@ export class ExposedPasswordsReportComponent extends CipherReportComponent imple
|
|||||||
|
|
||||||
private async isPasswordExposed(cv: CipherView): Promise<ReportResult | null> {
|
private async isPasswordExposed(cv: CipherView): Promise<ReportResult | null> {
|
||||||
const { login } = cv;
|
const { login } = cv;
|
||||||
|
if (login.password == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
return await this.auditService.passwordLeaked(login.password).then((exposedCount) => {
|
return await this.auditService.passwordLeaked(login.password).then((exposedCount) => {
|
||||||
if (exposedCount > 0) {
|
if (exposedCount > 0) {
|
||||||
return { ...cv, exposedXTimes: exposedCount } as ReportResult;
|
return { ...cv, exposedXTimes: exposedCount } as ReportResult;
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ export function getTrimmedCipherUris(cipher: CipherView): string[] {
|
|||||||
|
|
||||||
const uniqueDomains = new Set<string>();
|
const uniqueDomains = new Set<string>();
|
||||||
|
|
||||||
uris.forEach((u: { uri: string }) => {
|
uris.forEach((u: { uri: string | undefined }) => {
|
||||||
const domain = Utils.getDomain(u.uri) ?? u.uri;
|
const domain = Utils.getDomain(u.uri) ?? u.uri;
|
||||||
uniqueDomains.add(domain);
|
uniqueDomains.add(domain);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ export class PasswordHealthService {
|
|||||||
filter((cipher) => this.isValidCipher(cipher)),
|
filter((cipher) => this.isValidCipher(cipher)),
|
||||||
mergeMap((cipher) =>
|
mergeMap((cipher) =>
|
||||||
this.auditService
|
this.auditService
|
||||||
.passwordLeaked(cipher.login.password)
|
.passwordLeaked(cipher.login.password!)
|
||||||
.then((exposedCount) => ({ cipher, exposedCount })),
|
.then((exposedCount) => ({ cipher, exposedCount })),
|
||||||
),
|
),
|
||||||
// [FIXME] ExposedDetails is can still return a null
|
// [FIXME] ExposedDetails is can still return a null
|
||||||
@@ -74,11 +74,11 @@ export class PasswordHealthService {
|
|||||||
|
|
||||||
// Check the username
|
// Check the username
|
||||||
const userInput = this.isUserNameNotEmpty(cipher)
|
const userInput = this.isUserNameNotEmpty(cipher)
|
||||||
? this.extractUsernameParts(cipher.login.username)
|
? this.extractUsernameParts(cipher.login.username!)
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
const { score } = this.passwordStrengthService.getPasswordStrength(
|
const { score } = this.passwordStrengthService.getPasswordStrength(
|
||||||
cipher.login.password,
|
cipher.login.password!,
|
||||||
undefined, // No email available in this context
|
undefined, // No email available in this context
|
||||||
userInput,
|
userInput,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -436,13 +436,13 @@ export class RiskInsightsReportService {
|
|||||||
const weakPassword = this.passwordHealthService.findWeakPasswordDetails(cipher);
|
const weakPassword = this.passwordHealthService.findWeakPasswordDetails(cipher);
|
||||||
// Looping over all ciphers needs to happen first to determine reused passwords over all ciphers.
|
// Looping over all ciphers needs to happen first to determine reused passwords over all ciphers.
|
||||||
// Store in the set and evaluate later
|
// Store in the set and evaluate later
|
||||||
if (passwordUseMap.has(cipher.login.password)) {
|
if (passwordUseMap.has(cipher.login.password!)) {
|
||||||
passwordUseMap.set(
|
passwordUseMap.set(
|
||||||
cipher.login.password,
|
cipher.login.password!,
|
||||||
(passwordUseMap.get(cipher.login.password) || 0) + 1,
|
(passwordUseMap.get(cipher.login.password!) || 0) + 1,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
passwordUseMap.set(cipher.login.password, 1);
|
passwordUseMap.set(cipher.login.password!, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
const exposedPassword = exposedDetails.find((x) => x?.cipherId === cipher.id);
|
const exposedPassword = exposedDetails.find((x) => x?.cipherId === cipher.id);
|
||||||
@@ -466,7 +466,7 @@ export class RiskInsightsReportService {
|
|||||||
|
|
||||||
// loop for reused passwords
|
// loop for reused passwords
|
||||||
cipherHealthReports.forEach((detail) => {
|
cipherHealthReports.forEach((detail) => {
|
||||||
detail.reusedPasswordCount = passwordUseMap.get(detail.login.password) ?? 0;
|
detail.reusedPasswordCount = passwordUseMap.get(detail.login.password!) ?? 0;
|
||||||
});
|
});
|
||||||
return cipherHealthReports;
|
return cipherHealthReports;
|
||||||
}
|
}
|
||||||
@@ -514,7 +514,7 @@ export class RiskInsightsReportService {
|
|||||||
private _buildPasswordUseMap(ciphers: CipherView[]): Map<string, number> {
|
private _buildPasswordUseMap(ciphers: CipherView[]): Map<string, number> {
|
||||||
const passwordUseMap = new Map<string, number>();
|
const passwordUseMap = new Map<string, number>();
|
||||||
ciphers.forEach((cipher) => {
|
ciphers.forEach((cipher) => {
|
||||||
const password = cipher.login.password;
|
const password = cipher.login.password!;
|
||||||
passwordUseMap.set(password, (passwordUseMap.get(password) || 0) + 1);
|
passwordUseMap.set(password, (passwordUseMap.get(password) || 0) + 1);
|
||||||
});
|
});
|
||||||
return passwordUseMap;
|
return passwordUseMap;
|
||||||
@@ -686,7 +686,7 @@ export class RiskInsightsReportService {
|
|||||||
healthData: {
|
healthData: {
|
||||||
weakPasswordDetail: this.passwordHealthService.findWeakPasswordDetails(cipher),
|
weakPasswordDetail: this.passwordHealthService.findWeakPasswordDetails(cipher),
|
||||||
exposedPasswordDetail: exposedPassword,
|
exposedPasswordDetail: exposedPassword,
|
||||||
reusedPasswordCount: passwordUseMap.get(cipher.login.password) ?? 0,
|
reusedPasswordCount: passwordUseMap.get(cipher.login.password!) ?? 0,
|
||||||
},
|
},
|
||||||
applications: getTrimmedCipherUris(cipher),
|
applications: getTrimmedCipherUris(cipher),
|
||||||
} as CipherHealthReport;
|
} as CipherHealthReport;
|
||||||
|
|||||||
@@ -17,13 +17,13 @@ const CanLaunchWhitelist = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
export class SafeUrls {
|
export class SafeUrls {
|
||||||
static canLaunch(uri: string): boolean {
|
static canLaunch(uri: string | null | undefined): boolean {
|
||||||
if (Utils.isNullOrWhitespace(uri)) {
|
if (Utils.isNullOrWhitespace(uri)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let i = 0; i < CanLaunchWhitelist.length; i++) {
|
for (let i = 0; i < CanLaunchWhitelist.length; i++) {
|
||||||
if (uri.indexOf(CanLaunchWhitelist[i]) === 0) {
|
if (uri!.indexOf(CanLaunchWhitelist[i]) === 0) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -375,7 +375,7 @@ export class Utils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static getDomain(uriString: string): string {
|
static getDomain(uriString: string | null | undefined): string {
|
||||||
if (Utils.isNullOrWhitespace(uriString)) {
|
if (Utils.isNullOrWhitespace(uriString)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -457,7 +457,7 @@ export class Utils {
|
|||||||
return str == null || typeof str !== "string" || str.trim() === "";
|
return str == null || typeof str !== "string" || str.trim() === "";
|
||||||
}
|
}
|
||||||
|
|
||||||
static isNullOrEmpty(str: string | null): boolean {
|
static isNullOrEmpty(str: string | null | undefined): boolean {
|
||||||
return str == null || typeof str !== "string" || str == "";
|
return str == null || typeof str !== "string" || str == "";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -479,7 +479,7 @@ export class Utils {
|
|||||||
return (Object.keys(obj).filter((k) => Number.isNaN(+k)) as K[]).map((k) => obj[k]);
|
return (Object.keys(obj).filter((k) => Number.isNaN(+k)) as K[]).map((k) => obj[k]);
|
||||||
}
|
}
|
||||||
|
|
||||||
static getUrl(uriString: string): URL {
|
static getUrl(uriString: string | undefined | null): URL {
|
||||||
if (this.isNullOrWhitespace(uriString)) {
|
if (this.isNullOrWhitespace(uriString)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,8 +27,8 @@ export async function getCredentialsForAutofill(
|
|||||||
cipherId: cipher.id,
|
cipherId: cipher.id,
|
||||||
credentialId: credId,
|
credentialId: credId,
|
||||||
rpId: credential.rpId,
|
rpId: credential.rpId,
|
||||||
userHandle: credential.userHandle,
|
userHandle: credential.userHandle!,
|
||||||
userName: credential.userName,
|
userName: credential.userName!,
|
||||||
};
|
} satisfies Fido2CredentialAutofillView;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ export class Attachment extends Domain {
|
|||||||
): Promise<AttachmentView> {
|
): Promise<AttachmentView> {
|
||||||
const view = await this.decryptObj<Attachment, AttachmentView>(
|
const view = await this.decryptObj<Attachment, AttachmentView>(
|
||||||
this,
|
this,
|
||||||
|
// @ts-expect-error ViewEncryptableKeys type should be fixed to allow for optional values, but is out of scope for now.
|
||||||
new AttachmentView(this),
|
new AttachmentView(this),
|
||||||
["fileName"],
|
["fileName"],
|
||||||
orgId,
|
orgId,
|
||||||
|
|||||||
@@ -63,7 +63,6 @@ describe("Card", () => {
|
|||||||
expect(view).toEqual({
|
expect(view).toEqual({
|
||||||
_brand: "brand",
|
_brand: "brand",
|
||||||
_number: "number",
|
_number: "number",
|
||||||
_subTitle: null,
|
|
||||||
cardholderName: "cardHolder",
|
cardholderName: "cardHolder",
|
||||||
code: "code",
|
code: "code",
|
||||||
expMonth: "expMonth",
|
expMonth: "expMonth",
|
||||||
|
|||||||
@@ -161,6 +161,8 @@ export class Cipher extends Domain implements Decryptable<CipherView> {
|
|||||||
|
|
||||||
await this.decryptObj<Cipher, CipherView>(
|
await this.decryptObj<Cipher, CipherView>(
|
||||||
this,
|
this,
|
||||||
|
// @ts-expect-error Ciphers have optional Ids which are getting swallowed by the ViewEncryptableKeys type
|
||||||
|
// The ViewEncryptableKeys type should be fixed to allow for optional Ids, but is out of scope for now.
|
||||||
model,
|
model,
|
||||||
["name", "notes"],
|
["name", "notes"],
|
||||||
this.organizationId,
|
this.organizationId,
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ export class Fido2Credential extends Domain {
|
|||||||
async decrypt(orgId: string, encKey?: SymmetricCryptoKey): Promise<Fido2CredentialView> {
|
async decrypt(orgId: string, encKey?: SymmetricCryptoKey): Promise<Fido2CredentialView> {
|
||||||
const view = await this.decryptObj<Fido2Credential, Fido2CredentialView>(
|
const view = await this.decryptObj<Fido2Credential, Fido2CredentialView>(
|
||||||
this,
|
this,
|
||||||
|
// @ts-expect-error ViewEncryptableKeys type should be fixed to allow for optional values, but is out of scope for now.
|
||||||
new Fido2CredentialView(),
|
new Fido2CredentialView(),
|
||||||
[
|
[
|
||||||
"credentialId",
|
"credentialId",
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ export class Field extends Domain {
|
|||||||
decrypt(orgId: string, encKey?: SymmetricCryptoKey): Promise<FieldView> {
|
decrypt(orgId: string, encKey?: SymmetricCryptoKey): Promise<FieldView> {
|
||||||
return this.decryptObj<Field, FieldView>(
|
return this.decryptObj<Field, FieldView>(
|
||||||
this,
|
this,
|
||||||
|
// @ts-expect-error ViewEncryptableKeys type should be fixed to allow for optional values, but is out of scope for now.
|
||||||
new FieldView(this),
|
new FieldView(this),
|
||||||
["name", "value"],
|
["name", "value"],
|
||||||
orgId,
|
orgId,
|
||||||
|
|||||||
@@ -112,7 +112,6 @@ describe("Identity", () => {
|
|||||||
expect(view).toEqual({
|
expect(view).toEqual({
|
||||||
_firstName: "mockFirstName",
|
_firstName: "mockFirstName",
|
||||||
_lastName: "mockLastName",
|
_lastName: "mockLastName",
|
||||||
_subTitle: null,
|
|
||||||
address1: "mockAddress1",
|
address1: "mockAddress1",
|
||||||
address2: "mockAddress2",
|
address2: "mockAddress2",
|
||||||
address3: "mockAddress3",
|
address3: "mockAddress3",
|
||||||
|
|||||||
@@ -56,10 +56,6 @@ describe("LoginUri", () => {
|
|||||||
const view = await loginUri.decrypt(null);
|
const view = await loginUri.decrypt(null);
|
||||||
|
|
||||||
expect(view).toEqual({
|
expect(view).toEqual({
|
||||||
_canLaunch: null,
|
|
||||||
_domain: null,
|
|
||||||
_host: null,
|
|
||||||
_hostname: null,
|
|
||||||
_uri: "uri",
|
_uri: "uri",
|
||||||
match: 3,
|
match: 3,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { MockProxy, mock } from "jest-mock-extended";
|
|||||||
|
|
||||||
import { mockEnc, mockFromJson } from "../../../../spec";
|
import { mockEnc, mockFromJson } from "../../../../spec";
|
||||||
import { EncryptedString, EncString } from "../../../key-management/crypto/models/enc-string";
|
import { EncryptedString, EncString } from "../../../key-management/crypto/models/enc-string";
|
||||||
import { UriMatchStrategy, UriMatchStrategySetting } from "../../../models/domain/domain-service";
|
import { UriMatchStrategy } from "../../../models/domain/domain-service";
|
||||||
import { LoginData } from "../../models/data/login.data";
|
import { LoginData } from "../../models/data/login.data";
|
||||||
import { Login } from "../../models/domain/login";
|
import { Login } from "../../models/domain/login";
|
||||||
import { LoginUri } from "../../models/domain/login-uri";
|
import { LoginUri } from "../../models/domain/login-uri";
|
||||||
@@ -82,12 +82,7 @@ describe("Login DTO", () => {
|
|||||||
totp: "encrypted totp",
|
totp: "encrypted totp",
|
||||||
uris: [
|
uris: [
|
||||||
{
|
{
|
||||||
match: null as UriMatchStrategySetting,
|
|
||||||
_uri: "decrypted uri",
|
_uri: "decrypted uri",
|
||||||
_domain: null as string,
|
|
||||||
_hostname: null as string,
|
|
||||||
_host: null as string,
|
|
||||||
_canLaunch: null as boolean,
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
autofillOnPageLoad: true,
|
autofillOnPageLoad: true,
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
// FIXME: Update this file to be type safe and remove this and next line
|
|
||||||
// @ts-strict-ignore
|
|
||||||
import { Jsonify } from "type-fest";
|
import { Jsonify } from "type-fest";
|
||||||
|
|
||||||
import { AttachmentView as SdkAttachmentView } from "@bitwarden/sdk-internal";
|
import { AttachmentView as SdkAttachmentView } from "@bitwarden/sdk-internal";
|
||||||
@@ -10,12 +8,12 @@ import { SymmetricCryptoKey } from "../../../platform/models/domain/symmetric-cr
|
|||||||
import { Attachment } from "../domain/attachment";
|
import { Attachment } from "../domain/attachment";
|
||||||
|
|
||||||
export class AttachmentView implements View {
|
export class AttachmentView implements View {
|
||||||
id: string = null;
|
id?: string;
|
||||||
url: string = null;
|
url?: string;
|
||||||
size: string = null;
|
size?: string;
|
||||||
sizeName: string = null;
|
sizeName?: string;
|
||||||
fileName: string = null;
|
fileName?: string;
|
||||||
key: SymmetricCryptoKey = null;
|
key?: SymmetricCryptoKey;
|
||||||
/**
|
/**
|
||||||
* The SDK returns an encrypted key for the attachment.
|
* The SDK returns an encrypted key for the attachment.
|
||||||
*/
|
*/
|
||||||
@@ -35,7 +33,7 @@ export class AttachmentView implements View {
|
|||||||
get fileSize(): number {
|
get fileSize(): number {
|
||||||
try {
|
try {
|
||||||
if (this.size != null) {
|
if (this.size != null) {
|
||||||
return parseInt(this.size, null);
|
return parseInt(this.size);
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
// Invalid file size.
|
// Invalid file size.
|
||||||
@@ -71,7 +69,7 @@ export class AttachmentView implements View {
|
|||||||
fileName: this.fileName,
|
fileName: this.fileName,
|
||||||
key: this.encryptedKey?.toSdk(),
|
key: this.encryptedKey?.toSdk(),
|
||||||
// TODO: PM-23005 - Temporary field, should be removed when encrypted migration is complete
|
// TODO: PM-23005 - Temporary field, should be removed when encrypted migration is complete
|
||||||
decryptedKey: this.key ? this.key.toBase64() : null,
|
decryptedKey: this.key ? this.key.toBase64() : undefined,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,13 +82,13 @@ export class AttachmentView implements View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const view = new AttachmentView();
|
const view = new AttachmentView();
|
||||||
view.id = obj.id ?? null;
|
view.id = obj.id;
|
||||||
view.url = obj.url ?? null;
|
view.url = obj.url;
|
||||||
view.size = obj.size ?? null;
|
view.size = obj.size;
|
||||||
view.sizeName = obj.sizeName ?? null;
|
view.sizeName = obj.sizeName;
|
||||||
view.fileName = obj.fileName ?? null;
|
view.fileName = obj.fileName;
|
||||||
// TODO: PM-23005 - Temporary field, should be removed when encrypted migration is complete
|
// TODO: PM-23005 - Temporary field, should be removed when encrypted migration is complete
|
||||||
view.key = obj.decryptedKey ? SymmetricCryptoKey.fromString(obj.decryptedKey) : null;
|
view.key = obj.decryptedKey ? SymmetricCryptoKey.fromString(obj.decryptedKey) : undefined;
|
||||||
view.encryptedKey = obj.key ? new EncString(obj.key) : undefined;
|
view.encryptedKey = obj.key ? new EncString(obj.key) : undefined;
|
||||||
|
|
||||||
return view;
|
return view;
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
// FIXME: Update this file to be type safe and remove this and next line
|
|
||||||
// @ts-strict-ignore
|
|
||||||
import { Jsonify } from "type-fest";
|
import { Jsonify } from "type-fest";
|
||||||
|
|
||||||
import { CardView as SdkCardView } from "@bitwarden/sdk-internal";
|
import { CardView as SdkCardView } from "@bitwarden/sdk-internal";
|
||||||
@@ -12,45 +10,45 @@ import { ItemView } from "./item.view";
|
|||||||
|
|
||||||
export class CardView extends ItemView implements SdkCardView {
|
export class CardView extends ItemView implements SdkCardView {
|
||||||
@linkedFieldOption(LinkedId.CardholderName, { sortPosition: 0 })
|
@linkedFieldOption(LinkedId.CardholderName, { sortPosition: 0 })
|
||||||
cardholderName: string = null;
|
cardholderName: string | undefined;
|
||||||
@linkedFieldOption(LinkedId.ExpMonth, { sortPosition: 3, i18nKey: "expirationMonth" })
|
@linkedFieldOption(LinkedId.ExpMonth, { sortPosition: 3, i18nKey: "expirationMonth" })
|
||||||
expMonth: string = null;
|
expMonth: string | undefined;
|
||||||
@linkedFieldOption(LinkedId.ExpYear, { sortPosition: 4, i18nKey: "expirationYear" })
|
@linkedFieldOption(LinkedId.ExpYear, { sortPosition: 4, i18nKey: "expirationYear" })
|
||||||
expYear: string = null;
|
expYear: string | undefined;
|
||||||
@linkedFieldOption(LinkedId.Code, { sortPosition: 5, i18nKey: "securityCode" })
|
@linkedFieldOption(LinkedId.Code, { sortPosition: 5, i18nKey: "securityCode" })
|
||||||
code: string = null;
|
code: string | undefined;
|
||||||
|
|
||||||
private _brand: string = null;
|
private _brand?: string;
|
||||||
private _number: string = null;
|
private _number?: string;
|
||||||
private _subTitle: string = null;
|
private _subTitle?: string;
|
||||||
|
|
||||||
get maskedCode(): string {
|
get maskedCode(): string | undefined {
|
||||||
return this.code != null ? "•".repeat(this.code.length) : null;
|
return this.code != null ? "•".repeat(this.code.length) : undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
get maskedNumber(): string {
|
get maskedNumber(): string | undefined {
|
||||||
return this.number != null ? "•".repeat(this.number.length) : null;
|
return this.number != null ? "•".repeat(this.number.length) : undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
@linkedFieldOption(LinkedId.Brand, { sortPosition: 2 })
|
@linkedFieldOption(LinkedId.Brand, { sortPosition: 2 })
|
||||||
get brand(): string {
|
get brand(): string | undefined {
|
||||||
return this._brand;
|
return this._brand;
|
||||||
}
|
}
|
||||||
set brand(value: string) {
|
set brand(value: string | undefined) {
|
||||||
this._brand = value;
|
this._brand = value;
|
||||||
this._subTitle = null;
|
this._subTitle = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
@linkedFieldOption(LinkedId.Number, { sortPosition: 1 })
|
@linkedFieldOption(LinkedId.Number, { sortPosition: 1 })
|
||||||
get number(): string {
|
get number(): string | undefined {
|
||||||
return this._number;
|
return this._number;
|
||||||
}
|
}
|
||||||
set number(value: string) {
|
set number(value: string | undefined) {
|
||||||
this._number = value;
|
this._number = value;
|
||||||
this._subTitle = null;
|
this._subTitle = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
get subTitle(): string {
|
get subTitle(): string | undefined {
|
||||||
if (this._subTitle == null) {
|
if (this._subTitle == null) {
|
||||||
this._subTitle = this.brand;
|
this._subTitle = this.brand;
|
||||||
if (this.number != null && this.number.length >= 4) {
|
if (this.number != null && this.number.length >= 4) {
|
||||||
@@ -69,11 +67,11 @@ export class CardView extends ItemView implements SdkCardView {
|
|||||||
return this._subTitle;
|
return this._subTitle;
|
||||||
}
|
}
|
||||||
|
|
||||||
get expiration(): string {
|
get expiration(): string | undefined {
|
||||||
const normalizedYear = normalizeExpiryYearFormat(this.expYear);
|
const normalizedYear = this.expYear ? normalizeExpiryYearFormat(this.expYear) : undefined;
|
||||||
|
|
||||||
if (!this.expMonth && !normalizedYear) {
|
if (!this.expMonth && !normalizedYear) {
|
||||||
return null;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
let exp = this.expMonth != null ? ("0" + this.expMonth).slice(-2) : "__";
|
let exp = this.expMonth != null ? ("0" + this.expMonth).slice(-2) : "__";
|
||||||
@@ -82,14 +80,14 @@ export class CardView extends ItemView implements SdkCardView {
|
|||||||
return exp;
|
return exp;
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromJSON(obj: Partial<Jsonify<CardView>>): CardView {
|
static fromJSON(obj: Partial<Jsonify<CardView>> | undefined): CardView {
|
||||||
return Object.assign(new CardView(), obj);
|
return Object.assign(new CardView(), obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ref https://stackoverflow.com/a/5911300
|
// ref https://stackoverflow.com/a/5911300
|
||||||
static getCardBrandByPatterns(cardNum: string): string {
|
static getCardBrandByPatterns(cardNum: string | undefined | null): string | undefined {
|
||||||
if (cardNum == null || typeof cardNum !== "string" || cardNum.trim() === "") {
|
if (cardNum == null || typeof cardNum !== "string" || cardNum.trim() === "") {
|
||||||
return null;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Visa
|
// Visa
|
||||||
@@ -146,25 +144,21 @@ export class CardView extends ItemView implements SdkCardView {
|
|||||||
return "Visa";
|
return "Visa";
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts an SDK CardView to a CardView.
|
* Converts an SDK CardView to a CardView.
|
||||||
*/
|
*/
|
||||||
static fromSdkCardView(obj: SdkCardView): CardView | undefined {
|
static fromSdkCardView(obj: SdkCardView): CardView {
|
||||||
if (obj == null) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
const cardView = new CardView();
|
const cardView = new CardView();
|
||||||
|
|
||||||
cardView.cardholderName = obj.cardholderName ?? null;
|
cardView.cardholderName = obj.cardholderName;
|
||||||
cardView.brand = obj.brand ?? null;
|
cardView.brand = obj.brand;
|
||||||
cardView.number = obj.number ?? null;
|
cardView.number = obj.number;
|
||||||
cardView.expMonth = obj.expMonth ?? null;
|
cardView.expMonth = obj.expMonth;
|
||||||
cardView.expYear = obj.expYear ?? null;
|
cardView.expYear = obj.expYear;
|
||||||
cardView.code = obj.code ?? null;
|
cardView.code = obj.code;
|
||||||
|
|
||||||
return cardView;
|
return cardView;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -180,15 +180,12 @@ describe("CipherView", () => {
|
|||||||
folderId: "folderId",
|
folderId: "folderId",
|
||||||
collectionIds: ["collectionId"],
|
collectionIds: ["collectionId"],
|
||||||
name: "name",
|
name: "name",
|
||||||
notes: null,
|
|
||||||
type: CipherType.Login,
|
type: CipherType.Login,
|
||||||
favorite: true,
|
favorite: true,
|
||||||
edit: true,
|
edit: true,
|
||||||
reprompt: CipherRepromptType.None,
|
reprompt: CipherRepromptType.None,
|
||||||
organizationUseTotp: false,
|
organizationUseTotp: false,
|
||||||
viewPassword: true,
|
viewPassword: true,
|
||||||
localData: undefined,
|
|
||||||
permissions: undefined,
|
|
||||||
attachments: [
|
attachments: [
|
||||||
{
|
{
|
||||||
id: "attachmentId",
|
id: "attachmentId",
|
||||||
@@ -224,7 +221,6 @@ describe("CipherView", () => {
|
|||||||
passwordHistory: [],
|
passwordHistory: [],
|
||||||
creationDate: new Date("2022-01-01T12:00:00.000Z"),
|
creationDate: new Date("2022-01-01T12:00:00.000Z"),
|
||||||
revisionDate: new Date("2022-01-02T12:00:00.000Z"),
|
revisionDate: new Date("2022-01-02T12:00:00.000Z"),
|
||||||
deletedDate: null,
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -283,18 +279,12 @@ describe("CipherView", () => {
|
|||||||
restore: true,
|
restore: true,
|
||||||
delete: true,
|
delete: true,
|
||||||
},
|
},
|
||||||
deletedDate: undefined,
|
|
||||||
creationDate: "2022-01-02T12:00:00.000Z",
|
creationDate: "2022-01-02T12:00:00.000Z",
|
||||||
revisionDate: "2022-01-02T12:00:00.000Z",
|
revisionDate: "2022-01-02T12:00:00.000Z",
|
||||||
attachments: [],
|
attachments: [],
|
||||||
passwordHistory: [],
|
passwordHistory: [],
|
||||||
login: undefined,
|
|
||||||
identity: undefined,
|
|
||||||
card: undefined,
|
|
||||||
secureNote: undefined,
|
|
||||||
sshKey: undefined,
|
|
||||||
fields: [],
|
fields: [],
|
||||||
} as SdkCipherView);
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
// FIXME: Update this file to be type safe and remove this and next line
|
|
||||||
// @ts-strict-ignore
|
|
||||||
import { EncString } from "@bitwarden/common/key-management/crypto/models/enc-string";
|
import { EncString } from "@bitwarden/common/key-management/crypto/models/enc-string";
|
||||||
import { asUuid, uuidAsString } from "@bitwarden/common/platform/abstractions/sdk/sdk.service";
|
import { asUuid, uuidAsString } from "@bitwarden/common/platform/abstractions/sdk/sdk.service";
|
||||||
|
import { ItemView } from "@bitwarden/common/vault/models/view/item.view";
|
||||||
import { CipherView as SdkCipherView } from "@bitwarden/sdk-internal";
|
import { CipherView as SdkCipherView } from "@bitwarden/sdk-internal";
|
||||||
|
|
||||||
import { View } from "../../../models/view/view";
|
import { View } from "../../../models/view/view";
|
||||||
@@ -26,18 +25,18 @@ import { SshKeyView } from "./ssh-key.view";
|
|||||||
export class CipherView implements View, InitializerMetadata {
|
export class CipherView implements View, InitializerMetadata {
|
||||||
readonly initializerKey = InitializerKey.CipherView;
|
readonly initializerKey = InitializerKey.CipherView;
|
||||||
|
|
||||||
id: string = null;
|
id: string = "";
|
||||||
organizationId: string | undefined = null;
|
organizationId?: string;
|
||||||
folderId: string = null;
|
folderId?: string;
|
||||||
name: string = null;
|
name: string = "";
|
||||||
notes: string = null;
|
notes?: string;
|
||||||
type: CipherType = null;
|
type: CipherType = CipherType.Login;
|
||||||
favorite = false;
|
favorite = false;
|
||||||
organizationUseTotp = false;
|
organizationUseTotp = false;
|
||||||
permissions: CipherPermissionsApi = new CipherPermissionsApi();
|
permissions?: CipherPermissionsApi = new CipherPermissionsApi();
|
||||||
edit = false;
|
edit = false;
|
||||||
viewPassword = true;
|
viewPassword = true;
|
||||||
localData: LocalData;
|
localData?: LocalData;
|
||||||
login = new LoginView();
|
login = new LoginView();
|
||||||
identity = new IdentityView();
|
identity = new IdentityView();
|
||||||
card = new CardView();
|
card = new CardView();
|
||||||
@@ -46,11 +45,11 @@ export class CipherView implements View, InitializerMetadata {
|
|||||||
attachments: AttachmentView[] = [];
|
attachments: AttachmentView[] = [];
|
||||||
fields: FieldView[] = [];
|
fields: FieldView[] = [];
|
||||||
passwordHistory: PasswordHistoryView[] = [];
|
passwordHistory: PasswordHistoryView[] = [];
|
||||||
collectionIds: string[] = null;
|
collectionIds: string[] = [];
|
||||||
revisionDate: Date = null;
|
revisionDate: Date;
|
||||||
creationDate: Date = null;
|
creationDate: Date;
|
||||||
deletedDate: Date | null = null;
|
deletedDate?: Date;
|
||||||
archivedDate: Date | null = null;
|
archivedDate?: Date;
|
||||||
reprompt: CipherRepromptType = CipherRepromptType.None;
|
reprompt: CipherRepromptType = CipherRepromptType.None;
|
||||||
// We need a copy of the encrypted key so we can pass it to
|
// We need a copy of the encrypted key so we can pass it to
|
||||||
// the SdkCipherView during encryption
|
// the SdkCipherView during encryption
|
||||||
@@ -63,6 +62,7 @@ export class CipherView implements View, InitializerMetadata {
|
|||||||
|
|
||||||
constructor(c?: Cipher) {
|
constructor(c?: Cipher) {
|
||||||
if (!c) {
|
if (!c) {
|
||||||
|
this.creationDate = this.revisionDate = new Date();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,7 +86,7 @@ export class CipherView implements View, InitializerMetadata {
|
|||||||
this.key = c.key;
|
this.key = c.key;
|
||||||
}
|
}
|
||||||
|
|
||||||
private get item() {
|
private get item(): ItemView | undefined {
|
||||||
switch (this.type) {
|
switch (this.type) {
|
||||||
case CipherType.Login:
|
case CipherType.Login:
|
||||||
return this.login;
|
return this.login;
|
||||||
@@ -102,10 +102,10 @@ export class CipherView implements View, InitializerMetadata {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
get subTitle(): string {
|
get subTitle(): string | undefined {
|
||||||
return this.item?.subTitle;
|
return this.item?.subTitle;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,7 +114,7 @@ export class CipherView implements View, InitializerMetadata {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get hasAttachments(): boolean {
|
get hasAttachments(): boolean {
|
||||||
return this.attachments && this.attachments.length > 0;
|
return !!this.attachments && this.attachments.length > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
get hasOldAttachments(): boolean {
|
get hasOldAttachments(): boolean {
|
||||||
@@ -132,11 +132,11 @@ export class CipherView implements View, InitializerMetadata {
|
|||||||
return this.fields && this.fields.length > 0;
|
return this.fields && this.fields.length > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
get passwordRevisionDisplayDate(): Date {
|
get passwordRevisionDisplayDate(): Date | undefined {
|
||||||
if (this.type !== CipherType.Login || this.login == null) {
|
if (this.type !== CipherType.Login || this.login == null) {
|
||||||
return null;
|
return undefined;
|
||||||
} else if (this.login.password == null || this.login.password === "") {
|
} else if (this.login.password == null || this.login.password === "") {
|
||||||
return null;
|
return undefined;
|
||||||
}
|
}
|
||||||
return this.login.passwordRevisionDate;
|
return this.login.passwordRevisionDate;
|
||||||
}
|
}
|
||||||
@@ -170,23 +170,17 @@ export class CipherView implements View, InitializerMetadata {
|
|||||||
* Determines if the cipher can be launched in a new browser tab.
|
* Determines if the cipher can be launched in a new browser tab.
|
||||||
*/
|
*/
|
||||||
get canLaunch(): boolean {
|
get canLaunch(): boolean {
|
||||||
return this.type === CipherType.Login && this.login.canLaunch;
|
return this.type === CipherType.Login && this.login!.canLaunch;
|
||||||
}
|
}
|
||||||
|
|
||||||
linkedFieldValue(id: LinkedIdType) {
|
linkedFieldValue(id: LinkedIdType) {
|
||||||
const linkedFieldOption = this.linkedFieldOptions?.get(id);
|
const linkedFieldOption = this.linkedFieldOptions?.get(id);
|
||||||
if (linkedFieldOption == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: Remove when updating file. Eslint update
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
||||||
const item = this.item;
|
const item = this.item;
|
||||||
return this.item[linkedFieldOption.propertyKey as keyof typeof item];
|
if (linkedFieldOption == null || item == null) {
|
||||||
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
linkedFieldI18nKey(id: LinkedIdType): string {
|
return item[linkedFieldOption.propertyKey as keyof typeof item];
|
||||||
return this.linkedFieldOptions.get(id)?.i18nKey;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is used as a marker to indicate that the cipher view object still has its prototype
|
// This is used as a marker to indicate that the cipher view object still has its prototype
|
||||||
@@ -194,23 +188,31 @@ export class CipherView implements View, InitializerMetadata {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromJSON(obj: Partial<DeepJsonify<CipherView>>): CipherView {
|
static fromJSON(obj: Partial<DeepJsonify<CipherView>>): CipherView | null {
|
||||||
if (obj == null) {
|
if (obj == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const view = new CipherView();
|
const view = new CipherView();
|
||||||
const creationDate = obj.creationDate == null ? null : new Date(obj.creationDate);
|
view.type = obj.type ?? CipherType.Login;
|
||||||
const revisionDate = obj.revisionDate == null ? null : new Date(obj.revisionDate);
|
view.id = obj.id ?? "";
|
||||||
const deletedDate = obj.deletedDate == null ? null : new Date(obj.deletedDate);
|
view.name = obj.name ?? "";
|
||||||
const archivedDate = obj.archivedDate == null ? null : new Date(obj.archivedDate);
|
if (obj.creationDate) {
|
||||||
const attachments = obj.attachments?.map((a: any) => AttachmentView.fromJSON(a));
|
view.creationDate = new Date(obj.creationDate);
|
||||||
const fields = obj.fields?.map((f: any) => FieldView.fromJSON(f));
|
}
|
||||||
const passwordHistory = obj.passwordHistory?.map((ph: any) => PasswordHistoryView.fromJSON(ph));
|
if (obj.revisionDate) {
|
||||||
const permissions = CipherPermissionsApi.fromJSON(obj.permissions);
|
view.revisionDate = new Date(obj.revisionDate);
|
||||||
let key: EncString | undefined;
|
}
|
||||||
|
view.deletedDate = obj.deletedDate == null ? undefined : new Date(obj.deletedDate);
|
||||||
|
view.archivedDate = obj.archivedDate == null ? undefined : new Date(obj.archivedDate);
|
||||||
|
view.attachments = obj.attachments?.map((a: any) => AttachmentView.fromJSON(a)) ?? [];
|
||||||
|
view.fields = obj.fields?.map((f: any) => FieldView.fromJSON(f)) ?? [];
|
||||||
|
view.passwordHistory =
|
||||||
|
obj.passwordHistory?.map((ph: any) => PasswordHistoryView.fromJSON(ph)) ?? [];
|
||||||
|
view.permissions = obj.permissions ? CipherPermissionsApi.fromJSON(obj.permissions) : undefined;
|
||||||
|
|
||||||
if (obj.key != null) {
|
if (obj.key != null) {
|
||||||
|
let key: EncString | undefined;
|
||||||
if (typeof obj.key === "string") {
|
if (typeof obj.key === "string") {
|
||||||
// If the key is a string, we need to parse it as EncString
|
// If the key is a string, we need to parse it as EncString
|
||||||
key = EncString.fromJSON(obj.key);
|
key = EncString.fromJSON(obj.key);
|
||||||
@@ -218,20 +220,9 @@ export class CipherView implements View, InitializerMetadata {
|
|||||||
// If the key is already an EncString instance, we can use it directly
|
// If the key is already an EncString instance, we can use it directly
|
||||||
key = obj.key;
|
key = obj.key;
|
||||||
}
|
}
|
||||||
|
view.key = key;
|
||||||
}
|
}
|
||||||
|
|
||||||
Object.assign(view, obj, {
|
|
||||||
creationDate: creationDate,
|
|
||||||
revisionDate: revisionDate,
|
|
||||||
deletedDate: deletedDate,
|
|
||||||
archivedDate: archivedDate,
|
|
||||||
attachments: attachments,
|
|
||||||
fields: fields,
|
|
||||||
passwordHistory: passwordHistory,
|
|
||||||
permissions: permissions,
|
|
||||||
key: key,
|
|
||||||
});
|
|
||||||
|
|
||||||
switch (obj.type) {
|
switch (obj.type) {
|
||||||
case CipherType.Card:
|
case CipherType.Card:
|
||||||
view.card = CardView.fromJSON(obj.card);
|
view.card = CardView.fromJSON(obj.card);
|
||||||
@@ -264,46 +255,54 @@ export class CipherView implements View, InitializerMetadata {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const cipherView = new CipherView();
|
const cipherView = new CipherView();
|
||||||
cipherView.id = uuidAsString(obj.id) ?? null;
|
cipherView.id = uuidAsString(obj.id);
|
||||||
cipherView.organizationId = uuidAsString(obj.organizationId) ?? null;
|
cipherView.organizationId = uuidAsString(obj.organizationId);
|
||||||
cipherView.folderId = uuidAsString(obj.folderId) ?? null;
|
cipherView.folderId = uuidAsString(obj.folderId);
|
||||||
cipherView.name = obj.name;
|
cipherView.name = obj.name;
|
||||||
cipherView.notes = obj.notes ?? null;
|
cipherView.notes = obj.notes;
|
||||||
cipherView.type = obj.type;
|
cipherView.type = obj.type;
|
||||||
cipherView.favorite = obj.favorite;
|
cipherView.favorite = obj.favorite;
|
||||||
cipherView.organizationUseTotp = obj.organizationUseTotp;
|
cipherView.organizationUseTotp = obj.organizationUseTotp;
|
||||||
cipherView.permissions = CipherPermissionsApi.fromSdkCipherPermissions(obj.permissions);
|
cipherView.permissions = obj.permissions
|
||||||
|
? CipherPermissionsApi.fromSdkCipherPermissions(obj.permissions)
|
||||||
|
: undefined;
|
||||||
cipherView.edit = obj.edit;
|
cipherView.edit = obj.edit;
|
||||||
cipherView.viewPassword = obj.viewPassword;
|
cipherView.viewPassword = obj.viewPassword;
|
||||||
cipherView.localData = fromSdkLocalData(obj.localData);
|
cipherView.localData = fromSdkLocalData(obj.localData);
|
||||||
cipherView.attachments =
|
cipherView.attachments =
|
||||||
obj.attachments?.map((a) => AttachmentView.fromSdkAttachmentView(a)) ?? [];
|
obj.attachments?.map((a) => AttachmentView.fromSdkAttachmentView(a)!) ?? [];
|
||||||
cipherView.fields = obj.fields?.map((f) => FieldView.fromSdkFieldView(f)) ?? [];
|
cipherView.fields = obj.fields?.map((f) => FieldView.fromSdkFieldView(f)!) ?? [];
|
||||||
cipherView.passwordHistory =
|
cipherView.passwordHistory =
|
||||||
obj.passwordHistory?.map((ph) => PasswordHistoryView.fromSdkPasswordHistoryView(ph)) ?? [];
|
obj.passwordHistory?.map((ph) => PasswordHistoryView.fromSdkPasswordHistoryView(ph)!) ?? [];
|
||||||
cipherView.collectionIds = obj.collectionIds?.map((i) => uuidAsString(i)) ?? [];
|
cipherView.collectionIds = obj.collectionIds?.map((i) => uuidAsString(i)) ?? [];
|
||||||
cipherView.revisionDate = obj.revisionDate == null ? null : new Date(obj.revisionDate);
|
cipherView.revisionDate = new Date(obj.revisionDate);
|
||||||
cipherView.creationDate = obj.creationDate == null ? null : new Date(obj.creationDate);
|
cipherView.creationDate = new Date(obj.creationDate);
|
||||||
cipherView.deletedDate = obj.deletedDate == null ? null : new Date(obj.deletedDate);
|
cipherView.deletedDate = obj.deletedDate == null ? undefined : new Date(obj.deletedDate);
|
||||||
cipherView.archivedDate = obj.archivedDate == null ? null : new Date(obj.archivedDate);
|
cipherView.archivedDate = obj.archivedDate == null ? undefined : new Date(obj.archivedDate);
|
||||||
cipherView.reprompt = obj.reprompt ?? CipherRepromptType.None;
|
cipherView.reprompt = obj.reprompt ?? CipherRepromptType.None;
|
||||||
cipherView.key = EncString.fromJSON(obj.key);
|
cipherView.key = obj.key ? EncString.fromJSON(obj.key) : undefined;
|
||||||
|
|
||||||
switch (obj.type) {
|
switch (obj.type) {
|
||||||
case CipherType.Card:
|
case CipherType.Card:
|
||||||
cipherView.card = CardView.fromSdkCardView(obj.card);
|
cipherView.card = obj.card ? CardView.fromSdkCardView(obj.card) : new CardView();
|
||||||
break;
|
break;
|
||||||
case CipherType.Identity:
|
case CipherType.Identity:
|
||||||
cipherView.identity = IdentityView.fromSdkIdentityView(obj.identity);
|
cipherView.identity = obj.identity
|
||||||
|
? IdentityView.fromSdkIdentityView(obj.identity)
|
||||||
|
: new IdentityView();
|
||||||
break;
|
break;
|
||||||
case CipherType.Login:
|
case CipherType.Login:
|
||||||
cipherView.login = LoginView.fromSdkLoginView(obj.login);
|
cipherView.login = obj.login ? LoginView.fromSdkLoginView(obj.login) : new LoginView();
|
||||||
break;
|
break;
|
||||||
case CipherType.SecureNote:
|
case CipherType.SecureNote:
|
||||||
cipherView.secureNote = SecureNoteView.fromSdkSecureNoteView(obj.secureNote);
|
cipherView.secureNote = obj.secureNote
|
||||||
|
? SecureNoteView.fromSdkSecureNoteView(obj.secureNote)
|
||||||
|
: new SecureNoteView();
|
||||||
break;
|
break;
|
||||||
case CipherType.SshKey:
|
case CipherType.SshKey:
|
||||||
cipherView.sshKey = SshKeyView.fromSdkSshKeyView(obj.sshKey);
|
cipherView.sshKey = obj.sshKey
|
||||||
|
? SshKeyView.fromSdkSshKeyView(obj.sshKey)
|
||||||
|
: new SshKeyView();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@@ -354,19 +353,19 @@ export class CipherView implements View, InitializerMetadata {
|
|||||||
|
|
||||||
switch (this.type) {
|
switch (this.type) {
|
||||||
case CipherType.Card:
|
case CipherType.Card:
|
||||||
sdkCipherView.card = this.card.toSdkCardView();
|
sdkCipherView.card = this.card?.toSdkCardView();
|
||||||
break;
|
break;
|
||||||
case CipherType.Identity:
|
case CipherType.Identity:
|
||||||
sdkCipherView.identity = this.identity.toSdkIdentityView();
|
sdkCipherView.identity = this.identity?.toSdkIdentityView();
|
||||||
break;
|
break;
|
||||||
case CipherType.Login:
|
case CipherType.Login:
|
||||||
sdkCipherView.login = this.login.toSdkLoginView();
|
sdkCipherView.login = this.login?.toSdkLoginView();
|
||||||
break;
|
break;
|
||||||
case CipherType.SecureNote:
|
case CipherType.SecureNote:
|
||||||
sdkCipherView.secureNote = this.secureNote.toSdkSecureNoteView();
|
sdkCipherView.secureNote = this.secureNote?.toSdkSecureNoteView();
|
||||||
break;
|
break;
|
||||||
case CipherType.SshKey:
|
case CipherType.SshKey:
|
||||||
sdkCipherView.sshKey = this.sshKey.toSdkSshKeyView();
|
sdkCipherView.sshKey = this.sshKey?.toSdkSshKeyView();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
// FIXME: Update this file to be type safe and remove this and next line
|
|
||||||
// @ts-strict-ignore
|
|
||||||
import { Jsonify } from "type-fest";
|
import { Jsonify } from "type-fest";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -10,21 +8,55 @@ import {
|
|||||||
import { ItemView } from "./item.view";
|
import { ItemView } from "./item.view";
|
||||||
|
|
||||||
export class Fido2CredentialView extends ItemView {
|
export class Fido2CredentialView extends ItemView {
|
||||||
|
credentialId!: string;
|
||||||
|
keyType!: "public-key";
|
||||||
|
keyAlgorithm!: "ECDSA";
|
||||||
|
keyCurve!: "P-256";
|
||||||
|
keyValue!: string;
|
||||||
|
rpId!: string;
|
||||||
|
userHandle?: string;
|
||||||
|
userName?: string;
|
||||||
|
counter!: number;
|
||||||
|
rpName?: string;
|
||||||
|
userDisplayName?: string;
|
||||||
|
discoverable: boolean = false;
|
||||||
|
creationDate!: Date;
|
||||||
|
|
||||||
|
constructor(f?: {
|
||||||
credentialId: string;
|
credentialId: string;
|
||||||
keyType: "public-key";
|
keyType: "public-key";
|
||||||
keyAlgorithm: "ECDSA";
|
keyAlgorithm: "ECDSA";
|
||||||
keyCurve: "P-256";
|
keyCurve: "P-256";
|
||||||
keyValue: string;
|
keyValue: string;
|
||||||
rpId: string;
|
rpId: string;
|
||||||
userHandle: string;
|
userHandle?: string;
|
||||||
userName: string;
|
userName?: string;
|
||||||
counter: number;
|
counter: number;
|
||||||
rpName: string;
|
rpName?: string;
|
||||||
userDisplayName: string;
|
userDisplayName?: string;
|
||||||
discoverable: boolean;
|
discoverable?: boolean;
|
||||||
creationDate: Date = null;
|
creationDate: Date;
|
||||||
|
}) {
|
||||||
|
super();
|
||||||
|
if (f == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.credentialId = f.credentialId;
|
||||||
|
this.keyType = f.keyType;
|
||||||
|
this.keyAlgorithm = f.keyAlgorithm;
|
||||||
|
this.keyCurve = f.keyCurve;
|
||||||
|
this.keyValue = f.keyValue;
|
||||||
|
this.rpId = f.rpId;
|
||||||
|
this.userHandle = f.userHandle;
|
||||||
|
this.userName = f.userName;
|
||||||
|
this.counter = f.counter;
|
||||||
|
this.rpName = f.rpName;
|
||||||
|
this.userDisplayName = f.userDisplayName;
|
||||||
|
this.discoverable = f.discoverable ?? false;
|
||||||
|
this.creationDate = f.creationDate;
|
||||||
|
}
|
||||||
|
|
||||||
get subTitle(): string {
|
get subTitle(): string | undefined {
|
||||||
return this.userDisplayName;
|
return this.userDisplayName;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,21 +75,21 @@ export class Fido2CredentialView extends ItemView {
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const view = new Fido2CredentialView();
|
return new Fido2CredentialView({
|
||||||
view.credentialId = obj.credentialId;
|
credentialId: obj.credentialId,
|
||||||
view.keyType = obj.keyType as "public-key";
|
keyType: obj.keyType as "public-key",
|
||||||
view.keyAlgorithm = obj.keyAlgorithm as "ECDSA";
|
keyAlgorithm: obj.keyAlgorithm as "ECDSA",
|
||||||
view.keyCurve = obj.keyCurve as "P-256";
|
keyCurve: obj.keyCurve as "P-256",
|
||||||
view.rpId = obj.rpId;
|
keyValue: obj.keyValue,
|
||||||
view.userHandle = obj.userHandle;
|
rpId: obj.rpId,
|
||||||
view.userName = obj.userName;
|
userHandle: obj.userHandle,
|
||||||
view.counter = parseInt(obj.counter);
|
userName: obj.userName,
|
||||||
view.rpName = obj.rpName;
|
counter: parseInt(obj.counter),
|
||||||
view.userDisplayName = obj.userDisplayName;
|
rpName: obj.rpName,
|
||||||
view.discoverable = obj.discoverable?.toLowerCase() === "true" ? true : false;
|
userDisplayName: obj.userDisplayName,
|
||||||
view.creationDate = obj.creationDate ? new Date(obj.creationDate) : null;
|
discoverable: obj.discoverable?.toLowerCase() === "true",
|
||||||
|
creationDate: new Date(obj.creationDate),
|
||||||
return view;
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
toSdkFido2CredentialFullView(): Fido2CredentialFullView {
|
toSdkFido2CredentialFullView(): Fido2CredentialFullView {
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
// FIXME: Update this file to be type safe and remove this and next line
|
|
||||||
// @ts-strict-ignore
|
|
||||||
import { Jsonify } from "type-fest";
|
import { Jsonify } from "type-fest";
|
||||||
|
|
||||||
import { FieldView as SdkFieldView, FieldType as SdkFieldType } from "@bitwarden/sdk-internal";
|
import { FieldView as SdkFieldView, FieldType as SdkFieldType } from "@bitwarden/sdk-internal";
|
||||||
@@ -9,13 +7,13 @@ import { FieldType, LinkedIdType } from "../../enums";
|
|||||||
import { Field } from "../domain/field";
|
import { Field } from "../domain/field";
|
||||||
|
|
||||||
export class FieldView implements View {
|
export class FieldView implements View {
|
||||||
name: string = null;
|
name?: string;
|
||||||
value: string = null;
|
value?: string;
|
||||||
type: FieldType = null;
|
type: FieldType = FieldType.Text;
|
||||||
newField = false; // Marks if the field is new and hasn't been saved
|
newField = false; // Marks if the field is new and hasn't been saved
|
||||||
showValue = false;
|
showValue = false;
|
||||||
showCount = false;
|
showCount = false;
|
||||||
linkedId: LinkedIdType = null;
|
linkedId?: LinkedIdType;
|
||||||
|
|
||||||
constructor(f?: Field) {
|
constructor(f?: Field) {
|
||||||
if (!f) {
|
if (!f) {
|
||||||
@@ -26,8 +24,8 @@ export class FieldView implements View {
|
|||||||
this.linkedId = f.linkedId;
|
this.linkedId = f.linkedId;
|
||||||
}
|
}
|
||||||
|
|
||||||
get maskedValue(): string {
|
get maskedValue(): string | undefined {
|
||||||
return this.value != null ? "••••••••" : null;
|
return this.value != null ? "••••••••" : undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromJSON(obj: Partial<Jsonify<FieldView>>): FieldView {
|
static fromJSON(obj: Partial<Jsonify<FieldView>>): FieldView {
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
// FIXME: Update this file to be type safe and remove this and next line
|
|
||||||
// @ts-strict-ignore
|
|
||||||
import { Jsonify } from "type-fest";
|
import { Jsonify } from "type-fest";
|
||||||
|
|
||||||
import { IdentityView as SdkIdentityView } from "@bitwarden/sdk-internal";
|
import { IdentityView as SdkIdentityView } from "@bitwarden/sdk-internal";
|
||||||
@@ -12,65 +10,65 @@ import { ItemView } from "./item.view";
|
|||||||
|
|
||||||
export class IdentityView extends ItemView implements SdkIdentityView {
|
export class IdentityView extends ItemView implements SdkIdentityView {
|
||||||
@linkedFieldOption(LinkedId.Title, { sortPosition: 0 })
|
@linkedFieldOption(LinkedId.Title, { sortPosition: 0 })
|
||||||
title: string = null;
|
title: string | undefined;
|
||||||
@linkedFieldOption(LinkedId.MiddleName, { sortPosition: 2 })
|
@linkedFieldOption(LinkedId.MiddleName, { sortPosition: 2 })
|
||||||
middleName: string = null;
|
middleName: string | undefined;
|
||||||
@linkedFieldOption(LinkedId.Address1, { sortPosition: 12 })
|
@linkedFieldOption(LinkedId.Address1, { sortPosition: 12 })
|
||||||
address1: string = null;
|
address1: string | undefined;
|
||||||
@linkedFieldOption(LinkedId.Address2, { sortPosition: 13 })
|
@linkedFieldOption(LinkedId.Address2, { sortPosition: 13 })
|
||||||
address2: string = null;
|
address2: string | undefined;
|
||||||
@linkedFieldOption(LinkedId.Address3, { sortPosition: 14 })
|
@linkedFieldOption(LinkedId.Address3, { sortPosition: 14 })
|
||||||
address3: string = null;
|
address3: string | undefined;
|
||||||
@linkedFieldOption(LinkedId.City, { sortPosition: 15, i18nKey: "cityTown" })
|
@linkedFieldOption(LinkedId.City, { sortPosition: 15, i18nKey: "cityTown" })
|
||||||
city: string = null;
|
city: string | undefined;
|
||||||
@linkedFieldOption(LinkedId.State, { sortPosition: 16, i18nKey: "stateProvince" })
|
@linkedFieldOption(LinkedId.State, { sortPosition: 16, i18nKey: "stateProvince" })
|
||||||
state: string = null;
|
state: string | undefined;
|
||||||
@linkedFieldOption(LinkedId.PostalCode, { sortPosition: 17, i18nKey: "zipPostalCode" })
|
@linkedFieldOption(LinkedId.PostalCode, { sortPosition: 17, i18nKey: "zipPostalCode" })
|
||||||
postalCode: string = null;
|
postalCode: string | undefined;
|
||||||
@linkedFieldOption(LinkedId.Country, { sortPosition: 18 })
|
@linkedFieldOption(LinkedId.Country, { sortPosition: 18 })
|
||||||
country: string = null;
|
country: string | undefined;
|
||||||
@linkedFieldOption(LinkedId.Company, { sortPosition: 6 })
|
@linkedFieldOption(LinkedId.Company, { sortPosition: 6 })
|
||||||
company: string = null;
|
company: string | undefined;
|
||||||
@linkedFieldOption(LinkedId.Email, { sortPosition: 10 })
|
@linkedFieldOption(LinkedId.Email, { sortPosition: 10 })
|
||||||
email: string = null;
|
email: string | undefined;
|
||||||
@linkedFieldOption(LinkedId.Phone, { sortPosition: 11 })
|
@linkedFieldOption(LinkedId.Phone, { sortPosition: 11 })
|
||||||
phone: string = null;
|
phone: string | undefined;
|
||||||
@linkedFieldOption(LinkedId.Ssn, { sortPosition: 7 })
|
@linkedFieldOption(LinkedId.Ssn, { sortPosition: 7 })
|
||||||
ssn: string = null;
|
ssn: string | undefined;
|
||||||
@linkedFieldOption(LinkedId.Username, { sortPosition: 5 })
|
@linkedFieldOption(LinkedId.Username, { sortPosition: 5 })
|
||||||
username: string = null;
|
username: string | undefined;
|
||||||
@linkedFieldOption(LinkedId.PassportNumber, { sortPosition: 8 })
|
@linkedFieldOption(LinkedId.PassportNumber, { sortPosition: 8 })
|
||||||
passportNumber: string = null;
|
passportNumber: string | undefined;
|
||||||
@linkedFieldOption(LinkedId.LicenseNumber, { sortPosition: 9 })
|
@linkedFieldOption(LinkedId.LicenseNumber, { sortPosition: 9 })
|
||||||
licenseNumber: string = null;
|
licenseNumber: string | undefined;
|
||||||
|
|
||||||
private _firstName: string = null;
|
private _firstName: string | undefined;
|
||||||
private _lastName: string = null;
|
private _lastName: string | undefined;
|
||||||
private _subTitle: string = null;
|
private _subTitle: string | undefined;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
@linkedFieldOption(LinkedId.FirstName, { sortPosition: 1 })
|
@linkedFieldOption(LinkedId.FirstName, { sortPosition: 1 })
|
||||||
get firstName(): string {
|
get firstName(): string | undefined {
|
||||||
return this._firstName;
|
return this._firstName;
|
||||||
}
|
}
|
||||||
set firstName(value: string) {
|
set firstName(value: string | undefined) {
|
||||||
this._firstName = value;
|
this._firstName = value;
|
||||||
this._subTitle = null;
|
this._subTitle = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
@linkedFieldOption(LinkedId.LastName, { sortPosition: 4 })
|
@linkedFieldOption(LinkedId.LastName, { sortPosition: 4 })
|
||||||
get lastName(): string {
|
get lastName(): string | undefined {
|
||||||
return this._lastName;
|
return this._lastName;
|
||||||
}
|
}
|
||||||
set lastName(value: string) {
|
set lastName(value: string | undefined) {
|
||||||
this._lastName = value;
|
this._lastName = value;
|
||||||
this._subTitle = null;
|
this._subTitle = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
get subTitle(): string {
|
get subTitle(): string | undefined {
|
||||||
if (this._subTitle == null && (this.firstName != null || this.lastName != null)) {
|
if (this._subTitle == null && (this.firstName != null || this.lastName != null)) {
|
||||||
this._subTitle = "";
|
this._subTitle = "";
|
||||||
if (this.firstName != null) {
|
if (this.firstName != null) {
|
||||||
@@ -88,7 +86,7 @@ export class IdentityView extends ItemView implements SdkIdentityView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@linkedFieldOption(LinkedId.FullName, { sortPosition: 3 })
|
@linkedFieldOption(LinkedId.FullName, { sortPosition: 3 })
|
||||||
get fullName(): string {
|
get fullName(): string | undefined {
|
||||||
if (
|
if (
|
||||||
this.title != null ||
|
this.title != null ||
|
||||||
this.firstName != null ||
|
this.firstName != null ||
|
||||||
@@ -111,11 +109,11 @@ export class IdentityView extends ItemView implements SdkIdentityView {
|
|||||||
return name.trim();
|
return name.trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
get fullAddress(): string {
|
get fullAddress(): string | undefined {
|
||||||
let address = this.address1;
|
let address = this.address1 ?? "";
|
||||||
if (!Utils.isNullOrWhitespace(this.address2)) {
|
if (!Utils.isNullOrWhitespace(this.address2)) {
|
||||||
if (!Utils.isNullOrWhitespace(address)) {
|
if (!Utils.isNullOrWhitespace(address)) {
|
||||||
address += ", ";
|
address += ", ";
|
||||||
@@ -131,9 +129,9 @@ export class IdentityView extends ItemView implements SdkIdentityView {
|
|||||||
return address;
|
return address;
|
||||||
}
|
}
|
||||||
|
|
||||||
get fullAddressPart2(): string {
|
get fullAddressPart2(): string | undefined {
|
||||||
if (this.city == null && this.state == null && this.postalCode == null) {
|
if (this.city == null && this.state == null && this.postalCode == null) {
|
||||||
return null;
|
return undefined;
|
||||||
}
|
}
|
||||||
const city = this.city || "-";
|
const city = this.city || "-";
|
||||||
const state = this.state;
|
const state = this.state;
|
||||||
@@ -146,7 +144,7 @@ export class IdentityView extends ItemView implements SdkIdentityView {
|
|||||||
return addressPart2;
|
return addressPart2;
|
||||||
}
|
}
|
||||||
|
|
||||||
get fullAddressForCopy(): string {
|
get fullAddressForCopy(): string | undefined {
|
||||||
let address = this.fullAddress;
|
let address = this.fullAddress;
|
||||||
if (this.city != null || this.state != null || this.postalCode != null) {
|
if (this.city != null || this.state != null || this.postalCode != null) {
|
||||||
address += "\n" + this.fullAddressPart2;
|
address += "\n" + this.fullAddressPart2;
|
||||||
@@ -157,38 +155,34 @@ export class IdentityView extends ItemView implements SdkIdentityView {
|
|||||||
return address;
|
return address;
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromJSON(obj: Partial<Jsonify<IdentityView>>): IdentityView {
|
static fromJSON(obj: Partial<Jsonify<IdentityView>> | undefined): IdentityView {
|
||||||
return Object.assign(new IdentityView(), obj);
|
return Object.assign(new IdentityView(), obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts the SDK IdentityView to an IdentityView.
|
* Converts the SDK IdentityView to an IdentityView.
|
||||||
*/
|
*/
|
||||||
static fromSdkIdentityView(obj: SdkIdentityView): IdentityView | undefined {
|
static fromSdkIdentityView(obj: SdkIdentityView): IdentityView {
|
||||||
if (obj == null) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
const identityView = new IdentityView();
|
const identityView = new IdentityView();
|
||||||
|
|
||||||
identityView.title = obj.title ?? null;
|
identityView.title = obj.title;
|
||||||
identityView.firstName = obj.firstName ?? null;
|
identityView.firstName = obj.firstName;
|
||||||
identityView.middleName = obj.middleName ?? null;
|
identityView.middleName = obj.middleName;
|
||||||
identityView.lastName = obj.lastName ?? null;
|
identityView.lastName = obj.lastName;
|
||||||
identityView.address1 = obj.address1 ?? null;
|
identityView.address1 = obj.address1;
|
||||||
identityView.address2 = obj.address2 ?? null;
|
identityView.address2 = obj.address2;
|
||||||
identityView.address3 = obj.address3 ?? null;
|
identityView.address3 = obj.address3;
|
||||||
identityView.city = obj.city ?? null;
|
identityView.city = obj.city;
|
||||||
identityView.state = obj.state ?? null;
|
identityView.state = obj.state;
|
||||||
identityView.postalCode = obj.postalCode ?? null;
|
identityView.postalCode = obj.postalCode;
|
||||||
identityView.country = obj.country ?? null;
|
identityView.country = obj.country;
|
||||||
identityView.company = obj.company ?? null;
|
identityView.company = obj.company;
|
||||||
identityView.email = obj.email ?? null;
|
identityView.email = obj.email;
|
||||||
identityView.phone = obj.phone ?? null;
|
identityView.phone = obj.phone;
|
||||||
identityView.ssn = obj.ssn ?? null;
|
identityView.ssn = obj.ssn;
|
||||||
identityView.username = obj.username ?? null;
|
identityView.username = obj.username;
|
||||||
identityView.passportNumber = obj.passportNumber ?? null;
|
identityView.passportNumber = obj.passportNumber;
|
||||||
identityView.licenseNumber = obj.licenseNumber ?? null;
|
identityView.licenseNumber = obj.licenseNumber;
|
||||||
|
|
||||||
return identityView;
|
return identityView;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
// FIXME: Update this file to be type safe and remove this and next line
|
|
||||||
// @ts-strict-ignore
|
|
||||||
import { View } from "../../../models/view/view";
|
import { View } from "../../../models/view/view";
|
||||||
import { LinkedMetadata } from "../../linked-field-option.decorator";
|
import { LinkedMetadata } from "../../linked-field-option.decorator";
|
||||||
|
|
||||||
export abstract class ItemView implements View {
|
export abstract class ItemView implements View {
|
||||||
linkedFieldOptions: Map<number, LinkedMetadata>;
|
linkedFieldOptions?: Map<number, LinkedMetadata>;
|
||||||
abstract get subTitle(): string;
|
abstract get subTitle(): string | undefined;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
// FIXME: Update this file to be type safe and remove this and next line
|
|
||||||
// @ts-strict-ignore
|
|
||||||
import { Jsonify } from "type-fest";
|
import { Jsonify } from "type-fest";
|
||||||
|
|
||||||
import { LoginUriView as SdkLoginUriView } from "@bitwarden/sdk-internal";
|
import { LoginUriView as SdkLoginUriView } from "@bitwarden/sdk-internal";
|
||||||
@@ -11,13 +9,13 @@ import { Utils } from "../../../platform/misc/utils";
|
|||||||
import { LoginUri } from "../domain/login-uri";
|
import { LoginUri } from "../domain/login-uri";
|
||||||
|
|
||||||
export class LoginUriView implements View {
|
export class LoginUriView implements View {
|
||||||
match: UriMatchStrategySetting = null;
|
match?: UriMatchStrategySetting;
|
||||||
|
|
||||||
private _uri: string = null;
|
private _uri?: string;
|
||||||
private _domain: string = null;
|
private _domain?: string;
|
||||||
private _hostname: string = null;
|
private _hostname?: string;
|
||||||
private _host: string = null;
|
private _host?: string;
|
||||||
private _canLaunch: boolean = null;
|
private _canLaunch?: boolean;
|
||||||
|
|
||||||
constructor(u?: LoginUri) {
|
constructor(u?: LoginUri) {
|
||||||
if (!u) {
|
if (!u) {
|
||||||
@@ -27,59 +25,59 @@ export class LoginUriView implements View {
|
|||||||
this.match = u.match;
|
this.match = u.match;
|
||||||
}
|
}
|
||||||
|
|
||||||
get uri(): string {
|
get uri(): string | undefined {
|
||||||
return this._uri;
|
return this._uri;
|
||||||
}
|
}
|
||||||
set uri(value: string) {
|
set uri(value: string | undefined) {
|
||||||
this._uri = value;
|
this._uri = value;
|
||||||
this._domain = null;
|
this._domain = undefined;
|
||||||
this._canLaunch = null;
|
this._canLaunch = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
get domain(): string {
|
get domain(): string | undefined {
|
||||||
if (this._domain == null && this.uri != null) {
|
if (this._domain == null && this.uri != null) {
|
||||||
this._domain = Utils.getDomain(this.uri);
|
this._domain = Utils.getDomain(this.uri);
|
||||||
if (this._domain === "") {
|
if (this._domain === "") {
|
||||||
this._domain = null;
|
this._domain = undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return this._domain;
|
return this._domain;
|
||||||
}
|
}
|
||||||
|
|
||||||
get hostname(): string {
|
get hostname(): string | undefined {
|
||||||
if (this.match === UriMatchStrategy.RegularExpression) {
|
if (this.match === UriMatchStrategy.RegularExpression) {
|
||||||
return null;
|
return undefined;
|
||||||
}
|
}
|
||||||
if (this._hostname == null && this.uri != null) {
|
if (this._hostname == null && this.uri != null) {
|
||||||
this._hostname = Utils.getHostname(this.uri);
|
this._hostname = Utils.getHostname(this.uri);
|
||||||
if (this._hostname === "") {
|
if (this._hostname === "") {
|
||||||
this._hostname = null;
|
this._hostname = undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return this._hostname;
|
return this._hostname;
|
||||||
}
|
}
|
||||||
|
|
||||||
get host(): string {
|
get host(): string | undefined {
|
||||||
if (this.match === UriMatchStrategy.RegularExpression) {
|
if (this.match === UriMatchStrategy.RegularExpression) {
|
||||||
return null;
|
return undefined;
|
||||||
}
|
}
|
||||||
if (this._host == null && this.uri != null) {
|
if (this._host == null && this.uri != null) {
|
||||||
this._host = Utils.getHost(this.uri);
|
this._host = Utils.getHost(this.uri);
|
||||||
if (this._host === "") {
|
if (this._host === "") {
|
||||||
this._host = null;
|
this._host = undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return this._host;
|
return this._host;
|
||||||
}
|
}
|
||||||
|
|
||||||
get hostnameOrUri(): string {
|
get hostnameOrUri(): string | undefined {
|
||||||
return this.hostname != null ? this.hostname : this.uri;
|
return this.hostname != null ? this.hostname : this.uri;
|
||||||
}
|
}
|
||||||
|
|
||||||
get hostOrUri(): string {
|
get hostOrUri(): string | undefined {
|
||||||
return this.host != null ? this.host : this.uri;
|
return this.host != null ? this.host : this.uri;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,7 +102,10 @@ export class LoginUriView implements View {
|
|||||||
return this._canLaunch;
|
return this._canLaunch;
|
||||||
}
|
}
|
||||||
|
|
||||||
get launchUri(): string {
|
get launchUri(): string | undefined {
|
||||||
|
if (this.uri == null) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
return this.uri.indexOf("://") < 0 && !Utils.isNullOrWhitespace(Utils.getDomain(this.uri))
|
return this.uri.indexOf("://") < 0 && !Utils.isNullOrWhitespace(Utils.getDomain(this.uri))
|
||||||
? "http://" + this.uri
|
? "http://" + this.uri
|
||||||
: this.uri;
|
: this.uri;
|
||||||
@@ -141,7 +142,7 @@ export class LoginUriView implements View {
|
|||||||
matchesUri(
|
matchesUri(
|
||||||
targetUri: string,
|
targetUri: string,
|
||||||
equivalentDomains: Set<string>,
|
equivalentDomains: Set<string>,
|
||||||
defaultUriMatch: UriMatchStrategySetting = null,
|
defaultUriMatch?: UriMatchStrategySetting,
|
||||||
/** When present, will override the match strategy for the cipher if it is `Never` with `Domain` */
|
/** When present, will override the match strategy for the cipher if it is `Never` with `Domain` */
|
||||||
overrideNeverMatchStrategy?: true,
|
overrideNeverMatchStrategy?: true,
|
||||||
): boolean {
|
): boolean {
|
||||||
@@ -198,7 +199,7 @@ export class LoginUriView implements View {
|
|||||||
|
|
||||||
if (Utils.DomainMatchBlacklist.has(this.domain)) {
|
if (Utils.DomainMatchBlacklist.has(this.domain)) {
|
||||||
const domainUrlHost = Utils.getHost(targetUri);
|
const domainUrlHost = Utils.getHost(targetUri);
|
||||||
return !Utils.DomainMatchBlacklist.get(this.domain).has(domainUrlHost);
|
return !Utils.DomainMatchBlacklist.get(this.domain)!.has(domainUrlHost);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -29,11 +29,6 @@ describe("LoginView", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("fromSdkLoginView", () => {
|
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", () => {
|
it("should return a LoginView from an SdkLoginView", () => {
|
||||||
jest.spyOn(LoginUriView, "fromSdkLoginUriView").mockImplementation(mockFromSdk);
|
jest.spyOn(LoginUriView, "fromSdkLoginUriView").mockImplementation(mockFromSdk);
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
// 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 { LoginView as SdkLoginView } from "@bitwarden/sdk-internal";
|
||||||
|
|
||||||
import { UriMatchStrategySetting } from "../../../models/domain/domain-service";
|
import { UriMatchStrategySetting } from "../../../models/domain/domain-service";
|
||||||
@@ -15,15 +13,15 @@ import { LoginUriView } from "./login-uri.view";
|
|||||||
|
|
||||||
export class LoginView extends ItemView {
|
export class LoginView extends ItemView {
|
||||||
@linkedFieldOption(LinkedId.Username, { sortPosition: 0 })
|
@linkedFieldOption(LinkedId.Username, { sortPosition: 0 })
|
||||||
username: string = null;
|
username: string | undefined;
|
||||||
@linkedFieldOption(LinkedId.Password, { sortPosition: 1 })
|
@linkedFieldOption(LinkedId.Password, { sortPosition: 1 })
|
||||||
password: string = null;
|
password: string | undefined;
|
||||||
|
|
||||||
passwordRevisionDate?: Date = null;
|
passwordRevisionDate?: Date;
|
||||||
totp: string = null;
|
totp: string | undefined;
|
||||||
uris: LoginUriView[] = [];
|
uris: LoginUriView[] = [];
|
||||||
autofillOnPageLoad: boolean = null;
|
autofillOnPageLoad: boolean | undefined;
|
||||||
fido2Credentials: Fido2CredentialView[] = null;
|
fido2Credentials: Fido2CredentialView[] = [];
|
||||||
|
|
||||||
constructor(l?: Login) {
|
constructor(l?: Login) {
|
||||||
super();
|
super();
|
||||||
@@ -35,15 +33,15 @@ export class LoginView extends ItemView {
|
|||||||
this.autofillOnPageLoad = l.autofillOnPageLoad;
|
this.autofillOnPageLoad = l.autofillOnPageLoad;
|
||||||
}
|
}
|
||||||
|
|
||||||
get uri(): string {
|
get uri(): string | undefined {
|
||||||
return this.hasUris ? this.uris[0].uri : null;
|
return this.hasUris ? this.uris[0].uri : undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
get maskedPassword(): string {
|
get maskedPassword(): string | undefined {
|
||||||
return this.password != null ? "••••••••" : null;
|
return this.password != null ? "••••••••" : undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
get subTitle(): string {
|
get subTitle(): string | undefined {
|
||||||
// if there's a passkey available, use that as a fallback
|
// if there's a passkey available, use that as a fallback
|
||||||
if (Utils.isNullOrEmpty(this.username) && this.fido2Credentials?.length > 0) {
|
if (Utils.isNullOrEmpty(this.username) && this.fido2Credentials?.length > 0) {
|
||||||
return this.fido2Credentials[0].userName;
|
return this.fido2Credentials[0].userName;
|
||||||
@@ -60,14 +58,14 @@ export class LoginView extends ItemView {
|
|||||||
return !Utils.isNullOrWhitespace(this.totp);
|
return !Utils.isNullOrWhitespace(this.totp);
|
||||||
}
|
}
|
||||||
|
|
||||||
get launchUri(): string {
|
get launchUri(): string | undefined {
|
||||||
if (this.hasUris) {
|
if (this.hasUris) {
|
||||||
const uri = this.uris.find((u) => u.canLaunch);
|
const uri = this.uris.find((u) => u.canLaunch);
|
||||||
if (uri != null) {
|
if (uri != null) {
|
||||||
return uri.launchUri;
|
return uri.launchUri;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
get hasUris(): boolean {
|
get hasUris(): boolean {
|
||||||
@@ -81,7 +79,7 @@ export class LoginView extends ItemView {
|
|||||||
matchesUri(
|
matchesUri(
|
||||||
targetUri: string,
|
targetUri: string,
|
||||||
equivalentDomains: Set<string>,
|
equivalentDomains: Set<string>,
|
||||||
defaultUriMatch: UriMatchStrategySetting = null,
|
defaultUriMatch?: UriMatchStrategySetting,
|
||||||
/** When present, will override the match strategy for the cipher if it is `Never` with `Domain` */
|
/** When present, will override the match strategy for the cipher if it is `Never` with `Domain` */
|
||||||
overrideNeverMatchStrategy?: true,
|
overrideNeverMatchStrategy?: true,
|
||||||
): boolean {
|
): boolean {
|
||||||
@@ -94,17 +92,20 @@ export class LoginView extends ItemView {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromJSON(obj: Partial<DeepJsonify<LoginView>>): LoginView {
|
static fromJSON(obj: Partial<DeepJsonify<LoginView>> | undefined): LoginView {
|
||||||
const passwordRevisionDate =
|
if (obj == undefined) {
|
||||||
obj.passwordRevisionDate == null ? null : new Date(obj.passwordRevisionDate);
|
return new LoginView();
|
||||||
const uris = obj.uris.map((uri) => LoginUriView.fromJSON(uri));
|
}
|
||||||
const fido2Credentials = obj.fido2Credentials?.map((key) => Fido2CredentialView.fromJSON(key));
|
|
||||||
|
|
||||||
return Object.assign(new LoginView(), obj, {
|
const loginView = Object.assign(new LoginView(), obj) as LoginView;
|
||||||
passwordRevisionDate,
|
|
||||||
uris,
|
loginView.passwordRevisionDate =
|
||||||
fido2Credentials,
|
obj.passwordRevisionDate == null ? undefined : new Date(obj.passwordRevisionDate);
|
||||||
});
|
loginView.uris = obj.uris?.map((uri) => LoginUriView.fromJSON(uri)) ?? [];
|
||||||
|
loginView.fido2Credentials =
|
||||||
|
obj.fido2Credentials?.map((key) => Fido2CredentialView.fromJSON(key)) ?? [];
|
||||||
|
|
||||||
|
return loginView;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -115,25 +116,21 @@ export class LoginView extends ItemView {
|
|||||||
* the FIDO2 credentials in encrypted form. We can decrypt them later using a separate
|
* the FIDO2 credentials in encrypted form. We can decrypt them later using a separate
|
||||||
* call to client.vault().ciphers().decrypt_fido2_credentials().
|
* call to client.vault().ciphers().decrypt_fido2_credentials().
|
||||||
*/
|
*/
|
||||||
static fromSdkLoginView(obj: SdkLoginView): LoginView | undefined {
|
static fromSdkLoginView(obj: SdkLoginView): LoginView {
|
||||||
if (obj == null) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
const loginView = new LoginView();
|
const loginView = new LoginView();
|
||||||
|
|
||||||
loginView.username = obj.username ?? null;
|
loginView.username = obj.username;
|
||||||
loginView.password = obj.password ?? null;
|
loginView.password = obj.password;
|
||||||
loginView.passwordRevisionDate =
|
loginView.passwordRevisionDate =
|
||||||
obj.passwordRevisionDate == null ? null : new Date(obj.passwordRevisionDate);
|
obj.passwordRevisionDate == null ? undefined : new Date(obj.passwordRevisionDate);
|
||||||
loginView.totp = obj.totp ?? null;
|
loginView.totp = obj.totp;
|
||||||
loginView.autofillOnPageLoad = obj.autofillOnPageLoad ?? null;
|
loginView.autofillOnPageLoad = obj.autofillOnPageLoad;
|
||||||
loginView.uris =
|
loginView.uris =
|
||||||
obj.uris
|
obj.uris
|
||||||
?.filter((uri) => uri.uri != null && uri.uri !== "")
|
?.filter((uri) => uri.uri != null && uri.uri !== "")
|
||||||
.map((uri) => LoginUriView.fromSdkLoginUriView(uri)) || [];
|
.map((uri) => LoginUriView.fromSdkLoginUriView(uri)!) || [];
|
||||||
// FIDO2 credentials are not decrypted here, they remain encrypted
|
// FIDO2 credentials are not decrypted here, they remain encrypted
|
||||||
loginView.fido2Credentials = null;
|
loginView.fido2Credentials = [];
|
||||||
|
|
||||||
return loginView;
|
return loginView;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
// FIXME: Update this file to be type safe and remove this and next line
|
|
||||||
// @ts-strict-ignore
|
|
||||||
import { Jsonify } from "type-fest";
|
import { Jsonify } from "type-fest";
|
||||||
|
|
||||||
import { SecureNoteView as SdkSecureNoteView } from "@bitwarden/sdk-internal";
|
import { SecureNoteView as SdkSecureNoteView } from "@bitwarden/sdk-internal";
|
||||||
@@ -10,7 +8,7 @@ import { SecureNote } from "../domain/secure-note";
|
|||||||
import { ItemView } from "./item.view";
|
import { ItemView } from "./item.view";
|
||||||
|
|
||||||
export class SecureNoteView extends ItemView implements SdkSecureNoteView {
|
export class SecureNoteView extends ItemView implements SdkSecureNoteView {
|
||||||
type: SecureNoteType = null;
|
type: SecureNoteType = SecureNoteType.Generic;
|
||||||
|
|
||||||
constructor(n?: SecureNote) {
|
constructor(n?: SecureNote) {
|
||||||
super();
|
super();
|
||||||
@@ -21,24 +19,20 @@ export class SecureNoteView extends ItemView implements SdkSecureNoteView {
|
|||||||
this.type = n.type;
|
this.type = n.type;
|
||||||
}
|
}
|
||||||
|
|
||||||
get subTitle(): string {
|
get subTitle(): string | undefined {
|
||||||
return null;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromJSON(obj: Partial<Jsonify<SecureNoteView>>): SecureNoteView {
|
static fromJSON(obj: Partial<Jsonify<SecureNoteView>> | undefined): SecureNoteView {
|
||||||
return Object.assign(new SecureNoteView(), obj);
|
return Object.assign(new SecureNoteView(), obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts the SDK SecureNoteView to a SecureNoteView.
|
* Converts the SDK SecureNoteView to a SecureNoteView.
|
||||||
*/
|
*/
|
||||||
static fromSdkSecureNoteView(obj: SdkSecureNoteView): SecureNoteView | undefined {
|
static fromSdkSecureNoteView(obj: SdkSecureNoteView): SecureNoteView {
|
||||||
if (!obj) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
const secureNoteView = new SecureNoteView();
|
const secureNoteView = new SecureNoteView();
|
||||||
secureNoteView.type = obj.type ?? null;
|
secureNoteView.type = obj.type;
|
||||||
|
|
||||||
return secureNoteView;
|
return secureNoteView;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,24 +1,13 @@
|
|||||||
// FIXME: Update this file to be type safe and remove this and next line
|
|
||||||
// @ts-strict-ignore
|
|
||||||
import { Jsonify } from "type-fest";
|
import { Jsonify } from "type-fest";
|
||||||
|
|
||||||
import { SshKeyView as SdkSshKeyView } from "@bitwarden/sdk-internal";
|
import { SshKeyView as SdkSshKeyView } from "@bitwarden/sdk-internal";
|
||||||
|
|
||||||
import { SshKey } from "../domain/ssh-key";
|
|
||||||
|
|
||||||
import { ItemView } from "./item.view";
|
import { ItemView } from "./item.view";
|
||||||
|
|
||||||
export class SshKeyView extends ItemView {
|
export class SshKeyView extends ItemView {
|
||||||
privateKey: string = null;
|
privateKey!: string;
|
||||||
publicKey: string = null;
|
publicKey!: string;
|
||||||
keyFingerprint: string = null;
|
keyFingerprint!: string;
|
||||||
|
|
||||||
constructor(n?: SshKey) {
|
|
||||||
super();
|
|
||||||
if (!n) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
get maskedPrivateKey(): string {
|
get maskedPrivateKey(): string {
|
||||||
if (!this.privateKey || this.privateKey.length === 0) {
|
if (!this.privateKey || this.privateKey.length === 0) {
|
||||||
@@ -43,23 +32,19 @@ export class SshKeyView extends ItemView {
|
|||||||
return this.keyFingerprint;
|
return this.keyFingerprint;
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromJSON(obj: Partial<Jsonify<SshKeyView>>): SshKeyView {
|
static fromJSON(obj: Partial<Jsonify<SshKeyView>> | undefined): SshKeyView {
|
||||||
return Object.assign(new SshKeyView(), obj);
|
return Object.assign(new SshKeyView(), obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts the SDK SshKeyView to a SshKeyView.
|
* Converts the SDK SshKeyView to a SshKeyView.
|
||||||
*/
|
*/
|
||||||
static fromSdkSshKeyView(obj: SdkSshKeyView): SshKeyView | undefined {
|
static fromSdkSshKeyView(obj: SdkSshKeyView): SshKeyView {
|
||||||
if (!obj) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
const sshKeyView = new SshKeyView();
|
const sshKeyView = new SshKeyView();
|
||||||
|
|
||||||
sshKeyView.privateKey = obj.privateKey ?? null;
|
sshKeyView.privateKey = obj.privateKey;
|
||||||
sshKeyView.publicKey = obj.publicKey ?? null;
|
sshKeyView.publicKey = obj.publicKey;
|
||||||
sshKeyView.keyFingerprint = obj.fingerprint ?? null;
|
sshKeyView.keyFingerprint = obj.fingerprint;
|
||||||
|
|
||||||
return sshKeyView;
|
return sshKeyView;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -298,6 +298,10 @@ describe("CipherViewLikeUtils", () => {
|
|||||||
(cipherView.attachments as any) = null;
|
(cipherView.attachments as any) = null;
|
||||||
|
|
||||||
expect(CipherViewLikeUtils.hasAttachments(cipherView)).toBe(false);
|
expect(CipherViewLikeUtils.hasAttachments(cipherView)).toBe(false);
|
||||||
|
|
||||||
|
cipherView.attachments = [];
|
||||||
|
|
||||||
|
expect(CipherViewLikeUtils.hasAttachments(cipherView)).toBe(false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -193,7 +193,6 @@ export abstract class BaseImporter {
|
|||||||
if (this.isNullOrWhitespace(loginUri.uri)) {
|
if (this.isNullOrWhitespace(loginUri.uri)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
loginUri.match = null;
|
|
||||||
return [loginUri];
|
return [loginUri];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -205,7 +204,6 @@ export abstract class BaseImporter {
|
|||||||
if (this.isNullOrWhitespace(loginUri.uri)) {
|
if (this.isNullOrWhitespace(loginUri.uri)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
loginUri.match = null;
|
|
||||||
returnArr.push(loginUri);
|
returnArr.push(loginUri);
|
||||||
});
|
});
|
||||||
return returnArr.length === 0 ? null : returnArr;
|
return returnArr.length === 0 ? null : returnArr;
|
||||||
@@ -236,7 +234,7 @@ export abstract class BaseImporter {
|
|||||||
return hostname.startsWith("www.") ? hostname.replace("www.", "") : hostname;
|
return hostname.startsWith("www.") ? hostname.replace("www.", "") : hostname;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected isNullOrWhitespace(str: string): boolean {
|
protected isNullOrWhitespace(str: string | undefined | null): boolean {
|
||||||
return Utils.isNullOrWhitespace(str);
|
return Utils.isNullOrWhitespace(str);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,9 +11,6 @@ const CipherData = [
|
|||||||
title: "should parse app name",
|
title: "should parse app name",
|
||||||
csv: androidData,
|
csv: androidData,
|
||||||
expected: Object.assign(new CipherView(), {
|
expected: Object.assign(new CipherView(), {
|
||||||
id: null,
|
|
||||||
organizationId: null,
|
|
||||||
folderId: null,
|
|
||||||
name: "com.xyz.example.app.android",
|
name: "com.xyz.example.app.android",
|
||||||
login: Object.assign(new LoginView(), {
|
login: Object.assign(new LoginView(), {
|
||||||
username: "username@example.com",
|
username: "username@example.com",
|
||||||
@@ -24,7 +21,6 @@ const CipherData = [
|
|||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
notes: null,
|
|
||||||
type: 1,
|
type: 1,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
@@ -32,9 +28,6 @@ const CipherData = [
|
|||||||
title: "should parse password",
|
title: "should parse password",
|
||||||
csv: simplePasswordData,
|
csv: simplePasswordData,
|
||||||
expected: Object.assign(new CipherView(), {
|
expected: Object.assign(new CipherView(), {
|
||||||
id: null,
|
|
||||||
organizationId: null,
|
|
||||||
folderId: null,
|
|
||||||
name: "www.example.com",
|
name: "www.example.com",
|
||||||
login: Object.assign(new LoginView(), {
|
login: Object.assign(new LoginView(), {
|
||||||
username: "username@example.com",
|
username: "username@example.com",
|
||||||
@@ -45,7 +38,6 @@ const CipherData = [
|
|||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
notes: null,
|
|
||||||
type: 1,
|
type: 1,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
@@ -54,6 +46,7 @@ const CipherData = [
|
|||||||
describe("Chrome CSV Importer", () => {
|
describe("Chrome CSV Importer", () => {
|
||||||
CipherData.forEach((data) => {
|
CipherData.forEach((data) => {
|
||||||
it(data.title, async () => {
|
it(data.title, async () => {
|
||||||
|
jest.useFakeTimers().setSystemTime(data.expected.creationDate);
|
||||||
const importer = new ChromeCsvImporter();
|
const importer = new ChromeCsvImporter();
|
||||||
const result = await importer.parse(data.csv);
|
const result = await importer.parse(data.csv);
|
||||||
expect(result != null).toBe(true);
|
expect(result != null).toBe(true);
|
||||||
|
|||||||
@@ -59,12 +59,12 @@ describe("Dashlane CSV Importer", () => {
|
|||||||
const cipher = result.ciphers.shift();
|
const cipher = result.ciphers.shift();
|
||||||
expect(cipher.type).toBe(CipherType.Card);
|
expect(cipher.type).toBe(CipherType.Card);
|
||||||
expect(cipher.name).toBe("John's savings account");
|
expect(cipher.name).toBe("John's savings account");
|
||||||
expect(cipher.card.brand).toBeNull();
|
expect(cipher.card.brand).toBeUndefined();
|
||||||
expect(cipher.card.cardholderName).toBe("John Doe");
|
expect(cipher.card.cardholderName).toBe("John Doe");
|
||||||
expect(cipher.card.number).toBe("accountNumber");
|
expect(cipher.card.number).toBe("accountNumber");
|
||||||
expect(cipher.card.code).toBeNull();
|
expect(cipher.card.code).toBeUndefined();
|
||||||
expect(cipher.card.expMonth).toBeNull();
|
expect(cipher.card.expMonth).toBeUndefined();
|
||||||
expect(cipher.card.expYear).toBeNull();
|
expect(cipher.card.expYear).toBeUndefined();
|
||||||
|
|
||||||
expect(cipher.fields.length).toBe(4);
|
expect(cipher.fields.length).toBe(4);
|
||||||
|
|
||||||
@@ -112,7 +112,7 @@ describe("Dashlane CSV Importer", () => {
|
|||||||
expect(cipher.name).toBe("John Doe card");
|
expect(cipher.name).toBe("John Doe card");
|
||||||
expect(cipher.identity.fullName).toBe("John Doe");
|
expect(cipher.identity.fullName).toBe("John Doe");
|
||||||
expect(cipher.identity.firstName).toBe("John");
|
expect(cipher.identity.firstName).toBe("John");
|
||||||
expect(cipher.identity.middleName).toBeNull();
|
expect(cipher.identity.middleName).toBeUndefined();
|
||||||
expect(cipher.identity.lastName).toBe("Doe");
|
expect(cipher.identity.lastName).toBe("Doe");
|
||||||
expect(cipher.identity.licenseNumber).toBe("123123123");
|
expect(cipher.identity.licenseNumber).toBe("123123123");
|
||||||
|
|
||||||
@@ -133,7 +133,7 @@ describe("Dashlane CSV Importer", () => {
|
|||||||
expect(cipher2.name).toBe("John Doe passport");
|
expect(cipher2.name).toBe("John Doe passport");
|
||||||
expect(cipher2.identity.fullName).toBe("John Doe");
|
expect(cipher2.identity.fullName).toBe("John Doe");
|
||||||
expect(cipher2.identity.firstName).toBe("John");
|
expect(cipher2.identity.firstName).toBe("John");
|
||||||
expect(cipher2.identity.middleName).toBeNull();
|
expect(cipher2.identity.middleName).toBeUndefined();
|
||||||
expect(cipher2.identity.lastName).toBe("Doe");
|
expect(cipher2.identity.lastName).toBe("Doe");
|
||||||
expect(cipher2.identity.passportNumber).toBe("123123123");
|
expect(cipher2.identity.passportNumber).toBe("123123123");
|
||||||
|
|
||||||
@@ -154,7 +154,7 @@ describe("Dashlane CSV Importer", () => {
|
|||||||
expect(cipher3.name).toBe("John Doe license");
|
expect(cipher3.name).toBe("John Doe license");
|
||||||
expect(cipher3.identity.fullName).toBe("John Doe");
|
expect(cipher3.identity.fullName).toBe("John Doe");
|
||||||
expect(cipher3.identity.firstName).toBe("John");
|
expect(cipher3.identity.firstName).toBe("John");
|
||||||
expect(cipher3.identity.middleName).toBeNull();
|
expect(cipher3.identity.middleName).toBeUndefined();
|
||||||
expect(cipher3.identity.lastName).toBe("Doe");
|
expect(cipher3.identity.lastName).toBe("Doe");
|
||||||
expect(cipher3.identity.licenseNumber).toBe("1234556");
|
expect(cipher3.identity.licenseNumber).toBe("1234556");
|
||||||
expect(cipher3.identity.state).toBe("DC");
|
expect(cipher3.identity.state).toBe("DC");
|
||||||
@@ -173,7 +173,7 @@ describe("Dashlane CSV Importer", () => {
|
|||||||
expect(cipher4.name).toBe("John Doe social_security");
|
expect(cipher4.name).toBe("John Doe social_security");
|
||||||
expect(cipher4.identity.fullName).toBe("John Doe");
|
expect(cipher4.identity.fullName).toBe("John Doe");
|
||||||
expect(cipher4.identity.firstName).toBe("John");
|
expect(cipher4.identity.firstName).toBe("John");
|
||||||
expect(cipher4.identity.middleName).toBeNull();
|
expect(cipher4.identity.middleName).toBeUndefined();
|
||||||
expect(cipher4.identity.lastName).toBe("Doe");
|
expect(cipher4.identity.lastName).toBe("Doe");
|
||||||
expect(cipher4.identity.ssn).toBe("123123123");
|
expect(cipher4.identity.ssn).toBe("123123123");
|
||||||
|
|
||||||
|
|||||||
@@ -11,9 +11,6 @@ const CipherData = [
|
|||||||
title: "should parse password",
|
title: "should parse password",
|
||||||
csv: simplePasswordData,
|
csv: simplePasswordData,
|
||||||
expected: Object.assign(new CipherView(), {
|
expected: Object.assign(new CipherView(), {
|
||||||
id: null,
|
|
||||||
organizationId: null,
|
|
||||||
folderId: null,
|
|
||||||
name: "example.com",
|
name: "example.com",
|
||||||
login: Object.assign(new LoginView(), {
|
login: Object.assign(new LoginView(), {
|
||||||
username: "foo",
|
username: "foo",
|
||||||
@@ -24,7 +21,6 @@ const CipherData = [
|
|||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
notes: null,
|
|
||||||
type: 1,
|
type: 1,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
@@ -32,9 +28,6 @@ const CipherData = [
|
|||||||
title: 'should skip "chrome://FirefoxAccounts"',
|
title: 'should skip "chrome://FirefoxAccounts"',
|
||||||
csv: firefoxAccountsData,
|
csv: firefoxAccountsData,
|
||||||
expected: Object.assign(new CipherView(), {
|
expected: Object.assign(new CipherView(), {
|
||||||
id: null,
|
|
||||||
organizationId: null,
|
|
||||||
folderId: null,
|
|
||||||
name: "example.com",
|
name: "example.com",
|
||||||
login: Object.assign(new LoginView(), {
|
login: Object.assign(new LoginView(), {
|
||||||
username: "foo",
|
username: "foo",
|
||||||
@@ -45,7 +38,6 @@ const CipherData = [
|
|||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
notes: null,
|
|
||||||
type: 1,
|
type: 1,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
@@ -54,6 +46,7 @@ const CipherData = [
|
|||||||
describe("Firefox CSV Importer", () => {
|
describe("Firefox CSV Importer", () => {
|
||||||
CipherData.forEach((data) => {
|
CipherData.forEach((data) => {
|
||||||
it(data.title, async () => {
|
it(data.title, async () => {
|
||||||
|
jest.useFakeTimers().setSystemTime(data.expected.creationDate);
|
||||||
const importer = new FirefoxCsvImporter();
|
const importer = new FirefoxCsvImporter();
|
||||||
const result = await importer.parse(data.csv);
|
const result = await importer.parse(data.csv);
|
||||||
expect(result != null).toBe(true);
|
expect(result != null).toBe(true);
|
||||||
|
|||||||
@@ -51,10 +51,10 @@ describe("Keeper CSV Importer", () => {
|
|||||||
expect(result != null).toBe(true);
|
expect(result != null).toBe(true);
|
||||||
|
|
||||||
const cipher = result.ciphers.shift();
|
const cipher = result.ciphers.shift();
|
||||||
expect(cipher.login.totp).toBeNull();
|
expect(cipher.login.totp).toBeUndefined();
|
||||||
|
|
||||||
const cipher2 = result.ciphers.shift();
|
const cipher2 = result.ciphers.shift();
|
||||||
expect(cipher2.login.totp).toBeNull();
|
expect(cipher2.login.totp).toBeUndefined();
|
||||||
|
|
||||||
const cipher3 = result.ciphers.shift();
|
const cipher3 = result.ciphers.shift();
|
||||||
expect(cipher3.login.totp).toEqual(
|
expect(cipher3.login.totp).toEqual(
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ describe("Keeper Json Importer", () => {
|
|||||||
expect(result != null).toBe(true);
|
expect(result != null).toBe(true);
|
||||||
|
|
||||||
const cipher = result.ciphers.shift();
|
const cipher = result.ciphers.shift();
|
||||||
expect(cipher.login.totp).toBeNull();
|
expect(cipher.login.totp).toBeUndefined();
|
||||||
|
|
||||||
// 2nd Cipher
|
// 2nd Cipher
|
||||||
const cipher2 = result.ciphers.shift();
|
const cipher2 = result.ciphers.shift();
|
||||||
|
|||||||
@@ -37,9 +37,6 @@ Expiration Date:June,2020
|
|||||||
Notes:some text
|
Notes:some text
|
||||||
",Credit-card,,0`,
|
",Credit-card,,0`,
|
||||||
expected: Object.assign(new CipherView(), {
|
expected: Object.assign(new CipherView(), {
|
||||||
id: null,
|
|
||||||
organizationId: null,
|
|
||||||
folderId: null,
|
|
||||||
name: "Credit-card",
|
name: "Credit-card",
|
||||||
notes: "some text\n",
|
notes: "some text\n",
|
||||||
type: 3,
|
type: 3,
|
||||||
@@ -71,11 +68,7 @@ Start Date:,
|
|||||||
Expiration Date:,
|
Expiration Date:,
|
||||||
Notes:",empty,,0`,
|
Notes:",empty,,0`,
|
||||||
expected: Object.assign(new CipherView(), {
|
expected: Object.assign(new CipherView(), {
|
||||||
id: null,
|
|
||||||
organizationId: null,
|
|
||||||
folderId: null,
|
|
||||||
name: "empty",
|
name: "empty",
|
||||||
notes: null,
|
|
||||||
type: 3,
|
type: 3,
|
||||||
card: {
|
card: {
|
||||||
expMonth: undefined,
|
expMonth: undefined,
|
||||||
@@ -101,11 +94,7 @@ Start Date:,
|
|||||||
Expiration Date:January,
|
Expiration Date:January,
|
||||||
Notes:",noyear,,0`,
|
Notes:",noyear,,0`,
|
||||||
expected: Object.assign(new CipherView(), {
|
expected: Object.assign(new CipherView(), {
|
||||||
id: null,
|
|
||||||
organizationId: null,
|
|
||||||
folderId: null,
|
|
||||||
name: "noyear",
|
name: "noyear",
|
||||||
notes: null,
|
|
||||||
type: 3,
|
type: 3,
|
||||||
card: {
|
card: {
|
||||||
cardholderName: "John Doe",
|
cardholderName: "John Doe",
|
||||||
@@ -139,11 +128,7 @@ Start Date:,
|
|||||||
Expiration Date:,2020
|
Expiration Date:,2020
|
||||||
Notes:",nomonth,,0`,
|
Notes:",nomonth,,0`,
|
||||||
expected: Object.assign(new CipherView(), {
|
expected: Object.assign(new CipherView(), {
|
||||||
id: null,
|
|
||||||
organizationId: null,
|
|
||||||
folderId: null,
|
|
||||||
name: "nomonth",
|
name: "nomonth",
|
||||||
notes: null,
|
|
||||||
type: 3,
|
type: 3,
|
||||||
card: {
|
card: {
|
||||||
cardholderName: "John Doe",
|
cardholderName: "John Doe",
|
||||||
@@ -171,6 +156,7 @@ Notes:",nomonth,,0`,
|
|||||||
describe("Lastpass CSV Importer", () => {
|
describe("Lastpass CSV Importer", () => {
|
||||||
CipherData.forEach((data) => {
|
CipherData.forEach((data) => {
|
||||||
it(data.title, async () => {
|
it(data.title, async () => {
|
||||||
|
jest.useFakeTimers().setSystemTime(data.expected.creationDate);
|
||||||
const importer = new LastPassCsvImporter();
|
const importer = new LastPassCsvImporter();
|
||||||
const result = await importer.parse(data.csv);
|
const result = await importer.parse(data.csv);
|
||||||
expect(result != null).toBe(true);
|
expect(result != null).toBe(true);
|
||||||
|
|||||||
@@ -468,8 +468,8 @@ describe("Myki CSV Importer", () => {
|
|||||||
const cipher = result.ciphers.shift();
|
const cipher = result.ciphers.shift();
|
||||||
|
|
||||||
expect(cipher.name).toEqual("2FA nickname");
|
expect(cipher.name).toEqual("2FA nickname");
|
||||||
expect(cipher.login.username).toBeNull();
|
expect(cipher.login.username).toBeUndefined();
|
||||||
expect(cipher.login.password).toBeNull();
|
expect(cipher.login.password).toBeUndefined();
|
||||||
expect(cipher.login.totp).toBe("someTOTPSeed");
|
expect(cipher.login.totp).toBe("someTOTPSeed");
|
||||||
expect(cipher.notes).toEqual("Additional information field content.");
|
expect(cipher.notes).toEqual("Additional information field content.");
|
||||||
|
|
||||||
|
|||||||
@@ -17,8 +17,8 @@ const namesTestData = [
|
|||||||
fullName: "MyFirstName",
|
fullName: "MyFirstName",
|
||||||
expected: Object.assign(new IdentityView(), {
|
expected: Object.assign(new IdentityView(), {
|
||||||
firstName: "MyFirstName",
|
firstName: "MyFirstName",
|
||||||
middleName: null,
|
middleName: undefined,
|
||||||
lastName: null,
|
lastName: undefined,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -26,7 +26,7 @@ const namesTestData = [
|
|||||||
fullName: "MyFirstName MyLastName",
|
fullName: "MyFirstName MyLastName",
|
||||||
expected: Object.assign(new IdentityView(), {
|
expected: Object.assign(new IdentityView(), {
|
||||||
firstName: "MyFirstName",
|
firstName: "MyFirstName",
|
||||||
middleName: null,
|
middleName: undefined,
|
||||||
lastName: "MyLastName",
|
lastName: "MyLastName",
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -393,7 +393,7 @@ describe("1Password 1Pux Importer", () => {
|
|||||||
|
|
||||||
const identity = cipher.identity;
|
const identity = cipher.identity;
|
||||||
expect(identity.firstName).toEqual("Michael");
|
expect(identity.firstName).toEqual("Michael");
|
||||||
expect(identity.middleName).toBeNull();
|
expect(identity.middleName).toBeUndefined();
|
||||||
expect(identity.lastName).toEqual("Scarn");
|
expect(identity.lastName).toEqual("Scarn");
|
||||||
expect(identity.address1).toEqual("2120 Mifflin Rd.");
|
expect(identity.address1).toEqual("2120 Mifflin Rd.");
|
||||||
expect(identity.state).toEqual("Pennsylvania");
|
expect(identity.state).toEqual("Pennsylvania");
|
||||||
@@ -423,7 +423,7 @@ describe("1Password 1Pux Importer", () => {
|
|||||||
|
|
||||||
const identity = cipher.identity;
|
const identity = cipher.identity;
|
||||||
expect(identity.firstName).toEqual("Cash");
|
expect(identity.firstName).toEqual("Cash");
|
||||||
expect(identity.middleName).toBeNull();
|
expect(identity.middleName).toBeUndefined();
|
||||||
expect(identity.lastName).toEqual("Bandit");
|
expect(identity.lastName).toEqual("Bandit");
|
||||||
expect(identity.state).toEqual("Washington");
|
expect(identity.state).toEqual("Washington");
|
||||||
expect(identity.country).toEqual("United States of America");
|
expect(identity.country).toEqual("United States of America");
|
||||||
@@ -447,7 +447,7 @@ describe("1Password 1Pux Importer", () => {
|
|||||||
|
|
||||||
const identity = cipher.identity;
|
const identity = cipher.identity;
|
||||||
expect(identity.firstName).toEqual("George");
|
expect(identity.firstName).toEqual("George");
|
||||||
expect(identity.middleName).toBeNull();
|
expect(identity.middleName).toBeUndefined();
|
||||||
expect(identity.lastName).toEqual("Engels");
|
expect(identity.lastName).toEqual("Engels");
|
||||||
expect(identity.company).toEqual("National Public Library");
|
expect(identity.company).toEqual("National Public Library");
|
||||||
expect(identity.phone).toEqual("9995555555");
|
expect(identity.phone).toEqual("9995555555");
|
||||||
@@ -472,7 +472,7 @@ describe("1Password 1Pux Importer", () => {
|
|||||||
|
|
||||||
const identity = cipher.identity;
|
const identity = cipher.identity;
|
||||||
expect(identity.firstName).toEqual("David");
|
expect(identity.firstName).toEqual("David");
|
||||||
expect(identity.middleName).toBeNull();
|
expect(identity.middleName).toBeUndefined();
|
||||||
expect(identity.lastName).toEqual("Global");
|
expect(identity.lastName).toEqual("Global");
|
||||||
expect(identity.passportNumber).toEqual("76436847");
|
expect(identity.passportNumber).toEqual("76436847");
|
||||||
|
|
||||||
@@ -499,7 +499,7 @@ describe("1Password 1Pux Importer", () => {
|
|||||||
|
|
||||||
const identity = cipher.identity;
|
const identity = cipher.identity;
|
||||||
expect(identity.firstName).toEqual("Chef");
|
expect(identity.firstName).toEqual("Chef");
|
||||||
expect(identity.middleName).toBeNull();
|
expect(identity.middleName).toBeUndefined();
|
||||||
expect(identity.lastName).toEqual("Coldroom");
|
expect(identity.lastName).toEqual("Coldroom");
|
||||||
expect(identity.company).toEqual("Super Cool Store Co.");
|
expect(identity.company).toEqual("Super Cool Store Co.");
|
||||||
|
|
||||||
@@ -523,7 +523,7 @@ describe("1Password 1Pux Importer", () => {
|
|||||||
|
|
||||||
const identity = cipher.identity;
|
const identity = cipher.identity;
|
||||||
expect(identity.firstName).toEqual("Jack");
|
expect(identity.firstName).toEqual("Jack");
|
||||||
expect(identity.middleName).toBeNull();
|
expect(identity.middleName).toBeUndefined();
|
||||||
expect(identity.lastName).toEqual("Judd");
|
expect(identity.lastName).toEqual("Judd");
|
||||||
expect(identity.ssn).toEqual("131-216-1900");
|
expect(identity.ssn).toEqual("131-216-1900");
|
||||||
});
|
});
|
||||||
@@ -682,12 +682,12 @@ describe("1Password 1Pux Importer", () => {
|
|||||||
expect(folders[3].name).toBe("Education");
|
expect(folders[3].name).toBe("Education");
|
||||||
expect(folders[4].name).toBe("Starter Kit");
|
expect(folders[4].name).toBe("Starter Kit");
|
||||||
|
|
||||||
// Check that ciphers have a folder assigned to them
|
// Check that folder/cipher relationships
|
||||||
expect(result.ciphers.filter((c) => c.folderId === folders[0].id).length).toBeGreaterThan(0);
|
expect(result.folderRelationships.filter(([_, f]) => f == 0).length).toBeGreaterThan(0);
|
||||||
expect(result.ciphers.filter((c) => c.folderId === folders[1].id).length).toBeGreaterThan(0);
|
expect(result.folderRelationships.filter(([_, f]) => f == 1).length).toBeGreaterThan(0);
|
||||||
expect(result.ciphers.filter((c) => c.folderId === folders[2].id).length).toBeGreaterThan(0);
|
expect(result.folderRelationships.filter(([_, f]) => f == 2).length).toBeGreaterThan(0);
|
||||||
expect(result.ciphers.filter((c) => c.folderId === folders[3].id).length).toBeGreaterThan(0);
|
expect(result.folderRelationships.filter(([_, f]) => f == 3).length).toBeGreaterThan(0);
|
||||||
expect(result.ciphers.filter((c) => c.folderId === folders[4].id).length).toBeGreaterThan(0);
|
expect(result.folderRelationships.filter(([_, f]) => f == 4).length).toBeGreaterThan(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should create collections if part of an organization", async () => {
|
it("should create collections if part of an organization", async () => {
|
||||||
|
|||||||
@@ -11,9 +11,6 @@ const CipherData = [
|
|||||||
title: "should parse URLs in new CSV format",
|
title: "should parse URLs in new CSV format",
|
||||||
csv: simplePasswordData,
|
csv: simplePasswordData,
|
||||||
expected: Object.assign(new CipherView(), {
|
expected: Object.assign(new CipherView(), {
|
||||||
id: null,
|
|
||||||
organizationId: null,
|
|
||||||
folderId: null,
|
|
||||||
name: "example.com (example_user)",
|
name: "example.com (example_user)",
|
||||||
login: Object.assign(new LoginView(), {
|
login: Object.assign(new LoginView(), {
|
||||||
username: "example_user",
|
username: "example_user",
|
||||||
@@ -33,9 +30,6 @@ const CipherData = [
|
|||||||
title: "should parse URLs in old CSV format",
|
title: "should parse URLs in old CSV format",
|
||||||
csv: oldSimplePasswordData,
|
csv: oldSimplePasswordData,
|
||||||
expected: Object.assign(new CipherView(), {
|
expected: Object.assign(new CipherView(), {
|
||||||
id: null,
|
|
||||||
organizationId: null,
|
|
||||||
folderId: null,
|
|
||||||
name: "example.com (example_user)",
|
name: "example.com (example_user)",
|
||||||
login: Object.assign(new LoginView(), {
|
login: Object.assign(new LoginView(), {
|
||||||
username: "example_user",
|
username: "example_user",
|
||||||
@@ -45,6 +39,7 @@ const CipherData = [
|
|||||||
uri: "https://example.com",
|
uri: "https://example.com",
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
|
totp: null,
|
||||||
}),
|
}),
|
||||||
type: 1,
|
type: 1,
|
||||||
}),
|
}),
|
||||||
@@ -54,6 +49,7 @@ const CipherData = [
|
|||||||
describe("Safari CSV Importer", () => {
|
describe("Safari CSV Importer", () => {
|
||||||
CipherData.forEach((data) => {
|
CipherData.forEach((data) => {
|
||||||
it(data.title, async () => {
|
it(data.title, async () => {
|
||||||
|
jest.useFakeTimers().setSystemTime(data.expected.creationDate);
|
||||||
const importer = new SafariCsvImporter();
|
const importer = new SafariCsvImporter();
|
||||||
const result = await importer.parse(data.csv);
|
const result = await importer.parse(data.csv);
|
||||||
expect(result != null).toBe(true);
|
expect(result != null).toBe(true);
|
||||||
|
|||||||
@@ -11,9 +11,6 @@ const CipherData = [
|
|||||||
title: "should parse Zoho Vault CSV format",
|
title: "should parse Zoho Vault CSV format",
|
||||||
csv: samplezohovaultcsvdata,
|
csv: samplezohovaultcsvdata,
|
||||||
expected: Object.assign(new CipherView(), {
|
expected: Object.assign(new CipherView(), {
|
||||||
id: null,
|
|
||||||
organizationId: null,
|
|
||||||
folderId: null,
|
|
||||||
name: "XYZ Test",
|
name: "XYZ Test",
|
||||||
login: Object.assign(new LoginView(), {
|
login: Object.assign(new LoginView(), {
|
||||||
username: "email@domain.de",
|
username: "email@domain.de",
|
||||||
@@ -41,6 +38,7 @@ describe("Zoho Vault CSV Importer", () => {
|
|||||||
|
|
||||||
CipherData.forEach((data) => {
|
CipherData.forEach((data) => {
|
||||||
it(data.title, async () => {
|
it(data.title, async () => {
|
||||||
|
jest.useFakeTimers().setSystemTime(data.expected.creationDate);
|
||||||
const importer = new ZohoVaultCsvImporter();
|
const importer = new ZohoVaultCsvImporter();
|
||||||
const result = await importer.parse(data.csv);
|
const result = await importer.parse(data.csv);
|
||||||
expect(result != null).toBe(true);
|
expect(result != null).toBe(true);
|
||||||
|
|||||||
@@ -86,7 +86,10 @@ describe("AdditionalOptionsSectionComponent", () => {
|
|||||||
expect(cipherFormProvider.patchCipher).toHaveBeenCalled();
|
expect(cipherFormProvider.patchCipher).toHaveBeenCalled();
|
||||||
const patchFn = cipherFormProvider.patchCipher.mock.lastCall[0];
|
const patchFn = cipherFormProvider.patchCipher.mock.lastCall[0];
|
||||||
|
|
||||||
const updated = patchFn(new CipherView());
|
const newCipher = new CipherView();
|
||||||
|
newCipher.creationDate = newCipher.revisionDate = expectedCipher.creationDate;
|
||||||
|
|
||||||
|
const updated = patchFn(newCipher);
|
||||||
|
|
||||||
expect(updated).toEqual(expectedCipher);
|
expect(updated).toEqual(expectedCipher);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ export class DeleteAttachmentComponent {
|
|||||||
|
|
||||||
await this.cipherService.deleteAttachmentWithServer(
|
await this.cipherService.deleteAttachmentWithServer(
|
||||||
this.cipherId,
|
this.cipherId,
|
||||||
this.attachment.id,
|
this.attachment.id!,
|
||||||
activeUserId,
|
activeUserId,
|
||||||
this.admin,
|
this.admin,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ describe("CardDetailsSectionComponent", () => {
|
|||||||
cardView.brand = "Visa";
|
cardView.brand = "Visa";
|
||||||
cardView.expMonth = "";
|
cardView.expMonth = "";
|
||||||
cardView.code = "";
|
cardView.code = "";
|
||||||
|
cardView.expYear = "";
|
||||||
|
|
||||||
expect(patchCipherSpy).toHaveBeenCalled();
|
expect(patchCipherSpy).toHaveBeenCalled();
|
||||||
const patchFn = patchCipherSpy.mock.lastCall[0];
|
const patchFn = patchCipherSpy.mock.lastCall[0];
|
||||||
@@ -85,6 +86,7 @@ describe("CardDetailsSectionComponent", () => {
|
|||||||
cardView.number = "";
|
cardView.number = "";
|
||||||
cardView.expMonth = "";
|
cardView.expMonth = "";
|
||||||
cardView.code = "";
|
cardView.code = "";
|
||||||
|
cardView.brand = "";
|
||||||
cardView.expYear = "2022";
|
cardView.expYear = "2022";
|
||||||
|
|
||||||
expect(patchCipherSpy).toHaveBeenCalled();
|
expect(patchCipherSpy).toHaveBeenCalled();
|
||||||
@@ -122,8 +124,6 @@ describe("CardDetailsSectionComponent", () => {
|
|||||||
number,
|
number,
|
||||||
code,
|
code,
|
||||||
brand: cardView.brand,
|
brand: cardView.brand,
|
||||||
expMonth: null,
|
|
||||||
expYear: null,
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -52,12 +52,12 @@ export class CardDetailsSectionComponent implements OnInit {
|
|||||||
* leaving as just null gets inferred as `unknown`
|
* leaving as just null gets inferred as `unknown`
|
||||||
*/
|
*/
|
||||||
cardDetailsForm = this.formBuilder.group({
|
cardDetailsForm = this.formBuilder.group({
|
||||||
cardholderName: null as string | null,
|
cardholderName: "",
|
||||||
number: null as string | null,
|
number: "",
|
||||||
brand: null as string | null,
|
brand: "",
|
||||||
expMonth: null as string | null,
|
expMonth: "",
|
||||||
expYear: null as string | number | null,
|
expYear: "" as string | number,
|
||||||
code: null as string | null,
|
code: "",
|
||||||
});
|
});
|
||||||
|
|
||||||
/** Available Card Brands */
|
/** Available Card Brands */
|
||||||
@@ -110,16 +110,14 @@ export class CardDetailsSectionComponent implements OnInit {
|
|||||||
.pipe(takeUntilDestroyed())
|
.pipe(takeUntilDestroyed())
|
||||||
.subscribe(({ cardholderName, number, brand, expMonth, expYear, code }) => {
|
.subscribe(({ cardholderName, number, brand, expMonth, expYear, code }) => {
|
||||||
this.cipherFormContainer.patchCipher((cipher) => {
|
this.cipherFormContainer.patchCipher((cipher) => {
|
||||||
const expirationYear = normalizeExpiryYearFormat(expYear);
|
const expirationYear = normalizeExpiryYearFormat(expYear) ?? "";
|
||||||
|
|
||||||
Object.assign(cipher.card, {
|
cipher.card.cardholderName = cardholderName;
|
||||||
cardholderName,
|
cipher.card.number = number;
|
||||||
number,
|
cipher.card.brand = brand;
|
||||||
brand,
|
cipher.card.expMonth = expMonth;
|
||||||
expMonth,
|
cipher.card.expYear = expirationYear;
|
||||||
expYear: expirationYear,
|
cipher.card.code = code;
|
||||||
code,
|
|
||||||
});
|
|
||||||
|
|
||||||
return cipher;
|
return cipher;
|
||||||
});
|
});
|
||||||
@@ -167,6 +165,7 @@ export class CardDetailsSectionComponent implements OnInit {
|
|||||||
expMonth: this.initialValues?.expMonth || "",
|
expMonth: this.initialValues?.expMonth || "",
|
||||||
expYear: this.initialValues?.expYear || "",
|
expYear: this.initialValues?.expYear || "",
|
||||||
code: this.initialValues?.code || "",
|
code: this.initialValues?.code || "",
|
||||||
|
brand: CardView.getCardBrandByPatterns(this.initialValues?.number) || "",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -195,18 +194,4 @@ export class CardDetailsSectionComponent implements OnInit {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Set form initial form values from the current cipher */
|
|
||||||
private setInitialValues(cipherView: CipherView) {
|
|
||||||
const { cardholderName, number, brand, expMonth, expYear, code } = cipherView.card;
|
|
||||||
|
|
||||||
this.cardDetailsForm.setValue({
|
|
||||||
cardholderName: cardholderName,
|
|
||||||
number: number,
|
|
||||||
brand: brand,
|
|
||||||
expMonth: expMonth,
|
|
||||||
expYear: expYear,
|
|
||||||
code: code,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -386,7 +386,7 @@ export class CustomFieldsComponent implements OnInit, AfterViewInit {
|
|||||||
fieldView.type = field.type;
|
fieldView.type = field.type;
|
||||||
fieldView.name = field.name;
|
fieldView.name = field.name;
|
||||||
fieldView.value = value;
|
fieldView.value = value;
|
||||||
fieldView.linkedId = field.linkedId;
|
fieldView.linkedId = field.linkedId ?? undefined;
|
||||||
return fieldView;
|
return fieldView;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -172,7 +172,7 @@ export class IdentitySectionComponent implements OnInit {
|
|||||||
populateFormData(cipherView: CipherView) {
|
populateFormData(cipherView: CipherView) {
|
||||||
const { identity } = cipherView;
|
const { identity } = cipherView;
|
||||||
|
|
||||||
this.identityForm.setValue({
|
this.identityForm.patchValue({
|
||||||
title: identity.title,
|
title: identity.title,
|
||||||
firstName: identity.firstName,
|
firstName: identity.firstName,
|
||||||
middleName: identity.middleName,
|
middleName: identity.middleName,
|
||||||
|
|||||||
@@ -312,7 +312,7 @@ export class ItemDetailsSectionComponent implements OnInit {
|
|||||||
private async initFromExistingCipher(prefillCipher: CipherView) {
|
private async initFromExistingCipher(prefillCipher: CipherView) {
|
||||||
const { name, folderId, collectionIds } = prefillCipher;
|
const { name, folderId, collectionIds } = prefillCipher;
|
||||||
|
|
||||||
this.itemDetailsForm.setValue({
|
this.itemDetailsForm.patchValue({
|
||||||
name: name ? name : (this.initialValues?.name ?? ""),
|
name: name ? name : (this.initialValues?.name ?? ""),
|
||||||
organizationId: prefillCipher.organizationId, // We do not allow changing ownership of an existing cipher.
|
organizationId: prefillCipher.organizationId, // We do not allow changing ownership of an existing cipher.
|
||||||
folderId: folderId ? folderId : (this.initialValues?.folderId ?? null),
|
folderId: folderId ? folderId : (this.initialValues?.folderId ?? null),
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ export class DefaultCipherFormService implements CipherFormService {
|
|||||||
let savedCipher: Cipher;
|
let savedCipher: Cipher;
|
||||||
|
|
||||||
// Creating a new cipher
|
// Creating a new cipher
|
||||||
if (cipher.id == null) {
|
if (cipher.id == null || cipher.id === "") {
|
||||||
const encrypted = await this.cipherService.encrypt(cipher, activeUserId);
|
const encrypted = await this.cipherService.encrypt(cipher, activeUserId);
|
||||||
savedCipher = await this.cipherService.createWithServer(encrypted, config.admin);
|
savedCipher = await this.cipherService.createWithServer(encrypted, config.admin);
|
||||||
return await this.cipherService.decrypt(savedCipher, activeUserId);
|
return await this.cipherService.decrypt(savedCipher, activeUserId);
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ import { VaultAutosizeReadOnlyTextArea } from "../../directives/readonly-textare
|
|||||||
export class CustomFieldV2Component implements OnInit, OnChanges {
|
export class CustomFieldV2Component implements OnInit, OnChanges {
|
||||||
@Input({ required: true }) cipher!: CipherView;
|
@Input({ required: true }) cipher!: CipherView;
|
||||||
fieldType = FieldType;
|
fieldType = FieldType;
|
||||||
fieldOptions: Map<number, LinkedMetadata> | null = null;
|
fieldOptions: Map<number, LinkedMetadata> | undefined;
|
||||||
|
|
||||||
/** Indexes of hidden fields that are revealed */
|
/** Indexes of hidden fields that are revealed */
|
||||||
revealedHiddenFields: number[] = [];
|
revealedHiddenFields: number[] = [];
|
||||||
@@ -124,7 +124,7 @@ export class CustomFieldV2Component implements OnInit, OnChanges {
|
|||||||
case CipherType.Identity:
|
case CipherType.Identity:
|
||||||
return IdentityView.prototype.linkedFieldOptions;
|
return IdentityView.prototype.linkedFieldOptions;
|
||||||
default:
|
default:
|
||||||
return null;
|
return undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user