1
0
mirror of https://github.com/bitwarden/jslib synced 2026-01-07 02:53:13 +00:00

Merge branch 'master' of https://github.com/bitwarden/jslib into improve-hostname-and-domain-retrieval

This commit is contained in:
Daniel James Smith
2022-03-11 12:19:39 +01:00
492 changed files with 83679 additions and 5149 deletions

View File

@@ -1,9 +1,7 @@
import { PolicyType } from "../enums/policyType";
import { SetKeyConnectorKeyRequest } from "../models/request/account/setKeyConnectorKeyRequest";
import { VerifyOTPRequest } from "../models/request/account/verifyOTPRequest";
import { AttachmentRequest } from "../models/request/attachmentRequest";
import { BitPayInvoiceRequest } from "../models/request/bitPayInvoiceRequest";
import { CipherBulkDeleteRequest } from "../models/request/cipherBulkDeleteRequest";
import { CipherBulkMoveRequest } from "../models/request/cipherBulkMoveRequest";
@@ -26,6 +24,9 @@ import { EventRequest } from "../models/request/eventRequest";
import { FolderRequest } from "../models/request/folderRequest";
import { GroupRequest } from "../models/request/groupRequest";
import { IapCheckRequest } from "../models/request/iapCheckRequest";
import { ApiTokenRequest } from "../models/request/identityToken/apiTokenRequest";
import { PasswordTokenRequest } from "../models/request/identityToken/passwordTokenRequest";
import { SsoTokenRequest } from "../models/request/identityToken/ssoTokenRequest";
import { ImportCiphersRequest } from "../models/request/importCiphersRequest";
import { ImportDirectoryRequest } from "../models/request/importDirectoryRequest";
import { ImportOrganizationCiphersRequest } from "../models/request/importOrganizationCiphersRequest";
@@ -75,7 +76,6 @@ import { SendRequest } from "../models/request/sendRequest";
import { SetPasswordRequest } from "../models/request/setPasswordRequest";
import { StorageRequest } from "../models/request/storageRequest";
import { TaxInfoUpdateRequest } from "../models/request/taxInfoUpdateRequest";
import { TokenRequest } from "../models/request/tokenRequest";
import { TwoFactorEmailRequest } from "../models/request/twoFactorEmailRequest";
import { TwoFactorProviderRequest } from "../models/request/twoFactorProviderRequest";
import { TwoFactorRecoveryRequest } from "../models/request/twoFactorRecoveryRequest";
@@ -92,7 +92,6 @@ import { UpdateTwoFactorYubioOtpRequest } from "../models/request/updateTwoFacto
import { VerifyBankRequest } from "../models/request/verifyBankRequest";
import { VerifyDeleteRecoverRequest } from "../models/request/verifyDeleteRecoverRequest";
import { VerifyEmailRequest } from "../models/request/verifyEmailRequest";
import { ApiKeyResponse } from "../models/response/apiKeyResponse";
import { AttachmentResponse } from "../models/response/attachmentResponse";
import { AttachmentUploadDataResponse } from "../models/response/attachmentUploadDataResponse";
@@ -166,12 +165,11 @@ import {
} from "../models/response/twoFactorWebAuthnResponse";
import { TwoFactorYubiKeyResponse } from "../models/response/twoFactorYubiKeyResponse";
import { UserKeyResponse } from "../models/response/userKeyResponse";
import { SendAccessView } from "../models/view/sendAccessView";
export abstract class ApiService {
postIdentityToken: (
request: TokenRequest
request: PasswordTokenRequest | SsoTokenRequest | ApiTokenRequest
) => Promise<IdentityTokenResponse | IdentityTwoFactorResponse | IdentityCaptchaResponse>;
refreshIdentityToken: () => Promise<any>;
@@ -355,6 +353,10 @@ export abstract class ApiService {
email: string,
organizationUserId: string
) => Promise<ListResponse<PolicyResponse>>;
getPoliciesByInvitedUser: (
organizationId: string,
userId: string
) => Promise<ListResponse<PolicyResponse>>;
putPolicy: (
organizationId: string,
type: PolicyType,

View File

@@ -1,58 +1,23 @@
import { TwoFactorProviderType } from "../enums/twoFactorProviderType";
import { AuthResult } from "../models/domain/authResult";
import {
ApiLogInCredentials,
PasswordLogInCredentials,
SsoLogInCredentials,
} from "../models/domain/logInCredentials";
import { SymmetricCryptoKey } from "../models/domain/symmetricCryptoKey";
import { TokenRequestTwoFactor } from "../models/request/identityToken/tokenRequest";
export abstract class AuthService {
email: string;
masterPasswordHash: string;
code: string;
codeVerifier: string;
ssoRedirectUrl: string;
clientId: string;
clientSecret: string;
twoFactorProvidersData: Map<TwoFactorProviderType, { [key: string]: string }>;
selectedTwoFactorProviderType: TwoFactorProviderType;
logIn: (email: string, masterPassword: string, captchaToken?: string) => Promise<AuthResult>;
logInSso: (
code: string,
codeVerifier: string,
redirectUrl: string,
orgId: string
email: string;
logIn: (
credentials: ApiLogInCredentials | PasswordLogInCredentials | SsoLogInCredentials
) => Promise<AuthResult>;
logInApiKey: (clientId: string, clientSecret: string) => Promise<AuthResult>;
logInTwoFactor: (
twoFactorProvider: TwoFactorProviderType,
twoFactorToken: string,
remember?: boolean
twoFactor: TokenRequestTwoFactor,
captchaResponse: string
) => Promise<AuthResult>;
logInComplete: (
email: string,
masterPassword: string,
twoFactorProvider: TwoFactorProviderType,
twoFactorToken: string,
remember?: boolean,
captchaToken?: string
) => Promise<AuthResult>;
logInSsoComplete: (
code: string,
codeVerifier: string,
redirectUrl: string,
twoFactorProvider: TwoFactorProviderType,
twoFactorToken: string,
remember?: boolean
) => Promise<AuthResult>;
logInApiKeyComplete: (
clientId: string,
clientSecret: string,
twoFactorProvider: TwoFactorProviderType,
twoFactorToken: string,
remember?: boolean
) => Promise<AuthResult>;
logOut: (callback: Function) => void;
getSupportedTwoFactorProviders: (win: Window) => any[];
getDefaultTwoFactorProvider: (webAuthnSupported: boolean) => TwoFactorProviderType;
logOut: (callback: () => void) => void;
makePreloginKey: (masterPassword: string, email: string) => Promise<SymmetricCryptoKey>;
authingWithApiKey: () => boolean;
authingWithSso: () => boolean;

View File

@@ -1,12 +1,9 @@
import { CipherType } from "../enums/cipherType";
import { UriMatchType } from "../enums/uriMatchType";
import { CipherData } from "../models/data/cipherData";
import { Cipher } from "../models/domain/cipher";
import { Field } from "../models/domain/field";
import { SymmetricCryptoKey } from "../models/domain/symmetricCryptoKey";
import { CipherView } from "../models/view/cipherView";
import { FieldView } from "../models/view/fieldView";

View File

@@ -1,8 +1,6 @@
import { CollectionData } from "../models/data/collectionData";
import { Collection } from "../models/domain/collection";
import { TreeNode } from "../models/domain/treeNode";
import { CollectionView } from "../models/view/collectionView";
export abstract class CollectionService {

View File

@@ -1,14 +1,12 @@
import { EncArrayBuffer } from "../models/domain/encArrayBuffer";
import { EncString } from "../models/domain/encString";
import { SymmetricCryptoKey } from "../models/domain/symmetricCryptoKey";
import { ProfileOrganizationResponse } from "../models/response/profileOrganizationResponse";
import { ProfileProviderOrganizationResponse } from "../models/response/profileProviderOrganizationResponse";
import { ProfileProviderResponse } from "../models/response/profileProviderResponse";
import { HashPurpose } from "../enums/hashPurpose";
import { KdfType } from "../enums/kdfType";
import { KeySuffixOptions } from "../enums/keySuffixOptions";
import { EncArrayBuffer } from "../models/domain/encArrayBuffer";
import { EncString } from "../models/domain/encString";
import { SymmetricCryptoKey } from "../models/domain/symmetricCryptoKey";
import { ProfileOrganizationResponse } from "../models/response/profileOrganizationResponse";
import { ProfileProviderOrganizationResponse } from "../models/response/profileProviderOrganizationResponse";
import { ProfileProviderResponse } from "../models/response/profileProviderResponse";
export abstract class CryptoService {
setKey: (key: SymmetricCryptoKey) => Promise<any>;
@@ -21,7 +19,7 @@ export abstract class CryptoService {
) => Promise<void>;
setProviderKeys: (orgs: ProfileProviderResponse[]) => Promise<void>;
getKey: (keySuffix?: KeySuffixOptions, userId?: string) => Promise<SymmetricCryptoKey>;
getKeyFromStorage: (keySuffix: KeySuffixOptions) => Promise<SymmetricCryptoKey>;
getKeyFromStorage: (keySuffix: KeySuffixOptions, userId?: string) => Promise<SymmetricCryptoKey>;
getKeyHash: () => Promise<string>;
compareAndUpdateKeyHash: (masterPassword: string, key: SymmetricCryptoKey) => Promise<boolean>;
getEncKey: (key?: SymmetricCryptoKey) => Promise<SymmetricCryptoKey>;

View File

@@ -29,6 +29,6 @@ export abstract class EnvironmentService {
getEventsUrl: () => string;
getKeyConnectorUrl: () => string;
setUrlsFromStorage: () => Promise<void>;
setUrls: (urls: any, saveSettings?: boolean) => Promise<Urls>;
setUrls: (urls: Urls) => Promise<Urls>;
getUrls: () => Urls;
}

View File

@@ -1,11 +1,11 @@
import { EventView } from "../models/view/eventView";
export type ExportFormat = "csv" | "json" | "encrypted_json";
export abstract class ExportService {
getExport: (format?: "csv" | "json" | "encrypted_json") => Promise<string>;
getOrganizationExport: (
organizationId: string,
format?: "csv" | "json" | "encrypted_json"
) => Promise<string>;
getExport: (format?: ExportFormat, organizationId?: string) => Promise<string>;
getPasswordProtectedExport: (password: string, organizationId?: string) => Promise<string>;
getOrganizationExport: (organizationId: string, format?: ExportFormat) => Promise<string>;
getEventExport: (events: EventView[]) => Promise<string>;
getFileName: (prefix?: string, extension?: string) => string;
}

View File

@@ -1,9 +1,7 @@
import { FolderData } from "../models/data/folderData";
import { Folder } from "../models/domain/folder";
import { SymmetricCryptoKey } from "../models/domain/symmetricCryptoKey";
import { TreeNode } from "../models/domain/treeNode";
import { FolderView } from "../models/view/folderView";
export abstract class FolderService {

View File

@@ -1,13 +1,19 @@
import { ImportOption, ImportType } from "../enums/importOptions";
import { ImportError } from "../importers/importError";
import { Importer } from "../importers/importer";
export interface ImportOption {
id: string;
name: string;
}
export abstract class ImportService {
featuredImportOptions: ImportOption[];
regularImportOptions: ImportOption[];
featuredImportOptions: readonly ImportOption[];
regularImportOptions: readonly ImportOption[];
getImportOptions: () => ImportOption[];
import: (importer: Importer, fileContents: string, organizationId?: string) => Promise<Error>;
getImporter: (format: string, organizationId: string) => Importer;
import: (
importer: Importer,
fileContents: string,
organizationId?: string
) => Promise<ImportError>;
getImporter: (
format: ImportType | "bitwardenpasswordprotected",
organizationId: string,
password?: string
) => Importer;
}

View File

@@ -1,4 +1,5 @@
import { Organization } from "../models/domain/organization";
import { IdentityTokenResponse } from "../models/response/identityTokenResponse";
export abstract class KeyConnectorService {
getAndSetKey: (url?: string) => Promise<void>;
@@ -6,6 +7,10 @@ export abstract class KeyConnectorService {
getUsesKeyConnector: () => Promise<boolean>;
migrateUser: () => Promise<void>;
userNeedsMigration: () => Promise<boolean>;
convertNewSsoUserToKeyConnector: (
tokenResponse: IdentityTokenResponse,
orgId: string
) => Promise<void>;
setUsesKeyConnector: (enabled: boolean) => Promise<void>;
setConvertAccountRequired: (status: boolean) => Promise<void>;
getConvertAccountRequired: () => Promise<boolean>;

View File

@@ -1,5 +1,4 @@
import { OrganizationData } from "../models/data/organizationData";
import { Organization } from "../models/domain/organization";
export abstract class OrganizationService {
@@ -8,4 +7,5 @@ export abstract class OrganizationService {
getAll: (userId?: string) => Promise<Organization[]>;
save: (orgs: { [id: string]: OrganizationData }) => Promise<any>;
canManageSponsorships: () => Promise<boolean>;
hasOrganizations: (userId?: string) => Promise<boolean>;
}

View File

@@ -1,3 +1,4 @@
import { ClientType } from "../enums/clientType";
import { DeviceType } from "../enums/deviceType";
import { ThemeType } from "../enums/themeType";
@@ -6,9 +7,9 @@ interface ToastOptions {
}
export abstract class PlatformUtilsService {
identityClientId: string;
getDevice: () => DeviceType;
getDeviceString: () => string;
getClientType: () => ClientType;
isFirefox: () => boolean;
isChrome: () => boolean;
isEdge: () => boolean;

View File

@@ -1,20 +1,18 @@
import { PolicyType } from "../enums/policyType";
import { PolicyData } from "../models/data/policyData";
import { MasterPasswordPolicyOptions } from "../models/domain/masterPasswordPolicyOptions";
import { Policy } from "../models/domain/policy";
import { ResetPasswordPolicyOptions } from "../models/domain/resetPasswordPolicyOptions";
import { ListResponse } from "../models/response/listResponse";
import { PolicyResponse } from "../models/response/policyResponse";
import { PolicyType } from "../enums/policyType";
export abstract class PolicyService {
clearCache: () => void;
getAll: (type?: PolicyType, userId?: string) => Promise<Policy[]>;
getPolicyForOrganization: (policyType: PolicyType, organizationId: string) => Promise<Policy>;
replace: (policies: { [id: string]: PolicyData }) => Promise<any>;
clear: (userId?: string) => Promise<any>;
getMasterPasswordPoliciesForInvitedUsers: (orgId: string) => Promise<MasterPasswordPolicyOptions>;
getMasterPasswordPolicyOptions: (policies?: Policy[]) => Promise<MasterPasswordPolicyOptions>;
evaluateMasterPassword: (
passwordStrength: number,

View File

@@ -1,5 +1,4 @@
import { ProviderData } from "../models/data/providerData";
import { Provider } from "../models/domain/provider";
export abstract class ProviderService {

View File

@@ -1,9 +1,7 @@
import { SendData } from "../models/data/sendData";
import { EncArrayBuffer } from "../models/domain/encArrayBuffer";
import { Send } from "../models/domain/send";
import { SymmetricCryptoKey } from "../models/domain/symmetricCryptoKey";
import { SendView } from "../models/view/sendView";
export abstract class SendService {

View File

@@ -1,8 +1,8 @@
import { BehaviorSubject } from "rxjs";
import { KdfType } from "../enums/kdfType";
import { ThemeType } from "../enums/themeType";
import { UriMatchType } from "../enums/uriMatchType";
import { CipherData } from "../models/data/cipherData";
import { CollectionData } from "../models/data/collectionData";
import { EventData } from "../models/data/eventData";
@@ -11,24 +11,24 @@ import { OrganizationData } from "../models/data/organizationData";
import { PolicyData } from "../models/data/policyData";
import { ProviderData } from "../models/data/providerData";
import { SendData } from "../models/data/sendData";
import { Account } from "../models/domain/account";
import { EncString } from "../models/domain/encString";
import { EnvironmentUrls } from "../models/domain/environmentUrls";
import { GeneratedPasswordHistory } from "../models/domain/generatedPasswordHistory";
import { Policy } from "../models/domain/policy";
import { StorageOptions } from "../models/domain/storageOptions";
import { SymmetricCryptoKey } from "../models/domain/symmetricCryptoKey";
import { WindowState } from "../models/domain/windowState";
import { CipherView } from "../models/view/cipherView";
import { CollectionView } from "../models/view/collectionView";
import { FolderView } from "../models/view/folderView";
import { SendView } from "../models/view/sendView";
export abstract class StateService {
accounts: BehaviorSubject<{ [userId: string]: Account }>;
export abstract class StateService<T extends Account = Account> {
accounts: BehaviorSubject<{ [userId: string]: T }>;
activeAccount: BehaviorSubject<string>;
addAccount: (account: Account) => Promise<void>;
addAccount: (account: T) => Promise<void>;
setActiveUser: (userId: string) => Promise<void>;
clean: (options?: StorageOptions) => Promise<void>;
init: () => Promise<void>;
@@ -60,8 +60,8 @@ export abstract class StateService {
getCanAccessPremium: (options?: StorageOptions) => Promise<boolean>;
getClearClipboard: (options?: StorageOptions) => Promise<number>;
setClearClipboard: (value: number, options?: StorageOptions) => Promise<void>;
getCollapsedGroupings: (options?: StorageOptions) => Promise<Set<string>>;
setCollapsedGroupings: (value: Set<string>, options?: StorageOptions) => Promise<void>;
getCollapsedGroupings: (options?: StorageOptions) => Promise<string[]>;
setCollapsedGroupings: (value: string[], options?: StorageOptions) => Promise<void>;
getConvertAccountToKeyConnector: (options?: StorageOptions) => Promise<boolean>;
setConvertAccountToKeyConnector: (value: boolean, options?: StorageOptions) => Promise<void>;
getCryptoMasterKey: (options?: StorageOptions) => Promise<SymmetricCryptoKey>;
@@ -213,8 +213,8 @@ export abstract class StateService {
setEntityId: (value: string, options?: StorageOptions) => Promise<void>;
getEntityType: (options?: StorageOptions) => Promise<any>;
setEntityType: (value: string, options?: StorageOptions) => Promise<void>;
getEnvironmentUrls: (options?: StorageOptions) => Promise<any>;
setEnvironmentUrls: (value: any, options?: StorageOptions) => Promise<void>;
getEnvironmentUrls: (options?: StorageOptions) => Promise<EnvironmentUrls>;
setEnvironmentUrls: (value: EnvironmentUrls, options?: StorageOptions) => Promise<void>;
getEquivalentDomains: (options?: StorageOptions) => Promise<any>;
setEquivalentDomains: (value: string, options?: StorageOptions) => Promise<void>;
getEventCollection: (options?: StorageOptions) => Promise<EventData[]>;
@@ -285,8 +285,8 @@ export abstract class StateService {
setSsoOrganizationIdentifier: (value: string, options?: StorageOptions) => Promise<void>;
getSsoState: (options?: StorageOptions) => Promise<string>;
setSsoState: (value: string, options?: StorageOptions) => Promise<void>;
getTheme: (options?: StorageOptions) => Promise<string>;
setTheme: (value: string, options?: StorageOptions) => Promise<void>;
getTheme: (options?: StorageOptions) => Promise<ThemeType>;
setTheme: (value: ThemeType, options?: StorageOptions) => Promise<void>;
getTwoFactorToken: (options?: StorageOptions) => Promise<string>;
setTwoFactorToken: (value: string, options?: StorageOptions) => Promise<void>;
getUserId: (options?: StorageOptions) => Promise<string>;
@@ -298,6 +298,6 @@ export abstract class StateService {
setVaultTimeoutAction: (value: string, options?: StorageOptions) => Promise<void>;
getStateVersion: () => Promise<number>;
setStateVersion: (value: number) => Promise<void>;
getWindow: () => Promise<Map<string, any>>;
setWindow: (value: Map<string, any>) => Promise<void>;
getWindow: () => Promise<WindowState>;
setWindow: (value: WindowState) => Promise<void>;
}

View File

@@ -1,3 +1,5 @@
import { IdentityTokenResponse } from "../models/response/identityTokenResponse";
export abstract class TokenService {
setTokens: (
accessToken: string,
@@ -12,10 +14,9 @@ export abstract class TokenService {
getClientId: () => Promise<string>;
setClientSecret: (clientSecret: string) => Promise<any>;
getClientSecret: () => Promise<string>;
toggleTokens: () => Promise<any>;
setTwoFactorToken: (token: string, email: string) => Promise<any>;
getTwoFactorToken: (email: string) => Promise<string>;
clearTwoFactorToken: (email: string) => Promise<any>;
setTwoFactorToken: (tokenResponse: IdentityTokenResponse) => Promise<any>;
getTwoFactorToken: () => Promise<string>;
clearTwoFactorToken: () => Promise<any>;
clearToken: (userId?: string) => Promise<any>;
decodeToken: (token?: string) => any;
getTokenExpirationDate: () => Promise<Date>;

View File

@@ -0,0 +1,23 @@
import { TwoFactorProviderType } from "../enums/twoFactorProviderType";
import { IdentityTwoFactorResponse } from "../models/response/identityTwoFactorResponse";
export interface TwoFactorProviderDetails {
type: TwoFactorProviderType;
name: string;
description: string;
priority: number;
sort: number;
premium: boolean;
}
export abstract class TwoFactorService {
init: () => void;
getSupportedProviders: (win: Window) => TwoFactorProviderDetails[];
getDefaultProvider: (webAuthnSupported: boolean) => TwoFactorProviderType;
setSelectedProvider: (type: TwoFactorProviderType) => void;
clearSelectedProvider: () => void;
setProviders: (response: IdentityTwoFactorResponse) => void;
clearProviders: () => void;
getProviders: () => Map<TwoFactorProviderType, { [key: string]: string }>;
}

View File

@@ -1,5 +1,4 @@
import { SecretVerificationRequest } from "../models/request/secretVerificationRequest";
import { Verification } from "../types/verification";
export abstract class UserVerificationService {

View File

@@ -0,0 +1,5 @@
export enum AuthenticationType {
Password = 0,
Sso = 1,
Api = 2,
}

View File

@@ -0,0 +1,8 @@
export enum ClientType {
Web = "web",
Browser = "browser",
Desktop = "desktop",
Mobile = "mobile",
Cli = "cli",
DirectoryConnector = "connector",
}

View File

@@ -0,0 +1,74 @@
export interface ImportOption {
id: string;
name: string;
}
export const featuredImportOptions = [
{ id: "bitwardenjson", name: "Bitwarden (json)" },
{ id: "bitwardencsv", name: "Bitwarden (csv)" },
{ id: "chromecsv", name: "Chrome (csv)" },
{ id: "dashlanecsv", name: "Dashlane (csv)" },
{ id: "firefoxcsv", name: "Firefox (csv)" },
{ id: "keepass2xml", name: "KeePass 2 (xml)" },
{ id: "lastpasscsv", name: "LastPass (csv)" },
{ id: "safaricsv", name: "Safari and macOS (csv)" },
{ id: "1password1pux", name: "1Password (1pux)" },
] as const;
export const regularImportOptions = [
{ id: "keepassxcsv", name: "KeePassX (csv)" },
{ id: "1password1pif", name: "1Password (1pif)" },
{ id: "1passwordwincsv", name: "1Password 6 and 7 Windows (csv)" },
{ id: "1passwordmaccsv", name: "1Password 6 and 7 Mac (csv)" },
{ id: "dashlanejson", name: "Dashlane (json)" },
{ id: "roboformcsv", name: "RoboForm (csv)" },
{ id: "keepercsv", name: "Keeper (csv)" },
// Temporarily remove this option for the Feb release
// { id: "keeperjson", name: "Keeper (json)" },
{ id: "enpasscsv", name: "Enpass (csv)" },
{ id: "enpassjson", name: "Enpass (json)" },
{ id: "safeincloudxml", name: "SafeInCloud (xml)" },
{ id: "pwsafexml", name: "Password Safe (xml)" },
{ id: "stickypasswordxml", name: "Sticky Password (xml)" },
{ id: "msecurecsv", name: "mSecure (csv)" },
{ id: "truekeycsv", name: "True Key (csv)" },
{ id: "passwordbossjson", name: "Password Boss (json)" },
{ id: "zohovaultcsv", name: "Zoho Vault (csv)" },
{ id: "splashidcsv", name: "SplashID (csv)" },
{ id: "passworddragonxml", name: "Password Dragon (xml)" },
{ id: "padlockcsv", name: "Padlock (csv)" },
{ id: "passboltcsv", name: "Passbolt (csv)" },
{ id: "clipperzhtml", name: "Clipperz (html)" },
{ id: "aviracsv", name: "Avira (csv)" },
{ id: "saferpasscsv", name: "SaferPass (csv)" },
{ id: "upmcsv", name: "Universal Password Manager (csv)" },
{ id: "ascendocsv", name: "Ascendo DataVault (csv)" },
{ id: "meldiumcsv", name: "Meldium (csv)" },
{ id: "passkeepcsv", name: "PassKeep (csv)" },
{ id: "operacsv", name: "Opera (csv)" },
{ id: "vivaldicsv", name: "Vivaldi (csv)" },
{ id: "gnomejson", name: "GNOME Passwords and Keys/Seahorse (json)" },
{ id: "blurcsv", name: "Blur (csv)" },
{ id: "passwordagentcsv", name: "Password Agent (csv)" },
{ id: "passpackcsv", name: "Passpack (csv)" },
{ id: "passmanjson", name: "Passman (json)" },
{ id: "avastcsv", name: "Avast Passwords (csv)" },
{ id: "avastjson", name: "Avast Passwords (json)" },
{ id: "fsecurefsk", name: "F-Secure KEY (fsk)" },
{ id: "kasperskytxt", name: "Kaspersky Password Manager (txt)" },
{ id: "remembearcsv", name: "RememBear (csv)" },
{ id: "passwordwallettxt", name: "PasswordWallet (txt)" },
{ id: "mykicsv", name: "Myki (csv)" },
{ id: "securesafecsv", name: "SecureSafe (csv)" },
{ id: "logmeoncecsv", name: "LogMeOnce (csv)" },
{ id: "blackberrycsv", name: "BlackBerry Password Keeper (csv)" },
{ id: "buttercupcsv", name: "Buttercup (csv)" },
{ id: "codebookcsv", name: "Codebook (csv)" },
{ id: "encryptrcsv", name: "Encryptr (csv)" },
{ id: "yoticsv", name: "Yoti (csv)" },
{ id: "nordpasscsv", name: "Nordpass (csv)" },
] as const;
export type ImportType =
| typeof featuredImportOptions[number]["id"]
| typeof regularImportOptions[number]["id"];

View File

@@ -0,0 +1,33 @@
export enum SsoType {
None = 0,
OpenIdConnect = 1,
Saml2 = 2,
}
export enum OpenIdConnectRedirectBehavior {
RedirectGet = 0,
FormPost = 1,
}
export enum Saml2BindingType {
HttpRedirect = 1,
HttpPost = 2,
}
export enum Saml2NameIdFormat {
NotConfigured = 0,
Unspecified = 1,
EmailAddress = 2,
X509SubjectName = 3,
WindowsDomainQualifiedName = 4,
KerberosPrincipalName = 5,
EntityIdentifier = 6,
Persistent = 7,
Transient = 8,
}
export enum Saml2SigningBehavior {
IfIdpWantAuthnRequestsSigned = 0,
Always = 1,
Never = 3,
}

View File

@@ -0,0 +1,7 @@
export enum StateVersion {
One = 1, // Original flat key/value pair store
Two = 2, // Move to a typed State object
Three = 3, // Fix migration of users' premium status
Four = 4, // Fix 'Never Lock' option by removing stale data
Latest = Four,
}

View File

@@ -0,0 +1,13 @@
import { Account } from "../models/domain/account";
export class AccountFactory<T extends Account = Account> {
private accountConstructor: new (init: Partial<T>) => T;
constructor(accountConstructor: new (init: Partial<T>) => T) {
this.accountConstructor = accountConstructor;
}
create(args: Partial<T>) {
return new this.accountConstructor(args);
}
}

View File

@@ -0,0 +1,13 @@
import { GlobalState } from "../models/domain/globalState";
export class GlobalStateFactory<T extends GlobalState = GlobalState> {
private globalStateConstructor: new (init: Partial<T>) => T;
constructor(globalStateConstructor: new (init: Partial<T>) => T) {
this.globalStateConstructor = globalStateConstructor;
}
create(args?: Partial<T>) {
return new this.globalStateConstructor(args);
}
}

View File

@@ -0,0 +1,29 @@
import { Account } from "../models/domain/account";
import { GlobalState } from "../models/domain/globalState";
import { AccountFactory } from "./accountFactory";
import { GlobalStateFactory } from "./globalStateFactory";
export class StateFactory<
TGlobal extends GlobalState = GlobalState,
TAccount extends Account = Account
> {
private globalStateFactory: GlobalStateFactory<TGlobal>;
private accountFactory: AccountFactory<TAccount>;
constructor(
globalStateConstructor: new (init: Partial<TGlobal>) => TGlobal,
accountConstructor: new (init: Partial<TAccount>) => TAccount
) {
this.globalStateFactory = new GlobalStateFactory(globalStateConstructor);
this.accountFactory = new AccountFactory(accountConstructor);
}
createGlobal(args: Partial<TGlobal>): TGlobal {
return this.globalStateFactory.create(args);
}
createAccount(args: Partial<TAccount>): TAccount {
return this.accountFactory.create(args);
}
}

View File

@@ -1,8 +1,8 @@
import { ImportResult } from "../models/domain/importResult";
import { BaseImporter } from "./baseImporter";
import { Importer } from "./importer";
import { ImportResult } from "../models/domain/importResult";
export class AscendoCsvImporter extends BaseImporter implements Importer {
parse(data: string): Promise<ImportResult> {
const result = new ImportResult();

View File

@@ -1,8 +1,8 @@
import { ImportResult } from "../models/domain/importResult";
import { BaseImporter } from "./baseImporter";
import { Importer } from "./importer";
import { ImportResult } from "../models/domain/importResult";
export class AvastCsvImporter extends BaseImporter implements Importer {
parse(data: string): Promise<ImportResult> {
const result = new ImportResult();

View File

@@ -1,10 +1,9 @@
import { BaseImporter } from "./baseImporter";
import { Importer } from "./importer";
import { ImportResult } from "../models/domain/importResult";
import { CipherType } from "../enums/cipherType";
import { SecureNoteType } from "../enums/secureNoteType";
import { ImportResult } from "../models/domain/importResult";
import { BaseImporter } from "./baseImporter";
import { Importer } from "./importer";
export class AvastJsonImporter extends BaseImporter implements Importer {
parse(data: string): Promise<ImportResult> {

View File

@@ -1,8 +1,8 @@
import { ImportResult } from "../models/domain/importResult";
import { BaseImporter } from "./baseImporter";
import { Importer } from "./importer";
import { ImportResult } from "../models/domain/importResult";
export class AviraCsvImporter extends BaseImporter implements Importer {
parse(data: string): Promise<ImportResult> {
const result = new ImportResult();

View File

@@ -1,25 +1,18 @@
import * as papa from "papaparse";
import { LogService } from "../abstractions/log.service";
import { ImportResult } from "../models/domain/importResult";
import { CipherView } from "../models/view/cipherView";
import { CollectionView } from "../models/view/collectionView";
import { LoginUriView } from "../models/view/loginUriView";
import { Utils } from "../misc/utils";
import { FieldView } from "../models/view/fieldView";
import { FolderView } from "../models/view/folderView";
import { LoginView } from "../models/view/loginView";
import { SecureNoteView } from "../models/view/secureNoteView";
import { CipherRepromptType } from "../enums/cipherRepromptType";
import { CipherType } from "../enums/cipherType";
import { FieldType } from "../enums/fieldType";
import { SecureNoteType } from "../enums/secureNoteType";
import { Utils } from "../misc/utils";
import { ImportResult } from "../models/domain/importResult";
import { CipherView } from "../models/view/cipherView";
import { CollectionView } from "../models/view/collectionView";
import { FieldView } from "../models/view/fieldView";
import { FolderView } from "../models/view/folderView";
import { LoginUriView } from "../models/view/loginUriView";
import { LoginView } from "../models/view/loginView";
import { SecureNoteView } from "../models/view/secureNoteView";
import { ConsoleLogService } from "../services/consoleLog.service";
export abstract class BaseImporter {
@@ -161,7 +154,6 @@ export abstract class BaseImporter {
if (result.errors != null && result.errors.length > 0) {
result.errors.forEach((e) => {
if (e.row != null) {
// tslint:disable-next-line
this.logService.warning("Error parsing row " + e.row + ": " + e.message);
}
});
@@ -454,4 +446,21 @@ export abstract class BaseImporter {
cipher.secureNote.type = SecureNoteType.Generic;
}
}
protected processFullName(cipher: CipherView, fullName: string) {
if (this.isNullOrWhitespace(fullName)) {
return;
}
const nameParts = fullName.split(" ");
if (nameParts.length > 0) {
cipher.identity.firstName = this.getValueOrDefault(nameParts[0]);
}
if (nameParts.length === 2) {
cipher.identity.lastName = this.getValueOrDefault(nameParts[1]);
} else if (nameParts.length >= 3) {
cipher.identity.middleName = this.getValueOrDefault(nameParts[1]);
cipher.identity.lastName = nameParts.slice(2, nameParts.length).join(" ");
}
}
}

View File

@@ -1,19 +1,16 @@
import { BaseImporter } from "./baseImporter";
import { Importer } from "./importer";
import { ImportResult } from "../models/domain/importResult";
import { CipherView } from "../models/view/cipherView";
import { CollectionView } from "../models/view/collectionView";
import { FieldView } from "../models/view/fieldView";
import { FolderView } from "../models/view/folderView";
import { LoginView } from "../models/view/loginView";
import { SecureNoteView } from "../models/view/secureNoteView";
import { CipherRepromptType } from "../enums/cipherRepromptType";
import { CipherType } from "../enums/cipherType";
import { FieldType } from "../enums/fieldType";
import { SecureNoteType } from "../enums/secureNoteType";
import { ImportResult } from "../models/domain/importResult";
import { CipherView } from "../models/view/cipherView";
import { CollectionView } from "../models/view/collectionView";
import { FieldView } from "../models/view/fieldView";
import { LoginView } from "../models/view/loginView";
import { SecureNoteView } from "../models/view/secureNoteView";
import { BaseImporter } from "./baseImporter";
import { Importer } from "./importer";
export class BitwardenCsvImporter extends BaseImporter implements Importer {
parse(data: string): Promise<ImportResult> {
@@ -63,7 +60,7 @@ export class BitwardenCsvImporter extends BaseImporter implements Importer {
10
);
} catch (e) {
// tslint:disable-next-line
// eslint-disable-next-line
console.error("Unable to parse reprompt value", e);
cipher.reprompt = CipherRepromptType.None;
}
@@ -102,7 +99,7 @@ export class BitwardenCsvImporter extends BaseImporter implements Importer {
cipher.secureNote = new SecureNoteView();
cipher.secureNote.type = SecureNoteType.Generic;
break;
default:
default: {
cipher.type = CipherType.Login;
cipher.login = new LoginView();
cipher.login.totp = this.getValueOrDefault(value.login_totp || value.totp);
@@ -111,6 +108,7 @@ export class BitwardenCsvImporter extends BaseImporter implements Importer {
const uris = this.parseSingleRowCsv(value.login_uri || value.uri);
cipher.login.uris = this.makeUriArray(uris);
break;
}
}
result.ciphers.push(cipher);

View File

@@ -1,28 +1,33 @@
import { BaseImporter } from "./baseImporter";
import { Importer } from "./importer";
import { CryptoService } from "../abstractions/crypto.service";
import { I18nService } from "../abstractions/i18n.service";
import { EncString } from "../models/domain/encString";
import { ImportResult } from "../models/domain/importResult";
import { CipherWithIds } from "../models/export/cipherWithIds";
import { CollectionWithId } from "../models/export/collectionWithId";
import { FolderWithId } from "../models/export/folderWithId";
import { CryptoService } from "../abstractions/crypto.service";
import { I18nService } from "../abstractions/i18n.service";
import { BaseImporter } from "./baseImporter";
import { Importer } from "./importer";
export class BitwardenJsonImporter extends BaseImporter implements Importer {
private results: any;
private result: ImportResult;
constructor(private cryptoService: CryptoService, private i18nService: I18nService) {
constructor(protected cryptoService: CryptoService, protected i18nService: I18nService) {
super();
}
async parse(data: string): Promise<ImportResult> {
this.result = new ImportResult();
this.results = JSON.parse(data);
if (this.results == null || this.results.items == null || this.results.items.length === 0) {
if (this.results == null || this.results.items == null) {
if (this.results?.passwordProtected) {
this.result.success = false;
this.result.missingPassword = true;
this.result.errorMessage = this.i18nService.t("importPasswordRequired");
return this.result;
}
this.result.success = false;
return this.result;
}

View File

@@ -0,0 +1,81 @@
import { CryptoService } from "../abstractions/crypto.service";
import { I18nService } from "../abstractions/i18n.service";
import { KdfType } from "../enums/kdfType";
import { EncString } from "../models/domain/encString";
import { ImportResult } from "../models/domain/importResult";
import { SymmetricCryptoKey } from "../models/domain/symmetricCryptoKey";
import { BitwardenJsonImporter } from "./bitwardenJsonImporter";
import { Importer } from "./importer";
interface BitwardenPasswordProtectedFileFormat {
encrypted: boolean;
passwordProtected: boolean;
salt: string;
kdfIterations: number;
kdfType: number;
encKeyValidation_DO_NOT_EDIT: string;
data: string;
}
export class BitwardenPasswordProtectedImporter extends BitwardenJsonImporter implements Importer {
private key: SymmetricCryptoKey;
constructor(cryptoService: CryptoService, i18nService: I18nService, private password: string) {
super(cryptoService, i18nService);
}
async parse(data: string): Promise<ImportResult> {
const result = new ImportResult();
const parsedData = JSON.parse(data);
if (this.cannotParseFile(parsedData)) {
result.success = false;
return result;
}
if (!(await this.checkPassword(parsedData))) {
result.success = false;
result.errorMessage = this.i18nService.t("importEncKeyError");
return result;
}
const encData = new EncString(parsedData.data);
const clearTextData = await this.cryptoService.decryptToUtf8(encData, this.key);
return await super.parse(clearTextData);
}
private async checkPassword(jdoc: BitwardenPasswordProtectedFileFormat): Promise<boolean> {
this.key = await this.cryptoService.makePinKey(
this.password,
jdoc.salt,
KdfType.PBKDF2_SHA256,
jdoc.kdfIterations
);
const encKeyValidation = new EncString(jdoc.encKeyValidation_DO_NOT_EDIT);
const encKeyValidationDecrypt = await this.cryptoService.decryptToUtf8(
encKeyValidation,
this.key
);
if (encKeyValidationDecrypt === null) {
return false;
}
return true;
}
private cannotParseFile(jdoc: BitwardenPasswordProtectedFileFormat): boolean {
return (
!jdoc ||
!jdoc.encrypted ||
!jdoc.passwordProtected ||
!jdoc.salt ||
!jdoc.kdfIterations ||
typeof jdoc.kdfIterations !== "number" ||
jdoc.kdfType == null ||
KdfType[jdoc.kdfType] == null ||
!jdoc.encKeyValidation_DO_NOT_EDIT ||
!jdoc.data
);
}
}

View File

@@ -1,8 +1,8 @@
import { ImportResult } from "../models/domain/importResult";
import { BaseImporter } from "./baseImporter";
import { Importer } from "./importer";
import { ImportResult } from "../models/domain/importResult";
export class BlackBerryCsvImporter extends BaseImporter implements Importer {
parse(data: string): Promise<ImportResult> {
const result = new ImportResult();

View File

@@ -1,8 +1,8 @@
import { ImportResult } from "../models/domain/importResult";
import { BaseImporter } from "./baseImporter";
import { Importer } from "./importer";
import { ImportResult } from "../models/domain/importResult";
export class BlurCsvImporter extends BaseImporter implements Importer {
parse(data: string): Promise<ImportResult> {
const result = new ImportResult();

View File

@@ -1,8 +1,8 @@
import { ImportResult } from "../models/domain/importResult";
import { BaseImporter } from "./baseImporter";
import { Importer } from "./importer";
import { ImportResult } from "../models/domain/importResult";
const OfficialProps = ["!group_id", "!group_name", "title", "username", "password", "URL", "id"];
export class ButtercupCsvImporter extends BaseImporter implements Importer {
@@ -25,6 +25,7 @@ export class ButtercupCsvImporter extends BaseImporter implements Importer {
let processingCustomFields = false;
for (const prop in value) {
// eslint-disable-next-line
if (value.hasOwnProperty(prop)) {
if (!processingCustomFields && OfficialProps.indexOf(prop) === -1) {
processingCustomFields = true;

View File

@@ -1,8 +1,8 @@
import { ImportResult } from "../models/domain/importResult";
import { BaseImporter } from "./baseImporter";
import { Importer } from "./importer";
import { ImportResult } from "../models/domain/importResult";
export class ChromeCsvImporter extends BaseImporter implements Importer {
parse(data: string): Promise<ImportResult> {
const result = new ImportResult();

View File

@@ -1,8 +1,8 @@
import { ImportResult } from "../models/domain/importResult";
import { BaseImporter } from "./baseImporter";
import { Importer } from "./importer";
import { ImportResult } from "../models/domain/importResult";
export class ClipperzHtmlImporter extends BaseImporter implements Importer {
parse(data: string): Promise<ImportResult> {
const result = new ImportResult();
@@ -31,6 +31,7 @@ export class ClipperzHtmlImporter extends BaseImporter implements Importer {
if (entry.currentVersion != null && entry.currentVersion.fields != null) {
for (const property in entry.currentVersion.fields) {
// eslint-disable-next-line
if (!entry.currentVersion.fields.hasOwnProperty(property)) {
continue;
}
@@ -50,7 +51,7 @@ export class ClipperzHtmlImporter extends BaseImporter implements Importer {
case "url":
cipher.login.uris = this.makeUriArray(field.value);
break;
default:
default: {
const labelLower = field.label != null ? field.label.toLowerCase() : null;
if (
cipher.login.password == null &&
@@ -71,6 +72,7 @@ export class ClipperzHtmlImporter extends BaseImporter implements Importer {
this.processKvp(cipher, field.label, field.value);
}
break;
}
}
}
}

View File

@@ -1,8 +1,8 @@
import { ImportResult } from "../models/domain/importResult";
import { BaseImporter } from "./baseImporter";
import { Importer } from "./importer";
import { ImportResult } from "../models/domain/importResult";
export class CodebookCsvImporter extends BaseImporter implements Importer {
parse(data: string): Promise<ImportResult> {
const result = new ImportResult();

View File

@@ -0,0 +1,271 @@
import { CipherType } from "../../enums/cipherType";
import { SecureNoteType } from "../../enums/secureNoteType";
import { ImportResult } from "../../models/domain/importResult";
import { CardView } from "../../models/view/cardView";
import { CipherView } from "../../models/view/cipherView";
import { IdentityView } from "../../models/view/identityView";
import { LoginView } from "../../models/view/loginView";
import { BaseImporter } from "../baseImporter";
import { Importer } from "../importer";
import {
CredentialsRecord,
IdRecord,
PaymentsRecord,
PersonalInformationRecord,
SecureNoteRecord,
} from "./types/dashlaneCsvTypes";
const _mappedCredentialsColums = new Set([
"title",
"note",
"username",
"password",
"url",
"otpSecret",
"category",
]);
const _mappedPersonalInfoAsIdentiyColumns = new Set([
"type",
"title",
"first_name",
"middle_name",
"last_name",
"login",
"email",
"phone_number",
"address",
"country",
"state",
"city",
"zip",
// Skip item_name as we already have set a combined name
"item_name",
]);
const _mappedSecureNoteColumns = new Set(["title", "note"]);
export class DashlaneCsvImporter extends BaseImporter implements Importer {
parse(data: string): Promise<ImportResult> {
const result = new ImportResult();
const results = this.parseCsv(data, true);
if (results == null) {
result.success = false;
return Promise.resolve(result);
}
if (results[0].type != null && results[0].title != null) {
const personalRecords = results as PersonalInformationRecord[];
// If personalRecords has only one "name" then create an Identity-Cipher
if (personalRecords.filter((x) => x.type === "name").length === 1) {
const cipher = this.initLoginCipher();
cipher.type = CipherType.Identity;
cipher.identity = new IdentityView();
results.forEach((row) => {
this.parsePersonalInformationRecordAsIdentity(cipher, row);
});
this.cleanupCipher(cipher);
result.ciphers.push(cipher);
result.success = true;
return Promise.resolve(result);
}
}
results.forEach((row) => {
const cipher = this.initLoginCipher();
const rowKeys = Object.keys(row);
if (rowKeys[0] === "username") {
this.processFolder(result, row.category);
this.parseCredentialsRecord(cipher, row);
}
if (rowKeys[0] === "type" && rowKeys[1] === "account_name") {
this.parsePaymentRecord(cipher, row);
}
if (rowKeys[0] === "type" && rowKeys[1] === "number") {
this.parseIdRecord(cipher, row);
}
if ((rowKeys[0] === "type") != null && rowKeys[1] === "title") {
this.parsePersonalInformationRecord(cipher, row);
}
if (rowKeys[0] === "title" && rowKeys[1] === "note") {
this.parseSecureNoteRecords(cipher, row);
}
this.convertToNoteIfNeeded(cipher);
this.cleanupCipher(cipher);
result.ciphers.push(cipher);
});
if (this.organization) {
this.moveFoldersToCollections(result);
}
result.success = true;
return Promise.resolve(result);
}
parseCredentialsRecord(cipher: CipherView, row: CredentialsRecord) {
cipher.type = CipherType.Login;
cipher.login = new LoginView();
cipher.name = row.title;
cipher.notes = row.note;
cipher.login.username = row.username;
cipher.login.password = row.password;
cipher.login.totp = row.otpSecret;
cipher.login.uris = this.makeUriArray(row.url);
this.importUnmappedFields(cipher, row, _mappedCredentialsColums);
}
parsePaymentRecord(cipher: CipherView, row: PaymentsRecord) {
cipher.type = CipherType.Card;
cipher.card = new CardView();
cipher.name = row.account_name;
let mappedValues: string[] = [];
switch (row.type) {
case "credit_card":
cipher.card.cardholderName = row.account_name;
cipher.card.number = row.cc_number;
cipher.card.brand = this.getCardBrand(cipher.card.number);
cipher.card.code = row.code;
cipher.card.expMonth = row.expiration_month;
cipher.card.expYear = row.expiration_year.substring(2, 4);
// If you add more mapped fields please extend this
mappedValues = [
"account_name",
"account_holder",
"cc_number",
"code",
"expiration_month",
"expiration_year",
];
break;
case "bank":
cipher.card.cardholderName = row.account_holder;
cipher.card.number = row.account_number;
// If you add more mapped fields please extend this
mappedValues = ["account_name", "account_holder", "account_number"];
break;
default:
break;
}
this.importUnmappedFields(cipher, row, new Set(mappedValues));
}
parseIdRecord(cipher: CipherView, row: IdRecord) {
cipher.type = CipherType.Identity;
cipher.identity = new IdentityView();
const mappedValues: string[] = ["name", "number"];
switch (row.type) {
case "card":
cipher.name = `${row.name} ${row.type}`;
this.processFullName(cipher, row.name);
cipher.identity.licenseNumber = row.number;
break;
case "passport":
cipher.name = `${row.name} ${row.type}`;
this.processFullName(cipher, row.name);
cipher.identity.passportNumber = row.number;
break;
case "license":
cipher.name = `${row.name} ${row.type}`;
this.processFullName(cipher, row.name);
cipher.identity.licenseNumber = row.number;
cipher.identity.state = row.state;
mappedValues.push("state");
break;
case "social_security":
cipher.name = `${row.name} ${row.type}`;
this.processFullName(cipher, row.name);
cipher.identity.ssn = row.number;
break;
case "tax_number":
cipher.name = row.type;
cipher.identity.licenseNumber = row.number;
break;
default:
break;
}
// If you add more mapped fields please extend this
this.importUnmappedFields(cipher, row, new Set(mappedValues));
}
parsePersonalInformationRecord(cipher: CipherView, row: PersonalInformationRecord) {
cipher.type = CipherType.SecureNote;
cipher.secureNote.type = SecureNoteType.Generic;
if (row.type === "name") {
cipher.name = `${row.title} ${row.first_name} ${row.middle_name} ${row.last_name}`
.replace(" ", " ")
.trim();
} else {
cipher.name = row.item_name;
}
const dataRow = row as any;
Object.keys(row).forEach((key) => {
this.processKvp(cipher, key, dataRow[key]);
});
}
parsePersonalInformationRecordAsIdentity(cipher: CipherView, row: PersonalInformationRecord) {
switch (row.type) {
case "name":
this.processFullName(cipher, `${row.first_name} ${row.middle_name} ${row.last_name}`);
cipher.identity.title = row.title;
cipher.name = cipher.identity.fullName;
cipher.identity.username = row.login;
break;
case "email":
cipher.identity.email = row.email;
break;
case "number":
cipher.identity.phone = row.phone_number;
break;
case "address":
cipher.identity.address1 = row.address;
cipher.identity.city = row.city;
cipher.identity.postalCode = row.zip;
cipher.identity.state = row.state;
cipher.identity.country = row.country;
break;
default:
break;
}
this.importUnmappedFields(cipher, row, _mappedPersonalInfoAsIdentiyColumns);
}
parseSecureNoteRecords(cipher: CipherView, row: SecureNoteRecord) {
cipher.type = CipherType.SecureNote;
cipher.secureNote.type = SecureNoteType.Generic;
cipher.name = row.title;
cipher.notes = row.note;
this.importUnmappedFields(cipher, row, _mappedSecureNoteColumns);
}
importUnmappedFields(cipher: CipherView, row: any, mappedValues: Set<string>) {
const unmappedFields = Object.keys(row).filter((x) => !mappedValues.has(x));
unmappedFields.forEach((key) => {
const item = row as any;
this.processKvp(cipher, key, item[key]);
});
}
}

View File

@@ -1,15 +1,12 @@
import { BaseImporter } from "./baseImporter";
import { Importer } from "./importer";
import { ImportResult } from "../models/domain/importResult";
import { CardView } from "../models/view/cardView";
import { CipherView } from "../models/view/cipherView";
import { IdentityView } from "../models/view/identityView";
import { SecureNoteView } from "../models/view/secureNoteView";
import { CipherType } from "../enums/cipherType";
import { SecureNoteType } from "../enums/secureNoteType";
import { CipherType } from "../../enums/cipherType";
import { SecureNoteType } from "../../enums/secureNoteType";
import { ImportResult } from "../../models/domain/importResult";
import { CardView } from "../../models/view/cardView";
import { CipherView } from "../../models/view/cipherView";
import { IdentityView } from "../../models/view/identityView";
import { SecureNoteView } from "../../models/view/secureNoteView";
import { BaseImporter } from "../baseImporter";
import { Importer } from "../importer";
const HandledResults = new Set([
"ADDRESS",
@@ -53,6 +50,7 @@ export class DashlaneJsonImporter extends BaseImporter implements Importer {
}
for (const key in results) {
// eslint-disable-next-line
if (results.hasOwnProperty(key) && !HandledResults.has(key)) {
this.processNote(results[key], null, "Generic Note");
}
@@ -161,6 +159,7 @@ export class DashlaneJsonImporter extends BaseImporter implements Importer {
cipher.name = this.getValueOrDefault(obj[nameProperty]);
}
for (const key in obj) {
// eslint-disable-next-line
if (obj.hasOwnProperty(key) && key !== nameProperty) {
this.processKvp(cipher, key, obj[key].toString());
}

View File

@@ -0,0 +1,68 @@
// tslint:disable
export class CredentialsRecord {
username: string;
username2: string;
username3: string;
title: string;
password: string;
note: string;
url: string;
category: string;
otpSecret: string;
}
export class PaymentsRecord {
type: string;
account_name: string;
account_holder: string;
cc_number: string;
code: string;
expiration_month: string;
expiration_year: string;
routing_number: string;
account_number: string;
country: string;
issuing_bank: string;
}
export class IdRecord {
type: string;
number: string;
name: string;
issue_date: string;
expiration_date: string;
place_of_issue: string;
state: string;
}
export class PersonalInformationRecord {
type: string;
title: string;
first_name: string;
middle_name: string;
last_name: string;
login: string;
date_of_birth: string;
place_of_birth: string;
email: string;
email_type: string;
item_name: string;
phone_number: string;
address: string;
country: string;
state: string;
city: string;
zip: string;
address_recipient: string;
address_building: string;
address_apartment: string;
address_floor: string;
address_door_code: string;
job_title: string;
url: string;
}
export class SecureNoteRecord {
title: string;
note: string;
}

View File

@@ -1,11 +1,9 @@
import { BaseImporter } from "./baseImporter";
import { Importer } from "./importer";
import { CipherType } from "../enums/cipherType";
import { ImportResult } from "../models/domain/importResult";
import { CardView } from "../models/view/cardView";
import { CipherType } from "../enums/cipherType";
import { BaseImporter } from "./baseImporter";
import { Importer } from "./importer";
export class EncryptrCsvImporter extends BaseImporter implements Importer {
parse(data: string): Promise<ImportResult> {

View File

@@ -1,14 +1,12 @@
import { BaseImporter } from "./baseImporter";
import { Importer } from "./importer";
import { ImportResult } from "../models/domain/importResult";
import { CipherType } from "../enums/cipherType";
import { SecureNoteType } from "../enums/secureNoteType";
import { ImportResult } from "../models/domain/importResult";
import { CardView } from "../models/view/cardView";
import { SecureNoteView } from "../models/view/secureNoteView";
import { BaseImporter } from "./baseImporter";
import { Importer } from "./importer";
export class EnpassCsvImporter extends BaseImporter implements Importer {
parse(data: string): Promise<ImportResult> {
const result = new ImportResult();

View File

@@ -1,14 +1,12 @@
import { BaseImporter } from "./baseImporter";
import { Importer } from "./importer";
import { CipherType } from "../enums/cipherType";
import { FieldType } from "../enums/fieldType";
import { ImportResult } from "../models/domain/importResult";
import { CardView } from "../models/view/cardView";
import { CipherView } from "../models/view/cipherView";
import { FolderView } from "../models/view/folderView";
import { CipherType } from "../enums/cipherType";
import { FieldType } from "../enums/fieldType";
import { BaseImporter } from "./baseImporter";
import { Importer } from "./importer";
export class EnpassJsonImporter extends BaseImporter implements Importer {
parse(data: string): Promise<ImportResult> {

View File

@@ -1,8 +1,8 @@
import { ImportResult } from "../models/domain/importResult";
import { BaseImporter } from "./baseImporter";
import { Importer } from "./importer";
import { ImportResult } from "../models/domain/importResult";
export class FirefoxCsvImporter extends BaseImporter implements Importer {
parse(data: string): Promise<ImportResult> {
const result = new ImportResult();

View File

@@ -1,11 +1,9 @@
import { BaseImporter } from "./baseImporter";
import { Importer } from "./importer";
import { CipherType } from "../enums/cipherType";
import { ImportResult } from "../models/domain/importResult";
import { CardView } from "../models/view/cardView";
import { CipherType } from "../enums/cipherType";
import { BaseImporter } from "./baseImporter";
import { Importer } from "./importer";
export class FSecureFskImporter extends BaseImporter implements Importer {
parse(data: string): Promise<ImportResult> {
@@ -17,6 +15,7 @@ export class FSecureFskImporter extends BaseImporter implements Importer {
}
for (const key in results.data) {
// eslint-disable-next-line
if (!results.data.hasOwnProperty(key)) {
continue;
}

View File

@@ -1,8 +1,8 @@
import { ImportResult } from "../models/domain/importResult";
import { BaseImporter } from "./baseImporter";
import { Importer } from "./importer";
import { ImportResult } from "../models/domain/importResult";
export class GnomeJsonImporter extends BaseImporter implements Importer {
parse(data: string): Promise<ImportResult> {
const result = new ImportResult();
@@ -14,7 +14,7 @@ export class GnomeJsonImporter extends BaseImporter implements Importer {
for (const keyRing in results) {
if (
!results.hasOwnProperty(keyRing) ||
!results.hasOwnProperty(keyRing) || // eslint-disable-line
this.isNullOrWhitespace(keyRing) ||
results[keyRing].length === 0
) {
@@ -45,7 +45,7 @@ export class GnomeJsonImporter extends BaseImporter implements Importer {
: null;
for (const attr in value.attributes) {
if (
!value.attributes.hasOwnProperty(attr) ||
!value.attributes.hasOwnProperty(attr) || // eslint-disable-line
attr === "username_value" ||
attr === "xdg:schema"
) {

View File

@@ -0,0 +1,5 @@
export class ImportError extends Error {
constructor(message?: string, public passwordRequired: boolean = false) {
super(message);
}
}

View File

@@ -1,8 +1,8 @@
import { ImportResult } from "../models/domain/importResult";
import { BaseImporter } from "./baseImporter";
import { Importer } from "./importer";
import { ImportResult } from "../models/domain/importResult";
const NotesHeader = "Notes\n\n";
const ApplicationsHeader = "Applications\n\n";
const WebsitesHeader = "Websites\n\n";

View File

@@ -1,12 +1,10 @@
import { FieldType } from "../enums/fieldType";
import { ImportResult } from "../models/domain/importResult";
import { FolderView } from "../models/view/folderView";
import { BaseImporter } from "./baseImporter";
import { Importer } from "./importer";
import { FieldType } from "../enums/fieldType";
import { ImportResult } from "../models/domain/importResult";
import { FolderView } from "../models/view/folderView";
export class KeePass2XmlImporter extends BaseImporter implements Importer {
result = new ImportResult();

View File

@@ -1,8 +1,8 @@
import { ImportResult } from "../models/domain/importResult";
import { BaseImporter } from "./baseImporter";
import { Importer } from "./importer";
import { ImportResult } from "../models/domain/importResult";
export class KeePassXCsvImporter extends BaseImporter implements Importer {
parse(data: string): Promise<ImportResult> {
const result = new ImportResult();

View File

@@ -1,9 +1,6 @@
import { BaseImporter } from "./baseImporter";
import { Importer } from "./importer";
import { ImportResult } from "../models/domain/importResult";
import { FolderView } from "../models/view/folderView";
import { ImportResult } from "../../models/domain/importResult";
import { BaseImporter } from "../baseImporter";
import { Importer } from "../importer";
export class KeeperCsvImporter extends BaseImporter implements Importer {
parse(data: string): Promise<ImportResult> {

View File

@@ -0,0 +1,69 @@
import { ImportResult } from "../../models/domain/importResult";
import { BaseImporter } from "../baseImporter";
import { Importer } from "../importer";
import { KeeperJsonExport, RecordsEntity } from "./types/keeperJsonTypes";
export class KeeperJsonImporter extends BaseImporter implements Importer {
parse(data: string): Promise<ImportResult> {
const result = new ImportResult();
const keeperExport: KeeperJsonExport = JSON.parse(data);
if (keeperExport == null || keeperExport.records == null || keeperExport.records.length === 0) {
result.success = false;
return Promise.resolve(result);
}
keeperExport.records.forEach((record) => {
this.parseFolders(result, record);
const cipher = this.initLoginCipher();
cipher.name = record.title;
cipher.login.username = record.login;
cipher.login.password = record.password;
cipher.login.uris = this.makeUriArray(record.login_url);
cipher.notes = record.notes;
if (record.custom_fields != null) {
let customfieldKeys = Object.keys(record.custom_fields);
if (record.custom_fields["TFC:Keeper"] != null) {
customfieldKeys = customfieldKeys.filter((item) => item !== "TFC:Keeper");
cipher.login.totp = record.custom_fields["TFC:Keeper"];
}
customfieldKeys.forEach((key) => {
this.processKvp(cipher, key, record.custom_fields[key]);
});
}
this.convertToNoteIfNeeded(cipher);
this.cleanupCipher(cipher);
result.ciphers.push(cipher);
});
if (this.organization) {
this.moveFoldersToCollections(result);
}
result.success = true;
return Promise.resolve(result);
}
private parseFolders(result: ImportResult, record: RecordsEntity) {
if (record.folders == null || record.folders.length === 0) {
return;
}
record.folders.forEach((item) => {
if (item.folder != null) {
this.processFolder(result, item.folder);
return;
}
if (item.shared_folder != null) {
this.processFolder(result, item.shared_folder);
return;
}
});
}
}

View File

@@ -0,0 +1,41 @@
export interface KeeperJsonExport {
shared_folders?: SharedFoldersEntity[] | null;
records?: RecordsEntity[] | null;
}
export interface SharedFoldersEntity {
path: string;
manage_users: boolean;
manage_records: boolean;
can_edit: boolean;
can_share: boolean;
permissions?: PermissionsEntity[] | null;
}
export interface PermissionsEntity {
uid?: string | null;
manage_users: boolean;
manage_records: boolean;
name?: string | null;
}
export interface RecordsEntity {
title: string;
login: string;
password: string;
login_url: string;
notes?: string;
custom_fields?: CustomFields;
folders?: FoldersEntity[] | null;
}
export type CustomFields = {
[key: string]: string | null;
};
export interface FoldersEntity {
folder?: string | null;
shared_folder?: string | null;
can_edit?: boolean | null;
can_share?: boolean | null;
}

View File

@@ -1,8 +1,6 @@
import { BaseImporter } from "./baseImporter";
import { Importer } from "./importer";
import { CipherType } from "../enums/cipherType";
import { SecureNoteType } from "../enums/secureNoteType";
import { ImportResult } from "../models/domain/importResult";
import { CardView } from "../models/view/cardView";
import { CipherView } from "../models/view/cipherView";
import { FolderView } from "../models/view/folderView";
@@ -10,8 +8,8 @@ import { IdentityView } from "../models/view/identityView";
import { LoginView } from "../models/view/loginView";
import { SecureNoteView } from "../models/view/secureNoteView";
import { CipherType } from "../enums/cipherType";
import { SecureNoteType } from "../enums/secureNoteType";
import { BaseImporter } from "./baseImporter";
import { Importer } from "./importer";
export class LastPassCsvImporter extends BaseImporter implements Importer {
parse(data: string): Promise<ImportResult> {
@@ -22,11 +20,12 @@ export class LastPassCsvImporter extends BaseImporter implements Importer {
return Promise.resolve(result);
}
results.forEach((value, index) => {
results.forEach((value) => {
const cipherIndex = result.ciphers.length;
let folderIndex = result.folders.length;
let grouping = value.grouping;
if (grouping != null) {
// eslint-disable-next-line
grouping = grouping.replace(/\\/g, "/").replace(/[\x00-\x1F\x7F-\x9F]/g, "");
}
const hasFolder = this.getValueOrDefault(grouping, "(none)") !== "(none)";
@@ -90,6 +89,7 @@ export class LastPassCsvImporter extends BaseImporter implements Importer {
private buildBaseCipher(value: any) {
const cipher = new CipherView();
// eslint-disable-next-line
if (value.hasOwnProperty("profilename") && value.hasOwnProperty("profilelanguage")) {
// form fill
cipher.favorite = false;
@@ -272,6 +272,7 @@ export class LastPassCsvImporter extends BaseImporter implements Importer {
cipher.notes = val;
}
processingNotes = true;
// eslint-disable-next-line
} else if (map.hasOwnProperty(key)) {
dataObj[map[key]] = val;
} else {

View File

@@ -1,8 +1,8 @@
import { ImportResult } from "../models/domain/importResult";
import { BaseImporter } from "./baseImporter";
import { Importer } from "./importer";
import { ImportResult } from "../models/domain/importResult";
export class LogMeOnceCsvImporter extends BaseImporter implements Importer {
parse(data: string): Promise<ImportResult> {
const result = new ImportResult();

View File

@@ -1,8 +1,8 @@
import { ImportResult } from "../models/domain/importResult";
import { BaseImporter } from "./baseImporter";
import { Importer } from "./importer";
import { ImportResult } from "../models/domain/importResult";
export class MeldiumCsvImporter extends BaseImporter implements Importer {
parse(data: string): Promise<ImportResult> {
const result = new ImportResult();

View File

@@ -1,13 +1,11 @@
import { BaseImporter } from "./baseImporter";
import { Importer } from "./importer";
import { ImportResult } from "../models/domain/importResult";
import { CipherType } from "../enums/cipherType";
import { SecureNoteType } from "../enums/secureNoteType";
import { ImportResult } from "../models/domain/importResult";
import { SecureNoteView } from "../models/view/secureNoteView";
import { BaseImporter } from "./baseImporter";
import { Importer } from "./importer";
export class MSecureCsvImporter extends BaseImporter implements Importer {
parse(data: string): Promise<ImportResult> {
const result = new ImportResult();

View File

@@ -1,14 +1,42 @@
import { BaseImporter } from "./baseImporter";
import { Importer } from "./importer";
import { CipherType } from "../enums/cipherType";
import { SecureNoteType } from "../enums/secureNoteType";
import { ImportResult } from "../models/domain/importResult";
import { CardView } from "../models/view/cardView";
import { CipherView } from "../models/view/cipherView";
import { IdentityView } from "../models/view/identityView";
import { SecureNoteView } from "../models/view/secureNoteView";
import { ImportResult } from "../models/domain/importResult";
import { BaseImporter } from "./baseImporter";
import { Importer } from "./importer";
const mappedBaseColumns = ["nickname", "additionalInfo"];
const _mappedUserAccountColumns = new Set(
mappedBaseColumns.concat(["url", "username", "password", "twofaSecret"])
);
const _mappedCreditCardColumns = new Set(
mappedBaseColumns.concat(["cardNumber", "cardName", "exp_month", "exp_year", "cvv"])
);
const _mappedIdentityColumns = new Set(
mappedBaseColumns.concat([
"title",
"firstName",
"middleName",
"lastName",
"email",
"firstAddressLine",
"secondAddressLine",
"city",
"country",
"zipCode",
])
);
const _mappedIdCardColumns = new Set(mappedBaseColumns.concat(["idName", "idNumber", "idCountry"]));
const _mappedTwoFaColumns = new Set(mappedBaseColumns.concat(["authToken"]));
const _mappedUserNoteColumns = new Set(mappedBaseColumns.concat(["content"]));
export class MykiCsvImporter extends BaseImporter implements Importer {
parse(data: string): Promise<ImportResult> {
@@ -29,7 +57,14 @@ export class MykiCsvImporter extends BaseImporter implements Importer {
cipher.login.uris = this.makeUriArray(value.url);
cipher.login.username = this.getValueOrDefault(value.username);
cipher.login.password = this.getValueOrDefault(value.password);
cipher.login.totp = this.getValueOrDefault(value.twoFactAuthToken);
cipher.login.totp = this.getValueOrDefault(value.twofaSecret);
this.importUnmappedFields(cipher, value, _mappedUserAccountColumns);
} else if (value.authToken !== undefined) {
// TwoFA
cipher.login.totp = this.getValueOrDefault(value.authToken);
this.importUnmappedFields(cipher, value, _mappedTwoFaColumns);
} else if (value.cardNumber !== undefined) {
// Cards
cipher.card = new CardView();
@@ -40,6 +75,8 @@ export class MykiCsvImporter extends BaseImporter implements Importer {
cipher.card.expMonth = this.getValueOrDefault(value.exp_month);
cipher.card.expYear = this.getValueOrDefault(value.exp_year);
cipher.card.code = this.getValueOrDefault(value.cvv);
this.importUnmappedFields(cipher, value, _mappedCreditCardColumns);
} else if (value.firstName !== undefined) {
// Identities
cipher.identity = new IdentityView();
@@ -55,13 +92,49 @@ export class MykiCsvImporter extends BaseImporter implements Importer {
cipher.identity.city = this.getValueOrDefault(value.city);
cipher.identity.country = this.getValueOrDefault(value.country);
cipher.identity.postalCode = this.getValueOrDefault(value.zipCode);
this.importUnmappedFields(cipher, value, _mappedIdentityColumns);
} else if (value.idType !== undefined) {
// IdCards
cipher.identity = new IdentityView();
cipher.type = CipherType.Identity;
this.processFullName(cipher, value.idName);
cipher.identity.country = this.getValueOrDefault(value.idCountry);
switch (value.idType) {
// case "Driver's License":
// case "ID Card":
// case "Outdoor License":
// case "Software License":
// case "Tax Number":
// case "Bank Account":
// case "Insurance Card":
// case "Health Card":
// case "Membership":
// case "Database":
// case "Reward Program":
// case "Tour Visa":
case "Passport":
cipher.identity.passportNumber = value.idNumber;
break;
case "Social Security":
cipher.identity.ssn = value.idNumber;
break;
default:
cipher.identity.licenseNumber = value.idNumber;
break;
}
this.importUnmappedFields(cipher, value, _mappedIdCardColumns);
} else if (value.content !== undefined) {
// Notes
cipher.secureNote = new SecureNoteView();
cipher.type = CipherType.SecureNote;
cipher.secureNote.type = SecureNoteType.Generic;
cipher.name = this.getValueOrDefault(value.title, "--");
cipher.notes = this.getValueOrDefault(value.content);
this.importUnmappedFields(cipher, value, _mappedUserNoteColumns);
} else {
return;
}
@@ -73,4 +146,12 @@ export class MykiCsvImporter extends BaseImporter implements Importer {
result.success = true;
return Promise.resolve(result);
}
importUnmappedFields(cipher: CipherView, row: any, mappedValues: Set<string>) {
const unmappedFields = Object.keys(row).filter((x) => !mappedValues.has(x));
unmappedFields.forEach((key) => {
const item = row as any;
this.processKvp(cipher, key, item[key]);
});
}
}

View File

@@ -1,13 +1,11 @@
import { BaseImporter } from "./baseImporter";
import { Importer } from "./importer";
import { CipherType } from "../enums/cipherType";
import { SecureNoteType } from "../enums/secureNoteType";
import { ImportResult } from "../models/domain/importResult";
import { CipherView } from "../models/view/cipherView";
import { LoginView } from "../models/view/loginView";
import { CipherType } from "../enums/cipherType";
import { SecureNoteType } from "../enums/secureNoteType";
import { BaseImporter } from "./baseImporter";
import { Importer } from "./importer";
type nodePassCsvParsed = {
name: string;
@@ -74,7 +72,7 @@ export class NordPassCsvImporter extends BaseImporter implements Importer {
case CipherType.Identity:
cipher.type = CipherType.Identity;
this.processName(cipher, this.getValueOrDefault(record.full_name));
this.processFullName(cipher, this.getValueOrDefault(record.full_name));
cipher.identity.address1 = this.getValueOrDefault(record.address1);
cipher.identity.address2 = this.getValueOrDefault(record.address2);
cipher.identity.city = this.getValueOrDefault(record.city);
@@ -126,21 +124,4 @@ export class NordPassCsvImporter extends BaseImporter implements Importer {
return undefined;
}
private processName(cipher: CipherView, fullName: string) {
if (this.isNullOrWhitespace(fullName)) {
return;
}
const nameParts = fullName.split(" ");
if (nameParts.length > 0) {
cipher.identity.firstName = this.getValueOrDefault(nameParts[0]);
}
if (nameParts.length === 2) {
cipher.identity.lastName = this.getValueOrDefault(nameParts[1]);
} else if (nameParts.length >= 3) {
cipher.identity.middleName = this.getValueOrDefault(nameParts[1]);
cipher.identity.lastName = nameParts.slice(2, nameParts.length).join(" ");
}
}
}

View File

@@ -1,17 +1,14 @@
import { BaseImporter } from "../baseImporter";
import { Importer } from "../importer";
import { CipherType } from "../../enums/cipherType";
import { FieldType } from "../../enums/fieldType";
import { SecureNoteType } from "../../enums/secureNoteType";
import { ImportResult } from "../../models/domain/importResult";
import { CardView } from "../../models/view/cardView";
import { CipherView } from "../../models/view/cipherView";
import { IdentityView } from "../../models/view/identityView";
import { PasswordHistoryView } from "../../models/view/passwordHistoryView";
import { SecureNoteView } from "../../models/view/secureNoteView";
import { CipherType } from "../../enums/cipherType";
import { FieldType } from "../../enums/fieldType";
import { SecureNoteType } from "../../enums/secureNoteType";
import { BaseImporter } from "../baseImporter";
import { Importer } from "../importer";
export class OnePassword1PifImporter extends BaseImporter implements Importer {
result = new ImportResult();
@@ -172,7 +169,11 @@ export class OnePassword1PifImporter extends BaseImporter implements Importer {
return;
}
const fieldValue = field[valueKey].toString();
// TODO: when date FieldType exists, store this as a date field type instead of formatted Text if k is 'date'
const fieldValue =
field.k === "date"
? new Date(field[valueKey] * 1000).toUTCString()
: field[valueKey].toString();
const fieldDesignation =
field[designationKey] != null ? field[designationKey].toString() : null;

View File

@@ -0,0 +1,615 @@
import { CipherRepromptType } from "../../enums/cipherRepromptType";
import { CipherType } from "../../enums/cipherType";
import { FieldType } from "../../enums/fieldType";
import { SecureNoteType } from "../../enums/secureNoteType";
import { ImportResult } from "../../models/domain/importResult";
import { CardView } from "../../models/view/cardView";
import { CipherView } from "../../models/view/cipherView";
import { IdentityView } from "../../models/view/identityView";
import { LoginView } from "../../models/view/loginView";
import { PasswordHistoryView } from "../../models/view/passwordHistoryView";
import { SecureNoteView } from "../../models/view/secureNoteView";
import { BaseImporter } from "../baseImporter";
import { Importer } from "../importer";
import {
CategoryEnum,
Details,
ExportData,
FieldsEntity,
Item,
LoginFieldTypeEnum,
Overview,
PasswordHistoryEntity,
SectionsEntity,
UrlsEntity,
Value,
VaultsEntity,
} from "./types/onepassword1PuxImporterTypes";
export class OnePassword1PuxImporter extends BaseImporter implements Importer {
result = new ImportResult();
parse(data: string): Promise<ImportResult> {
const exportData: ExportData = JSON.parse(data);
const account = exportData.accounts[0];
// TODO Add handling of multiple vaults
// const personalVaults = account.vaults[0].filter((v) => v.attrs.type === VaultAttributeTypeEnum.Personal);
account.vaults.forEach((vault: VaultsEntity) => {
vault.items.forEach((item: Item) => {
if (item.trashed === true) {
return;
}
const cipher = this.initLoginCipher();
const category = item.categoryUuid as CategoryEnum;
switch (category) {
case CategoryEnum.Login:
case CategoryEnum.Database:
case CategoryEnum.Password:
case CategoryEnum.WirelessRouter:
case CategoryEnum.Server:
case CategoryEnum.API_Credential:
cipher.type = CipherType.Login;
cipher.login = new LoginView();
break;
case CategoryEnum.CreditCard:
case CategoryEnum.BankAccount:
cipher.type = CipherType.Card;
cipher.card = new CardView();
break;
case CategoryEnum.SecureNote:
case CategoryEnum.SoftwareLicense:
case CategoryEnum.EmailAccount:
case CategoryEnum.MedicalRecord:
// case CategoryEnum.Document:
cipher.type = CipherType.SecureNote;
cipher.secureNote = new SecureNoteView();
cipher.secureNote.type = SecureNoteType.Generic;
break;
case CategoryEnum.Identity:
case CategoryEnum.DriversLicense:
case CategoryEnum.OutdoorLicense:
case CategoryEnum.Membership:
case CategoryEnum.Passport:
case CategoryEnum.RewardsProgram:
case CategoryEnum.SocialSecurityNumber:
cipher.type = CipherType.Identity;
cipher.identity = new IdentityView();
break;
default:
break;
}
cipher.favorite = item.favIndex === 1 ? true : false;
this.processOverview(item.overview, cipher);
this.processLoginFields(item, cipher);
this.processDetails(category, item.details, cipher);
this.parsePasswordHistory(item.details.passwordHistory, cipher);
this.processSections(category, item.details.sections, cipher);
if (!this.isNullOrWhitespace(item.details.notesPlain)) {
cipher.notes = item.details.notesPlain.split(this.newLineRegex).join("\n") + "\n";
}
this.convertToNoteIfNeeded(cipher);
this.cleanupCipher(cipher);
this.result.ciphers.push(cipher);
});
});
if (this.organization) {
this.moveFoldersToCollections(this.result);
}
this.result.success = true;
return Promise.resolve(this.result);
}
private processOverview(overview: Overview, cipher: CipherView) {
if (overview == null) {
return;
}
cipher.name = this.getValueOrDefault(overview.title);
if (overview.urls != null) {
const urls: string[] = [];
overview.urls.forEach((url: UrlsEntity) => {
if (!this.isNullOrWhitespace(url.url)) {
urls.push(url.url);
}
});
cipher.login.uris = this.makeUriArray(urls);
}
if (overview.tags != null && overview.tags.length > 0) {
const folderName = this.capitalize(overview.tags[0]);
this.processFolder(this.result, folderName);
}
}
private capitalize(inputString: string): string {
return inputString.trim().replace(/\w\S*/g, (w) => w.replace(/^\w/, (c) => c.toUpperCase()));
}
private processLoginFields(item: Item, cipher: CipherView) {
if (item.details == null) {
return;
}
if (item.details.loginFields == null || item.details.loginFields.length === 0) {
return;
}
item.details.loginFields.forEach((loginField) => {
if (loginField.designation === "username" && loginField.value !== "") {
cipher.type = CipherType.Login;
cipher.login.username = loginField.value;
return;
}
if (loginField.designation === "password" && loginField.value !== "") {
cipher.type = CipherType.Login;
cipher.login.password = loginField.value;
return;
}
let fieldValue = loginField.value;
let fieldType: FieldType = FieldType.Text;
switch (loginField.fieldType) {
case LoginFieldTypeEnum.Password:
fieldType = FieldType.Hidden;
break;
case LoginFieldTypeEnum.CheckBox:
fieldValue = loginField.value !== "" ? "true" : "false";
fieldType = FieldType.Boolean;
break;
default:
break;
}
this.processKvp(cipher, loginField.name, fieldValue, fieldType);
});
}
private processDetails(category: CategoryEnum, details: Details, cipher: CipherView) {
if (category !== CategoryEnum.Password) {
return;
}
if (details == null) {
return;
}
cipher.login.password = details.password;
}
private processSections(category: CategoryEnum, sections: SectionsEntity[], cipher: CipherView) {
if (sections == null || sections.length === 0) {
return;
}
sections.forEach((section: SectionsEntity) => {
if (section.fields == null) {
return;
}
this.parseSectionFields(category, section.fields, cipher);
});
}
private parseSectionFields(category: CategoryEnum, fields: FieldsEntity[], cipher: CipherView) {
fields.forEach((field: FieldsEntity) => {
const valueKey = Object.keys(field.value)[0];
const anyField = field as any;
if (
anyField.value == null ||
anyField.value[valueKey] == null ||
anyField.value[valueKey] === ""
) {
return;
}
const fieldName = this.getFieldName(field.id, field.title);
const fieldValue = this.extractValue(field.value, valueKey);
if (cipher.type === CipherType.Login) {
if (this.fillLogin(field, fieldValue, cipher)) {
return;
}
switch (category) {
case CategoryEnum.Login:
case CategoryEnum.Database:
case CategoryEnum.EmailAccount:
case CategoryEnum.WirelessRouter:
break;
case CategoryEnum.Server:
if (this.isNullOrWhitespace(cipher.login.uri) && field.id === "url") {
cipher.login.uris = this.makeUriArray(fieldValue);
return;
}
break;
case CategoryEnum.API_Credential:
if (this.fillApiCredentials(field, fieldValue, cipher)) {
return;
}
break;
default:
break;
}
} else if (cipher.type === CipherType.Card) {
if (this.fillCreditCard(field, fieldValue, cipher)) {
return;
}
if (category === CategoryEnum.BankAccount) {
if (this.fillBankAccount(field, fieldValue, cipher)) {
return;
}
}
} else if (cipher.type === CipherType.Identity) {
if (this.fillIdentity(field, fieldValue, cipher)) {
return;
}
if (valueKey === "address") {
// fieldValue is an object casted into a string, so access the plain value instead
const { street, city, country, zip, state } = field.value.address;
cipher.identity.address1 = this.getValueOrDefault(street);
cipher.identity.city = this.getValueOrDefault(city);
if (!this.isNullOrWhitespace(country)) {
cipher.identity.country = country.toUpperCase();
}
cipher.identity.postalCode = this.getValueOrDefault(zip);
cipher.identity.state = this.getValueOrDefault(state);
return;
}
switch (category) {
case CategoryEnum.Identity:
break;
case CategoryEnum.DriversLicense:
if (this.fillDriversLicense(field, fieldValue, cipher)) {
return;
}
break;
case CategoryEnum.OutdoorLicense:
if (this.fillOutdoorLicense(field, fieldValue, cipher)) {
return;
}
break;
case CategoryEnum.Membership:
if (this.fillMembership(field, fieldValue, cipher)) {
return;
}
break;
case CategoryEnum.Passport:
if (this.fillPassport(field, fieldValue, cipher)) {
return;
}
break;
case CategoryEnum.RewardsProgram:
if (this.fillRewardsProgram(field, fieldValue, cipher)) {
return;
}
break;
case CategoryEnum.SocialSecurityNumber:
if (this.fillSSN(field, fieldValue, cipher)) {
return;
}
break;
default:
break;
}
}
// Do not include a password field if it's already in the history
if (
field.title === "password" &&
cipher.passwordHistory != null &&
cipher.passwordHistory.some((h) => h.password === fieldValue)
) {
return;
}
// TODO ?? If one of the fields is marked as guarded, then activate Password-Reprompt for the entire item
if (field.guarded && cipher.reprompt === CipherRepromptType.None) {
cipher.reprompt = CipherRepromptType.Password;
}
const fieldType = valueKey === "concealed" ? FieldType.Hidden : FieldType.Text;
this.processKvp(cipher, fieldName, fieldValue, fieldType);
});
}
private getFieldName(id: string, title: string): string {
if (this.isNullOrWhitespace(title)) {
return id;
}
// Naive approach of checking if the fields id is usable
if (id.length > 25 && RegExp(/[0-9]{2}[A-Z]{2}/, "i").test(id)) {
return title;
}
return id;
}
private extractValue(value: Value, valueKey: string): string {
if (valueKey === "date") {
return new Date(value.date * 1000).toUTCString();
}
if (valueKey === "monthYear") {
return value.monthYear.toString();
}
return (value as any)[valueKey];
}
private fillLogin(field: FieldsEntity, fieldValue: string, cipher: CipherView): boolean {
const fieldName = this.getFieldName(field.id, field.title);
if (this.isNullOrWhitespace(cipher.login.username) && fieldName === "username") {
cipher.login.username = fieldValue;
return true;
}
if (this.isNullOrWhitespace(cipher.login.password) && fieldName === "password") {
cipher.login.password = fieldValue;
return true;
}
if (
this.isNullOrWhitespace(cipher.login.totp) &&
field.id != null &&
field.id.startsWith("TOTP_")
) {
cipher.login.totp = fieldValue;
return true;
}
return false;
}
private fillApiCredentials(field: FieldsEntity, fieldValue: string, cipher: CipherView): boolean {
const fieldName = this.getFieldName(field.id, field.title);
if (this.isNullOrWhitespace(cipher.login.password) && fieldName === "credential") {
cipher.login.password = fieldValue;
return true;
}
if (this.isNullOrWhitespace(cipher.login.uri) && fieldName === "hostname") {
cipher.login.uris = this.makeUriArray(fieldValue);
return true;
}
return false;
}
private fillCreditCard(field: FieldsEntity, fieldValue: string, cipher: CipherView): boolean {
if (this.isNullOrWhitespace(cipher.card.number) && field.id === "ccnum") {
cipher.card.number = fieldValue;
cipher.card.brand = this.getCardBrand(fieldValue);
return true;
}
if (this.isNullOrWhitespace(cipher.card.code) && field.id === "cvv") {
cipher.card.code = fieldValue;
return true;
}
if (this.isNullOrWhitespace(cipher.card.cardholderName) && field.id === "cardholder") {
cipher.card.cardholderName = fieldValue;
return true;
}
if (this.isNullOrWhitespace(cipher.card.expiration) && field.id === "expiry") {
const monthYear: string = fieldValue.toString().trim();
cipher.card.expMonth = monthYear.substring(4, 6);
if (cipher.card.expMonth[0] === "0") {
cipher.card.expMonth = cipher.card.expMonth.substring(1, 2);
}
cipher.card.expYear = monthYear.substring(0, 4);
return true;
}
if (field.id === "type") {
// Skip since brand was determined from number above
return true;
}
return false;
}
private fillBankAccount(field: FieldsEntity, fieldValue: string, cipher: CipherView): boolean {
if (this.isNullOrWhitespace(cipher.card.cardholderName) && field.id === "owner") {
cipher.card.cardholderName = fieldValue;
return true;
}
return false;
}
private fillIdentity(field: FieldsEntity, fieldValue: string, cipher: CipherView): boolean {
if (this.isNullOrWhitespace(cipher.identity.firstName) && field.id === "firstname") {
cipher.identity.firstName = fieldValue;
return true;
}
if (this.isNullOrWhitespace(cipher.identity.lastName) && field.id === "lastname") {
cipher.identity.lastName = fieldValue;
return true;
}
if (this.isNullOrWhitespace(cipher.identity.middleName) && field.id === "initial") {
cipher.identity.middleName = fieldValue;
return true;
}
if (this.isNullOrWhitespace(cipher.identity.phone) && field.id === "defphone") {
cipher.identity.phone = fieldValue;
return true;
}
if (this.isNullOrWhitespace(cipher.identity.company) && field.id === "company") {
cipher.identity.company = fieldValue;
return true;
}
if (this.isNullOrWhitespace(cipher.identity.email) && field.id === "email") {
cipher.identity.email = fieldValue;
return true;
}
if (this.isNullOrWhitespace(cipher.identity.username) && field.id === "username") {
cipher.identity.username = fieldValue;
return true;
}
return false;
}
private fillDriversLicense(field: FieldsEntity, fieldValue: string, cipher: CipherView): boolean {
if (this.isNullOrWhitespace(cipher.identity.firstName) && field.id === "fullname") {
this.processFullName(cipher, fieldValue);
return true;
}
if (this.isNullOrWhitespace(cipher.identity.address1) && field.id === "address") {
cipher.identity.address1 = fieldValue;
return true;
}
// TODO ISO code
if (this.isNullOrWhitespace(cipher.identity.country) && field.id === "country") {
cipher.identity.country = fieldValue;
return true;
}
if (this.isNullOrWhitespace(cipher.identity.state) && field.id === "state") {
cipher.identity.state = fieldValue;
return true;
}
if (this.isNullOrWhitespace(cipher.identity.licenseNumber) && field.id === "number") {
cipher.identity.licenseNumber = fieldValue;
return true;
}
return false;
}
private fillOutdoorLicense(field: FieldsEntity, fieldValue: string, cipher: CipherView): boolean {
if (this.isNullOrWhitespace(cipher.identity.firstName) && field.id === "name") {
this.processFullName(cipher, fieldValue);
return true;
}
// TODO ISO code
if (this.isNullOrWhitespace(cipher.identity.country) && field.id === "country") {
cipher.identity.country = fieldValue;
return true;
}
if (this.isNullOrWhitespace(cipher.identity.state) && field.id === "state") {
cipher.identity.state = fieldValue;
return true;
}
return false;
}
private fillMembership(field: FieldsEntity, fieldValue: string, cipher: CipherView): boolean {
if (this.isNullOrWhitespace(cipher.identity.firstName) && field.id === "member_name") {
this.processFullName(cipher, fieldValue);
return true;
}
if (this.isNullOrWhitespace(cipher.identity.company) && field.id === "org_name") {
cipher.identity.company = fieldValue;
return true;
}
if (this.isNullOrWhitespace(cipher.identity.phone) && field.id === "phone") {
cipher.identity.phone = fieldValue;
return true;
}
return false;
}
private fillPassport(field: FieldsEntity, fieldValue: string, cipher: CipherView): boolean {
if (this.isNullOrWhitespace(cipher.identity.firstName) && field.id === "fullname") {
this.processFullName(cipher, fieldValue);
return true;
}
// TODO Iso
if (this.isNullOrWhitespace(cipher.identity.country) && field.id === "issuing_country") {
cipher.identity.country = fieldValue;
return true;
}
if (this.isNullOrWhitespace(cipher.identity.passportNumber) && field.id === "number") {
cipher.identity.passportNumber = fieldValue;
return true;
}
return false;
}
private fillRewardsProgram(field: FieldsEntity, fieldValue: string, cipher: CipherView): boolean {
if (this.isNullOrWhitespace(cipher.identity.firstName) && field.id === "member_name") {
this.processFullName(cipher, fieldValue);
return true;
}
if (this.isNullOrWhitespace(cipher.identity.company) && field.id === "company_name") {
cipher.identity.company = fieldValue;
return true;
}
return false;
}
private fillSSN(field: FieldsEntity, fieldValue: string, cipher: CipherView): boolean {
if (this.isNullOrWhitespace(cipher.identity.firstName) && field.id === "name") {
this.processFullName(cipher, fieldValue);
return true;
}
if (this.isNullOrWhitespace(cipher.identity.ssn) && field.id === "number") {
cipher.identity.ssn = fieldValue;
return true;
}
return false;
}
private parsePasswordHistory(historyItems: PasswordHistoryEntity[], cipher: CipherView) {
if (historyItems == null || historyItems.length === 0) {
return;
}
const maxSize = historyItems.length > 5 ? 5 : historyItems.length;
cipher.passwordHistory = historyItems
.filter((h: any) => !this.isNullOrWhitespace(h.value) && h.time != null)
.sort((a, b) => b.time - a.time)
.slice(0, maxSize)
.map((h: any) => {
const ph = new PasswordHistoryView();
ph.password = h.value;
ph.lastUsedDate = new Date(("" + h.time).length >= 13 ? h.time : h.time * 1000);
return ph;
});
}
}

View File

@@ -1,10 +1,10 @@
import { CipherType } from "../../enums/cipherType";
import { FieldType } from "../../enums/fieldType";
import { ImportResult } from "../../models/domain/importResult";
import { CipherView } from "../../models/view/cipherView";
import { BaseImporter } from "../baseImporter";
import { Importer } from "../importer";
import { CipherType } from "../../enums/cipherType";
import { FieldType } from "../../enums/fieldType";
import { CipherView } from "../../models/view/cipherView";
import { CipherImportContext } from "./cipherImportContext";
export const IgnoredProperties = [
@@ -68,6 +68,7 @@ export abstract class OnePasswordCsvImporter extends BaseImporter implements Imp
let altUsername: string = null;
for (const property in value) {
// eslint-disable-next-line
if (!value.hasOwnProperty(property) || this.isNullOrWhitespace(value[property])) {
continue;
}

View File

@@ -1,10 +1,10 @@
import { Importer } from "../importer";
import { IgnoredProperties, OnePasswordCsvImporter } from "./onepasswordCsvImporter";
import { CipherType } from "../../enums/cipherType";
import { CardView } from "../../models/view/cardView";
import { CipherView } from "../../models/view/cipherView";
import { IdentityView } from "../../models/view/identityView";
import { Importer } from "../importer";
import { IgnoredProperties, OnePasswordCsvImporter } from "./onepasswordCsvImporter";
export class OnePasswordMacCsvImporter extends OnePasswordCsvImporter implements Importer {
setCipherType(value: any, cipher: CipherView) {
@@ -23,6 +23,7 @@ export class OnePasswordMacCsvImporter extends OnePasswordCsvImporter implements
case "Login":
case "Secure Note":
IgnoredProperties.push("type");
break;
default:
break;
}

View File

@@ -1,12 +1,12 @@
import { Importer } from "../importer";
import { CipherImportContext } from "./cipherImportContext";
import { OnePasswordCsvImporter } from "./onepasswordCsvImporter";
import { CipherType } from "../../enums/cipherType";
import { CardView } from "../../models/view/cardView";
import { CipherView } from "../../models/view/cipherView";
import { IdentityView } from "../../models/view/identityView";
import { LoginView } from "../../models/view/loginView";
import { Importer } from "../importer";
import { CipherImportContext } from "./cipherImportContext";
import { OnePasswordCsvImporter } from "./onepasswordCsvImporter";
export class OnePasswordWinCsvImporter extends OnePasswordCsvImporter implements Importer {
constructor() {

View File

@@ -0,0 +1,154 @@
export interface ExportData {
accounts?: AccountsEntity[] | null;
}
export interface AccountsEntity {
attrs: AccountAttributes;
vaults?: VaultsEntity[] | null;
}
export interface AccountAttributes {
accountName: string;
name: string;
avatar: string;
email: string;
uuid: string;
domain: string;
}
export interface VaultsEntity {
attrs: VaultAttributes;
items?: Item[] | null;
}
export interface VaultAttributes {
uuid: string;
desc: string;
avatar: string;
name: string;
type: string;
}
export enum CategoryEnum {
Login = "001",
CreditCard = "002",
SecureNote = "003",
Identity = "004",
Password = "005",
Document = "006",
SoftwareLicense = "100",
BankAccount = "101",
Database = "102",
DriversLicense = "103",
OutdoorLicense = "104",
Membership = "105",
Passport = "106",
RewardsProgram = "107",
SocialSecurityNumber = "108",
WirelessRouter = "109",
Server = "110",
EmailAccount = "111",
API_Credential = "112",
MedicalRecord = "113",
}
export interface Item {
uuid: string;
favIndex: number;
createdAt: number;
updatedAt: number;
trashed?: boolean;
categoryUuid: string;
details: Details;
overview: Overview;
}
export interface Details {
loginFields?: (LoginFieldsEntity | null)[] | null;
notesPlain?: string | null;
sections?: (SectionsEntity | null)[] | null;
passwordHistory?: (PasswordHistoryEntity | null)[] | null;
documentAttributes?: DocumentAttributes | null;
password?: string | null;
}
export enum LoginFieldTypeEnum {
TextOrHtml = "T",
EmailAddress = "E",
URL = "U",
Number = "N",
Password = "P",
TextArea = "A",
PhoneNumber = "T",
CheckBox = "C",
}
export interface LoginFieldsEntity {
value: string;
id: string;
name: string;
fieldType: LoginFieldTypeEnum | string;
designation?: string | null;
}
export interface SectionsEntity {
title: string;
name?: string | null;
fields?: FieldsEntity[] | null;
}
export interface FieldsEntity {
title: string;
id: string;
value: Value;
indexAtSource: number;
guarded: boolean;
multiline: boolean;
dontGenerate: boolean;
placeholder?: string;
inputTraits: InputTraits;
clipboardFilter?: string | null;
}
export interface Value {
totp?: string | null;
date?: number | null;
string?: string | null;
concealed?: string | null;
email?: string | null;
phone?: string | null;
menu?: string | null;
gender?: string | null;
monthYear?: number | null;
url?: string | null;
address?: Address | null;
creditCardType?: string | null;
creditCardNumber?: string | null;
reference?: string | null;
}
export interface Address {
street: string;
city: string;
country: string;
zip: string;
state: string;
}
export interface InputTraits {
keyboard: string;
correction: string;
capitalization: string;
}
export interface PasswordHistoryEntity {
value: string;
time: number;
}
export interface DocumentAttributes {
fileName: string;
documentId: string;
decryptedSize: number;
}
export interface Overview {
subtitle: string;
title: string;
url: string;
urls?: UrlsEntity[] | null;
ps?: number | null;
pbe?: number | null;
pgrng?: boolean | null;
tags?: string[] | null;
}
export interface UrlsEntity {
label: string;
url: string;
}

View File

@@ -1,11 +1,9 @@
import { ImportResult } from "../models/domain/importResult";
import { CollectionView } from "../models/view/collectionView";
import { BaseImporter } from "./baseImporter";
import { Importer } from "./importer";
import { ImportResult } from "../models/domain/importResult";
import { CollectionView } from "../models/view/collectionView";
import { FolderView } from "../models/view/folderView";
export class PadlockCsvImporter extends BaseImporter implements Importer {
parse(data: string): Promise<ImportResult> {
const result = new ImportResult();

View File

@@ -1,8 +1,8 @@
import { ImportResult } from "../models/domain/importResult";
import { BaseImporter } from "./baseImporter";
import { Importer } from "./importer";
import { ImportResult } from "../models/domain/importResult";
export class PassKeepCsvImporter extends BaseImporter implements Importer {
parse(data: string): Promise<ImportResult> {
const result = new ImportResult();

View File

@@ -1,8 +1,8 @@
import { ImportResult } from "../models/domain/importResult";
import { BaseImporter } from "./baseImporter";
import { Importer } from "./importer";
import { ImportResult } from "../models/domain/importResult";
export class PassmanJsonImporter extends BaseImporter implements Importer {
parse(data: string): Promise<ImportResult> {
const result = new ImportResult();

View File

@@ -1,10 +1,9 @@
import { ImportResult } from "../models/domain/importResult";
import { CollectionView } from "../models/view/collectionView";
import { BaseImporter } from "./baseImporter";
import { Importer } from "./importer";
import { ImportResult } from "../models/domain/importResult";
import { CollectionView } from "../models/view/collectionView";
export class PasspackCsvImporter extends BaseImporter implements Importer {
parse(data: string): Promise<ImportResult> {
const result = new ImportResult();

View File

@@ -1,8 +1,8 @@
import { ImportResult } from "../models/domain/importResult";
import { BaseImporter } from "./baseImporter";
import { Importer } from "./importer";
import { ImportResult } from "../models/domain/importResult";
export class PasswordAgentCsvImporter extends BaseImporter implements Importer {
parse(data: string): Promise<ImportResult> {
const result = new ImportResult();

View File

@@ -1,12 +1,10 @@
import { BaseImporter } from "./baseImporter";
import { Importer } from "./importer";
import { CipherType } from "../enums/cipherType";
import { ImportResult } from "../models/domain/importResult";
import { CardView } from "../models/view/cardView";
import { FolderView } from "../models/view/folderView";
import { CipherType } from "../enums/cipherType";
import { BaseImporter } from "./baseImporter";
import { Importer } from "./importer";
export class PasswordBossJsonImporter extends BaseImporter implements Importer {
parse(data: string): Promise<ImportResult> {
@@ -52,6 +50,7 @@ export class PasswordBossJsonImporter extends BaseImporter implements Importer {
}
for (const property in value.identifiers) {
// eslint-disable-next-line
if (!value.identifiers.hasOwnProperty(property)) {
continue;
}

View File

@@ -1,8 +1,8 @@
import { ImportResult } from "../models/domain/importResult";
import { BaseImporter } from "./baseImporter";
import { Importer } from "./importer";
import { ImportResult } from "../models/domain/importResult";
export class PasswordDragonXmlImporter extends BaseImporter implements Importer {
parse(data: string): Promise<ImportResult> {
const result = new ImportResult();

View File

@@ -1,8 +1,8 @@
import { ImportResult } from "../models/domain/importResult";
import { BaseImporter } from "./baseImporter";
import { Importer } from "./importer";
import { ImportResult } from "../models/domain/importResult";
export class PasswordSafeXmlImporter extends BaseImporter implements Importer {
parse(data: string): Promise<ImportResult> {
const result = new ImportResult();

View File

@@ -1,8 +1,8 @@
import { ImportResult } from "../models/domain/importResult";
import { BaseImporter } from "./baseImporter";
import { Importer } from "./importer";
import { ImportResult } from "../models/domain/importResult";
export class PasswordWalletTxtImporter extends BaseImporter implements Importer {
parse(data: string): Promise<ImportResult> {
const result = new ImportResult();

View File

@@ -1,12 +1,10 @@
import { CipherType } from "../enums/cipherType";
import { ImportResult } from "../models/domain/importResult";
import { CardView } from "../models/view/cardView";
import { BaseImporter } from "./baseImporter";
import { Importer } from "./importer";
import { CipherType } from "../enums/cipherType";
import { ImportResult } from "../models/domain/importResult";
import { CardView } from "../models/view/cardView";
export class RememBearCsvImporter extends BaseImporter implements Importer {
parse(data: string): Promise<ImportResult> {
const result = new ImportResult();

View File

@@ -1,8 +1,8 @@
import { ImportResult } from "../models/domain/importResult";
import { BaseImporter } from "./baseImporter";
import { Importer } from "./importer";
import { ImportResult } from "../models/domain/importResult";
export class RoboFormCsvImporter extends BaseImporter implements Importer {
parse(data: string): Promise<ImportResult> {
const result = new ImportResult();

View File

@@ -1,8 +1,8 @@
import { ImportResult } from "../models/domain/importResult";
import { BaseImporter } from "./baseImporter";
import { Importer } from "./importer";
import { ImportResult } from "../models/domain/importResult";
export class SafariCsvImporter extends BaseImporter implements Importer {
parse(data: string): Promise<ImportResult> {
const result = new ImportResult();

View File

@@ -1,17 +1,14 @@
import { BaseImporter } from "./baseImporter";
import { Importer } from "./importer";
import { CipherType } from "../enums/cipherType";
import { FieldType } from "../enums/fieldType";
import { SecureNoteType } from "../enums/secureNoteType";
import { ImportResult } from "../models/domain/importResult";
import { CipherView } from "../models/view/cipherView";
import { FieldView } from "../models/view/fieldView";
import { FolderView } from "../models/view/folderView";
import { SecureNoteView } from "../models/view/secureNoteView";
import { CipherType } from "../enums/cipherType";
import { SecureNoteType } from "../enums/secureNoteType";
import { FieldType } from "../enums/fieldType";
import { CipherView } from "../models/view/cipherView";
import { FieldView } from "../models/view/fieldView";
import { BaseImporter } from "./baseImporter";
import { Importer } from "./importer";
export class SafeInCloudXmlImporter extends BaseImporter implements Importer {
parse(data: string): Promise<ImportResult> {

View File

@@ -1,8 +1,8 @@
import { ImportResult } from "../models/domain/importResult";
import { BaseImporter } from "./baseImporter";
import { Importer } from "./importer";
import { ImportResult } from "../models/domain/importResult";
export class SaferPassCsvImporter extends BaseImporter implements Importer {
parse(data: string): Promise<ImportResult> {
const result = new ImportResult();

View File

@@ -1,8 +1,8 @@
import { ImportResult } from "../models/domain/importResult";
import { BaseImporter } from "./baseImporter";
import { Importer } from "./importer";
import { ImportResult } from "../models/domain/importResult";
export class SecureSafeCsvImporter extends BaseImporter implements Importer {
parse(data: string): Promise<ImportResult> {
const result = new ImportResult();

View File

@@ -1,9 +1,9 @@
import { BaseImporter } from "./baseImporter";
import { Importer } from "./importer";
import { ImportResult } from "../models/domain/importResult";
import { CipherView } from "../models/view/cipherView";
import { BaseImporter } from "./baseImporter";
import { Importer } from "./importer";
export class SplashIdCsvImporter extends BaseImporter implements Importer {
parse(data: string): Promise<ImportResult> {
const result = new ImportResult();

View File

@@ -1,8 +1,8 @@
import { ImportResult } from "../models/domain/importResult";
import { BaseImporter } from "./baseImporter";
import { Importer } from "./importer";
import { ImportResult } from "../models/domain/importResult";
export class StickyPasswordXmlImporter extends BaseImporter implements Importer {
parse(data: string): Promise<ImportResult> {
const result = new ImportResult();

View File

@@ -1,13 +1,11 @@
import { BaseImporter } from "./baseImporter";
import { Importer } from "./importer";
import { CipherType } from "../enums/cipherType";
import { SecureNoteType } from "../enums/secureNoteType";
import { ImportResult } from "../models/domain/importResult";
import { CardView } from "../models/view/cardView";
import { SecureNoteView } from "../models/view/secureNoteView";
import { CipherType } from "../enums/cipherType";
import { SecureNoteType } from "../enums/secureNoteType";
import { BaseImporter } from "./baseImporter";
import { Importer } from "./importer";
const PropertiesToIgnore = [
"kind",
@@ -70,7 +68,7 @@ export class TrueKeyCsvImporter extends BaseImporter implements Importer {
}
for (const property in value) {
if (
value.hasOwnProperty(property) &&
value.hasOwnProperty(property) && // eslint-disable-line
PropertiesToIgnore.indexOf(property.toLowerCase()) < 0 &&
!this.isNullOrWhitespace(value[property])
) {

View File

@@ -1,8 +1,8 @@
import { ImportResult } from "../models/domain/importResult";
import { BaseImporter } from "./baseImporter";
import { Importer } from "./importer";
import { ImportResult } from "../models/domain/importResult";
export class UpmCsvImporter extends BaseImporter implements Importer {
parse(data: string): Promise<ImportResult> {
const result = new ImportResult();

View File

@@ -1,8 +1,8 @@
import { ImportResult } from "../models/domain/importResult";
import { BaseImporter } from "./baseImporter";
import { Importer } from "./importer";
import { ImportResult } from "../models/domain/importResult";
export class YotiCsvImporter extends BaseImporter implements Importer {
parse(data: string): Promise<ImportResult> {
const result = new ImportResult();

View File

@@ -1,9 +1,9 @@
import { BaseImporter } from "./baseImporter";
import { Importer } from "./importer";
import { ImportResult } from "../models/domain/importResult";
import { CipherView } from "../models/view/cipherView";
import { BaseImporter } from "./baseImporter";
import { Importer } from "./importer";
export class ZohoVaultCsvImporter extends BaseImporter implements Importer {
parse(data: string): Promise<ImportResult> {
const result = new ImportResult();

View File

@@ -1,4 +1,5 @@
import { I18nService } from "../abstractions/i18n.service";
import { IFrameComponent } from "./iframe_component";
export class CaptchaIFrame extends IFrameComponent {

View File

@@ -1,5 +1,3 @@
import { I18nService } from "../abstractions/i18n.service";
export abstract class IFrameComponent {
iframe: HTMLIFrameElement;
private connectorLink: HTMLAnchorElement;

View File

@@ -1,6 +1,5 @@
import { ItemView } from "../models/view/itemView";
import { LinkedIdType } from "../enums/linkedIdType";
import { ItemView } from "../models/view/itemView";
export class LinkedMetadata {
constructor(readonly propertyKey: string, private readonly _i18nKey?: string) {}

View File

@@ -0,0 +1,70 @@
import { ApiService } from "../../abstractions/api.service";
import { AppIdService } from "../../abstractions/appId.service";
import { CryptoService } from "../../abstractions/crypto.service";
import { EnvironmentService } from "../../abstractions/environment.service";
import { KeyConnectorService } from "../../abstractions/keyConnector.service";
import { LogService } from "../../abstractions/log.service";
import { MessagingService } from "../../abstractions/messaging.service";
import { PlatformUtilsService } from "../../abstractions/platformUtils.service";
import { StateService } from "../../abstractions/state.service";
import { TokenService } from "../../abstractions/token.service";
import { TwoFactorService } from "../../abstractions/twoFactor.service";
import { ApiLogInCredentials } from "../../models/domain/logInCredentials";
import { ApiTokenRequest } from "../../models/request/identityToken/apiTokenRequest";
import { IdentityTokenResponse } from "../../models/response/identityTokenResponse";
import { LogInStrategy } from "./logIn.strategy";
export class ApiLogInStrategy extends LogInStrategy {
tokenRequest: ApiTokenRequest;
constructor(
cryptoService: CryptoService,
apiService: ApiService,
tokenService: TokenService,
appIdService: AppIdService,
platformUtilsService: PlatformUtilsService,
messagingService: MessagingService,
logService: LogService,
stateService: StateService,
twoFactorService: TwoFactorService,
private environmentService: EnvironmentService,
private keyConnectorService: KeyConnectorService
) {
super(
cryptoService,
apiService,
tokenService,
appIdService,
platformUtilsService,
messagingService,
logService,
stateService,
twoFactorService
);
}
async onSuccessfulLogin(tokenResponse: IdentityTokenResponse) {
if (tokenResponse.apiUseKeyConnector) {
const keyConnectorUrl = this.environmentService.getKeyConnectorUrl();
await this.keyConnectorService.getAndSetKey(keyConnectorUrl);
}
}
async logIn(credentials: ApiLogInCredentials) {
this.tokenRequest = new ApiTokenRequest(
credentials.clientId,
credentials.clientSecret,
await this.buildTwoFactor(),
await this.buildDeviceRequest()
);
return this.startLogIn();
}
protected async saveAccountInformation(tokenResponse: IdentityTokenResponse) {
await super.saveAccountInformation(tokenResponse);
await this.stateService.setApiKeyClientId(this.tokenRequest.clientId);
await this.stateService.setApiKeyClientSecret(this.tokenRequest.clientSecret);
}
}

View File

@@ -0,0 +1,178 @@
import { ApiService } from "../../abstractions/api.service";
import { AppIdService } from "../../abstractions/appId.service";
import { CryptoService } from "../../abstractions/crypto.service";
import { LogService } from "../../abstractions/log.service";
import { MessagingService } from "../../abstractions/messaging.service";
import { PlatformUtilsService } from "../../abstractions/platformUtils.service";
import { StateService } from "../../abstractions/state.service";
import { TokenService } from "../../abstractions/token.service";
import { TwoFactorService } from "../../abstractions/twoFactor.service";
import { TwoFactorProviderType } from "../../enums/twoFactorProviderType";
import { Account, AccountProfile, AccountTokens } from "../../models/domain/account";
import { AuthResult } from "../../models/domain/authResult";
import {
ApiLogInCredentials,
PasswordLogInCredentials,
SsoLogInCredentials,
} from "../../models/domain/logInCredentials";
import { DeviceRequest } from "../../models/request/deviceRequest";
import { ApiTokenRequest } from "../../models/request/identityToken/apiTokenRequest";
import { PasswordTokenRequest } from "../../models/request/identityToken/passwordTokenRequest";
import { SsoTokenRequest } from "../../models/request/identityToken/ssoTokenRequest";
import { TokenRequestTwoFactor } from "../../models/request/identityToken/tokenRequest";
import { KeysRequest } from "../../models/request/keysRequest";
import { IdentityCaptchaResponse } from "../../models/response/identityCaptchaResponse";
import { IdentityTokenResponse } from "../../models/response/identityTokenResponse";
import { IdentityTwoFactorResponse } from "../../models/response/identityTwoFactorResponse";
export abstract class LogInStrategy {
protected abstract tokenRequest: ApiTokenRequest | PasswordTokenRequest | SsoTokenRequest;
protected captchaBypassToken: string = null;
constructor(
protected cryptoService: CryptoService,
protected apiService: ApiService,
protected tokenService: TokenService,
protected appIdService: AppIdService,
protected platformUtilsService: PlatformUtilsService,
protected messagingService: MessagingService,
protected logService: LogService,
protected stateService: StateService,
protected twoFactorService: TwoFactorService
) {}
abstract logIn(
credentials: ApiLogInCredentials | PasswordLogInCredentials | SsoLogInCredentials
): Promise<AuthResult>;
async logInTwoFactor(
twoFactor: TokenRequestTwoFactor,
captchaResponse: string = null
): Promise<AuthResult> {
this.tokenRequest.setTwoFactor(twoFactor);
return this.startLogIn();
}
protected async startLogIn(): Promise<AuthResult> {
this.twoFactorService.clearSelectedProvider();
const response = await this.apiService.postIdentityToken(this.tokenRequest);
if (response instanceof IdentityTwoFactorResponse) {
return this.processTwoFactorResponse(response);
} else if (response instanceof IdentityCaptchaResponse) {
return this.processCaptchaResponse(response);
} else if (response instanceof IdentityTokenResponse) {
return this.processTokenResponse(response);
}
throw new Error("Invalid response object.");
}
protected onSuccessfulLogin(response: IdentityTokenResponse): Promise<void> {
// Implemented in subclass if required
return null;
}
protected async buildDeviceRequest() {
const appId = await this.appIdService.getAppId();
return new DeviceRequest(appId, this.platformUtilsService);
}
protected async buildTwoFactor(userProvidedTwoFactor?: TokenRequestTwoFactor) {
if (userProvidedTwoFactor != null) {
return userProvidedTwoFactor;
}
const storedTwoFactorToken = await this.tokenService.getTwoFactorToken();
if (storedTwoFactorToken != null) {
return {
token: storedTwoFactorToken,
provider: TwoFactorProviderType.Remember,
remember: false,
};
}
return {
token: null,
provider: null,
remember: false,
};
}
protected async saveAccountInformation(tokenResponse: IdentityTokenResponse) {
const accountInformation = await this.tokenService.decodeToken(tokenResponse.accessToken);
await this.stateService.addAccount(
new Account({
profile: {
...new AccountProfile(),
...{
userId: accountInformation.sub,
email: accountInformation.email,
hasPremiumPersonally: accountInformation.premium,
kdfIterations: tokenResponse.kdfIterations,
kdfType: tokenResponse.kdf,
},
},
tokens: {
...new AccountTokens(),
...{
accessToken: tokenResponse.accessToken,
refreshToken: tokenResponse.refreshToken,
},
},
})
);
}
protected async processTokenResponse(response: IdentityTokenResponse): Promise<AuthResult> {
const result = new AuthResult();
result.resetMasterPassword = response.resetMasterPassword;
result.forcePasswordReset = response.forcePasswordReset;
await this.saveAccountInformation(response);
if (response.twoFactorToken != null) {
await this.tokenService.setTwoFactorToken(response);
}
const newSsoUser = response.key == null;
if (!newSsoUser) {
await this.cryptoService.setEncKey(response.key);
await this.cryptoService.setEncPrivateKey(
response.privateKey ?? (await this.createKeyPairForOldAccount())
);
}
await this.onSuccessfulLogin(response);
await this.stateService.setBiometricLocked(false);
this.messagingService.send("loggedIn");
return result;
}
private async processTwoFactorResponse(response: IdentityTwoFactorResponse): Promise<AuthResult> {
const result = new AuthResult();
result.twoFactorProviders = response.twoFactorProviders2;
this.twoFactorService.setProviders(response);
this.captchaBypassToken = response.captchaToken ?? null;
return result;
}
private async processCaptchaResponse(response: IdentityCaptchaResponse): Promise<AuthResult> {
const result = new AuthResult();
result.captchaSiteKey = response.siteKey;
return result;
}
private async createKeyPairForOldAccount() {
try {
const [publicKey, privateKey] = await this.cryptoService.makeKeyPair();
await this.apiService.postAccountKeys(new KeysRequest(publicKey, privateKey.encryptedString));
return privateKey.encryptedString;
} catch (e) {
this.logService.error(e);
}
}
}

View File

@@ -0,0 +1,95 @@
import { ApiService } from "../../abstractions/api.service";
import { AppIdService } from "../../abstractions/appId.service";
import { AuthService } from "../../abstractions/auth.service";
import { CryptoService } from "../../abstractions/crypto.service";
import { LogService } from "../../abstractions/log.service";
import { MessagingService } from "../../abstractions/messaging.service";
import { PlatformUtilsService } from "../../abstractions/platformUtils.service";
import { StateService } from "../../abstractions/state.service";
import { TokenService } from "../../abstractions/token.service";
import { TwoFactorService } from "../../abstractions/twoFactor.service";
import { HashPurpose } from "../../enums/hashPurpose";
import { AuthResult } from "../../models/domain/authResult";
import { PasswordLogInCredentials } from "../../models/domain/logInCredentials";
import { SymmetricCryptoKey } from "../../models/domain/symmetricCryptoKey";
import { PasswordTokenRequest } from "../../models/request/identityToken/passwordTokenRequest";
import { TokenRequestTwoFactor } from "../../models/request/identityToken/tokenRequest";
import { LogInStrategy } from "./logIn.strategy";
export class PasswordLogInStrategy extends LogInStrategy {
get email() {
return this.tokenRequest.email;
}
get masterPasswordHash() {
return this.tokenRequest.masterPasswordHash;
}
tokenRequest: PasswordTokenRequest;
private localHashedPassword: string;
private key: SymmetricCryptoKey;
constructor(
cryptoService: CryptoService,
apiService: ApiService,
tokenService: TokenService,
appIdService: AppIdService,
platformUtilsService: PlatformUtilsService,
messagingService: MessagingService,
logService: LogService,
stateService: StateService,
twoFactorService: TwoFactorService,
private authService: AuthService
) {
super(
cryptoService,
apiService,
tokenService,
appIdService,
platformUtilsService,
messagingService,
logService,
stateService,
twoFactorService
);
}
async onSuccessfulLogin() {
await this.cryptoService.setKey(this.key);
await this.cryptoService.setKeyHash(this.localHashedPassword);
}
async logInTwoFactor(
twoFactor: TokenRequestTwoFactor,
captchaResponse: string
): Promise<AuthResult> {
this.tokenRequest.captchaResponse = captchaResponse ?? this.captchaBypassToken;
return super.logInTwoFactor(twoFactor);
}
async logIn(credentials: PasswordLogInCredentials) {
const { email, masterPassword, captchaToken, twoFactor } = credentials;
this.key = await this.authService.makePreloginKey(masterPassword, email);
// Hash the password early (before authentication) so we don't persist it in memory in plaintext
this.localHashedPassword = await this.cryptoService.hashPassword(
masterPassword,
this.key,
HashPurpose.LocalAuthorization
);
const hashedPassword = await this.cryptoService.hashPassword(masterPassword, this.key);
this.tokenRequest = new PasswordTokenRequest(
email,
hashedPassword,
captchaToken,
await this.buildTwoFactor(twoFactor),
await this.buildDeviceRequest()
);
return this.startLogIn();
}
}

View File

@@ -0,0 +1,70 @@
import { ApiService } from "../../abstractions/api.service";
import { AppIdService } from "../../abstractions/appId.service";
import { CryptoService } from "../../abstractions/crypto.service";
import { KeyConnectorService } from "../../abstractions/keyConnector.service";
import { LogService } from "../../abstractions/log.service";
import { MessagingService } from "../../abstractions/messaging.service";
import { PlatformUtilsService } from "../../abstractions/platformUtils.service";
import { StateService } from "../../abstractions/state.service";
import { TokenService } from "../../abstractions/token.service";
import { TwoFactorService } from "../../abstractions/twoFactor.service";
import { SsoLogInCredentials } from "../../models/domain/logInCredentials";
import { SsoTokenRequest } from "../../models/request/identityToken/ssoTokenRequest";
import { IdentityTokenResponse } from "../../models/response/identityTokenResponse";
import { LogInStrategy } from "./logIn.strategy";
export class SsoLogInStrategy extends LogInStrategy {
tokenRequest: SsoTokenRequest;
orgId: string;
constructor(
cryptoService: CryptoService,
apiService: ApiService,
tokenService: TokenService,
appIdService: AppIdService,
platformUtilsService: PlatformUtilsService,
messagingService: MessagingService,
logService: LogService,
stateService: StateService,
twoFactorService: TwoFactorService,
private keyConnectorService: KeyConnectorService
) {
super(
cryptoService,
apiService,
tokenService,
appIdService,
platformUtilsService,
messagingService,
logService,
stateService,
twoFactorService
);
}
async onSuccessfulLogin(tokenResponse: IdentityTokenResponse) {
const newSsoUser = tokenResponse.key == null;
if (tokenResponse.keyConnectorUrl != null) {
if (!newSsoUser) {
await this.keyConnectorService.getAndSetKey(tokenResponse.keyConnectorUrl);
} else {
await this.keyConnectorService.convertNewSsoUserToKeyConnector(tokenResponse, this.orgId);
}
}
}
async logIn(credentials: SsoLogInCredentials) {
this.orgId = credentials.orgId;
this.tokenRequest = new SsoTokenRequest(
credentials.code,
credentials.codeVerifier,
credentials.redirectUrl,
await this.buildTwoFactor(credentials.twoFactor),
await this.buildDeviceRequest()
);
return this.startLogIn();
}
}

View File

@@ -1,13 +1,12 @@
/* eslint-disable no-useless-escape */
import * as tldts from "tldts";
import { I18nService } from "../abstractions/i18n.service";
// tslint:disable-next-line
const nodeURL = typeof window === "undefined" ? require("url") : null;
export class Utils {
static inited = false;
static isNativeScript = false;
static isNode = false;
static isBrowser = true;
static isMobileBrowser = false;
@@ -31,18 +30,13 @@ export class Utils {
(process as any).release != null &&
(process as any).release.name === "node";
Utils.isBrowser = typeof window !== "undefined";
Utils.isNativeScript = !Utils.isNode && !Utils.isBrowser;
Utils.isMobileBrowser = Utils.isBrowser && this.isMobile(window);
Utils.isAppleMobileBrowser = Utils.isBrowser && this.isAppleMobile(window);
Utils.global = Utils.isNativeScript
? global
: Utils.isNode && !Utils.isBrowser
? global
: window;
Utils.global = Utils.isNode && !Utils.isBrowser ? global : window;
}
static fromB64ToArray(str: string): Uint8Array {
if (Utils.isNode || Utils.isNativeScript) {
if (Utils.isNode) {
return new Uint8Array(Buffer.from(str, "base64"));
} else {
const binaryString = window.atob(str);
@@ -59,7 +53,7 @@ export class Utils {
}
static fromHexToArray(str: string): Uint8Array {
if (Utils.isNode || Utils.isNativeScript) {
if (Utils.isNode) {
return new Uint8Array(Buffer.from(str, "hex"));
} else {
const bytes = new Uint8Array(str.length / 2);
@@ -71,7 +65,7 @@ export class Utils {
}
static fromUtf8ToArray(str: string): Uint8Array {
if (Utils.isNode || Utils.isNativeScript) {
if (Utils.isNode) {
return new Uint8Array(Buffer.from(str, "utf8"));
} else {
const strUtf8 = unescape(encodeURIComponent(str));
@@ -92,7 +86,7 @@ export class Utils {
}
static fromBufferToB64(buffer: ArrayBuffer): string {
if (Utils.isNode || Utils.isNativeScript) {
if (Utils.isNode) {
return Buffer.from(buffer).toString("base64");
} else {
let binary = "";
@@ -113,7 +107,7 @@ export class Utils {
}
static fromBufferToUtf8(buffer: ArrayBuffer): string {
if (Utils.isNode || Utils.isNativeScript) {
if (Utils.isNode) {
return Buffer.from(buffer).toString("utf8");
} else {
const bytes = new Uint8Array(buffer);
@@ -128,7 +122,7 @@ export class Utils {
// ref: https://stackoverflow.com/a/40031979/1090359
static fromBufferToHex(buffer: ArrayBuffer): string {
if (Utils.isNode || Utils.isNativeScript) {
if (Utils.isNode) {
return Buffer.from(buffer).toString("hex");
} else {
const bytes = new Uint8Array(buffer);
@@ -161,7 +155,7 @@ export class Utils {
}
static fromUtf8ToB64(utfStr: string): string {
if (Utils.isNode || Utils.isNativeScript) {
if (Utils.isNode) {
return Buffer.from(utfStr, "utf8").toString("base64");
} else {
return decodeURIComponent(escape(window.btoa(utfStr)));
@@ -173,7 +167,7 @@ export class Utils {
}
static fromB64ToUtf8(b64Str: string): string {
if (Utils.isNode || Utils.isNativeScript) {
if (Utils.isNode) {
return Buffer.from(b64Str, "base64").toString("utf8");
} else {
return decodeURIComponent(escape(window.atob(b64Str)));
@@ -183,9 +177,7 @@ export class Utils {
// ref: http://stackoverflow.com/a/2117523/1090359
static newGuid(): string {
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
// tslint:disable-next-line
const r = (Math.random() * 16) | 0;
// tslint:disable-next-line
const v = c === "x" ? r : (r & 0x3) | 0x8;
return v.toString(16);
});
@@ -313,6 +305,10 @@ export class Utils {
return str == null || typeof str !== "string" || str.trim() === "";
}
static isNullOrEmpty(str: string): boolean {
return str == null || typeof str !== "string" || str == "";
}
static nameOf<T>(name: string & keyof T) {
return name;
}
@@ -350,7 +346,6 @@ export class Utils {
private static isMobile(win: Window) {
let mobile = false;
((a) => {
// tslint:disable-next-line
if (
/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(
a
@@ -375,7 +370,7 @@ export class Utils {
private static getUrlObject(uriString: string): URL {
try {
if (nodeURL != null) {
return nodeURL.URL ? new nodeURL.URL(uriString) : nodeURL.parse(uriString);
return new nodeURL.URL(uriString);
} else if (typeof URL === "function") {
return new URL(uriString);
} else if (window != null) {

View File

@@ -12,9 +12,9 @@ export class WebAuthnIFrame {
private webAuthnNewTab: boolean,
private platformUtilsService: PlatformUtilsService,
private i18nService: I18nService,
private successCallback: Function,
private errorCallback: Function,
private infoCallback: Function
private successCallback: Function, // eslint-disable-line
private errorCallback: Function, // eslint-disable-line
private infoCallback: Function // eslint-disable-line
) {
this.connectorLink = win.document.createElement("a");
}

View File

@@ -1,7 +1,6 @@
import { BaseResponse } from "../response/baseResponse";
import { FieldType } from "../../enums/fieldType";
import { LinkedIdType } from "../../enums/linkedIdType";
import { BaseResponse } from "../response/baseResponse";
export class FieldApi extends BaseResponse {
name: string;

Some files were not shown because too many files have changed in this diff Show More