mirror of
https://github.com/bitwarden/browser
synced 2025-12-14 23:33:31 +00:00
Split jslib into multiple modules (#363)
* Split jslib into multiple modules
This commit is contained in:
1495
common/package-lock.json
generated
Normal file
1495
common/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
43
common/package.json
Normal file
43
common/package.json
Normal file
@@ -0,0 +1,43 @@
|
||||
{
|
||||
"name": "@bitwarden/jslib-common",
|
||||
"version": "0.0.0",
|
||||
"description": "Common code used across Bitwarden JavaScript projects.",
|
||||
"keywords": [
|
||||
"bitwarden"
|
||||
],
|
||||
"author": "Bitwarden Inc.",
|
||||
"homepage": "https://bitwarden.com",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/bitwarden/jslib"
|
||||
},
|
||||
"license": "GPL-3.0",
|
||||
"scripts": {
|
||||
"clean": "rimraf dist/**/*",
|
||||
"build": "npm run clean && tsc",
|
||||
"build:watch": "npm run clean && tsc -watch",
|
||||
"lint": "tslint 'src/**/*.ts' 'spec/**/*.ts'",
|
||||
"lint:fix": "tslint 'src/**/*.ts' 'spec/**/*.ts' --fix"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/lunr": "^2.3.3",
|
||||
"@types/node": "^14.17.1",
|
||||
"@types/node-forge": "^0.9.7",
|
||||
"@types/papaparse": "^5.2.5",
|
||||
"@types/tldjs": "^2.3.0",
|
||||
"@types/zxcvbn": "^4.4.1",
|
||||
"rimraf": "^3.0.2",
|
||||
"typescript": "4.1.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@microsoft/signalr": "3.1.13",
|
||||
"@microsoft/signalr-protocol-msgpack": "3.1.13",
|
||||
"big-integer": "1.6.48",
|
||||
"browser-hrtime": "^1.1.8",
|
||||
"lunr": "^2.3.9",
|
||||
"node-forge": "^0.10.0",
|
||||
"papaparse": "^5.3.0",
|
||||
"tldjs": "^2.3.1",
|
||||
"zxcvbn": "^4.4.2"
|
||||
}
|
||||
}
|
||||
405
common/src/abstractions/api.service.ts
Normal file
405
common/src/abstractions/api.service.ts
Normal file
@@ -0,0 +1,405 @@
|
||||
import { PolicyType } from '../enums/policyType';
|
||||
|
||||
import { EnvironmentUrls } from '../models/domain/environmentUrls';
|
||||
import { AttachmentRequest } from '../models/request/attachmentRequest';
|
||||
|
||||
import { BitPayInvoiceRequest } from '../models/request/bitPayInvoiceRequest';
|
||||
import { CipherBulkDeleteRequest } from '../models/request/cipherBulkDeleteRequest';
|
||||
import { CipherBulkMoveRequest } from '../models/request/cipherBulkMoveRequest';
|
||||
import { CipherBulkRestoreRequest } from '../models/request/cipherBulkRestoreRequest';
|
||||
import { CipherBulkShareRequest } from '../models/request/cipherBulkShareRequest';
|
||||
import { CipherCollectionsRequest } from '../models/request/cipherCollectionsRequest';
|
||||
import { CipherCreateRequest } from '../models/request/cipherCreateRequest';
|
||||
import { CipherRequest } from '../models/request/cipherRequest';
|
||||
import { CipherShareRequest } from '../models/request/cipherShareRequest';
|
||||
import { CollectionRequest } from '../models/request/collectionRequest';
|
||||
import { DeleteRecoverRequest } from '../models/request/deleteRecoverRequest';
|
||||
import { EmailRequest } from '../models/request/emailRequest';
|
||||
import { EmailTokenRequest } from '../models/request/emailTokenRequest';
|
||||
import { EmergencyAccessAcceptRequest } from '../models/request/emergencyAccessAcceptRequest';
|
||||
import { EmergencyAccessConfirmRequest } from '../models/request/emergencyAccessConfirmRequest';
|
||||
import { EmergencyAccessInviteRequest } from '../models/request/emergencyAccessInviteRequest';
|
||||
import { EmergencyAccessPasswordRequest } from '../models/request/emergencyAccessPasswordRequest';
|
||||
import { EmergencyAccessUpdateRequest } from '../models/request/emergencyAccessUpdateRequest';
|
||||
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 { ImportCiphersRequest } from '../models/request/importCiphersRequest';
|
||||
import { ImportDirectoryRequest } from '../models/request/importDirectoryRequest';
|
||||
import { ImportOrganizationCiphersRequest } from '../models/request/importOrganizationCiphersRequest';
|
||||
import { KdfRequest } from '../models/request/kdfRequest';
|
||||
import { KeysRequest } from '../models/request/keysRequest';
|
||||
import { OrganizationCreateRequest } from '../models/request/organizationCreateRequest';
|
||||
import { OrganizationImportRequest } from '../models/request/organizationImportRequest';
|
||||
import { OrganizationKeysRequest } from '../models/request/organizationKeysRequest';
|
||||
import { OrganizationTaxInfoUpdateRequest } from '../models/request/organizationTaxInfoUpdateRequest';
|
||||
import { OrganizationUpdateRequest } from '../models/request/organizationUpdateRequest';
|
||||
import { OrganizationUpgradeRequest } from '../models/request/organizationUpgradeRequest';
|
||||
import { OrganizationUserAcceptRequest } from '../models/request/organizationUserAcceptRequest';
|
||||
import { OrganizationUserBulkConfirmRequest } from '../models/request/organizationUserBulkConfirmRequest';
|
||||
import { OrganizationUserBulkRequest } from '../models/request/organizationUserBulkRequest';
|
||||
import { OrganizationUserConfirmRequest } from '../models/request/organizationUserConfirmRequest';
|
||||
import { OrganizationUserInviteRequest } from '../models/request/organizationUserInviteRequest';
|
||||
import { OrganizationUserResetPasswordEnrollmentRequest } from '../models/request/organizationUserResetPasswordEnrollmentRequest';
|
||||
import { OrganizationUserResetPasswordRequest } from '../models/request/organizationUserResetPasswordRequest';
|
||||
import { OrganizationUserUpdateGroupsRequest } from '../models/request/organizationUserUpdateGroupsRequest';
|
||||
import { OrganizationUserUpdateRequest } from '../models/request/organizationUserUpdateRequest';
|
||||
import { PasswordHintRequest } from '../models/request/passwordHintRequest';
|
||||
import { PasswordRequest } from '../models/request/passwordRequest';
|
||||
import { PasswordVerificationRequest } from '../models/request/passwordVerificationRequest';
|
||||
import { PaymentRequest } from '../models/request/paymentRequest';
|
||||
import { PolicyRequest } from '../models/request/policyRequest';
|
||||
import { PreloginRequest } from '../models/request/preloginRequest';
|
||||
import { RegisterRequest } from '../models/request/registerRequest';
|
||||
import { SeatRequest } from '../models/request/seatRequest';
|
||||
import { SelectionReadOnlyRequest } from '../models/request/selectionReadOnlyRequest';
|
||||
import { SendAccessRequest } from '../models/request/sendAccessRequest';
|
||||
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';
|
||||
import { UpdateDomainsRequest } from '../models/request/updateDomainsRequest';
|
||||
import { UpdateKeyRequest } from '../models/request/updateKeyRequest';
|
||||
import { UpdateProfileRequest } from '../models/request/updateProfileRequest';
|
||||
import { UpdateTwoFactorAuthenticatorRequest } from '../models/request/updateTwoFactorAuthenticatorRequest';
|
||||
import { UpdateTwoFactorDuoRequest } from '../models/request/updateTwoFactorDuoRequest';
|
||||
import { UpdateTwoFactorEmailRequest } from '../models/request/updateTwoFactorEmailRequest';
|
||||
import { UpdateTwoFactorWebAuthnDeleteRequest } from '../models/request/updateTwoFactorWebAuthnDeleteRequest';
|
||||
import { UpdateTwoFactorWebAuthnRequest } from '../models/request/updateTwoFactorWebAuthnRequest';
|
||||
import { UpdateTwoFactorYubioOtpRequest } from '../models/request/updateTwoFactorYubioOtpRequest';
|
||||
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';
|
||||
import { BillingResponse } from '../models/response/billingResponse';
|
||||
import { BreachAccountResponse } from '../models/response/breachAccountResponse';
|
||||
import { CipherResponse } from '../models/response/cipherResponse';
|
||||
import {
|
||||
CollectionGroupDetailsResponse,
|
||||
CollectionResponse,
|
||||
} from '../models/response/collectionResponse';
|
||||
import { DomainsResponse } from '../models/response/domainsResponse';
|
||||
import {
|
||||
EmergencyAccessGranteeDetailsResponse,
|
||||
EmergencyAccessGrantorDetailsResponse,
|
||||
EmergencyAccessTakeoverResponse,
|
||||
EmergencyAccessViewResponse
|
||||
} from '../models/response/emergencyAccessResponse';
|
||||
import { EventResponse } from '../models/response/eventResponse';
|
||||
import { FolderResponse } from '../models/response/folderResponse';
|
||||
import {
|
||||
GroupDetailsResponse,
|
||||
GroupResponse,
|
||||
} from '../models/response/groupResponse';
|
||||
import { IdentityTokenResponse } from '../models/response/identityTokenResponse';
|
||||
import { IdentityTwoFactorResponse } from '../models/response/identityTwoFactorResponse';
|
||||
import { ListResponse } from '../models/response/listResponse';
|
||||
import { OrganizationKeysResponse } from '../models/response/organizationKeysResponse';
|
||||
import { OrganizationResponse } from '../models/response/organizationResponse';
|
||||
import { OrganizationSubscriptionResponse } from '../models/response/organizationSubscriptionResponse';
|
||||
import { OrganizationUserBulkPublicKeyResponse } from '../models/response/organizationUserBulkPublicKeyResponse';
|
||||
import { OrganizationUserBulkResponse } from '../models/response/organizationUserBulkResponse';
|
||||
import {
|
||||
OrganizationUserDetailsResponse,
|
||||
OrganizationUserResetPasswordDetailsReponse,
|
||||
OrganizationUserUserDetailsResponse,
|
||||
} from '../models/response/organizationUserResponse';
|
||||
import { PaymentResponse } from '../models/response/paymentResponse';
|
||||
import { PlanResponse } from '../models/response/planResponse';
|
||||
import { PolicyResponse } from '../models/response/policyResponse';
|
||||
import { PreloginResponse } from '../models/response/preloginResponse';
|
||||
import { ProfileResponse } from '../models/response/profileResponse';
|
||||
import { SelectionReadOnlyResponse } from '../models/response/selectionReadOnlyResponse';
|
||||
import { SendAccessResponse } from '../models/response/sendAccessResponse';
|
||||
import { SendFileDownloadDataResponse } from '../models/response/sendFileDownloadDataResponse';
|
||||
import { SendFileUploadDataResponse } from '../models/response/sendFileUploadDataResponse';
|
||||
import { SendResponse } from '../models/response/sendResponse';
|
||||
import { SubscriptionResponse } from '../models/response/subscriptionResponse';
|
||||
import { SyncResponse } from '../models/response/syncResponse';
|
||||
import { TaxInfoResponse } from '../models/response/taxInfoResponse';
|
||||
import { TaxRateResponse } from '../models/response/taxRateResponse';
|
||||
import { TwoFactorAuthenticatorResponse } from '../models/response/twoFactorAuthenticatorResponse';
|
||||
import { TwoFactorDuoResponse } from '../models/response/twoFactorDuoResponse';
|
||||
import { TwoFactorEmailResponse } from '../models/response/twoFactorEmailResponse';
|
||||
import { TwoFactorProviderResponse } from '../models/response/twoFactorProviderResponse';
|
||||
import { TwoFactorRecoverResponse } from '../models/response/twoFactorRescoverResponse';
|
||||
import { ChallengeResponse, TwoFactorWebAuthnResponse } 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 {
|
||||
urlsSet: boolean;
|
||||
apiBaseUrl: string;
|
||||
identityBaseUrl: string;
|
||||
eventsBaseUrl: string;
|
||||
|
||||
setUrls: (urls: EnvironmentUrls) => void;
|
||||
postIdentityToken: (request: TokenRequest) => Promise<IdentityTokenResponse | IdentityTwoFactorResponse>;
|
||||
refreshIdentityToken: () => Promise<any>;
|
||||
|
||||
getProfile: () => Promise<ProfileResponse>;
|
||||
getUserBilling: () => Promise<BillingResponse>;
|
||||
getUserSubscription: () => Promise<SubscriptionResponse>;
|
||||
getTaxInfo: () => Promise<TaxInfoResponse>;
|
||||
putProfile: (request: UpdateProfileRequest) => Promise<ProfileResponse>;
|
||||
putTaxInfo: (request: TaxInfoUpdateRequest) => Promise<any>;
|
||||
postPrelogin: (request: PreloginRequest) => Promise<PreloginResponse>;
|
||||
postEmailToken: (request: EmailTokenRequest) => Promise<any>;
|
||||
postEmail: (request: EmailRequest) => Promise<any>;
|
||||
postPassword: (request: PasswordRequest) => Promise<any>;
|
||||
setPassword: (request: SetPasswordRequest) => Promise<any>;
|
||||
postSecurityStamp: (request: PasswordVerificationRequest) => Promise<any>;
|
||||
deleteAccount: (request: PasswordVerificationRequest) => Promise<any>;
|
||||
getAccountRevisionDate: () => Promise<number>;
|
||||
postPasswordHint: (request: PasswordHintRequest) => Promise<any>;
|
||||
postRegister: (request: RegisterRequest) => Promise<any>;
|
||||
postPremium: (data: FormData) => Promise<PaymentResponse>;
|
||||
postIapCheck: (request: IapCheckRequest) => Promise<any>;
|
||||
postReinstatePremium: () => Promise<any>;
|
||||
postCancelPremium: () => Promise<any>;
|
||||
postAccountStorage: (request: StorageRequest) => Promise<PaymentResponse>;
|
||||
postAccountPayment: (request: PaymentRequest) => Promise<any>;
|
||||
postAccountLicense: (data: FormData) => Promise<any>;
|
||||
postAccountKey: (request: UpdateKeyRequest) => Promise<any>;
|
||||
postAccountKeys: (request: KeysRequest) => Promise<any>;
|
||||
postAccountVerifyEmail: () => Promise<any>;
|
||||
postAccountVerifyEmailToken: (request: VerifyEmailRequest) => Promise<any>;
|
||||
postAccountVerifyPassword: (request: PasswordVerificationRequest) => Promise<any>;
|
||||
postAccountRecoverDelete: (request: DeleteRecoverRequest) => Promise<any>;
|
||||
postAccountRecoverDeleteToken: (request: VerifyDeleteRecoverRequest) => Promise<any>;
|
||||
postAccountKdf: (request: KdfRequest) => Promise<any>;
|
||||
getEnterprisePortalSignInToken: () => Promise<string>;
|
||||
postUserApiKey: (id: string, request: PasswordVerificationRequest) => Promise<ApiKeyResponse>;
|
||||
postUserRotateApiKey: (id: string, request: PasswordVerificationRequest) => Promise<ApiKeyResponse>;
|
||||
|
||||
getFolder: (id: string) => Promise<FolderResponse>;
|
||||
postFolder: (request: FolderRequest) => Promise<FolderResponse>;
|
||||
putFolder: (id: string, request: FolderRequest) => Promise<FolderResponse>;
|
||||
deleteFolder: (id: string) => Promise<any>;
|
||||
|
||||
getSend: (id: string) => Promise<SendResponse>;
|
||||
postSendAccess: (id: string, request: SendAccessRequest, apiUrl?: string) => Promise<SendAccessResponse>;
|
||||
getSends: () => Promise<ListResponse<SendResponse>>;
|
||||
postSend: (request: SendRequest) => Promise<SendResponse>;
|
||||
postFileTypeSend: (request: SendRequest) => Promise<SendFileUploadDataResponse>;
|
||||
postSendFile: (sendId: string, fileId: string, data: FormData) => Promise<any>;
|
||||
/**
|
||||
* @deprecated Mar 25 2021: This method has been deprecated in favor of direct uploads.
|
||||
* This method still exists for backward compatibility with old server versions.
|
||||
*/
|
||||
postSendFileLegacy: (data: FormData) => Promise<SendResponse>;
|
||||
putSend: (id: string, request: SendRequest) => Promise<SendResponse>;
|
||||
putSendRemovePassword: (id: string) => Promise<SendResponse>;
|
||||
deleteSend: (id: string) => Promise<any>;
|
||||
getSendFileDownloadData: (send: SendAccessView, request: SendAccessRequest, apiUrl?: string) => Promise<SendFileDownloadDataResponse>;
|
||||
renewSendFileUploadUrl: (sendId: string, fileId: string) => Promise<SendFileUploadDataResponse>;
|
||||
|
||||
getCipher: (id: string) => Promise<CipherResponse>;
|
||||
getCipherAdmin: (id: string) => Promise<CipherResponse>;
|
||||
getAttachmentData: (cipherId: string, attachmentId: string, emergencyAccessId?: string) => Promise<AttachmentResponse>;
|
||||
getCiphersOrganization: (organizationId: string) => Promise<ListResponse<CipherResponse>>;
|
||||
postCipher: (request: CipherRequest) => Promise<CipherResponse>;
|
||||
postCipherCreate: (request: CipherCreateRequest) => Promise<CipherResponse>;
|
||||
postCipherAdmin: (request: CipherCreateRequest) => Promise<CipherResponse>;
|
||||
putCipher: (id: string, request: CipherRequest) => Promise<CipherResponse>;
|
||||
putCipherAdmin: (id: string, request: CipherRequest) => Promise<CipherResponse>;
|
||||
deleteCipher: (id: string) => Promise<any>;
|
||||
deleteCipherAdmin: (id: string) => Promise<any>;
|
||||
deleteManyCiphers: (request: CipherBulkDeleteRequest) => Promise<any>;
|
||||
deleteManyCiphersAdmin: (request: CipherBulkDeleteRequest) => Promise<any>;
|
||||
putMoveCiphers: (request: CipherBulkMoveRequest) => Promise<any>;
|
||||
putShareCipher: (id: string, request: CipherShareRequest) => Promise<CipherResponse>;
|
||||
putShareCiphers: (request: CipherBulkShareRequest) => Promise<any>;
|
||||
putCipherCollections: (id: string, request: CipherCollectionsRequest) => Promise<any>;
|
||||
putCipherCollectionsAdmin: (id: string, request: CipherCollectionsRequest) => Promise<any>;
|
||||
postPurgeCiphers: (request: PasswordVerificationRequest, organizationId?: string) => Promise<any>;
|
||||
postImportCiphers: (request: ImportCiphersRequest) => Promise<any>;
|
||||
postImportOrganizationCiphers: (organizationId: string, request: ImportOrganizationCiphersRequest) => Promise<any>;
|
||||
putDeleteCipher: (id: string) => Promise<any>;
|
||||
putDeleteCipherAdmin: (id: string) => Promise<any>;
|
||||
putDeleteManyCiphers: (request: CipherBulkDeleteRequest) => Promise<any>;
|
||||
putDeleteManyCiphersAdmin: (request: CipherBulkDeleteRequest) => Promise<any>;
|
||||
putRestoreCipher: (id: string) => Promise<CipherResponse>;
|
||||
putRestoreCipherAdmin: (id: string) => Promise<CipherResponse>;
|
||||
putRestoreManyCiphers: (request: CipherBulkRestoreRequest) => Promise<ListResponse<CipherResponse>>;
|
||||
|
||||
/**
|
||||
* @deprecated Mar 25 2021: This method has been deprecated in favor of direct uploads.
|
||||
* This method still exists for backward compatibility with old server versions.
|
||||
*/
|
||||
postCipherAttachmentLegacy: (id: string, data: FormData) => Promise<CipherResponse>;
|
||||
/**
|
||||
* @deprecated Mar 25 2021: This method has been deprecated in favor of direct uploads.
|
||||
* This method still exists for backward compatibility with old server versions.
|
||||
*/
|
||||
postCipherAttachmentAdminLegacy: (id: string, data: FormData) => Promise<CipherResponse>;
|
||||
postCipherAttachment: (id: string, request: AttachmentRequest) => Promise<AttachmentUploadDataResponse>;
|
||||
deleteCipherAttachment: (id: string, attachmentId: string) => Promise<any>;
|
||||
deleteCipherAttachmentAdmin: (id: string, attachmentId: string) => Promise<any>;
|
||||
postShareCipherAttachment: (id: string, attachmentId: string, data: FormData,
|
||||
organizationId: string) => Promise<any>;
|
||||
renewAttachmentUploadUrl: (id: string, attachmentId: string) => Promise<AttachmentUploadDataResponse>;
|
||||
postAttachmentFile: (id: string, attachmentId: string, data: FormData) => Promise<any>;
|
||||
|
||||
getCollectionDetails: (organizationId: string, id: string) => Promise<CollectionGroupDetailsResponse>;
|
||||
getUserCollections: () => Promise<ListResponse<CollectionResponse>>;
|
||||
getCollections: (organizationId: string) => Promise<ListResponse<CollectionResponse>>;
|
||||
getCollectionUsers: (organizationId: string, id: string) => Promise<SelectionReadOnlyResponse[]>;
|
||||
postCollection: (organizationId: string, request: CollectionRequest) => Promise<CollectionResponse>;
|
||||
putCollectionUsers: (organizationId: string, id: string, request: SelectionReadOnlyRequest[]) => Promise<any>;
|
||||
putCollection: (organizationId: string, id: string, request: CollectionRequest) => Promise<CollectionResponse>;
|
||||
deleteCollection: (organizationId: string, id: string) => Promise<any>;
|
||||
deleteCollectionUser: (organizationId: string, id: string, organizationUserId: string) => Promise<any>;
|
||||
|
||||
getGroupDetails: (organizationId: string, id: string) => Promise<GroupDetailsResponse>;
|
||||
getGroups: (organizationId: string) => Promise<ListResponse<GroupResponse>>;
|
||||
getGroupUsers: (organizationId: string, id: string) => Promise<string[]>;
|
||||
postGroup: (organizationId: string, request: GroupRequest) => Promise<GroupResponse>;
|
||||
putGroup: (organizationId: string, id: string, request: GroupRequest) => Promise<GroupResponse>;
|
||||
putGroupUsers: (organizationId: string, id: string, request: string[]) => Promise<any>;
|
||||
deleteGroup: (organizationId: string, id: string) => Promise<any>;
|
||||
deleteGroupUser: (organizationId: string, id: string, organizationUserId: string) => Promise<any>;
|
||||
|
||||
getPolicy: (organizationId: string, type: PolicyType) => Promise<PolicyResponse>;
|
||||
getPolicies: (organizationId: string) => Promise<ListResponse<PolicyResponse>>;
|
||||
getPoliciesByToken: (organizationId: string, token: string, email: string, organizationUserId: string) =>
|
||||
Promise<ListResponse<PolicyResponse>>;
|
||||
putPolicy: (organizationId: string, type: PolicyType, request: PolicyRequest) => Promise<PolicyResponse>;
|
||||
|
||||
getOrganizationUser: (organizationId: string, id: string) => Promise<OrganizationUserDetailsResponse>;
|
||||
getOrganizationUserGroups: (organizationId: string, id: string) => Promise<string[]>;
|
||||
getOrganizationUsers: (organizationId: string) => Promise<ListResponse<OrganizationUserUserDetailsResponse>>;
|
||||
getOrganizationUserResetPasswordDetails: (organizationId: string, id: string)
|
||||
=> Promise<OrganizationUserResetPasswordDetailsReponse>;
|
||||
postOrganizationUserInvite: (organizationId: string, request: OrganizationUserInviteRequest) => Promise<any>;
|
||||
postOrganizationUserReinvite: (organizationId: string, id: string) => Promise<any>;
|
||||
postManyOrganizationUserReinvite: (organizationId: string, request: OrganizationUserBulkRequest) => Promise<ListResponse<OrganizationUserBulkResponse>>;
|
||||
postOrganizationUserAccept: (organizationId: string, id: string,
|
||||
request: OrganizationUserAcceptRequest) => Promise<any>;
|
||||
postOrganizationUserConfirm: (organizationId: string, id: string,
|
||||
request: OrganizationUserConfirmRequest) => Promise<any>;
|
||||
postOrganizationUsersPublicKey: (organizationId: string, request: OrganizationUserBulkRequest) =>
|
||||
Promise<ListResponse<OrganizationUserBulkPublicKeyResponse>>;
|
||||
postOrganizationUserBulkConfirm: (organizationId: string, request: OrganizationUserBulkConfirmRequest) => Promise<ListResponse<OrganizationUserBulkResponse>>;
|
||||
|
||||
putOrganizationUser: (organizationId: string, id: string, request: OrganizationUserUpdateRequest) => Promise<any>;
|
||||
putOrganizationUserGroups: (organizationId: string, id: string,
|
||||
request: OrganizationUserUpdateGroupsRequest) => Promise<any>;
|
||||
putOrganizationUserResetPasswordEnrollment: (organizationId: string, userId: string,
|
||||
request: OrganizationUserResetPasswordEnrollmentRequest) => Promise<any>;
|
||||
putOrganizationUserResetPassword: (organizationId: string, id: string,
|
||||
request: OrganizationUserResetPasswordRequest) => Promise<any>;
|
||||
deleteOrganizationUser: (organizationId: string, id: string) => Promise<any>;
|
||||
deleteManyOrganizationUsers: (organizationId: string, request: OrganizationUserBulkRequest) => Promise<ListResponse<OrganizationUserBulkResponse>>;
|
||||
|
||||
getSync: () => Promise<SyncResponse>;
|
||||
postImportDirectory: (organizationId: string, request: ImportDirectoryRequest) => Promise<any>;
|
||||
postPublicImportDirectory: (request: OrganizationImportRequest) => Promise<any>;
|
||||
|
||||
getSettingsDomains: () => Promise<DomainsResponse>;
|
||||
putSettingsDomains: (request: UpdateDomainsRequest) => Promise<DomainsResponse>;
|
||||
|
||||
getTwoFactorProviders: () => Promise<ListResponse<TwoFactorProviderResponse>>;
|
||||
getTwoFactorOrganizationProviders: (organizationId: string) => Promise<ListResponse<TwoFactorProviderResponse>>;
|
||||
getTwoFactorAuthenticator: (request: PasswordVerificationRequest) => Promise<TwoFactorAuthenticatorResponse>;
|
||||
getTwoFactorEmail: (request: PasswordVerificationRequest) => Promise<TwoFactorEmailResponse>;
|
||||
getTwoFactorDuo: (request: PasswordVerificationRequest) => Promise<TwoFactorDuoResponse>;
|
||||
getTwoFactorOrganizationDuo: (organizationId: string,
|
||||
request: PasswordVerificationRequest) => Promise<TwoFactorDuoResponse>;
|
||||
getTwoFactorYubiKey: (request: PasswordVerificationRequest) => Promise<TwoFactorYubiKeyResponse>;
|
||||
getTwoFactorWebAuthn: (request: PasswordVerificationRequest) => Promise<TwoFactorWebAuthnResponse>;
|
||||
getTwoFactorWebAuthnChallenge: (request: PasswordVerificationRequest) => Promise<ChallengeResponse>;
|
||||
getTwoFactorRecover: (request: PasswordVerificationRequest) => Promise<TwoFactorRecoverResponse>;
|
||||
putTwoFactorAuthenticator: (
|
||||
request: UpdateTwoFactorAuthenticatorRequest) => Promise<TwoFactorAuthenticatorResponse>;
|
||||
putTwoFactorEmail: (request: UpdateTwoFactorEmailRequest) => Promise<TwoFactorEmailResponse>;
|
||||
putTwoFactorDuo: (request: UpdateTwoFactorDuoRequest) => Promise<TwoFactorDuoResponse>;
|
||||
putTwoFactorOrganizationDuo: (organizationId: string,
|
||||
request: UpdateTwoFactorDuoRequest) => Promise<TwoFactorDuoResponse>;
|
||||
putTwoFactorYubiKey: (request: UpdateTwoFactorYubioOtpRequest) => Promise<TwoFactorYubiKeyResponse>;
|
||||
putTwoFactorWebAuthn: (request: UpdateTwoFactorWebAuthnRequest) => Promise<TwoFactorWebAuthnResponse>;
|
||||
deleteTwoFactorWebAuthn: (request: UpdateTwoFactorWebAuthnDeleteRequest) => Promise<TwoFactorWebAuthnResponse>;
|
||||
putTwoFactorDisable: (request: TwoFactorProviderRequest) => Promise<TwoFactorProviderResponse>;
|
||||
putTwoFactorOrganizationDisable: (organizationId: string,
|
||||
request: TwoFactorProviderRequest) => Promise<TwoFactorProviderResponse>;
|
||||
postTwoFactorRecover: (request: TwoFactorRecoveryRequest) => Promise<any>;
|
||||
postTwoFactorEmailSetup: (request: TwoFactorEmailRequest) => Promise<any>;
|
||||
postTwoFactorEmail: (request: TwoFactorEmailRequest) => Promise<any>;
|
||||
|
||||
getEmergencyAccessTrusted: () => Promise<ListResponse<EmergencyAccessGranteeDetailsResponse>>;
|
||||
getEmergencyAccessGranted: () => Promise<ListResponse<EmergencyAccessGrantorDetailsResponse>>;
|
||||
getEmergencyAccess: (id: string) => Promise<EmergencyAccessGranteeDetailsResponse>;
|
||||
getEmergencyGrantorPolicies: (id: string) => Promise<ListResponse<PolicyResponse>>;
|
||||
putEmergencyAccess: (id: string, request: EmergencyAccessUpdateRequest) => Promise<any>;
|
||||
deleteEmergencyAccess: (id: string) => Promise<any>;
|
||||
postEmergencyAccessInvite: (request: EmergencyAccessInviteRequest) => Promise<any>;
|
||||
postEmergencyAccessReinvite: (id: string) => Promise<any>;
|
||||
postEmergencyAccessAccept: (id: string, request: EmergencyAccessAcceptRequest) => Promise<any>;
|
||||
postEmergencyAccessConfirm: (id: string, request: EmergencyAccessConfirmRequest) => Promise<any>;
|
||||
postEmergencyAccessInitiate: (id: string) => Promise<any>;
|
||||
postEmergencyAccessApprove: (id: string) => Promise<any>;
|
||||
postEmergencyAccessReject: (id: string) => Promise<any>;
|
||||
postEmergencyAccessTakeover: (id: string) => Promise<EmergencyAccessTakeoverResponse>;
|
||||
postEmergencyAccessPassword: (id: string, request: EmergencyAccessPasswordRequest) => Promise<any>;
|
||||
postEmergencyAccessView: (id: string) => Promise<EmergencyAccessViewResponse>;
|
||||
|
||||
getOrganization: (id: string) => Promise<OrganizationResponse>;
|
||||
getOrganizationBilling: (id: string) => Promise<BillingResponse>;
|
||||
getOrganizationSubscription: (id: string) => Promise<OrganizationSubscriptionResponse>;
|
||||
getOrganizationLicense: (id: string, installationId: string) => Promise<any>;
|
||||
getOrganizationTaxInfo: (id: string) => Promise<TaxInfoResponse>;
|
||||
postOrganization: (request: OrganizationCreateRequest) => Promise<OrganizationResponse>;
|
||||
putOrganization: (id: string, request: OrganizationUpdateRequest) => Promise<OrganizationResponse>;
|
||||
putOrganizationTaxInfo: (id: string, request: OrganizationTaxInfoUpdateRequest) => Promise<any>;
|
||||
postLeaveOrganization: (id: string) => Promise<any>;
|
||||
postOrganizationLicense: (data: FormData) => Promise<OrganizationResponse>;
|
||||
postOrganizationLicenseUpdate: (id: string, data: FormData) => Promise<any>;
|
||||
postOrganizationApiKey: (id: string, request: PasswordVerificationRequest) => Promise<ApiKeyResponse>;
|
||||
postOrganizationRotateApiKey: (id: string, request: PasswordVerificationRequest) => Promise<ApiKeyResponse>;
|
||||
postOrganizationUpgrade: (id: string, request: OrganizationUpgradeRequest) => Promise<PaymentResponse>;
|
||||
postOrganizationSeat: (id: string, request: SeatRequest) => Promise<PaymentResponse>;
|
||||
postOrganizationStorage: (id: string, request: StorageRequest) => Promise<any>;
|
||||
postOrganizationPayment: (id: string, request: PaymentRequest) => Promise<any>;
|
||||
postOrganizationVerifyBank: (id: string, request: VerifyBankRequest) => Promise<any>;
|
||||
postOrganizationCancel: (id: string) => Promise<any>;
|
||||
postOrganizationReinstate: (id: string) => Promise<any>;
|
||||
deleteOrganization: (id: string, request: PasswordVerificationRequest) => Promise<any>;
|
||||
getPlans: () => Promise<ListResponse<PlanResponse>>;
|
||||
getTaxRates: () => Promise<ListResponse<TaxRateResponse>>;
|
||||
getOrganizationKeys: (id: string) => Promise<OrganizationKeysResponse>;
|
||||
postOrganizationKeys: (id: string, request: OrganizationKeysRequest) => Promise<OrganizationKeysResponse>;
|
||||
|
||||
getEvents: (start: string, end: string, token: string) => Promise<ListResponse<EventResponse>>;
|
||||
getEventsCipher: (id: string, start: string, end: string, token: string) => Promise<ListResponse<EventResponse>>;
|
||||
getEventsOrganization: (id: string, start: string, end: string,
|
||||
token: string) => Promise<ListResponse<EventResponse>>;
|
||||
getEventsOrganizationUser: (organizationId: string, id: string,
|
||||
start: string, end: string, token: string) => Promise<ListResponse<EventResponse>>;
|
||||
postEventsCollect: (request: EventRequest[]) => Promise<any>;
|
||||
|
||||
deleteSsoUser: (organizationId: string) => Promise<any>;
|
||||
getSsoUserIdentifier: () => Promise<string>;
|
||||
|
||||
getUserPublicKey: (id: string) => Promise<UserKeyResponse>;
|
||||
|
||||
getHibpBreach: (username: string) => Promise<BreachAccountResponse[]>;
|
||||
|
||||
postBitPayInvoice: (request: BitPayInvoiceRequest) => Promise<string>;
|
||||
postSetupPayment: () => Promise<string>;
|
||||
|
||||
getActiveBearerToken: () => Promise<string>;
|
||||
fetch: (request: Request) => Promise<Response>;
|
||||
nativeFetch: (request: Request) => Promise<Response>;
|
||||
|
||||
preValidateSso: (identifier: string) => Promise<boolean>;
|
||||
}
|
||||
7
common/src/abstractions/apiKey.service.ts
Normal file
7
common/src/abstractions/apiKey.service.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export abstract class ApiKeyService {
|
||||
setInformation: (clientId: string) => Promise<any>;
|
||||
clear: () => Promise<any>;
|
||||
getEntityType: () => Promise<string>;
|
||||
getEntityId: () => Promise<string>;
|
||||
isAuthenticated: () => Promise<boolean>;
|
||||
}
|
||||
4
common/src/abstractions/appId.service.ts
Normal file
4
common/src/abstractions/appId.service.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export abstract class AppIdService {
|
||||
getAppId: () => Promise<string>;
|
||||
getAnonymousAppId: () => Promise<string>;
|
||||
}
|
||||
6
common/src/abstractions/audit.service.ts
Normal file
6
common/src/abstractions/audit.service.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { BreachAccountResponse } from '../models/response/breachAccountResponse';
|
||||
|
||||
export abstract class AuditService {
|
||||
passwordLeaked: (password: string) => Promise<number>;
|
||||
breachedAccounts: (username: string) => Promise<BreachAccountResponse[]>;
|
||||
}
|
||||
35
common/src/abstractions/auth.service.ts
Normal file
35
common/src/abstractions/auth.service.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import { TwoFactorProviderType } from '../enums/twoFactorProviderType';
|
||||
|
||||
import { AuthResult } from '../models/domain/authResult';
|
||||
import { SymmetricCryptoKey } from '../models/domain/symmetricCryptoKey';
|
||||
|
||||
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) => Promise<AuthResult>;
|
||||
logInSso: (code: string, codeVerifier: string, redirectUrl: string) => Promise<AuthResult>;
|
||||
logInApiKey: (clientId: string, clientSecret: string) => Promise<AuthResult>;
|
||||
logInTwoFactor: (twoFactorProvider: TwoFactorProviderType, twoFactorToken: string,
|
||||
remember?: boolean) => Promise<AuthResult>;
|
||||
logInComplete: (email: string, masterPassword: string, twoFactorProvider: TwoFactorProviderType,
|
||||
twoFactorToken: string, remember?: boolean) => 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;
|
||||
makePreloginKey: (masterPassword: string, email: string) => Promise<SymmetricCryptoKey>;
|
||||
authingWithApiKey: () => boolean;
|
||||
authingWithSso: () => boolean;
|
||||
authingWithPassword: () => boolean;
|
||||
}
|
||||
6
common/src/abstractions/biometric.main.ts
Normal file
6
common/src/abstractions/biometric.main.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
export abstract class BiometricMain {
|
||||
isError: boolean;
|
||||
init: () => Promise<void>;
|
||||
supportsBiometric: () => Promise<boolean>;
|
||||
requestCreate: () => Promise<boolean>;
|
||||
}
|
||||
5
common/src/abstractions/broadcaster.service.ts
Normal file
5
common/src/abstractions/broadcaster.service.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export abstract class BroadcasterService {
|
||||
send: (message: any, id?: string) => void;
|
||||
subscribe: (id: string, messageCallback: (message: any) => any) => void;
|
||||
unsubscribe: (id: string) => void;
|
||||
}
|
||||
59
common/src/abstractions/cipher.service.ts
Normal file
59
common/src/abstractions/cipher.service.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
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';
|
||||
|
||||
export abstract class CipherService {
|
||||
decryptedCipherCache: CipherView[];
|
||||
|
||||
clearCache: () => void;
|
||||
encrypt: (model: CipherView, key?: SymmetricCryptoKey, originalCipher?: Cipher) => Promise<Cipher>;
|
||||
encryptFields: (fieldsModel: FieldView[], key: SymmetricCryptoKey) => Promise<Field[]>;
|
||||
encryptField: (fieldModel: FieldView, key: SymmetricCryptoKey) => Promise<Field>;
|
||||
get: (id: string) => Promise<Cipher>;
|
||||
getAll: () => Promise<Cipher[]>;
|
||||
getAllDecrypted: () => Promise<CipherView[]>;
|
||||
getAllDecryptedForGrouping: (groupingId: string, folder?: boolean) => Promise<CipherView[]>;
|
||||
getAllDecryptedForUrl: (url: string, includeOtherTypes?: CipherType[],
|
||||
defaultMatch?: UriMatchType) => Promise<CipherView[]>;
|
||||
getAllFromApiForOrganization: (organizationId: string) => Promise<CipherView[]>;
|
||||
getLastUsedForUrl: (url: string, autofillOnPageLoad: boolean) => Promise<CipherView>;
|
||||
getLastLaunchedForUrl: (url: string, autofillOnPageLoad: boolean) => Promise<CipherView>;
|
||||
getNextCipherForUrl: (url: string) => Promise<CipherView>;
|
||||
updateLastUsedIndexForUrl: (url: string) => void;
|
||||
updateLastUsedDate: (id: string) => Promise<void>;
|
||||
updateLastLaunchedDate: (id: string) => Promise<void>;
|
||||
saveNeverDomain: (domain: string) => Promise<void>;
|
||||
saveWithServer: (cipher: Cipher) => Promise<any>;
|
||||
shareWithServer: (cipher: CipherView, organizationId: string, collectionIds: string[]) => Promise<any>;
|
||||
shareManyWithServer: (ciphers: CipherView[], organizationId: string, collectionIds: string[]) => Promise<any>;
|
||||
saveAttachmentWithServer: (cipher: Cipher, unencryptedFile: any, admin?: boolean) => Promise<Cipher>;
|
||||
saveAttachmentRawWithServer: (cipher: Cipher, filename: string, data: ArrayBuffer,
|
||||
admin?: boolean) => Promise<Cipher>;
|
||||
saveCollectionsWithServer: (cipher: Cipher) => Promise<any>;
|
||||
upsert: (cipher: CipherData | CipherData[]) => Promise<any>;
|
||||
replace: (ciphers: { [id: string]: CipherData; }) => Promise<any>;
|
||||
clear: (userId: string) => Promise<any>;
|
||||
moveManyWithServer: (ids: string[], folderId: string) => Promise<any>;
|
||||
delete: (id: string | string[]) => Promise<any>;
|
||||
deleteWithServer: (id: string) => Promise<any>;
|
||||
deleteManyWithServer: (ids: string[]) => Promise<any>;
|
||||
deleteAttachment: (id: string, attachmentId: string) => Promise<void>;
|
||||
deleteAttachmentWithServer: (id: string, attachmentId: string) => Promise<void>;
|
||||
sortCiphersByLastUsed: (a: any, b: any) => number;
|
||||
sortCiphersByLastUsedThenName: (a: any, b: any) => number;
|
||||
getLocaleSortingFunction: () => (a: CipherView, b: CipherView) => number;
|
||||
softDelete: (id: string | string[]) => Promise<any>;
|
||||
softDeleteWithServer: (id: string) => Promise<any>;
|
||||
softDeleteManyWithServer: (ids: string[]) => Promise<any>;
|
||||
restore: (cipher: { id: string, revisionDate: string; } | { id: string, revisionDate: string; }[]) => Promise<any>;
|
||||
restoreWithServer: (id: string) => Promise<any>;
|
||||
restoreManyWithServer: (ids: string[]) => Promise<any>;
|
||||
}
|
||||
23
common/src/abstractions/collection.service.ts
Normal file
23
common/src/abstractions/collection.service.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
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 {
|
||||
decryptedCollectionCache: CollectionView[];
|
||||
|
||||
clearCache: () => void;
|
||||
encrypt: (model: CollectionView) => Promise<Collection>;
|
||||
decryptMany: (collections: Collection[]) => Promise<CollectionView[]>;
|
||||
get: (id: string) => Promise<Collection>;
|
||||
getAll: () => Promise<Collection[]>;
|
||||
getAllDecrypted: () => Promise<CollectionView[]>;
|
||||
getAllNested: (collections?: CollectionView[]) => Promise<TreeNode<CollectionView>[]>;
|
||||
getNested: (id: string) => Promise<TreeNode<CollectionView>>;
|
||||
upsert: (collection: CollectionData | CollectionData[]) => Promise<any>;
|
||||
replace: (collections: { [id: string]: CollectionData; }) => Promise<any>;
|
||||
clear: (userId: string) => Promise<any>;
|
||||
delete: (id: string | string[]) => Promise<any>;
|
||||
}
|
||||
52
common/src/abstractions/crypto.service.ts
Normal file
52
common/src/abstractions/crypto.service.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
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 { KdfType } from '../enums/kdfType';
|
||||
|
||||
export abstract class CryptoService {
|
||||
setKey: (key: SymmetricCryptoKey) => Promise<any>;
|
||||
setKeyHash: (keyHash: string) => Promise<{}>;
|
||||
setEncKey: (encKey: string) => Promise<{}>;
|
||||
setEncPrivateKey: (encPrivateKey: string) => Promise<{}>;
|
||||
setOrgKeys: (orgs: ProfileOrganizationResponse[]) => Promise<{}>;
|
||||
getKey: () => Promise<SymmetricCryptoKey>;
|
||||
getKeyHash: () => Promise<string>;
|
||||
getEncKey: (key?: SymmetricCryptoKey) => Promise<SymmetricCryptoKey>;
|
||||
getPublicKey: () => Promise<ArrayBuffer>;
|
||||
getPrivateKey: () => Promise<ArrayBuffer>;
|
||||
getFingerprint: (userId: string, publicKey?: ArrayBuffer) => Promise<string[]>;
|
||||
getOrgKeys: () => Promise<Map<string, SymmetricCryptoKey>>;
|
||||
getOrgKey: (orgId: string) => Promise<SymmetricCryptoKey>;
|
||||
hasKey: () => Promise<boolean>;
|
||||
hasEncKey: () => Promise<boolean>;
|
||||
clearKey: () => Promise<any>;
|
||||
clearKeyHash: () => Promise<any>;
|
||||
clearEncKey: (memoryOnly?: boolean) => Promise<any>;
|
||||
clearKeyPair: (memoryOnly?: boolean) => Promise<any>;
|
||||
clearOrgKeys: (memoryOnly?: boolean) => Promise<any>;
|
||||
clearPinProtectedKey: () => Promise<any>;
|
||||
clearKeys: () => Promise<any>;
|
||||
toggleKey: () => Promise<any>;
|
||||
makeKey: (password: string, salt: string, kdf: KdfType, kdfIterations: number) => Promise<SymmetricCryptoKey>;
|
||||
makeKeyFromPin: (pin: string, salt: string, kdf: KdfType, kdfIterations: number,
|
||||
protectedKeyCs?: EncString) => Promise<SymmetricCryptoKey>;
|
||||
makeShareKey: () => Promise<[EncString, SymmetricCryptoKey]>;
|
||||
makeKeyPair: (key?: SymmetricCryptoKey) => Promise<[string, EncString]>;
|
||||
makePinKey: (pin: string, salt: string, kdf: KdfType, kdfIterations: number) => Promise<SymmetricCryptoKey>;
|
||||
makeSendKey: (keyMaterial: ArrayBuffer) => Promise<SymmetricCryptoKey>;
|
||||
hashPassword: (password: string, key: SymmetricCryptoKey) => Promise<string>;
|
||||
makeEncKey: (key: SymmetricCryptoKey) => Promise<[SymmetricCryptoKey, EncString]>;
|
||||
remakeEncKey: (key: SymmetricCryptoKey, encKey?: SymmetricCryptoKey) => Promise<[SymmetricCryptoKey, EncString]>;
|
||||
encrypt: (plainValue: string | ArrayBuffer, key?: SymmetricCryptoKey) => Promise<EncString>;
|
||||
encryptToBytes: (plainValue: ArrayBuffer, key?: SymmetricCryptoKey) => Promise<EncArrayBuffer>;
|
||||
rsaEncrypt: (data: ArrayBuffer, publicKey?: ArrayBuffer) => Promise<EncString>;
|
||||
rsaDecrypt: (encValue: string, privateKeyValue?: ArrayBuffer) => Promise<ArrayBuffer>;
|
||||
decryptToBytes: (encString: EncString, key?: SymmetricCryptoKey) => Promise<ArrayBuffer>;
|
||||
decryptToUtf8: (encString: EncString, key?: SymmetricCryptoKey) => Promise<string>;
|
||||
decryptFromBytes: (encBuf: ArrayBuffer, key: SymmetricCryptoKey) => Promise<ArrayBuffer>;
|
||||
randomNumber: (min: number, max: number) => Promise<number>;
|
||||
validateKey: (key: SymmetricCryptoKey) => Promise<boolean>;
|
||||
}
|
||||
27
common/src/abstractions/cryptoFunction.service.ts
Normal file
27
common/src/abstractions/cryptoFunction.service.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { DecryptParameters } from '../models/domain/decryptParameters';
|
||||
import { SymmetricCryptoKey } from '../models/domain/symmetricCryptoKey';
|
||||
|
||||
export abstract class CryptoFunctionService {
|
||||
pbkdf2: (password: string | ArrayBuffer, salt: string | ArrayBuffer, algorithm: 'sha256' | 'sha512',
|
||||
iterations: number) => Promise<ArrayBuffer>;
|
||||
hkdf: (ikm: ArrayBuffer, salt: string | ArrayBuffer, info: string | ArrayBuffer,
|
||||
outputByteSize: number, algorithm: 'sha256' | 'sha512') => Promise<ArrayBuffer>;
|
||||
hkdfExpand: (prk: ArrayBuffer, info: string | ArrayBuffer, outputByteSize: number,
|
||||
algorithm: 'sha256' | 'sha512') => Promise<ArrayBuffer>;
|
||||
hash: (value: string | ArrayBuffer, algorithm: 'sha1' | 'sha256' | 'sha512' | 'md5') => Promise<ArrayBuffer>;
|
||||
hmac: (value: ArrayBuffer, key: ArrayBuffer, algorithm: 'sha1' | 'sha256' | 'sha512') => Promise<ArrayBuffer>;
|
||||
compare: (a: ArrayBuffer, b: ArrayBuffer) => Promise<boolean>;
|
||||
hmacFast: (value: ArrayBuffer | string, key: ArrayBuffer | string, algorithm: 'sha1' | 'sha256' | 'sha512') =>
|
||||
Promise<ArrayBuffer | string>;
|
||||
compareFast: (a: ArrayBuffer | string, b: ArrayBuffer | string) => Promise<boolean>;
|
||||
aesEncrypt: (data: ArrayBuffer, iv: ArrayBuffer, key: ArrayBuffer) => Promise<ArrayBuffer>;
|
||||
aesDecryptFastParameters: (data: string, iv: string, mac: string, key: SymmetricCryptoKey) =>
|
||||
DecryptParameters<ArrayBuffer | string>;
|
||||
aesDecryptFast: (parameters: DecryptParameters<ArrayBuffer | string>) => Promise<string>;
|
||||
aesDecrypt: (data: ArrayBuffer, iv: ArrayBuffer, key: ArrayBuffer) => Promise<ArrayBuffer>;
|
||||
rsaEncrypt: (data: ArrayBuffer, publicKey: ArrayBuffer, algorithm: 'sha1' | 'sha256') => Promise<ArrayBuffer>;
|
||||
rsaDecrypt: (data: ArrayBuffer, privateKey: ArrayBuffer, algorithm: 'sha1' | 'sha256') => Promise<ArrayBuffer>;
|
||||
rsaExtractPublicKey: (privateKey: ArrayBuffer) => Promise<ArrayBuffer>;
|
||||
rsaGenerateKeyPair: (length: 1024 | 2048 | 4096) => Promise<[ArrayBuffer, ArrayBuffer]>;
|
||||
randomBytes: (length: number) => Promise<ArrayBuffer>;
|
||||
}
|
||||
14
common/src/abstractions/environment.service.ts
Normal file
14
common/src/abstractions/environment.service.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
export abstract class EnvironmentService {
|
||||
baseUrl: string;
|
||||
webVaultUrl: string;
|
||||
apiUrl: string;
|
||||
identityUrl: string;
|
||||
iconsUrl: string;
|
||||
notificationsUrl: string;
|
||||
eventsUrl: string;
|
||||
enterpriseUrl: string;
|
||||
|
||||
getWebVaultUrl: () => string;
|
||||
setUrlsFromStorage: () => Promise<void>;
|
||||
setUrls: (urls: any) => Promise<any>;
|
||||
}
|
||||
7
common/src/abstractions/event.service.ts
Normal file
7
common/src/abstractions/event.service.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { EventType } from '../enums/eventType';
|
||||
|
||||
export abstract class EventService {
|
||||
collect: (eventType: EventType, cipherId?: string, uploadImmediately?: boolean) => Promise<any>;
|
||||
uploadEvents: () => Promise<any>;
|
||||
clearEvents: () => Promise<any>;
|
||||
}
|
||||
8
common/src/abstractions/export.service.ts
Normal file
8
common/src/abstractions/export.service.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { EventView } from '../models/view/eventView';
|
||||
|
||||
export abstract class ExportService {
|
||||
getExport: (format?: 'csv' | 'json' | 'encrypted_json') => Promise<string>;
|
||||
getOrganizationExport: (organizationId: string, format?: 'csv' | 'json' | 'encrypted_json') => Promise<string>;
|
||||
getEventExport: (events: EventView[]) => Promise<string>;
|
||||
getFileName: (prefix?: string, extension?: string) => string;
|
||||
}
|
||||
11
common/src/abstractions/fileUpload.service.ts
Normal file
11
common/src/abstractions/fileUpload.service.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { EncArrayBuffer } from '../models/domain/encArrayBuffer';
|
||||
import { EncString } from '../models/domain/encString';
|
||||
import { AttachmentUploadDataResponse } from '../models/response/attachmentUploadDataResponse';
|
||||
import { SendFileUploadDataResponse } from '../models/response/sendFileUploadDataResponse';
|
||||
|
||||
export abstract class FileUploadService {
|
||||
uploadSendFile: (uploadData: SendFileUploadDataResponse, fileName: EncString,
|
||||
encryptedFileData: EncArrayBuffer) => Promise<any>;
|
||||
uploadCipherAttachment: (admin: boolean, uploadData: AttachmentUploadDataResponse, fileName: string,
|
||||
encryptedFileData: EncArrayBuffer) => Promise<any>;
|
||||
}
|
||||
25
common/src/abstractions/folder.service.ts
Normal file
25
common/src/abstractions/folder.service.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
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 {
|
||||
decryptedFolderCache: FolderView[];
|
||||
|
||||
clearCache: () => void;
|
||||
encrypt: (model: FolderView, key?: SymmetricCryptoKey) => Promise<Folder>;
|
||||
get: (id: string) => Promise<Folder>;
|
||||
getAll: () => Promise<Folder[]>;
|
||||
getAllDecrypted: () => Promise<FolderView[]>;
|
||||
getAllNested: () => Promise<TreeNode<FolderView>[]>;
|
||||
getNested: (id: string) => Promise<TreeNode<FolderView>>;
|
||||
saveWithServer: (folder: Folder) => Promise<any>;
|
||||
upsert: (folder: FolderData | FolderData[]) => Promise<any>;
|
||||
replace: (folders: { [id: string]: FolderData; }) => Promise<any>;
|
||||
clear: (userId: string) => Promise<any>;
|
||||
delete: (id: string | string[]) => Promise<any>;
|
||||
deleteWithServer: (id: string) => Promise<any>;
|
||||
}
|
||||
9
common/src/abstractions/i18n.service.ts
Normal file
9
common/src/abstractions/i18n.service.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
export abstract class I18nService {
|
||||
locale: string;
|
||||
supportedTranslationLocales: string[];
|
||||
translationLocale: string;
|
||||
collator: Intl.Collator;
|
||||
localeNames: Map<string, string>;
|
||||
t: (id: string, p1?: string, p2?: string, p3?: string) => string;
|
||||
translate: (id: string, p1?: string, p2?: string, p3?: string) => string;
|
||||
}
|
||||
13
common/src/abstractions/import.service.ts
Normal file
13
common/src/abstractions/import.service.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { Importer } from '../importers/importer';
|
||||
|
||||
export interface ImportOption {
|
||||
id: string;
|
||||
name: string;
|
||||
}
|
||||
export abstract class ImportService {
|
||||
featuredImportOptions: ImportOption[];
|
||||
regularImportOptions: ImportOption[];
|
||||
getImportOptions: () => ImportOption[];
|
||||
import: (importer: Importer, fileContents: string, organizationId?: string) => Promise<Error>;
|
||||
getImporter: (format: string, organizationId: string) => Importer;
|
||||
}
|
||||
24
common/src/abstractions/index.ts
Normal file
24
common/src/abstractions/index.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
// Using index.ts is deprecated, please do not extend
|
||||
export { ApiService } from './api.service';
|
||||
export { AppIdService } from './appId.service';
|
||||
export { AuditService } from './audit.service';
|
||||
export { AuthService } from './auth.service';
|
||||
export { CipherService } from './cipher.service';
|
||||
export { CollectionService } from './collection.service';
|
||||
export { CryptoService } from './crypto.service';
|
||||
export { EnvironmentService } from './environment.service';
|
||||
export { FolderService } from './folder.service';
|
||||
export { I18nService } from './i18n.service';
|
||||
export { LogService } from './log.service';
|
||||
export { MessagingService } from './messaging.service';
|
||||
export { PasswordGenerationService } from './passwordGeneration.service';
|
||||
export { PlatformUtilsService } from './platformUtils.service';
|
||||
export { SearchService } from './search.service';
|
||||
export { SettingsService } from './settings.service';
|
||||
export { StorageService } from './storage.service';
|
||||
export { StateService } from './state.service';
|
||||
export { SyncService } from './sync.service';
|
||||
export { TokenService } from './token.service';
|
||||
export { TotpService } from './totp.service';
|
||||
export { UserService } from './user.service';
|
||||
export { VaultTimeoutService } from './vaultTimeout.service';
|
||||
11
common/src/abstractions/log.service.ts
Normal file
11
common/src/abstractions/log.service.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { LogLevelType } from '../enums/logLevelType';
|
||||
|
||||
export abstract class LogService {
|
||||
debug: (message: string) => void;
|
||||
info: (message: string) => void;
|
||||
warning: (message: string) => void;
|
||||
error: (message: string) => void;
|
||||
write: (level: LogLevelType, message: string) => void;
|
||||
time: (label: string) => void;
|
||||
timeEnd: (label: string) => [number, number];
|
||||
}
|
||||
3
common/src/abstractions/messaging.service.ts
Normal file
3
common/src/abstractions/messaging.service.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export abstract class MessagingService {
|
||||
send: (subscriber: string, arg?: any) => void;
|
||||
}
|
||||
8
common/src/abstractions/notifications.service.ts
Normal file
8
common/src/abstractions/notifications.service.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { EnvironmentService } from './environment.service';
|
||||
|
||||
export abstract class NotificationsService {
|
||||
init: (environmentService: EnvironmentService) => Promise<void>;
|
||||
updateConnection: (sync?: boolean) => Promise<void>;
|
||||
reconnectFromActivity: () => Promise<void>;
|
||||
disconnectFromInactivity: () => Promise<void>;
|
||||
}
|
||||
18
common/src/abstractions/passwordGeneration.service.ts
Normal file
18
common/src/abstractions/passwordGeneration.service.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import * as zxcvbn from 'zxcvbn';
|
||||
|
||||
import { GeneratedPasswordHistory } from '../models/domain/generatedPasswordHistory';
|
||||
import { PasswordGeneratorPolicyOptions } from '../models/domain/passwordGeneratorPolicyOptions';
|
||||
|
||||
export abstract class PasswordGenerationService {
|
||||
generatePassword: (options: any) => Promise<string>;
|
||||
generatePassphrase: (options: any) => Promise<string>;
|
||||
getOptions: () => Promise<[any, PasswordGeneratorPolicyOptions]>;
|
||||
enforcePasswordGeneratorPoliciesOnOptions: (options: any) => Promise<[any, PasswordGeneratorPolicyOptions]>;
|
||||
getPasswordGeneratorPolicyOptions: () => Promise<PasswordGeneratorPolicyOptions>;
|
||||
saveOptions: (options: any) => Promise<any>;
|
||||
getHistory: () => Promise<GeneratedPasswordHistory[]>;
|
||||
addHistory: (password: string) => Promise<any>;
|
||||
clear: () => Promise<any>;
|
||||
passwordStrength: (password: string, userInputs?: string[]) => zxcvbn.ZXCVBNResult;
|
||||
normalizeOptions: (options: any, enforcedPolicyOptions: PasswordGeneratorPolicyOptions) => void;
|
||||
}
|
||||
4
common/src/abstractions/passwordReprompt.service.ts
Normal file
4
common/src/abstractions/passwordReprompt.service.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export abstract class PasswordRepromptService {
|
||||
protectedFields: () => string[];
|
||||
showPasswordPrompt: () => Promise<boolean>;
|
||||
}
|
||||
39
common/src/abstractions/platformUtils.service.ts
Normal file
39
common/src/abstractions/platformUtils.service.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import { DeviceType } from '../enums/deviceType';
|
||||
|
||||
export abstract class PlatformUtilsService {
|
||||
identityClientId: string;
|
||||
getDevice: () => DeviceType;
|
||||
getDeviceString: () => string;
|
||||
isFirefox: () => boolean;
|
||||
isChrome: () => boolean;
|
||||
isEdge: () => boolean;
|
||||
isOpera: () => boolean;
|
||||
isVivaldi: () => boolean;
|
||||
isSafari: () => boolean;
|
||||
isIE: () => boolean;
|
||||
isMacAppStore: () => boolean;
|
||||
isViewOpen: () => Promise<boolean>;
|
||||
/**
|
||||
* @deprecated This only ever returns null. Pull from your platform's storage using ConstantsService.vaultTimeoutKey
|
||||
*/
|
||||
lockTimeout: () => number;
|
||||
launchUri: (uri: string, options?: any) => void;
|
||||
saveFile: (win: Window, blobData: any, blobOptions: any, fileName: string) => void;
|
||||
getApplicationVersion: () => Promise<string>;
|
||||
supportsWebAuthn: (win: Window) => boolean;
|
||||
supportsDuo: () => boolean;
|
||||
showToast: (type: 'error' | 'success' | 'warning' | 'info', title: string, text: string | string[],
|
||||
options?: any) => void;
|
||||
showDialog: (body: string, title?: string, confirmText?: string, cancelText?: string,
|
||||
type?: string, bodyIsHtml?: boolean) => Promise<boolean>;
|
||||
showPasswordDialog: (title: string, body: string, passwordValidation: (value: string) => Promise<boolean>) => Promise<boolean>;
|
||||
isDev: () => boolean;
|
||||
isSelfHost: () => boolean;
|
||||
copyToClipboard: (text: string, options?: any) => void | boolean;
|
||||
readFromClipboard: (options?: any) => Promise<string>;
|
||||
supportsBiometric: () => Promise<boolean>;
|
||||
authenticateBiometric: () => Promise<boolean>;
|
||||
getDefaultSystemTheme: () => Promise<'light' | 'dark'>;
|
||||
onDefaultSystemThemeChange: (callback: ((theme: 'light' | 'dark') => unknown)) => unknown;
|
||||
supportsSecureStorage: () => boolean;
|
||||
}
|
||||
24
common/src/abstractions/policy.service.ts
Normal file
24
common/src/abstractions/policy.service.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
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 { PolicyType } from '../enums/policyType';
|
||||
|
||||
import { ListResponse } from '../models/response/listResponse';
|
||||
import { PolicyResponse } from '../models/response/policyResponse';
|
||||
|
||||
export abstract class PolicyService {
|
||||
policyCache: Policy[];
|
||||
|
||||
clearCache: () => void;
|
||||
getAll: (type?: PolicyType) => Promise<Policy[]>;
|
||||
replace: (policies: { [id: string]: PolicyData; }) => Promise<any>;
|
||||
clear: (userId: string) => Promise<any>;
|
||||
getMasterPasswordPolicyOptions: (policies?: Policy[]) => Promise<MasterPasswordPolicyOptions>;
|
||||
evaluateMasterPassword: (passwordStrength: number, newPassword: string,
|
||||
enforcedPolicyOptions?: MasterPasswordPolicyOptions) => boolean;
|
||||
getResetPasswordPolicyOptions: (policies: Policy[], orgId: string) => [ResetPasswordPolicyOptions, boolean];
|
||||
mapPoliciesFromToken: (policiesResponse: ListResponse<PolicyResponse>) => Policy[];
|
||||
}
|
||||
14
common/src/abstractions/search.service.ts
Normal file
14
common/src/abstractions/search.service.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { CipherView } from '../models/view/cipherView';
|
||||
import { SendView } from '../models/view/sendView';
|
||||
|
||||
export abstract class SearchService {
|
||||
indexedEntityId?: string = null;
|
||||
clearIndex: () => void;
|
||||
isSearchable: (query: string) => boolean;
|
||||
indexCiphers: (indexedEntityGuid?: string, ciphersToIndex?: CipherView[]) => Promise<void>;
|
||||
searchCiphers: (query: string,
|
||||
filter?: ((cipher: CipherView) => boolean) | (((cipher: CipherView) => boolean)[]),
|
||||
ciphers?: CipherView[]) => Promise<CipherView[]>;
|
||||
searchCiphersBasic: (ciphers: CipherView[], query: string, deleted?: boolean) => CipherView[];
|
||||
searchSends: (sends: SendView[], query: string) => SendView[];
|
||||
}
|
||||
24
common/src/abstractions/send.service.ts
Normal file
24
common/src/abstractions/send.service.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
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 {
|
||||
decryptedSendCache: SendView[];
|
||||
|
||||
clearCache: () => void;
|
||||
encrypt: (model: SendView, file: File | ArrayBuffer, password: string, key?: SymmetricCryptoKey) => Promise<[Send, EncArrayBuffer]>;
|
||||
get: (id: string) => Promise<Send>;
|
||||
getAll: () => Promise<Send[]>;
|
||||
getAllDecrypted: () => Promise<SendView[]>;
|
||||
saveWithServer: (sendData: [Send, EncArrayBuffer]) => Promise<any>;
|
||||
upsert: (send: SendData | SendData[]) => Promise<any>;
|
||||
replace: (sends: { [id: string]: SendData; }) => Promise<any>;
|
||||
clear: (userId: string) => Promise<any>;
|
||||
delete: (id: string | string[]) => Promise<any>;
|
||||
deleteWithServer: (id: string) => Promise<any>;
|
||||
removePasswordWithServer: (id: string) => Promise<any>;
|
||||
}
|
||||
6
common/src/abstractions/settings.service.ts
Normal file
6
common/src/abstractions/settings.service.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
export abstract class SettingsService {
|
||||
clearCache: () => void;
|
||||
getEquivalentDomains: () => Promise<any>;
|
||||
setEquivalentDomains: (equivalentDomains: string[][]) => Promise<any>;
|
||||
clear: (userId: string) => Promise<void>;
|
||||
}
|
||||
6
common/src/abstractions/state.service.ts
Normal file
6
common/src/abstractions/state.service.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
export abstract class StateService {
|
||||
get: <T>(key: string) => Promise<T>;
|
||||
save: (key: string, obj: any) => Promise<any>;
|
||||
remove: (key: string) => Promise<any>;
|
||||
purge: () => Promise<any>;
|
||||
}
|
||||
5
common/src/abstractions/storage.service.ts
Normal file
5
common/src/abstractions/storage.service.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export abstract class StorageService {
|
||||
get: <T>(key: string) => Promise<T>;
|
||||
save: (key: string, obj: any) => Promise<any>;
|
||||
remove: (key: string) => Promise<any>;
|
||||
}
|
||||
19
common/src/abstractions/sync.service.ts
Normal file
19
common/src/abstractions/sync.service.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import {
|
||||
SyncCipherNotification,
|
||||
SyncFolderNotification,
|
||||
SyncSendNotification,
|
||||
} from '../models/response/notificationResponse';
|
||||
|
||||
export abstract class SyncService {
|
||||
syncInProgress: boolean;
|
||||
|
||||
getLastSync: () => Promise<Date>;
|
||||
setLastSync: (date: Date) => Promise<any>;
|
||||
fullSync: (forceSync: boolean, allowThrowOnError?: boolean) => Promise<boolean>;
|
||||
syncUpsertFolder: (notification: SyncFolderNotification, isEdit: boolean) => Promise<boolean>;
|
||||
syncDeleteFolder: (notification: SyncFolderNotification) => Promise<boolean>;
|
||||
syncUpsertCipher: (notification: SyncCipherNotification, isEdit: boolean) => Promise<boolean>;
|
||||
syncDeleteCipher: (notification: SyncFolderNotification) => Promise<boolean>;
|
||||
syncUpsertSend: (notification: SyncSendNotification, isEdit: boolean) => Promise<boolean>;
|
||||
syncDeleteSend: (notification: SyncSendNotification) => Promise<boolean>;
|
||||
}
|
||||
6
common/src/abstractions/system.service.ts
Normal file
6
common/src/abstractions/system.service.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
export abstract class SystemService {
|
||||
startProcessReload: () => void;
|
||||
cancelProcessReload: () => void;
|
||||
clearClipboard: (clipboardValue: string, timeoutMs?: number) => void;
|
||||
clearPendingClipboard: () => Promise<any>;
|
||||
}
|
||||
25
common/src/abstractions/token.service.ts
Normal file
25
common/src/abstractions/token.service.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
export abstract class TokenService {
|
||||
token: string;
|
||||
decodedToken: any;
|
||||
refreshToken: string;
|
||||
setTokens: (accessToken: string, refreshToken: string) => Promise<any>;
|
||||
setToken: (token: string) => Promise<any>;
|
||||
getToken: () => Promise<string>;
|
||||
setRefreshToken: (refreshToken: string) => Promise<any>;
|
||||
getRefreshToken: () => Promise<string>;
|
||||
toggleTokens: () => Promise<any>;
|
||||
setTwoFactorToken: (token: string, email: string) => Promise<any>;
|
||||
getTwoFactorToken: (email: string) => Promise<string>;
|
||||
clearTwoFactorToken: (email: string) => Promise<any>;
|
||||
clearToken: () => Promise<any>;
|
||||
decodeToken: () => any;
|
||||
getTokenExpirationDate: () => Date;
|
||||
tokenSecondsRemaining: (offsetSeconds?: number) => number;
|
||||
tokenNeedsRefresh: (minutes?: number) => boolean;
|
||||
getUserId: () => string;
|
||||
getEmail: () => string;
|
||||
getEmailVerified: () => boolean;
|
||||
getName: () => string;
|
||||
getPremium: () => boolean;
|
||||
getIssuer: () => string;
|
||||
}
|
||||
5
common/src/abstractions/totp.service.ts
Normal file
5
common/src/abstractions/totp.service.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export abstract class TotpService {
|
||||
getCode: (key: string) => Promise<string>;
|
||||
getTimeInterval: (key: string) => number;
|
||||
isAutoCopyEnabled: () => Promise<boolean>;
|
||||
}
|
||||
23
common/src/abstractions/user.service.ts
Normal file
23
common/src/abstractions/user.service.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { OrganizationData } from '../models/data/organizationData';
|
||||
import { Organization } from '../models/domain/organization';
|
||||
|
||||
import { KdfType } from '../enums/kdfType';
|
||||
|
||||
export abstract class UserService {
|
||||
setInformation: (userId: string, email: string, kdf: KdfType, kdfIterations: number) => Promise<any>;
|
||||
setEmailVerified: (emailVerified: boolean) => Promise<any>;
|
||||
setSecurityStamp: (stamp: string) => Promise<any>;
|
||||
getUserId: () => Promise<string>;
|
||||
getEmail: () => Promise<string>;
|
||||
getSecurityStamp: () => Promise<string>;
|
||||
getKdf: () => Promise<KdfType>;
|
||||
getKdfIterations: () => Promise<number>;
|
||||
getEmailVerified: () => Promise<boolean>;
|
||||
clear: () => Promise<any>;
|
||||
isAuthenticated: () => Promise<boolean>;
|
||||
canAccessPremium: () => Promise<boolean>;
|
||||
getOrganization: (id: string) => Promise<Organization>;
|
||||
getAllOrganizations: () => Promise<Organization[]>;
|
||||
replaceOrganizations: (organizations: { [id: string]: OrganizationData; }) => Promise<any>;
|
||||
clearOrganizations: (userId: string) => Promise<any>;
|
||||
}
|
||||
14
common/src/abstractions/vaultTimeout.service.ts
Normal file
14
common/src/abstractions/vaultTimeout.service.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { EncString } from '../models/domain/encString';
|
||||
|
||||
export abstract class VaultTimeoutService {
|
||||
biometricLocked: boolean;
|
||||
pinProtectedKey: EncString;
|
||||
isLocked: () => Promise<boolean>;
|
||||
checkVaultTimeout: () => Promise<void>;
|
||||
lock: (allowSoftLock?: boolean) => Promise<void>;
|
||||
logOut: () => Promise<void>;
|
||||
setVaultTimeoutOptions: (vaultTimeout: number, vaultTimeoutAction: string) => Promise<void>;
|
||||
isPinLockSet: () => Promise<[boolean, boolean]>;
|
||||
isBiometricLockSet: () => Promise<boolean>;
|
||||
clear: () => Promise<any>;
|
||||
}
|
||||
4
common/src/enums/cipherRepromptType.ts
Normal file
4
common/src/enums/cipherRepromptType.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export enum CipherRepromptType {
|
||||
None = 0,
|
||||
Password = 1,
|
||||
}
|
||||
6
common/src/enums/cipherType.ts
Normal file
6
common/src/enums/cipherType.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
export enum CipherType {
|
||||
Login = 1,
|
||||
SecureNote = 2,
|
||||
Card = 3,
|
||||
Identity = 4,
|
||||
}
|
||||
23
common/src/enums/deviceType.ts
Normal file
23
common/src/enums/deviceType.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
export enum DeviceType {
|
||||
Android = 0,
|
||||
iOS = 1,
|
||||
ChromeExtension = 2,
|
||||
FirefoxExtension = 3,
|
||||
OperaExtension = 4,
|
||||
EdgeExtension = 5,
|
||||
WindowsDesktop = 6,
|
||||
MacOsDesktop = 7,
|
||||
LinuxDesktop = 8,
|
||||
ChromeBrowser = 9,
|
||||
FirefoxBrowser = 10,
|
||||
OperaBrowser = 11,
|
||||
EdgeBrowser = 12,
|
||||
IEBrowser = 13,
|
||||
UnknownBrowser = 14,
|
||||
AndroidAmazon = 15,
|
||||
UWP = 16,
|
||||
SafariBrowser = 17,
|
||||
VivaldiBrowser = 18,
|
||||
VivaldiExtension = 19,
|
||||
SafariExtension = 20,
|
||||
}
|
||||
7
common/src/enums/emergencyAccessStatusType.ts
Normal file
7
common/src/enums/emergencyAccessStatusType.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export enum EmergencyAccessStatusType {
|
||||
Invited = 0,
|
||||
Accepted = 1,
|
||||
Confirmed = 2,
|
||||
RecoveryInitiated = 3,
|
||||
RecoveryApproved = 4,
|
||||
}
|
||||
5
common/src/enums/emergencyAccessType.ts
Normal file
5
common/src/enums/emergencyAccessType.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export enum EmergencyAccessType
|
||||
{
|
||||
View = 0,
|
||||
Takeover = 1,
|
||||
}
|
||||
9
common/src/enums/encryptionType.ts
Normal file
9
common/src/enums/encryptionType.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
export enum EncryptionType {
|
||||
AesCbc256_B64 = 0,
|
||||
AesCbc128_HmacSha256_B64 = 1,
|
||||
AesCbc256_HmacSha256_B64 = 2,
|
||||
Rsa2048_OaepSha256_B64 = 3,
|
||||
Rsa2048_OaepSha1_B64 = 4,
|
||||
Rsa2048_OaepSha256_HmacSha256_B64 = 5,
|
||||
Rsa2048_OaepSha1_HmacSha256_B64 = 6,
|
||||
}
|
||||
53
common/src/enums/eventType.ts
Normal file
53
common/src/enums/eventType.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
export enum EventType {
|
||||
User_LoggedIn = 1000,
|
||||
User_ChangedPassword = 1001,
|
||||
User_Updated2fa = 1002,
|
||||
User_Disabled2fa = 1003,
|
||||
User_Recovered2fa = 1004,
|
||||
User_FailedLogIn = 1005,
|
||||
User_FailedLogIn2fa = 1006,
|
||||
User_ClientExportedVault = 1007,
|
||||
|
||||
Cipher_Created = 1100,
|
||||
Cipher_Updated = 1101,
|
||||
Cipher_Deleted = 1102,
|
||||
Cipher_AttachmentCreated = 1103,
|
||||
Cipher_AttachmentDeleted = 1104,
|
||||
Cipher_Shared = 1105,
|
||||
Cipher_UpdatedCollections = 1106,
|
||||
Cipher_ClientViewed = 1107,
|
||||
Cipher_ClientToggledPasswordVisible = 1108,
|
||||
Cipher_ClientToggledHiddenFieldVisible = 1109,
|
||||
Cipher_ClientToggledCardCodeVisible = 1110,
|
||||
Cipher_ClientCopiedPassword = 1111,
|
||||
Cipher_ClientCopiedHiddenField = 1112,
|
||||
Cipher_ClientCopiedCardCode = 1113,
|
||||
Cipher_ClientAutofilled = 1114,
|
||||
Cipher_SoftDeleted = 1115,
|
||||
Cipher_Restored = 1116,
|
||||
Cipher_ClientToggledCardNumberVisible = 1117,
|
||||
|
||||
Collection_Created = 1300,
|
||||
Collection_Updated = 1301,
|
||||
Collection_Deleted = 1302,
|
||||
|
||||
Group_Created = 1400,
|
||||
Group_Updated = 1401,
|
||||
Group_Deleted = 1402,
|
||||
|
||||
OrganizationUser_Invited = 1500,
|
||||
OrganizationUser_Confirmed = 1501,
|
||||
OrganizationUser_Updated = 1502,
|
||||
OrganizationUser_Removed = 1503,
|
||||
OrganizationUser_UpdatedGroups = 1504,
|
||||
OrganizationUser_UnlinkedSso = 1505,
|
||||
OrganizationUser_ResetPassword_Enroll = 1506,
|
||||
OrganizationUser_ResetPassword_Withdraw = 1507,
|
||||
OrganizationUser_AdminResetPassword = 1508,
|
||||
|
||||
Organization_Updated = 1600,
|
||||
Organization_PurgedVault = 1601,
|
||||
// Organization_ClientExportedVault = 1602,
|
||||
|
||||
Policy_Updated = 1700,
|
||||
}
|
||||
5
common/src/enums/fieldType.ts
Normal file
5
common/src/enums/fieldType.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export enum FieldType {
|
||||
Text = 0,
|
||||
Hidden = 1,
|
||||
Boolean = 2,
|
||||
}
|
||||
4
common/src/enums/fileUploadType.ts
Normal file
4
common/src/enums/fileUploadType.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export enum FileUploadType {
|
||||
Direct = 0,
|
||||
Azure = 1,
|
||||
}
|
||||
8
common/src/enums/index.ts
Normal file
8
common/src/enums/index.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
export { CipherType } from './cipherType';
|
||||
export { DeviceType } from './deviceType';
|
||||
export { EncryptionType } from './encryptionType';
|
||||
export { FieldType } from './fieldType';
|
||||
export { LogLevelType } from './logLevelType';
|
||||
export { SecureNoteType } from './secureNoteType';
|
||||
export { TwoFactorProviderType } from './twoFactorProviderType';
|
||||
export { UriMatchType } from './uriMatchType';
|
||||
3
common/src/enums/kdfType.ts
Normal file
3
common/src/enums/kdfType.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export enum KdfType {
|
||||
PBKDF2_SHA256 = 0,
|
||||
}
|
||||
6
common/src/enums/logLevelType.ts
Normal file
6
common/src/enums/logLevelType.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
export enum LogLevelType {
|
||||
Debug,
|
||||
Info,
|
||||
Warning,
|
||||
Error,
|
||||
}
|
||||
20
common/src/enums/notificationType.ts
Normal file
20
common/src/enums/notificationType.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
export enum NotificationType {
|
||||
SyncCipherUpdate = 0,
|
||||
SyncCipherCreate = 1,
|
||||
SyncLoginDelete = 2,
|
||||
SyncFolderDelete = 3,
|
||||
SyncCiphers = 4,
|
||||
|
||||
SyncVault = 5,
|
||||
SyncOrgKeys = 6,
|
||||
SyncFolderCreate = 7,
|
||||
SyncFolderUpdate = 8,
|
||||
SyncCipherDelete = 9,
|
||||
SyncSettings = 10,
|
||||
|
||||
LogOut = 11,
|
||||
|
||||
SyncSendCreate = 12,
|
||||
SyncSendUpdate = 13,
|
||||
SyncSendDelete = 14,
|
||||
}
|
||||
5
common/src/enums/organizationUserStatusType.ts
Normal file
5
common/src/enums/organizationUserStatusType.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export enum OrganizationUserStatusType {
|
||||
Invited = 0,
|
||||
Accepted = 1,
|
||||
Confirmed = 2,
|
||||
}
|
||||
7
common/src/enums/organizationUserType.ts
Normal file
7
common/src/enums/organizationUserType.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export enum OrganizationUserType {
|
||||
Owner = 0,
|
||||
Admin = 1,
|
||||
User = 2,
|
||||
Manager = 3,
|
||||
Custom = 4,
|
||||
}
|
||||
11
common/src/enums/paymentMethodType.ts
Normal file
11
common/src/enums/paymentMethodType.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
export enum PaymentMethodType {
|
||||
Card = 0,
|
||||
BankAccount = 1,
|
||||
PayPal = 2,
|
||||
BitPay = 3,
|
||||
Credit = 4,
|
||||
WireTransfer = 5,
|
||||
AppleInApp = 6,
|
||||
GoogleInApp = 7,
|
||||
Check = 8,
|
||||
}
|
||||
13
common/src/enums/permissions.ts
Normal file
13
common/src/enums/permissions.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
export enum Permissions {
|
||||
AccessBusinessPortal,
|
||||
AccessEventLogs,
|
||||
AccessImportExport,
|
||||
AccessReports,
|
||||
ManageAllCollections,
|
||||
ManageAssignedCollections,
|
||||
ManageGroups,
|
||||
ManageOrganization,
|
||||
ManagePolicies,
|
||||
ManageUsers,
|
||||
ManageUsersPassword,
|
||||
}
|
||||
14
common/src/enums/planType.ts
Normal file
14
common/src/enums/planType.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
export enum PlanType {
|
||||
Free = 0,
|
||||
FamiliesAnnually2019 = 1,
|
||||
TeamsMonthly2019 = 2,
|
||||
TeamsAnnually2019 = 3,
|
||||
EnterpriseMonthly2019 = 4,
|
||||
EnterpriseAnnually2019 = 5,
|
||||
Custom = 6,
|
||||
FamiliesAnnually = 7,
|
||||
TeamsMonthly = 8,
|
||||
TeamsAnnually = 9,
|
||||
EnterpriseMonthly = 10,
|
||||
EnterpriseAnnually = 11,
|
||||
}
|
||||
11
common/src/enums/policyType.ts
Normal file
11
common/src/enums/policyType.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
export enum PolicyType {
|
||||
TwoFactorAuthentication = 0, // Requires users to have 2fa enabled
|
||||
MasterPassword = 1, // Sets minimum requirements for master password complexity
|
||||
PasswordGenerator = 2, // Sets minimum requirements/default type for generated passwords/passphrases
|
||||
SingleOrg = 3, // Allows users to only be apart of one organization
|
||||
RequireSso = 4, // Requires users to authenticate with SSO
|
||||
PersonalOwnership = 5, // Disables personal vault ownership for adding/cloning items
|
||||
DisableSend = 6, // Disables the ability to create and edit Bitwarden Sends
|
||||
SendOptions = 7, // Sets restrictions or defaults for Bitwarden Sends
|
||||
ResetPassword = 8, // Allows orgs to use reset password : also can enable auto-enrollment during invite flow
|
||||
}
|
||||
6
common/src/enums/productType.ts
Normal file
6
common/src/enums/productType.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
export enum ProductType {
|
||||
Free = 0,
|
||||
Families = 1,
|
||||
Teams = 2,
|
||||
Enterprise = 3,
|
||||
}
|
||||
3
common/src/enums/secureNoteType.ts
Normal file
3
common/src/enums/secureNoteType.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export enum SecureNoteType {
|
||||
Generic = 0,
|
||||
}
|
||||
4
common/src/enums/sendType.ts
Normal file
4
common/src/enums/sendType.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export enum SendType {
|
||||
Text = 0,
|
||||
File = 1,
|
||||
}
|
||||
7
common/src/enums/transactionType.ts
Normal file
7
common/src/enums/transactionType.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export enum TransactionType {
|
||||
Charge = 0,
|
||||
Credit = 1,
|
||||
PromotionalCredit = 2,
|
||||
ReferralCredit = 3,
|
||||
Refund = 4,
|
||||
}
|
||||
10
common/src/enums/twoFactorProviderType.ts
Normal file
10
common/src/enums/twoFactorProviderType.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
export enum TwoFactorProviderType {
|
||||
Authenticator = 0,
|
||||
Email = 1,
|
||||
Duo = 2,
|
||||
Yubikey = 3,
|
||||
U2f = 4,
|
||||
Remember = 5,
|
||||
OrganizationDuo = 6,
|
||||
WebAuthn = 7,
|
||||
}
|
||||
8
common/src/enums/uriMatchType.ts
Normal file
8
common/src/enums/uriMatchType.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
export enum UriMatchType {
|
||||
Domain = 0,
|
||||
Host = 1,
|
||||
StartsWith = 2,
|
||||
Exact = 3,
|
||||
RegularExpression = 4,
|
||||
Never = 5,
|
||||
}
|
||||
2
common/src/globals.d.ts
vendored
Normal file
2
common/src/globals.d.ts
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
declare function escape(s: string): string;
|
||||
declare function unescape(s: string): string;
|
||||
55
common/src/importers/ascendoCsvImporter.ts
Normal file
55
common/src/importers/ascendoCsvImporter.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
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();
|
||||
const results = this.parseCsv(data, false);
|
||||
if (results == null) {
|
||||
result.success = false;
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
|
||||
results.forEach(value => {
|
||||
if (value.length < 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
const cipher = this.initLoginCipher();
|
||||
cipher.notes = this.getValueOrDefault(value[value.length - 1]);
|
||||
cipher.name = this.getValueOrDefault(value[0], '--');
|
||||
|
||||
if (value.length > 2 && (value.length % 2) === 0) {
|
||||
for (let i = 0; i < value.length - 2; i += 2) {
|
||||
const val: string = value[i + 2];
|
||||
const field: string = value[i + 1];
|
||||
if (this.isNullOrWhitespace(val) || this.isNullOrWhitespace(field)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const fieldLower = field.toLowerCase();
|
||||
if (cipher.login.password == null && this.passwordFieldNames.indexOf(fieldLower) > -1) {
|
||||
cipher.login.password = this.getValueOrDefault(val);
|
||||
} else if (cipher.login.username == null &&
|
||||
this.usernameFieldNames.indexOf(fieldLower) > -1) {
|
||||
cipher.login.username = this.getValueOrDefault(val);
|
||||
} else if ((cipher.login.uris == null || cipher.login.uris.length === 0) &&
|
||||
this.uriFieldNames.indexOf(fieldLower) > -1) {
|
||||
cipher.login.uris = this.makeUriArray(val);
|
||||
} else {
|
||||
this.processKvp(cipher, field, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.convertToNoteIfNeeded(cipher);
|
||||
this.cleanupCipher(cipher);
|
||||
result.ciphers.push(cipher);
|
||||
});
|
||||
|
||||
result.success = true;
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
}
|
||||
28
common/src/importers/avastCsvImporter.ts
Normal file
28
common/src/importers/avastCsvImporter.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
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();
|
||||
const results = this.parseCsv(data, true);
|
||||
if (results == null) {
|
||||
result.success = false;
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
|
||||
results.forEach(value => {
|
||||
const cipher = this.initLoginCipher();
|
||||
cipher.name = this.getValueOrDefault(value.name);
|
||||
cipher.login.uris = this.makeUriArray(value.web);
|
||||
cipher.login.password = this.getValueOrDefault(value.password);
|
||||
cipher.login.username = this.getValueOrDefault(value.login);
|
||||
this.cleanupCipher(cipher);
|
||||
result.ciphers.push(cipher);
|
||||
});
|
||||
|
||||
result.success = true;
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
}
|
||||
69
common/src/importers/avastJsonImporter.ts
Normal file
69
common/src/importers/avastJsonImporter.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
import { BaseImporter } from './baseImporter';
|
||||
import { Importer } from './importer';
|
||||
|
||||
import { ImportResult } from '../models/domain/importResult';
|
||||
|
||||
import { CipherType } from '../enums/cipherType';
|
||||
import { SecureNoteType } from '../enums/secureNoteType';
|
||||
|
||||
export class AvastJsonImporter extends BaseImporter implements Importer {
|
||||
parse(data: string): Promise<ImportResult> {
|
||||
const result = new ImportResult();
|
||||
const results = JSON.parse(data);
|
||||
if (results == null) {
|
||||
result.success = false;
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
|
||||
if (results.logins != null) {
|
||||
results.logins.forEach((value: any) => {
|
||||
const cipher = this.initLoginCipher();
|
||||
cipher.name = this.getValueOrDefault(value.custName);
|
||||
cipher.notes = this.getValueOrDefault(value.note);
|
||||
cipher.login.uris = this.makeUriArray(value.url);
|
||||
cipher.login.password = this.getValueOrDefault(value.pwd);
|
||||
cipher.login.username = this.getValueOrDefault(value.loginName);
|
||||
this.cleanupCipher(cipher);
|
||||
result.ciphers.push(cipher);
|
||||
});
|
||||
}
|
||||
|
||||
if (results.notes != null) {
|
||||
results.notes.forEach((value: any) => {
|
||||
const cipher = this.initLoginCipher();
|
||||
cipher.type = CipherType.SecureNote;
|
||||
cipher.secureNote.type = SecureNoteType.Generic;
|
||||
cipher.name = this.getValueOrDefault(value.label);
|
||||
cipher.notes = this.getValueOrDefault(value.text);
|
||||
this.cleanupCipher(cipher);
|
||||
result.ciphers.push(cipher);
|
||||
});
|
||||
}
|
||||
|
||||
if (results.cards != null) {
|
||||
results.cards.forEach((value: any) => {
|
||||
const cipher = this.initLoginCipher();
|
||||
cipher.type = CipherType.Card;
|
||||
cipher.name = this.getValueOrDefault(value.custName);
|
||||
cipher.notes = this.getValueOrDefault(value.note);
|
||||
cipher.card.cardholderName = this.getValueOrDefault(value.holderName);
|
||||
cipher.card.number = this.getValueOrDefault(value.cardNumber);
|
||||
cipher.card.code = this.getValueOrDefault(value.cvv);
|
||||
cipher.card.brand = this.getCardBrand(cipher.card.number);
|
||||
if (value.expirationDate != null) {
|
||||
if (value.expirationDate.month != null) {
|
||||
cipher.card.expMonth = value.expirationDate.month + '';
|
||||
}
|
||||
if (value.expirationDate.year != null) {
|
||||
cipher.card.expYear = value.expirationDate.year + '';
|
||||
}
|
||||
}
|
||||
this.cleanupCipher(cipher);
|
||||
result.ciphers.push(cipher);
|
||||
});
|
||||
}
|
||||
|
||||
result.success = true;
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
}
|
||||
36
common/src/importers/aviraCsvImporter.ts
Normal file
36
common/src/importers/aviraCsvImporter.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
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();
|
||||
const results = this.parseCsv(data, true);
|
||||
if (results == null) {
|
||||
result.success = false;
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
|
||||
results.forEach(value => {
|
||||
const cipher = this.initLoginCipher();
|
||||
cipher.name = this.getValueOrDefault(value.name,
|
||||
this.getValueOrDefault(this.nameFromUrl(value.website), '--'));
|
||||
cipher.login.uris = this.makeUriArray(value.website);
|
||||
cipher.login.password = this.getValueOrDefault(value.password);
|
||||
|
||||
if (this.isNullOrWhitespace(value.username) && !this.isNullOrWhitespace(value.secondary_username)) {
|
||||
cipher.login.username = value.secondary_username;
|
||||
} else {
|
||||
cipher.login.username = this.getValueOrDefault(value.username);
|
||||
cipher.notes = this.getValueOrDefault(value.secondary_username);
|
||||
}
|
||||
|
||||
this.cleanupCipher(cipher);
|
||||
result.ciphers.push(cipher);
|
||||
});
|
||||
|
||||
result.success = true;
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
}
|
||||
377
common/src/importers/baseImporter.ts
Normal file
377
common/src/importers/baseImporter.ts
Normal file
@@ -0,0 +1,377 @@
|
||||
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 { ConsoleLogService } from '../services/consoleLog.service';
|
||||
|
||||
export abstract class BaseImporter {
|
||||
organizationId: string = null;
|
||||
|
||||
protected logService: LogService = new ConsoleLogService(false);
|
||||
|
||||
protected newLineRegex = /(?:\r\n|\r|\n)/;
|
||||
|
||||
protected passwordFieldNames = [
|
||||
'password', 'pass word', 'passphrase', 'pass phrase',
|
||||
'pass', 'code', 'code word', 'codeword',
|
||||
'secret', 'secret word', 'personpwd',
|
||||
'key', 'keyword', 'key word', 'keyphrase', 'key phrase',
|
||||
'form_pw', 'wppassword', 'pin', 'pwd', 'pw', 'pword', 'passwd',
|
||||
'p', 'serial', 'serial#', 'license key', 'reg #',
|
||||
|
||||
// Non-English names
|
||||
'passwort',
|
||||
];
|
||||
|
||||
protected usernameFieldNames = [
|
||||
'user', 'name', 'user name', 'username', 'login name',
|
||||
'email', 'e-mail', 'id', 'userid', 'user id',
|
||||
'login', 'form_loginname', 'wpname', 'mail',
|
||||
'loginid', 'login id', 'log', 'personlogin',
|
||||
'first name', 'last name', 'card#', 'account #',
|
||||
'member', 'member #',
|
||||
|
||||
// Non-English names
|
||||
'nom', 'benutzername',
|
||||
];
|
||||
|
||||
protected notesFieldNames = [
|
||||
'note', 'notes', 'comment', 'comments', 'memo',
|
||||
'description', 'free form', 'freeform',
|
||||
'free text', 'freetext', 'free',
|
||||
|
||||
// Non-English names
|
||||
'kommentar',
|
||||
];
|
||||
|
||||
protected uriFieldNames: string[] = [
|
||||
'url', 'hyper link', 'hyperlink', 'link',
|
||||
'host', 'hostname', 'host name', 'server', 'address',
|
||||
'hyper ref', 'href', 'web', 'website', 'web site', 'site',
|
||||
'web-site', 'uri',
|
||||
|
||||
// Non-English names
|
||||
'ort', 'adresse',
|
||||
];
|
||||
|
||||
protected parseCsvOptions = {
|
||||
encoding: 'UTF-8',
|
||||
skipEmptyLines: false,
|
||||
};
|
||||
|
||||
protected get organization() {
|
||||
return this.organizationId != null;
|
||||
}
|
||||
|
||||
protected parseXml(data: string): Document {
|
||||
const parser = new DOMParser();
|
||||
const doc = parser.parseFromString(data, 'application/xml');
|
||||
return doc != null && doc.querySelector('parsererror') == null ? doc : null;
|
||||
}
|
||||
|
||||
protected parseCsv(data: string, header: boolean, options: any = {}): any[] {
|
||||
const parseOptions = Object.assign({ header: header }, this.parseCsvOptions, options);
|
||||
data = this.splitNewLine(data).join('\n').trim();
|
||||
const result = papa.parse(data, parseOptions);
|
||||
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);
|
||||
}
|
||||
});
|
||||
}
|
||||
return result.data && result.data.length > 0 ? result.data : null;
|
||||
}
|
||||
|
||||
protected parseSingleRowCsv(rowData: string) {
|
||||
if (this.isNullOrWhitespace(rowData)) {
|
||||
return null;
|
||||
}
|
||||
const parsedRow = this.parseCsv(rowData, false);
|
||||
if (parsedRow != null && parsedRow.length > 0 && parsedRow[0].length > 0) {
|
||||
return parsedRow[0];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected makeUriArray(uri: string | string[]): LoginUriView[] {
|
||||
if (uri == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (typeof uri === 'string') {
|
||||
const loginUri = new LoginUriView();
|
||||
loginUri.uri = this.fixUri(uri);
|
||||
if (this.isNullOrWhitespace(loginUri.uri)) {
|
||||
return null;
|
||||
}
|
||||
loginUri.match = null;
|
||||
return [loginUri];
|
||||
}
|
||||
|
||||
if (uri.length > 0) {
|
||||
const returnArr: LoginUriView[] = [];
|
||||
uri.forEach(u => {
|
||||
const loginUri = new LoginUriView();
|
||||
loginUri.uri = this.fixUri(u);
|
||||
if (this.isNullOrWhitespace(loginUri.uri)) {
|
||||
return;
|
||||
}
|
||||
loginUri.match = null;
|
||||
returnArr.push(loginUri);
|
||||
});
|
||||
return returnArr.length === 0 ? null : returnArr;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected fixUri(uri: string) {
|
||||
if (uri == null) {
|
||||
return null;
|
||||
}
|
||||
uri = uri.trim();
|
||||
if (uri.indexOf('://') === -1 && uri.indexOf('.') >= 0) {
|
||||
uri = 'http://' + uri;
|
||||
}
|
||||
if (uri.length > 1000) {
|
||||
return uri.substring(0, 1000);
|
||||
}
|
||||
return uri;
|
||||
}
|
||||
|
||||
protected nameFromUrl(url: string) {
|
||||
const hostname = Utils.getHostname(url);
|
||||
if (this.isNullOrWhitespace(hostname)) {
|
||||
return null;
|
||||
}
|
||||
return hostname.startsWith('www.') ? hostname.replace('www.', '') : hostname;
|
||||
}
|
||||
|
||||
protected isNullOrWhitespace(str: string): boolean {
|
||||
return Utils.isNullOrWhitespace(str);
|
||||
}
|
||||
|
||||
protected getValueOrDefault(str: string, defaultValue: string = null): string {
|
||||
if (this.isNullOrWhitespace(str)) {
|
||||
return defaultValue;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
protected splitNewLine(str: string): string[] {
|
||||
return str.split(this.newLineRegex);
|
||||
}
|
||||
|
||||
// ref https://stackoverflow.com/a/5911300
|
||||
protected getCardBrand(cardNum: string) {
|
||||
if (this.isNullOrWhitespace(cardNum)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Visa
|
||||
let re = new RegExp('^4');
|
||||
if (cardNum.match(re) != null) {
|
||||
return 'Visa';
|
||||
}
|
||||
|
||||
// Mastercard
|
||||
// Updated for Mastercard 2017 BINs expansion
|
||||
if (/^(5[1-5][0-9]{14}|2(22[1-9][0-9]{12}|2[3-9][0-9]{13}|[3-6][0-9]{14}|7[0-1][0-9]{13}|720[0-9]{12}))$/
|
||||
.test(cardNum)) {
|
||||
return 'Mastercard';
|
||||
}
|
||||
|
||||
// AMEX
|
||||
re = new RegExp('^3[47]');
|
||||
if (cardNum.match(re) != null) {
|
||||
return 'Amex';
|
||||
}
|
||||
|
||||
// Discover
|
||||
re = new RegExp('^(6011|622(12[6-9]|1[3-9][0-9]|[2-8][0-9]{2}|9[0-1][0-9]|92[0-5]|64[4-9])|65)');
|
||||
if (cardNum.match(re) != null) {
|
||||
return 'Discover';
|
||||
}
|
||||
|
||||
// Diners
|
||||
re = new RegExp('^36');
|
||||
if (cardNum.match(re) != null) {
|
||||
return 'Diners Club';
|
||||
}
|
||||
|
||||
// Diners - Carte Blanche
|
||||
re = new RegExp('^30[0-5]');
|
||||
if (cardNum.match(re) != null) {
|
||||
return 'Diners Club';
|
||||
}
|
||||
|
||||
// JCB
|
||||
re = new RegExp('^35(2[89]|[3-8][0-9])');
|
||||
if (cardNum.match(re) != null) {
|
||||
return 'JCB';
|
||||
}
|
||||
|
||||
// Visa Electron
|
||||
re = new RegExp('^(4026|417500|4508|4844|491(3|7))');
|
||||
if (cardNum.match(re) != null) {
|
||||
return 'Visa';
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected setCardExpiration(cipher: CipherView, expiration: string): boolean {
|
||||
if (!this.isNullOrWhitespace(expiration)) {
|
||||
expiration = expiration.replace(/\s/g, '');
|
||||
const parts = expiration.split('/');
|
||||
if (parts.length === 2) {
|
||||
let month: string = null;
|
||||
let year: string = null;
|
||||
if (parts[0].length === 1 || parts[0].length === 2) {
|
||||
month = parts[0];
|
||||
if (month.length === 2 && month[0] === '0') {
|
||||
month = month.substr(1, 1);
|
||||
}
|
||||
}
|
||||
if (parts[1].length === 2 || parts[1].length === 4) {
|
||||
year = month.length === 2 ? '20' + parts[1] : parts[1];
|
||||
}
|
||||
if (month != null && year != null) {
|
||||
cipher.card.expMonth = month;
|
||||
cipher.card.expYear = year;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected moveFoldersToCollections(result: ImportResult) {
|
||||
result.folderRelationships.forEach(r => result.collectionRelationships.push(r));
|
||||
result.collections = result.folders.map(f => {
|
||||
const collection = new CollectionView();
|
||||
collection.name = f.name;
|
||||
return collection;
|
||||
});
|
||||
result.folderRelationships = [];
|
||||
result.folders = [];
|
||||
}
|
||||
|
||||
protected querySelectorDirectChild(parentEl: Element, query: string) {
|
||||
const els = this.querySelectorAllDirectChild(parentEl, query);
|
||||
return els.length === 0 ? null : els[0];
|
||||
}
|
||||
|
||||
protected querySelectorAllDirectChild(parentEl: Element, query: string) {
|
||||
return Array.from(parentEl.querySelectorAll(query)).filter(el => el.parentNode === parentEl);
|
||||
}
|
||||
|
||||
protected initLoginCipher() {
|
||||
const cipher = new CipherView();
|
||||
cipher.favorite = false;
|
||||
cipher.notes = '';
|
||||
cipher.fields = [];
|
||||
cipher.login = new LoginView();
|
||||
cipher.type = CipherType.Login;
|
||||
return cipher;
|
||||
}
|
||||
|
||||
protected cleanupCipher(cipher: CipherView) {
|
||||
if (cipher == null) {
|
||||
return;
|
||||
}
|
||||
if (cipher.type !== CipherType.Login) {
|
||||
cipher.login = null;
|
||||
}
|
||||
if (this.isNullOrWhitespace(cipher.name)) {
|
||||
cipher.name = '--';
|
||||
}
|
||||
if (this.isNullOrWhitespace(cipher.notes)) {
|
||||
cipher.notes = null;
|
||||
} else {
|
||||
cipher.notes = cipher.notes.trim();
|
||||
}
|
||||
if (cipher.fields != null && cipher.fields.length === 0) {
|
||||
cipher.fields = null;
|
||||
}
|
||||
}
|
||||
|
||||
protected processKvp(cipher: CipherView, key: string, value: string, type: FieldType = FieldType.Text) {
|
||||
if (this.isNullOrWhitespace(value)) {
|
||||
return;
|
||||
}
|
||||
if (this.isNullOrWhitespace(key)) {
|
||||
key = '';
|
||||
}
|
||||
if (value.length > 200 || value.trim().search(this.newLineRegex) > -1) {
|
||||
if (cipher.notes == null) {
|
||||
cipher.notes = '';
|
||||
}
|
||||
cipher.notes += (key + ': ' + this.splitNewLine(value).join('\n') + '\n');
|
||||
} else {
|
||||
if (cipher.fields == null) {
|
||||
cipher.fields = [];
|
||||
}
|
||||
const field = new FieldView();
|
||||
field.type = type;
|
||||
field.name = key;
|
||||
field.value = value;
|
||||
cipher.fields.push(field);
|
||||
}
|
||||
}
|
||||
|
||||
protected processFolder(result: ImportResult, folderName: string) {
|
||||
let folderIndex = result.folders.length;
|
||||
const hasFolder = !this.isNullOrWhitespace(folderName);
|
||||
let addFolder = hasFolder;
|
||||
|
||||
if (hasFolder) {
|
||||
for (let i = 0; i < result.folders.length; i++) {
|
||||
if (result.folders[i].name === folderName) {
|
||||
addFolder = false;
|
||||
folderIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (addFolder) {
|
||||
const f = new FolderView();
|
||||
f.name = folderName;
|
||||
result.folders.push(f);
|
||||
}
|
||||
if (hasFolder) {
|
||||
result.folderRelationships.push([result.ciphers.length, folderIndex]);
|
||||
}
|
||||
}
|
||||
|
||||
protected convertToNoteIfNeeded(cipher: CipherView) {
|
||||
if (cipher.type === CipherType.Login && this.isNullOrWhitespace(cipher.login.username) &&
|
||||
this.isNullOrWhitespace(cipher.login.password) &&
|
||||
(cipher.login.uris == null || cipher.login.uris.length === 0)) {
|
||||
cipher.type = CipherType.SecureNote;
|
||||
cipher.secureNote = new SecureNoteView();
|
||||
cipher.secureNote.type = SecureNoteType.Generic;
|
||||
}
|
||||
}
|
||||
}
|
||||
118
common/src/importers/bitwardenCsvImporter.ts
Normal file
118
common/src/importers/bitwardenCsvImporter.ts
Normal file
@@ -0,0 +1,118 @@
|
||||
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';
|
||||
|
||||
export class BitwardenCsvImporter 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);
|
||||
}
|
||||
|
||||
results.forEach(value => {
|
||||
if (this.organization && !this.isNullOrWhitespace(value.collections)) {
|
||||
const collections = (value.collections as string).split(',');
|
||||
collections.forEach(col => {
|
||||
let addCollection = true;
|
||||
let collectionIndex = result.collections.length;
|
||||
|
||||
for (let i = 0; i < result.collections.length; i++) {
|
||||
if (result.collections[i].name === col) {
|
||||
addCollection = false;
|
||||
collectionIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (addCollection) {
|
||||
const collection = new CollectionView();
|
||||
collection.name = col;
|
||||
result.collections.push(collection);
|
||||
}
|
||||
|
||||
result.collectionRelationships.push([result.ciphers.length, collectionIndex]);
|
||||
});
|
||||
} else if (!this.organization) {
|
||||
this.processFolder(result, value.folder);
|
||||
}
|
||||
|
||||
const cipher = new CipherView();
|
||||
cipher.favorite = !this.organization && this.getValueOrDefault(value.favorite, '0') !== '0' ? true : false;
|
||||
cipher.type = CipherType.Login;
|
||||
cipher.notes = this.getValueOrDefault(value.notes);
|
||||
cipher.name = this.getValueOrDefault(value.name, '--');
|
||||
try {
|
||||
cipher.reprompt = parseInt(this.getValueOrDefault(value.reprompt, CipherRepromptType.None.toString()), 10);
|
||||
} catch (e) {
|
||||
// tslint:disable-next-line
|
||||
console.error('Unable to parse reprompt value', e);
|
||||
cipher.reprompt = CipherRepromptType.None;
|
||||
}
|
||||
|
||||
if (!this.isNullOrWhitespace(value.fields)) {
|
||||
const fields = this.splitNewLine(value.fields);
|
||||
for (let i = 0; i < fields.length; i++) {
|
||||
if (this.isNullOrWhitespace(fields[i])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const delimPosition = fields[i].lastIndexOf(': ');
|
||||
if (delimPosition === -1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cipher.fields == null) {
|
||||
cipher.fields = [];
|
||||
}
|
||||
|
||||
const field = new FieldView();
|
||||
field.name = fields[i].substr(0, delimPosition);
|
||||
field.value = null;
|
||||
field.type = FieldType.Text;
|
||||
if (fields[i].length > (delimPosition + 2)) {
|
||||
field.value = fields[i].substr(delimPosition + 2);
|
||||
}
|
||||
cipher.fields.push(field);
|
||||
}
|
||||
}
|
||||
|
||||
const valueType = value.type != null ? value.type.toLowerCase() : null;
|
||||
switch (valueType) {
|
||||
case 'note':
|
||||
cipher.type = CipherType.SecureNote;
|
||||
cipher.secureNote = new SecureNoteView();
|
||||
cipher.secureNote.type = SecureNoteType.Generic;
|
||||
break;
|
||||
default:
|
||||
cipher.type = CipherType.Login;
|
||||
cipher.login = new LoginView();
|
||||
cipher.login.totp = this.getValueOrDefault(value.login_totp || value.totp);
|
||||
cipher.login.username = this.getValueOrDefault(value.login_username || value.username);
|
||||
cipher.login.password = this.getValueOrDefault(value.login_password || value.password);
|
||||
const uris = this.parseSingleRowCsv(value.login_uri || value.uri);
|
||||
cipher.login.uris = this.makeUriArray(uris);
|
||||
break;
|
||||
}
|
||||
|
||||
result.ciphers.push(cipher);
|
||||
});
|
||||
|
||||
result.success = true;
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
}
|
||||
159
common/src/importers/bitwardenJsonImporter.ts
Normal file
159
common/src/importers/bitwardenJsonImporter.ts
Normal file
@@ -0,0 +1,159 @@
|
||||
import { BaseImporter } from './baseImporter';
|
||||
import { Importer } from './importer';
|
||||
|
||||
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';
|
||||
|
||||
export class BitwardenJsonImporter extends BaseImporter implements Importer {
|
||||
private results: any;
|
||||
private result: ImportResult;
|
||||
|
||||
constructor(private cryptoService: CryptoService, private 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) {
|
||||
this.result.success = false;
|
||||
return this.result;
|
||||
}
|
||||
|
||||
if (this.results.encrypted) {
|
||||
await this.parseEncrypted();
|
||||
} else {
|
||||
this.parseDecrypted();
|
||||
}
|
||||
|
||||
return this.result;
|
||||
}
|
||||
|
||||
private async parseEncrypted() {
|
||||
if (this.results.encKeyValidation_DO_NOT_EDIT != null) {
|
||||
const orgKey = await this.cryptoService.getOrgKey(this.organizationId);
|
||||
const encKeyValidation = new EncString(this.results.encKeyValidation_DO_NOT_EDIT);
|
||||
const encKeyValidationDecrypt = await this.cryptoService.decryptToUtf8(encKeyValidation, orgKey);
|
||||
if (encKeyValidationDecrypt === null) {
|
||||
this.result.success = false;
|
||||
this.result.errorMessage = this.i18nService.t('importEncKeyError');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const groupingsMap = new Map<string, number>();
|
||||
|
||||
if (this.organization && this.results.collections != null) {
|
||||
for (const c of this.results.collections as CollectionWithId[]) {
|
||||
const collection = CollectionWithId.toDomain(c);
|
||||
if (collection != null) {
|
||||
collection.id = null;
|
||||
collection.organizationId = this.organizationId;
|
||||
const view = await collection.decrypt();
|
||||
groupingsMap.set(c.id, this.result.collections.length);
|
||||
this.result.collections.push(view);
|
||||
}
|
||||
}
|
||||
} else if (!this.organization && this.results.folders != null) {
|
||||
for (const f of this.results.folders as FolderWithId[]) {
|
||||
const folder = FolderWithId.toDomain(f);
|
||||
if (folder != null) {
|
||||
folder.id = null;
|
||||
const view = await folder.decrypt();
|
||||
groupingsMap.set(f.id, this.result.folders.length);
|
||||
this.result.folders.push(view);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const c of this.results.items as CipherWithIds[]) {
|
||||
const cipher = CipherWithIds.toDomain(c);
|
||||
// reset ids incase they were set for some reason
|
||||
cipher.id = null;
|
||||
cipher.folderId = null;
|
||||
cipher.organizationId = this.organizationId;
|
||||
cipher.collectionIds = null;
|
||||
|
||||
// make sure password history is limited
|
||||
if (cipher.passwordHistory != null && cipher.passwordHistory.length > 5) {
|
||||
cipher.passwordHistory = cipher.passwordHistory.slice(0, 5);
|
||||
}
|
||||
|
||||
if (!this.organization && c.folderId != null && groupingsMap.has(c.folderId)) {
|
||||
this.result.folderRelationships.push([this.result.ciphers.length, groupingsMap.get(c.folderId)]);
|
||||
} else if (this.organization && c.collectionIds != null) {
|
||||
c.collectionIds.forEach(cId => {
|
||||
if (groupingsMap.has(cId)) {
|
||||
this.result.collectionRelationships.push([this.result.ciphers.length, groupingsMap.get(cId)]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const view = await cipher.decrypt();
|
||||
this.cleanupCipher(view);
|
||||
this.result.ciphers.push(view);
|
||||
}
|
||||
|
||||
this.result.success = true;
|
||||
}
|
||||
|
||||
private parseDecrypted() {
|
||||
const groupingsMap = new Map<string, number>();
|
||||
if (this.organization && this.results.collections != null) {
|
||||
this.results.collections.forEach((c: CollectionWithId) => {
|
||||
const collection = CollectionWithId.toView(c);
|
||||
if (collection != null) {
|
||||
collection.id = null;
|
||||
collection.organizationId = null;
|
||||
groupingsMap.set(c.id, this.result.collections.length);
|
||||
this.result.collections.push(collection);
|
||||
}
|
||||
});
|
||||
} else if (!this.organization && this.results.folders != null) {
|
||||
this.results.folders.forEach((f: FolderWithId) => {
|
||||
const folder = FolderWithId.toView(f);
|
||||
if (folder != null) {
|
||||
folder.id = null;
|
||||
groupingsMap.set(f.id, this.result.folders.length);
|
||||
this.result.folders.push(folder);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this.results.items.forEach((c: CipherWithIds) => {
|
||||
const cipher = CipherWithIds.toView(c);
|
||||
// reset ids incase they were set for some reason
|
||||
cipher.id = null;
|
||||
cipher.folderId = null;
|
||||
cipher.organizationId = null;
|
||||
cipher.collectionIds = null;
|
||||
|
||||
// make sure password history is limited
|
||||
if (cipher.passwordHistory != null && cipher.passwordHistory.length > 5) {
|
||||
cipher.passwordHistory = cipher.passwordHistory.slice(0, 5);
|
||||
}
|
||||
|
||||
if (!this.organization && c.folderId != null && groupingsMap.has(c.folderId)) {
|
||||
this.result.folderRelationships.push([this.result.ciphers.length, groupingsMap.get(c.folderId)]);
|
||||
} else if (this.organization && c.collectionIds != null) {
|
||||
c.collectionIds.forEach(cId => {
|
||||
if (groupingsMap.has(cId)) {
|
||||
this.result.collectionRelationships.push([this.result.ciphers.length, groupingsMap.get(cId)]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this.cleanupCipher(cipher);
|
||||
this.result.ciphers.push(cipher);
|
||||
});
|
||||
|
||||
this.result.success = true;
|
||||
}
|
||||
}
|
||||
36
common/src/importers/blackBerryCsvImporter.ts
Normal file
36
common/src/importers/blackBerryCsvImporter.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
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();
|
||||
const results = this.parseCsv(data, true);
|
||||
if (results == null) {
|
||||
result.success = false;
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
|
||||
results.forEach(value => {
|
||||
if (value.grouping === 'list') {
|
||||
return;
|
||||
}
|
||||
const cipher = this.initLoginCipher();
|
||||
cipher.favorite = value.fav === '1';
|
||||
cipher.name = this.getValueOrDefault(value.name);
|
||||
cipher.notes = this.getValueOrDefault(value.extra);
|
||||
if (value.grouping !== 'note') {
|
||||
cipher.login.uris = this.makeUriArray(value.url);
|
||||
cipher.login.password = this.getValueOrDefault(value.password);
|
||||
cipher.login.username = this.getValueOrDefault(value.username);
|
||||
}
|
||||
this.convertToNoteIfNeeded(cipher);
|
||||
this.cleanupCipher(cipher);
|
||||
result.ciphers.push(cipher);
|
||||
});
|
||||
|
||||
result.success = true;
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
}
|
||||
39
common/src/importers/blurCsvImporter.ts
Normal file
39
common/src/importers/blurCsvImporter.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
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();
|
||||
const results = this.parseCsv(data, true);
|
||||
if (results == null) {
|
||||
result.success = false;
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
|
||||
results.forEach(value => {
|
||||
if (value.label === 'null') {
|
||||
value.label = null;
|
||||
}
|
||||
const cipher = this.initLoginCipher();
|
||||
cipher.name = this.getValueOrDefault(value.label,
|
||||
this.getValueOrDefault(this.nameFromUrl(value.domain), '--'));
|
||||
cipher.login.uris = this.makeUriArray(value.domain);
|
||||
cipher.login.password = this.getValueOrDefault(value.password);
|
||||
|
||||
if (this.isNullOrWhitespace(value.email) && !this.isNullOrWhitespace(value.username)) {
|
||||
cipher.login.username = value.username;
|
||||
} else {
|
||||
cipher.login.username = this.getValueOrDefault(value.email);
|
||||
cipher.notes = this.getValueOrDefault(value.username);
|
||||
}
|
||||
|
||||
this.cleanupCipher(cipher);
|
||||
result.ciphers.push(cipher);
|
||||
});
|
||||
|
||||
result.success = true;
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
}
|
||||
51
common/src/importers/buttercupCsvImporter.ts
Normal file
51
common/src/importers/buttercupCsvImporter.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
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 {
|
||||
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);
|
||||
}
|
||||
|
||||
results.forEach(value => {
|
||||
this.processFolder(result, this.getValueOrDefault(value['!group_name']));
|
||||
|
||||
const cipher = this.initLoginCipher();
|
||||
cipher.name = this.getValueOrDefault(value.title, '--');
|
||||
cipher.login.username = this.getValueOrDefault(value.username);
|
||||
cipher.login.password = this.getValueOrDefault(value.password);
|
||||
cipher.login.uris = this.makeUriArray(value.URL);
|
||||
|
||||
let processingCustomFields = false;
|
||||
for (const prop in value) {
|
||||
if (value.hasOwnProperty(prop)) {
|
||||
if (!processingCustomFields && OfficialProps.indexOf(prop) === -1) {
|
||||
processingCustomFields = true;
|
||||
}
|
||||
if (processingCustomFields) {
|
||||
this.processKvp(cipher, prop, value[prop]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.cleanupCipher(cipher);
|
||||
result.ciphers.push(cipher);
|
||||
});
|
||||
|
||||
if (this.organization) {
|
||||
this.moveFoldersToCollections(result);
|
||||
}
|
||||
|
||||
result.success = true;
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
}
|
||||
28
common/src/importers/chromeCsvImporter.ts
Normal file
28
common/src/importers/chromeCsvImporter.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
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();
|
||||
const results = this.parseCsv(data, true);
|
||||
if (results == null) {
|
||||
result.success = false;
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
|
||||
results.forEach(value => {
|
||||
const cipher = this.initLoginCipher();
|
||||
cipher.name = this.getValueOrDefault(value.name, '--');
|
||||
cipher.login.username = this.getValueOrDefault(value.username);
|
||||
cipher.login.password = this.getValueOrDefault(value.password);
|
||||
cipher.login.uris = this.makeUriArray(value.url);
|
||||
this.cleanupCipher(cipher);
|
||||
result.ciphers.push(cipher);
|
||||
});
|
||||
|
||||
result.success = true;
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
}
|
||||
79
common/src/importers/clipperzHtmlImporter.ts
Normal file
79
common/src/importers/clipperzHtmlImporter.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
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();
|
||||
const doc = this.parseXml(data);
|
||||
if (doc == null) {
|
||||
result.success = false;
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
|
||||
const textarea = doc.querySelector('textarea');
|
||||
if (textarea == null || this.isNullOrWhitespace(textarea.textContent)) {
|
||||
result.errorMessage = 'Missing textarea.';
|
||||
result.success = false;
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
|
||||
const entries = JSON.parse(textarea.textContent);
|
||||
entries.forEach((entry: any) => {
|
||||
const cipher = this.initLoginCipher();
|
||||
if (!this.isNullOrWhitespace(entry.label)) {
|
||||
cipher.name = entry.label.split(' ')[0];
|
||||
}
|
||||
if (entry.data != null && !this.isNullOrWhitespace(entry.data.notes)) {
|
||||
cipher.notes = entry.data.notes.split('\\n').join('\n');
|
||||
}
|
||||
|
||||
if (entry.currentVersion != null && entry.currentVersion.fields != null) {
|
||||
for (const property in entry.currentVersion.fields) {
|
||||
if (!entry.currentVersion.fields.hasOwnProperty(property)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const field = entry.currentVersion.fields[property];
|
||||
const actionType = field.actionType != null ? field.actionType.toLowerCase() : null;
|
||||
switch (actionType) {
|
||||
case 'password':
|
||||
cipher.login.password = this.getValueOrDefault(field.value);
|
||||
break;
|
||||
case 'email':
|
||||
case 'username':
|
||||
case 'user':
|
||||
case 'name':
|
||||
cipher.login.username = this.getValueOrDefault(field.value);
|
||||
break;
|
||||
case 'url':
|
||||
cipher.login.uris = this.makeUriArray(field.value);
|
||||
break;
|
||||
default:
|
||||
const labelLower = field.label != null ? field.label.toLowerCase() : null;
|
||||
if (cipher.login.password == null && this.passwordFieldNames.indexOf(labelLower) > -1) {
|
||||
cipher.login.password = this.getValueOrDefault(field.value);
|
||||
} else if (cipher.login.username == null &&
|
||||
this.usernameFieldNames.indexOf(labelLower) > -1) {
|
||||
cipher.login.username = this.getValueOrDefault(field.value);
|
||||
} else if ((cipher.login.uris == null || cipher.login.uris.length === 0) &&
|
||||
this.uriFieldNames.indexOf(labelLower) > -1) {
|
||||
cipher.login.uris = this.makeUriArray(field.value);
|
||||
} else {
|
||||
this.processKvp(cipher, field.label, field.value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.convertToNoteIfNeeded(cipher);
|
||||
this.cleanupCipher(cipher);
|
||||
result.ciphers.push(cipher);
|
||||
});
|
||||
|
||||
result.success = true;
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
}
|
||||
47
common/src/importers/codebookCsvImporter.ts
Normal file
47
common/src/importers/codebookCsvImporter.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
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();
|
||||
const results = this.parseCsv(data, true);
|
||||
if (results == null) {
|
||||
result.success = false;
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
|
||||
results.forEach(value => {
|
||||
this.processFolder(result, this.getValueOrDefault(value.Category));
|
||||
|
||||
const cipher = this.initLoginCipher();
|
||||
cipher.favorite = this.getValueOrDefault(value.Favorite) === 'True';
|
||||
cipher.name = this.getValueOrDefault(value.Entry, '--');
|
||||
cipher.notes = this.getValueOrDefault(value.Note);
|
||||
cipher.login.username = this.getValueOrDefault(value.Username, value.Email);
|
||||
cipher.login.password = this.getValueOrDefault(value.Password);
|
||||
cipher.login.totp = this.getValueOrDefault(value.TOTP);
|
||||
cipher.login.uris = this.makeUriArray(value.Website);
|
||||
|
||||
if (!this.isNullOrWhitespace(value.Username)) {
|
||||
this.processKvp(cipher, 'Email', value.Email);
|
||||
}
|
||||
this.processKvp(cipher, 'Phone', value.Phone);
|
||||
this.processKvp(cipher, 'PIN', value.PIN);
|
||||
this.processKvp(cipher, 'Account', value.Account);
|
||||
this.processKvp(cipher, 'Date', value.Date);
|
||||
|
||||
this.convertToNoteIfNeeded(cipher);
|
||||
this.cleanupCipher(cipher);
|
||||
result.ciphers.push(cipher);
|
||||
});
|
||||
|
||||
if (this.organization) {
|
||||
this.moveFoldersToCollections(result);
|
||||
}
|
||||
|
||||
result.success = true;
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
}
|
||||
162
common/src/importers/dashlaneJsonImporter.ts
Normal file
162
common/src/importers/dashlaneJsonImporter.ts
Normal file
@@ -0,0 +1,162 @@
|
||||
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';
|
||||
|
||||
const HandledResults = new Set(['ADDRESS', 'AUTHENTIFIANT', 'BANKSTATEMENT', 'IDCARD', 'IDENTITY',
|
||||
'PAYMENTMEANS_CREDITCARD', 'PAYMENTMEAN_PAYPAL', 'EMAIL']);
|
||||
|
||||
export class DashlaneJsonImporter extends BaseImporter implements Importer {
|
||||
private result: ImportResult;
|
||||
|
||||
parse(data: string): Promise<ImportResult> {
|
||||
this.result = new ImportResult();
|
||||
const results = JSON.parse(data);
|
||||
if (results == null || results.length === 0) {
|
||||
this.result.success = false;
|
||||
return Promise.resolve(this.result);
|
||||
}
|
||||
|
||||
if (results.ADDRESS != null) {
|
||||
this.processAddress(results.ADDRESS);
|
||||
}
|
||||
if (results.AUTHENTIFIANT != null) {
|
||||
this.processAuth(results.AUTHENTIFIANT);
|
||||
}
|
||||
if (results.BANKSTATEMENT != null) {
|
||||
this.processNote(results.BANKSTATEMENT, 'BankAccountName');
|
||||
}
|
||||
if (results.IDCARD != null) {
|
||||
this.processNote(results.IDCARD, 'Fullname');
|
||||
}
|
||||
if (results.PAYMENTMEANS_CREDITCARD != null) {
|
||||
this.processCard(results.PAYMENTMEANS_CREDITCARD);
|
||||
}
|
||||
if (results.IDENTITY != null) {
|
||||
this.processIdentity(results.IDENTITY);
|
||||
}
|
||||
|
||||
for (const key in results) {
|
||||
if (results.hasOwnProperty(key) && !HandledResults.has(key)) {
|
||||
this.processNote(results[key], null, 'Generic Note');
|
||||
}
|
||||
}
|
||||
|
||||
this.result.success = true;
|
||||
return Promise.resolve(this.result);
|
||||
}
|
||||
|
||||
private processAuth(results: any[]) {
|
||||
results.forEach((credential: any) => {
|
||||
const cipher = this.initLoginCipher();
|
||||
cipher.name = this.getValueOrDefault(credential.title);
|
||||
|
||||
cipher.login.username = this.getValueOrDefault(credential.login,
|
||||
this.getValueOrDefault(credential.secondaryLogin));
|
||||
if (this.isNullOrWhitespace(cipher.login.username)) {
|
||||
cipher.login.username = this.getValueOrDefault(credential.email);
|
||||
} else if (!this.isNullOrWhitespace(credential.email)) {
|
||||
cipher.notes = ('Email: ' + credential.email + '\n');
|
||||
}
|
||||
|
||||
cipher.login.password = this.getValueOrDefault(credential.password);
|
||||
cipher.login.uris = this.makeUriArray(credential.domain);
|
||||
cipher.notes += this.getValueOrDefault(credential.note, '');
|
||||
|
||||
this.convertToNoteIfNeeded(cipher);
|
||||
this.cleanupCipher(cipher);
|
||||
this.result.ciphers.push(cipher);
|
||||
});
|
||||
}
|
||||
|
||||
private processIdentity(results: any[]) {
|
||||
results.forEach((obj: any) => {
|
||||
const cipher = new CipherView();
|
||||
cipher.identity = new IdentityView();
|
||||
cipher.type = CipherType.Identity;
|
||||
cipher.name = this.getValueOrDefault(obj.fullName, '');
|
||||
const nameParts = cipher.name.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 = this.getValueOrDefault(nameParts[2]);
|
||||
}
|
||||
cipher.identity.username = this.getValueOrDefault(obj.pseudo);
|
||||
this.cleanupCipher(cipher);
|
||||
this.result.ciphers.push(cipher);
|
||||
});
|
||||
}
|
||||
|
||||
private processAddress(results: any[]) {
|
||||
results.forEach((obj: any) => {
|
||||
const cipher = new CipherView();
|
||||
cipher.identity = new IdentityView();
|
||||
cipher.type = CipherType.Identity;
|
||||
cipher.name = this.getValueOrDefault(obj.addressName);
|
||||
cipher.identity.address1 = this.getValueOrDefault(obj.addressFull);
|
||||
cipher.identity.city = this.getValueOrDefault(obj.city);
|
||||
cipher.identity.state = this.getValueOrDefault(obj.state);
|
||||
cipher.identity.postalCode = this.getValueOrDefault(obj.zipcode);
|
||||
cipher.identity.country = this.getValueOrDefault(obj.country);
|
||||
if (cipher.identity.country != null) {
|
||||
cipher.identity.country = cipher.identity.country.toUpperCase();
|
||||
}
|
||||
this.cleanupCipher(cipher);
|
||||
this.result.ciphers.push(cipher);
|
||||
});
|
||||
}
|
||||
|
||||
private processCard(results: any[]) {
|
||||
results.forEach((obj: any) => {
|
||||
const cipher = new CipherView();
|
||||
cipher.card = new CardView();
|
||||
cipher.type = CipherType.Card;
|
||||
cipher.name = this.getValueOrDefault(obj.bank);
|
||||
cipher.card.number = this.getValueOrDefault(obj.cardNumber);
|
||||
cipher.card.brand = this.getCardBrand(cipher.card.number);
|
||||
cipher.card.cardholderName = this.getValueOrDefault(obj.owner);
|
||||
if (!this.isNullOrWhitespace(cipher.card.brand)) {
|
||||
if (this.isNullOrWhitespace(cipher.name)) {
|
||||
cipher.name = cipher.card.brand;
|
||||
} else {
|
||||
cipher.name += (' - ' + cipher.card.brand);
|
||||
}
|
||||
}
|
||||
this.cleanupCipher(cipher);
|
||||
this.result.ciphers.push(cipher);
|
||||
});
|
||||
}
|
||||
|
||||
private processNote(results: any[], nameProperty: string, name: string = null) {
|
||||
results.forEach((obj: any) => {
|
||||
const cipher = new CipherView();
|
||||
cipher.secureNote = new SecureNoteView();
|
||||
cipher.type = CipherType.SecureNote;
|
||||
cipher.secureNote.type = SecureNoteType.Generic;
|
||||
if (name != null) {
|
||||
cipher.name = name;
|
||||
} else {
|
||||
cipher.name = this.getValueOrDefault(obj[nameProperty]);
|
||||
}
|
||||
for (const key in obj) {
|
||||
if (obj.hasOwnProperty(key) && key !== nameProperty) {
|
||||
this.processKvp(cipher, key, obj[key].toString());
|
||||
}
|
||||
}
|
||||
this.cleanupCipher(cipher);
|
||||
this.result.ciphers.push(cipher);
|
||||
});
|
||||
}
|
||||
}
|
||||
62
common/src/importers/encryptrCsvImporter.ts
Normal file
62
common/src/importers/encryptrCsvImporter.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
import { BaseImporter } from './baseImporter';
|
||||
import { Importer } from './importer';
|
||||
|
||||
import { ImportResult } from '../models/domain/importResult';
|
||||
|
||||
import { CardView } from '../models/view/cardView';
|
||||
|
||||
import { CipherType } from '../enums/cipherType';
|
||||
|
||||
export class EncryptrCsvImporter 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);
|
||||
}
|
||||
|
||||
results.forEach(value => {
|
||||
const cipher = this.initLoginCipher();
|
||||
cipher.name = this.getValueOrDefault(value.Label, '--');
|
||||
cipher.notes = this.getValueOrDefault(value.Notes);
|
||||
const text = this.getValueOrDefault(value.Text);
|
||||
if (!this.isNullOrWhitespace(text)) {
|
||||
if (this.isNullOrWhitespace(cipher.notes)) {
|
||||
cipher.notes = text;
|
||||
} else {
|
||||
cipher.notes += ('\n\n' + text);
|
||||
}
|
||||
}
|
||||
|
||||
const type = value['Entry Type'];
|
||||
if (type === 'Password') {
|
||||
cipher.login.username = this.getValueOrDefault(value.Username);
|
||||
cipher.login.password = this.getValueOrDefault(value.Password);
|
||||
cipher.login.uris = this.makeUriArray(value['Site URL']);
|
||||
} else if (type === 'Credit Card') {
|
||||
cipher.type = CipherType.Card;
|
||||
cipher.card = new CardView();
|
||||
cipher.card.cardholderName = this.getValueOrDefault(value['Name on card']);
|
||||
cipher.card.number = this.getValueOrDefault(value['Card Number']);
|
||||
cipher.card.brand = this.getCardBrand(cipher.card.number);
|
||||
cipher.card.code = this.getValueOrDefault(value.CVV);
|
||||
const expiry = this.getValueOrDefault(value.Expiry);
|
||||
if (!this.isNullOrWhitespace(expiry)) {
|
||||
const expParts = expiry.split('/');
|
||||
if (expParts.length > 1) {
|
||||
cipher.card.expMonth = parseInt(expParts[0], null).toString();
|
||||
cipher.card.expYear = (2000 + parseInt(expParts[1], null)).toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.convertToNoteIfNeeded(cipher);
|
||||
this.cleanupCipher(cipher);
|
||||
result.ciphers.push(cipher);
|
||||
});
|
||||
|
||||
result.success = true;
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
}
|
||||
112
common/src/importers/enpassCsvImporter.ts
Normal file
112
common/src/importers/enpassCsvImporter.ts
Normal file
@@ -0,0 +1,112 @@
|
||||
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 { CardView } from '../models/view/cardView';
|
||||
import { SecureNoteView } from '../models/view/secureNoteView';
|
||||
|
||||
export class EnpassCsvImporter extends BaseImporter implements Importer {
|
||||
parse(data: string): Promise<ImportResult> {
|
||||
const result = new ImportResult();
|
||||
const results = this.parseCsv(data, false);
|
||||
if (results == null) {
|
||||
result.success = false;
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
|
||||
let firstRow = true;
|
||||
results.forEach(value => {
|
||||
if (value.length < 2 || (firstRow && (value[0] === 'Title' || value[0] === 'title'))) {
|
||||
firstRow = false;
|
||||
return;
|
||||
}
|
||||
|
||||
const cipher = this.initLoginCipher();
|
||||
cipher.notes = this.getValueOrDefault(value[value.length - 1]);
|
||||
cipher.name = this.getValueOrDefault(value[0], '--');
|
||||
|
||||
if (value.length === 2 || (!this.containsField(value, 'username') &&
|
||||
!this.containsField(value, 'password') && !this.containsField(value, 'email') &&
|
||||
!this.containsField(value, 'url'))) {
|
||||
cipher.type = CipherType.SecureNote;
|
||||
cipher.secureNote = new SecureNoteView();
|
||||
cipher.secureNote.type = SecureNoteType.Generic;
|
||||
}
|
||||
|
||||
if (this.containsField(value, 'cardholder') && this.containsField(value, 'number') &&
|
||||
this.containsField(value, 'expiry date')) {
|
||||
cipher.type = CipherType.Card;
|
||||
cipher.card = new CardView();
|
||||
}
|
||||
|
||||
if (value.length > 2 && (value.length % 2) === 0) {
|
||||
for (let i = 0; i < value.length - 2; i += 2) {
|
||||
const fieldValue: string = value[i + 2];
|
||||
if (this.isNullOrWhitespace(fieldValue)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const fieldName: string = value[i + 1];
|
||||
const fieldNameLower = fieldName.toLowerCase();
|
||||
|
||||
if (cipher.type === CipherType.Login) {
|
||||
if (fieldNameLower === 'url' && (cipher.login.uris == null || cipher.login.uris.length === 0)) {
|
||||
cipher.login.uris = this.makeUriArray(fieldValue);
|
||||
continue;
|
||||
} else if ((fieldNameLower === 'username' || fieldNameLower === 'email') &&
|
||||
this.isNullOrWhitespace(cipher.login.username)) {
|
||||
cipher.login.username = fieldValue;
|
||||
continue;
|
||||
} else if (fieldNameLower === 'password' && this.isNullOrWhitespace(cipher.login.password)) {
|
||||
cipher.login.password = fieldValue;
|
||||
continue;
|
||||
} else if (fieldNameLower === 'totp' && this.isNullOrWhitespace(cipher.login.totp)) {
|
||||
cipher.login.totp = fieldValue;
|
||||
continue;
|
||||
}
|
||||
} else if (cipher.type === CipherType.Card) {
|
||||
if (fieldNameLower === 'cardholder' && this.isNullOrWhitespace(cipher.card.cardholderName)) {
|
||||
cipher.card.cardholderName = fieldValue;
|
||||
continue;
|
||||
} else if (fieldNameLower === 'number' && this.isNullOrWhitespace(cipher.card.number)) {
|
||||
cipher.card.number = fieldValue;
|
||||
cipher.card.brand = this.getCardBrand(fieldValue);
|
||||
continue;
|
||||
} else if (fieldNameLower === 'cvc' && this.isNullOrWhitespace(cipher.card.code)) {
|
||||
cipher.card.code = fieldValue;
|
||||
continue;
|
||||
} else if (fieldNameLower === 'expiry date' && this.isNullOrWhitespace(cipher.card.expMonth) &&
|
||||
this.isNullOrWhitespace(cipher.card.expYear)) {
|
||||
if (this.setCardExpiration(cipher, fieldValue)) {
|
||||
continue;
|
||||
}
|
||||
} else if (fieldNameLower === 'type') {
|
||||
// Skip since brand was determined from number above
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
this.processKvp(cipher, fieldName, fieldValue);
|
||||
}
|
||||
}
|
||||
|
||||
this.cleanupCipher(cipher);
|
||||
result.ciphers.push(cipher);
|
||||
});
|
||||
|
||||
result.success = true;
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
|
||||
private containsField(fields: any[], name: string) {
|
||||
if (fields == null || name == null) {
|
||||
return false;
|
||||
}
|
||||
return fields.filter(f => !this.isNullOrWhitespace(f) &&
|
||||
f.toLowerCase() === name.toLowerCase()).length > 0;
|
||||
}
|
||||
}
|
||||
163
common/src/importers/enpassJsonImporter.ts
Normal file
163
common/src/importers/enpassJsonImporter.ts
Normal file
@@ -0,0 +1,163 @@
|
||||
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 { FolderView } from '../models/view/folderView';
|
||||
|
||||
import { CipherType } from '../enums/cipherType';
|
||||
import { FieldType } from '../enums/fieldType';
|
||||
|
||||
export class EnpassJsonImporter extends BaseImporter implements Importer {
|
||||
parse(data: string): Promise<ImportResult> {
|
||||
const result = new ImportResult();
|
||||
const results = JSON.parse(data);
|
||||
if (results == null || results.items == null || results.items.length === 0) {
|
||||
result.success = false;
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
|
||||
const foldersMap = new Map<string, string>();
|
||||
const foldersIndexMap = new Map<string, number>();
|
||||
const folderTree = this.buildFolderTree(results.folders);
|
||||
this.flattenFolderTree(null, folderTree, foldersMap);
|
||||
foldersMap.forEach((val, key) => {
|
||||
foldersIndexMap.set(key, result.folders.length);
|
||||
const f = new FolderView();
|
||||
f.name = val;
|
||||
result.folders.push(f);
|
||||
});
|
||||
|
||||
results.items.forEach((item: any) => {
|
||||
if (item.folders != null && item.folders.length > 0 && foldersIndexMap.has(item.folders[0])) {
|
||||
result.folderRelationships.push([result.ciphers.length, foldersIndexMap.get(item.folders[0])]);
|
||||
}
|
||||
|
||||
const cipher = this.initLoginCipher();
|
||||
cipher.name = this.getValueOrDefault(item.title);
|
||||
cipher.favorite = item.favorite > 0;
|
||||
|
||||
if (item.template_type != null && item.fields != null && item.fields.length > 0) {
|
||||
if (item.template_type.indexOf('login.') === 0 || item.template_type.indexOf('password.') === 0) {
|
||||
this.processLogin(cipher, item.fields);
|
||||
} else if (item.template_type.indexOf('creditcard.') === 0) {
|
||||
this.processCard(cipher, item.fields);
|
||||
} else if (item.template_type.indexOf('identity.') < 0 &&
|
||||
item.fields.some((f: any) => f.type === 'password' && !this.isNullOrWhitespace(f.value))) {
|
||||
this.processLogin(cipher, item.fields);
|
||||
} else {
|
||||
this.processNote(cipher, item.fields);
|
||||
}
|
||||
}
|
||||
|
||||
cipher.notes += ('\n' + this.getValueOrDefault(item.note, ''));
|
||||
this.convertToNoteIfNeeded(cipher);
|
||||
this.cleanupCipher(cipher);
|
||||
result.ciphers.push(cipher);
|
||||
});
|
||||
|
||||
result.success = true;
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
|
||||
private processLogin(cipher: CipherView, fields: any[]) {
|
||||
const urls: string[] = [];
|
||||
fields.forEach((field: any) => {
|
||||
if (this.isNullOrWhitespace(field.value) || field.type === 'section') {
|
||||
return;
|
||||
}
|
||||
|
||||
if ((field.type === 'username' || field.type === 'email') &&
|
||||
this.isNullOrWhitespace(cipher.login.username)) {
|
||||
cipher.login.username = field.value;
|
||||
} else if (field.type === 'password' && this.isNullOrWhitespace(cipher.login.password)) {
|
||||
cipher.login.password = field.value;
|
||||
} else if (field.type === 'totp' && this.isNullOrWhitespace(cipher.login.totp)) {
|
||||
cipher.login.totp = field.value;
|
||||
} else if (field.type === 'url') {
|
||||
urls.push(field.value);
|
||||
} else {
|
||||
this.processKvp(cipher, field.label, field.value,
|
||||
field.sensitive === 1 ? FieldType.Hidden : FieldType.Text);
|
||||
}
|
||||
});
|
||||
cipher.login.uris = this.makeUriArray(urls);
|
||||
}
|
||||
|
||||
private processCard(cipher: CipherView, fields: any[]) {
|
||||
cipher.card = new CardView();
|
||||
cipher.type = CipherType.Card;
|
||||
fields.forEach((field: any) => {
|
||||
if (this.isNullOrWhitespace(field.value) || field.type === 'section' || field.type === 'ccType') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (field.type === 'ccName' && this.isNullOrWhitespace(cipher.card.cardholderName)) {
|
||||
cipher.card.cardholderName = field.value;
|
||||
} else if (field.type === 'ccNumber' && this.isNullOrWhitespace(cipher.card.number)) {
|
||||
cipher.card.number = field.value;
|
||||
cipher.card.brand = this.getCardBrand(cipher.card.number);
|
||||
} else if (field.type === 'ccCvc' && this.isNullOrWhitespace(cipher.card.code)) {
|
||||
cipher.card.code = field.value;
|
||||
} else if (field.type === 'ccExpiry' && this.isNullOrWhitespace(cipher.card.expYear)) {
|
||||
if (!this.setCardExpiration(cipher, field.value)) {
|
||||
this.processKvp(cipher, field.label, field.value,
|
||||
field.sensitive === 1 ? FieldType.Hidden : FieldType.Text);
|
||||
}
|
||||
} else {
|
||||
this.processKvp(cipher, field.label, field.value,
|
||||
field.sensitive === 1 ? FieldType.Hidden : FieldType.Text);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private processNote(cipher: CipherView, fields: any[]) {
|
||||
fields.forEach((field: any) => {
|
||||
if (this.isNullOrWhitespace(field.value) || field.type === 'section') {
|
||||
return;
|
||||
}
|
||||
this.processKvp(cipher, field.label, field.value,
|
||||
field.sensitive === 1 ? FieldType.Hidden : FieldType.Text);
|
||||
});
|
||||
}
|
||||
|
||||
private buildFolderTree(folders: any[]): any[] {
|
||||
if (folders == null) {
|
||||
return [];
|
||||
}
|
||||
const folderTree: any[] = [];
|
||||
const map = new Map<string, any>([]);
|
||||
folders.forEach((obj: any) => {
|
||||
map.set(obj.uuid, obj);
|
||||
obj.children = [];
|
||||
});
|
||||
folders.forEach((obj: any) => {
|
||||
if (obj.parent_uuid != null && obj.parent_uuid !== '' && map.has(obj.parent_uuid)) {
|
||||
map.get(obj.parent_uuid).children.push(obj);
|
||||
} else {
|
||||
folderTree.push(obj);
|
||||
}
|
||||
});
|
||||
return folderTree;
|
||||
}
|
||||
|
||||
private flattenFolderTree(titlePrefix: string, tree: any[], map: Map<string, string>) {
|
||||
if (tree == null) {
|
||||
return;
|
||||
}
|
||||
tree.forEach((f: any) => {
|
||||
if (f.title != null && f.title.trim() !== '') {
|
||||
let title = f.title.trim();
|
||||
if (titlePrefix != null && titlePrefix.trim() !== '') {
|
||||
title = titlePrefix + '/' + title;
|
||||
}
|
||||
map.set(f.uuid, title);
|
||||
if (f.children != null && f.children.length !== 0) {
|
||||
this.flattenFolderTree(title, f.children, map);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
31
common/src/importers/firefoxCsvImporter.ts
Normal file
31
common/src/importers/firefoxCsvImporter.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
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();
|
||||
const results = this.parseCsv(data, true);
|
||||
if (results == null) {
|
||||
result.success = false;
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
|
||||
results.filter(value => {
|
||||
return value.url !== 'chrome://FirefoxAccounts';
|
||||
}).forEach(value => {
|
||||
const cipher = this.initLoginCipher();
|
||||
const url = this.getValueOrDefault(value.url, this.getValueOrDefault(value.hostname));
|
||||
cipher.name = this.getValueOrDefault(this.nameFromUrl(url), '--');
|
||||
cipher.login.username = this.getValueOrDefault(value.username);
|
||||
cipher.login.password = this.getValueOrDefault(value.password);
|
||||
cipher.login.uris = this.makeUriArray(url);
|
||||
this.cleanupCipher(cipher);
|
||||
result.ciphers.push(cipher);
|
||||
});
|
||||
|
||||
result.success = true;
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
}
|
||||
60
common/src/importers/fsecureFskImporter.ts
Normal file
60
common/src/importers/fsecureFskImporter.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
import { BaseImporter } from './baseImporter';
|
||||
import { Importer } from './importer';
|
||||
|
||||
import { ImportResult } from '../models/domain/importResult';
|
||||
|
||||
import { CardView } from '../models/view/cardView';
|
||||
|
||||
import { CipherType } from '../enums/cipherType';
|
||||
|
||||
export class FSecureFskImporter extends BaseImporter implements Importer {
|
||||
parse(data: string): Promise<ImportResult> {
|
||||
const result = new ImportResult();
|
||||
const results = JSON.parse(data);
|
||||
if (results == null || results.data == null) {
|
||||
result.success = false;
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
|
||||
for (const key in results.data) {
|
||||
if (!results.data.hasOwnProperty(key)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const value = results.data[key];
|
||||
const cipher = this.initLoginCipher();
|
||||
cipher.name = this.getValueOrDefault(value.service);
|
||||
cipher.notes = this.getValueOrDefault(value.notes);
|
||||
|
||||
if (value.style === 'website') {
|
||||
cipher.login.username = this.getValueOrDefault(value.username);
|
||||
cipher.login.password = this.getValueOrDefault(value.password);
|
||||
cipher.login.uris = this.makeUriArray(value.url);
|
||||
} else if (value.style === 'creditcard') {
|
||||
cipher.type = CipherType.Card;
|
||||
cipher.card = new CardView();
|
||||
cipher.card.cardholderName = this.getValueOrDefault(value.username);
|
||||
cipher.card.number = this.getValueOrDefault(value.creditNumber);
|
||||
cipher.card.brand = this.getCardBrand(cipher.card.number);
|
||||
cipher.card.code = this.getValueOrDefault(value.creditCvv);
|
||||
if (!this.isNullOrWhitespace(value.creditExpiry)) {
|
||||
if (!this.setCardExpiration(cipher, value.creditExpiry)) {
|
||||
this.processKvp(cipher, 'Expiration', value.creditExpiry);
|
||||
}
|
||||
}
|
||||
if (!this.isNullOrWhitespace(value.password)) {
|
||||
this.processKvp(cipher, 'PIN', value.password);
|
||||
}
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
this.convertToNoteIfNeeded(cipher);
|
||||
this.cleanupCipher(cipher);
|
||||
result.ciphers.push(cipher);
|
||||
}
|
||||
|
||||
result.success = true;
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
}
|
||||
60
common/src/importers/gnomeJsonImporter.ts
Normal file
60
common/src/importers/gnomeJsonImporter.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
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();
|
||||
const results = JSON.parse(data);
|
||||
if (results == null || Object.keys(results).length === 0) {
|
||||
result.success = false;
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
|
||||
for (const keyRing in results) {
|
||||
if (!results.hasOwnProperty(keyRing) || this.isNullOrWhitespace(keyRing) ||
|
||||
results[keyRing].length === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
results[keyRing].forEach((value: any) => {
|
||||
if (this.isNullOrWhitespace(value.display_name) || value.display_name.indexOf('http') !== 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.processFolder(result, keyRing);
|
||||
const cipher = this.initLoginCipher();
|
||||
cipher.name = value.display_name.replace('http://', '').replace('https://', '');
|
||||
if (cipher.name.length > 30) {
|
||||
cipher.name = cipher.name.substring(0, 30);
|
||||
}
|
||||
cipher.login.password = this.getValueOrDefault(value.secret);
|
||||
cipher.login.uris = this.makeUriArray(value.display_name);
|
||||
|
||||
if (value.attributes != null) {
|
||||
cipher.login.username = value.attributes != null ?
|
||||
this.getValueOrDefault(value.attributes.username_value) : null;
|
||||
for (const attr in value.attributes) {
|
||||
if (!value.attributes.hasOwnProperty(attr) || attr === 'username_value' ||
|
||||
attr === 'xdg:schema') {
|
||||
continue;
|
||||
}
|
||||
this.processKvp(cipher, attr, value.attributes[attr]);
|
||||
}
|
||||
}
|
||||
|
||||
this.convertToNoteIfNeeded(cipher);
|
||||
this.cleanupCipher(cipher);
|
||||
result.ciphers.push(cipher);
|
||||
});
|
||||
}
|
||||
|
||||
if (this.organization) {
|
||||
this.moveFoldersToCollections(result);
|
||||
}
|
||||
|
||||
result.success = true;
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
}
|
||||
6
common/src/importers/importer.ts
Normal file
6
common/src/importers/importer.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { ImportResult } from '../models/domain/importResult';
|
||||
|
||||
export interface Importer {
|
||||
organizationId: string;
|
||||
parse(data: string): Promise<ImportResult>;
|
||||
}
|
||||
124
common/src/importers/kasperskyTxtImporter.ts
Normal file
124
common/src/importers/kasperskyTxtImporter.ts
Normal file
@@ -0,0 +1,124 @@
|
||||
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';
|
||||
const Delimiter = '\n---\n';
|
||||
|
||||
export class KasperskyTxtImporter extends BaseImporter implements Importer {
|
||||
parse(data: string): Promise<ImportResult> {
|
||||
const result = new ImportResult();
|
||||
|
||||
let notesData: string;
|
||||
let applicationsData: string;
|
||||
let websitesData: string;
|
||||
let workingData = this.splitNewLine(data).join('\n');
|
||||
|
||||
if (workingData.indexOf(NotesHeader) !== -1) {
|
||||
const parts = workingData.split(NotesHeader);
|
||||
if (parts.length > 1) {
|
||||
workingData = parts[0];
|
||||
notesData = parts[1];
|
||||
}
|
||||
}
|
||||
if (workingData.indexOf(ApplicationsHeader) !== -1) {
|
||||
const parts = workingData.split(ApplicationsHeader);
|
||||
if (parts.length > 1) {
|
||||
workingData = parts[0];
|
||||
applicationsData = parts[1];
|
||||
}
|
||||
}
|
||||
if (workingData.indexOf(WebsitesHeader) === 0) {
|
||||
const parts = workingData.split(WebsitesHeader);
|
||||
if (parts.length > 1) {
|
||||
workingData = parts[0];
|
||||
websitesData = parts[1];
|
||||
}
|
||||
}
|
||||
|
||||
const notes = this.parseDataCategory(notesData);
|
||||
const applications = this.parseDataCategory(applicationsData);
|
||||
const websites = this.parseDataCategory(websitesData);
|
||||
|
||||
notes.forEach(n => {
|
||||
const cipher = this.initLoginCipher();
|
||||
cipher.name = this.getValueOrDefault(n.get('Name'));
|
||||
cipher.notes = this.getValueOrDefault(n.get('Text'));
|
||||
this.cleanupCipher(cipher);
|
||||
result.ciphers.push(cipher);
|
||||
});
|
||||
|
||||
websites.concat(applications).forEach(w => {
|
||||
const cipher = this.initLoginCipher();
|
||||
const nameKey = w.has('Website name') ? 'Website name' : 'Application';
|
||||
cipher.name = this.getValueOrDefault(w.get(nameKey), '');
|
||||
if (!this.isNullOrWhitespace(w.get('Login name'))) {
|
||||
if (!this.isNullOrWhitespace(cipher.name)) {
|
||||
cipher.name += ': ';
|
||||
}
|
||||
cipher.name += w.get('Login name');
|
||||
}
|
||||
cipher.notes = this.getValueOrDefault(w.get('Comment'));
|
||||
if (w.has('Website URL')) {
|
||||
cipher.login.uris = this.makeUriArray(w.get('Website URL'));
|
||||
}
|
||||
cipher.login.username = this.getValueOrDefault(w.get('Login'));
|
||||
cipher.login.password = this.getValueOrDefault(w.get('Password'));
|
||||
this.cleanupCipher(cipher);
|
||||
result.ciphers.push(cipher);
|
||||
});
|
||||
|
||||
result.success = true;
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
|
||||
private parseDataCategory(data: string): Map<string, string>[] {
|
||||
if (this.isNullOrWhitespace(data) || data.indexOf(Delimiter) === -1) {
|
||||
return [];
|
||||
}
|
||||
const items: Map<string, string>[] = [];
|
||||
data.split(Delimiter).forEach(p => {
|
||||
if (p.indexOf('\n') === -1) {
|
||||
return;
|
||||
}
|
||||
const item = new Map<string, string>();
|
||||
let itemComment: string;
|
||||
let itemCommentKey: string;
|
||||
p.split('\n').forEach(l => {
|
||||
if (itemComment != null) {
|
||||
itemComment += ('\n' + l);
|
||||
return;
|
||||
}
|
||||
const colonIndex = l.indexOf(':');
|
||||
let key: string;
|
||||
let val: string;
|
||||
if (colonIndex === -1) {
|
||||
return;
|
||||
} else {
|
||||
key = l.substring(0, colonIndex);
|
||||
if (l.length > colonIndex + 1) {
|
||||
val = l.substring(colonIndex + 2);
|
||||
}
|
||||
}
|
||||
if (key != null) {
|
||||
item.set(key, val);
|
||||
}
|
||||
if (key === 'Comment' || key === 'Text') {
|
||||
itemComment = val;
|
||||
itemCommentKey = key;
|
||||
}
|
||||
});
|
||||
if (itemComment != null && itemCommentKey != null) {
|
||||
item.set(itemCommentKey, itemComment);
|
||||
}
|
||||
if (item.size === 0) {
|
||||
return;
|
||||
}
|
||||
items.push(item);
|
||||
});
|
||||
return items;
|
||||
}
|
||||
}
|
||||
100
common/src/importers/keepass2XmlImporter.ts
Normal file
100
common/src/importers/keepass2XmlImporter.ts
Normal file
@@ -0,0 +1,100 @@
|
||||
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();
|
||||
|
||||
parse(data: string): Promise<ImportResult> {
|
||||
const doc = this.parseXml(data);
|
||||
if (doc == null) {
|
||||
this.result.success = false;
|
||||
return Promise.resolve(this.result);
|
||||
}
|
||||
|
||||
const rootGroup = doc.querySelector('KeePassFile > Root > Group');
|
||||
if (rootGroup == null) {
|
||||
this.result.errorMessage = 'Missing `KeePassFile > Root > Group` node.';
|
||||
this.result.success = false;
|
||||
return Promise.resolve(this.result);
|
||||
}
|
||||
|
||||
this.traverse(rootGroup, true, '');
|
||||
|
||||
if (this.organization) {
|
||||
this.moveFoldersToCollections(this.result);
|
||||
}
|
||||
|
||||
this.result.success = true;
|
||||
return Promise.resolve(this.result);
|
||||
}
|
||||
|
||||
traverse(node: Element, isRootNode: boolean, groupPrefixName: string) {
|
||||
const folderIndex = this.result.folders.length;
|
||||
let groupName = groupPrefixName;
|
||||
|
||||
if (!isRootNode) {
|
||||
if (groupName !== '') {
|
||||
groupName += '/';
|
||||
}
|
||||
const nameEl = this.querySelectorDirectChild(node, 'Name');
|
||||
groupName += nameEl == null ? '-' : nameEl.textContent;
|
||||
const folder = new FolderView();
|
||||
folder.name = groupName;
|
||||
this.result.folders.push(folder);
|
||||
}
|
||||
|
||||
this.querySelectorAllDirectChild(node, 'Entry').forEach(entry => {
|
||||
const cipherIndex = this.result.ciphers.length;
|
||||
|
||||
const cipher = this.initLoginCipher();
|
||||
this.querySelectorAllDirectChild(entry, 'String').forEach(entryString => {
|
||||
const valueEl = this.querySelectorDirectChild(entryString, 'Value');
|
||||
const value = valueEl != null ? valueEl.textContent : null;
|
||||
if (this.isNullOrWhitespace(value)) {
|
||||
return;
|
||||
}
|
||||
const keyEl = this.querySelectorDirectChild(entryString, 'Key');
|
||||
const key = keyEl != null ? keyEl.textContent : null;
|
||||
|
||||
if (key === 'URL') {
|
||||
cipher.login.uris = this.makeUriArray(value);
|
||||
} else if (key === 'UserName') {
|
||||
cipher.login.username = value;
|
||||
} else if (key === 'Password') {
|
||||
cipher.login.password = value;
|
||||
} else if (key === 'otp') {
|
||||
cipher.login.totp = value.replace('key=', '');
|
||||
} else if (key === 'Title') {
|
||||
cipher.name = value;
|
||||
} else if (key === 'Notes') {
|
||||
cipher.notes += (value + '\n');
|
||||
} else {
|
||||
let type = FieldType.Text;
|
||||
const attrs = (valueEl.attributes as any);
|
||||
if (attrs.length > 0 && attrs.ProtectInMemory != null &&
|
||||
attrs.ProtectInMemory.value === 'True') {
|
||||
type = FieldType.Hidden;
|
||||
}
|
||||
this.processKvp(cipher, key, value, type);
|
||||
}
|
||||
});
|
||||
|
||||
this.cleanupCipher(cipher);
|
||||
this.result.ciphers.push(cipher);
|
||||
|
||||
if (!isRootNode) {
|
||||
this.result.folderRelationships.push([cipherIndex, folderIndex]);
|
||||
}
|
||||
});
|
||||
|
||||
this.querySelectorAllDirectChild(node, 'Group').forEach(group => {
|
||||
this.traverse(group, false, groupName);
|
||||
});
|
||||
}
|
||||
}
|
||||
42
common/src/importers/keepassxCsvImporter.ts
Normal file
42
common/src/importers/keepassxCsvImporter.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
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();
|
||||
const results = this.parseCsv(data, true);
|
||||
if (results == null) {
|
||||
result.success = false;
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
|
||||
results.forEach(value => {
|
||||
if (this.isNullOrWhitespace(value.Title)) {
|
||||
return;
|
||||
}
|
||||
|
||||
value.Group = !this.isNullOrWhitespace(value.Group) && value.Group.startsWith('Root/') ?
|
||||
value.Group.replace('Root/', '') : value.Group;
|
||||
const groupName = !this.isNullOrWhitespace(value.Group) ? value.Group : null;
|
||||
this.processFolder(result, groupName);
|
||||
|
||||
const cipher = this.initLoginCipher();
|
||||
cipher.notes = this.getValueOrDefault(value.Notes);
|
||||
cipher.name = this.getValueOrDefault(value.Title, '--');
|
||||
cipher.login.username = this.getValueOrDefault(value.Username);
|
||||
cipher.login.password = this.getValueOrDefault(value.Password);
|
||||
cipher.login.uris = this.makeUriArray(value.URL);
|
||||
this.cleanupCipher(cipher);
|
||||
result.ciphers.push(cipher);
|
||||
});
|
||||
|
||||
if (this.organization) {
|
||||
this.moveFoldersToCollections(result);
|
||||
}
|
||||
|
||||
result.success = true;
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
}
|
||||
48
common/src/importers/keeperCsvImporter.ts
Normal file
48
common/src/importers/keeperCsvImporter.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import { BaseImporter } from './baseImporter';
|
||||
import { Importer } from './importer';
|
||||
|
||||
import { ImportResult } from '../models/domain/importResult';
|
||||
|
||||
import { FolderView } from '../models/view/folderView';
|
||||
|
||||
export class KeeperCsvImporter extends BaseImporter implements Importer {
|
||||
parse(data: string): Promise<ImportResult> {
|
||||
const result = new ImportResult();
|
||||
const results = this.parseCsv(data, false);
|
||||
if (results == null) {
|
||||
result.success = false;
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
|
||||
results.forEach(value => {
|
||||
if (value.length < 6) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.processFolder(result, value[0]);
|
||||
const cipher = this.initLoginCipher();
|
||||
cipher.notes = this.getValueOrDefault(value[5]) + '\n';
|
||||
cipher.name = this.getValueOrDefault(value[1], '--');
|
||||
cipher.login.username = this.getValueOrDefault(value[2]);
|
||||
cipher.login.password = this.getValueOrDefault(value[3]);
|
||||
cipher.login.uris = this.makeUriArray(value[4]);
|
||||
|
||||
if (value.length > 7) {
|
||||
// we have some custom fields.
|
||||
for (let i = 7; i < value.length; i = i + 2) {
|
||||
this.processKvp(cipher, value[i], value[i + 1]);
|
||||
}
|
||||
}
|
||||
|
||||
this.cleanupCipher(cipher);
|
||||
result.ciphers.push(cipher);
|
||||
});
|
||||
|
||||
if (this.organization) {
|
||||
this.moveFoldersToCollections(result);
|
||||
}
|
||||
|
||||
result.success = true;
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
}
|
||||
276
common/src/importers/lastpassCsvImporter.ts
Normal file
276
common/src/importers/lastpassCsvImporter.ts
Normal file
@@ -0,0 +1,276 @@
|
||||
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 { FolderView } from '../models/view/folderView';
|
||||
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';
|
||||
|
||||
export class LastPassCsvImporter 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);
|
||||
}
|
||||
|
||||
results.forEach((value, index) => {
|
||||
const cipherIndex = result.ciphers.length;
|
||||
let folderIndex = result.folders.length;
|
||||
let grouping = value.grouping;
|
||||
if (grouping != null) {
|
||||
grouping = grouping.replace(/\\/g, '/').replace(/[\x00-\x1F\x7F-\x9F]/g, '');
|
||||
}
|
||||
const hasFolder = this.getValueOrDefault(grouping, '(none)') !== '(none)';
|
||||
let addFolder = hasFolder;
|
||||
|
||||
if (hasFolder) {
|
||||
for (let i = 0; i < result.folders.length; i++) {
|
||||
if (result.folders[i].name === grouping) {
|
||||
addFolder = false;
|
||||
folderIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const cipher = this.buildBaseCipher(value);
|
||||
if (cipher.type === CipherType.Login) {
|
||||
cipher.notes = this.getValueOrDefault(value.extra);
|
||||
cipher.login = new LoginView();
|
||||
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.totp);
|
||||
} else if (cipher.type === CipherType.SecureNote) {
|
||||
this.parseSecureNote(value, cipher);
|
||||
} else if (cipher.type === CipherType.Card) {
|
||||
cipher.card = this.parseCard(value);
|
||||
cipher.notes = this.getValueOrDefault(value.notes);
|
||||
} else if (cipher.type === CipherType.Identity) {
|
||||
cipher.identity = this.parseIdentity(value);
|
||||
cipher.notes = this.getValueOrDefault(value.notes);
|
||||
if (!this.isNullOrWhitespace(value.ccnum)) {
|
||||
// there is a card on this identity too
|
||||
const cardCipher = this.buildBaseCipher(value);
|
||||
cardCipher.identity = null;
|
||||
cardCipher.type = CipherType.Card;
|
||||
cardCipher.card = this.parseCard(value);
|
||||
result.ciphers.push(cardCipher);
|
||||
}
|
||||
}
|
||||
|
||||
result.ciphers.push(cipher);
|
||||
|
||||
if (addFolder) {
|
||||
const f = new FolderView();
|
||||
f.name = grouping;
|
||||
result.folders.push(f);
|
||||
}
|
||||
if (hasFolder) {
|
||||
result.folderRelationships.push([cipherIndex, folderIndex]);
|
||||
}
|
||||
});
|
||||
|
||||
if (this.organization) {
|
||||
this.moveFoldersToCollections(result);
|
||||
}
|
||||
|
||||
result.success = true;
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
|
||||
private buildBaseCipher(value: any) {
|
||||
const cipher = new CipherView();
|
||||
if (value.hasOwnProperty('profilename') && value.hasOwnProperty('profilelanguage')) {
|
||||
// form fill
|
||||
cipher.favorite = false;
|
||||
cipher.name = this.getValueOrDefault(value.profilename, '--');
|
||||
cipher.type = CipherType.Card;
|
||||
|
||||
if (!this.isNullOrWhitespace(value.title) || !this.isNullOrWhitespace(value.firstname) ||
|
||||
!this.isNullOrWhitespace(value.lastname) || !this.isNullOrWhitespace(value.address1) ||
|
||||
!this.isNullOrWhitespace(value.phone) || !this.isNullOrWhitespace(value.username) ||
|
||||
!this.isNullOrWhitespace(value.email)) {
|
||||
cipher.type = CipherType.Identity;
|
||||
}
|
||||
} else {
|
||||
// site or secure note
|
||||
cipher.favorite = !this.organization && this.getValueOrDefault(value.fav, '0') === '1';
|
||||
cipher.name = this.getValueOrDefault(value.name, '--');
|
||||
cipher.type = value.url === 'http://sn' ? CipherType.SecureNote : CipherType.Login;
|
||||
}
|
||||
return cipher;
|
||||
}
|
||||
|
||||
private parseCard(value: any): CardView {
|
||||
const card = new CardView();
|
||||
card.cardholderName = this.getValueOrDefault(value.ccname);
|
||||
card.number = this.getValueOrDefault(value.ccnum);
|
||||
card.code = this.getValueOrDefault(value.cccsc);
|
||||
card.brand = this.getCardBrand(value.ccnum);
|
||||
|
||||
if (!this.isNullOrWhitespace(value.ccexp) && value.ccexp.indexOf('-') > -1) {
|
||||
const ccexpParts = (value.ccexp as string).split('-');
|
||||
if (ccexpParts.length > 1) {
|
||||
card.expYear = ccexpParts[0];
|
||||
card.expMonth = ccexpParts[1];
|
||||
if (card.expMonth.length === 2 && card.expMonth[0] === '0') {
|
||||
card.expMonth = card.expMonth[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return card;
|
||||
}
|
||||
|
||||
private parseIdentity(value: any): IdentityView {
|
||||
const identity = new IdentityView();
|
||||
identity.title = this.getValueOrDefault(value.title);
|
||||
identity.firstName = this.getValueOrDefault(value.firstname);
|
||||
identity.middleName = this.getValueOrDefault(value.middlename);
|
||||
identity.lastName = this.getValueOrDefault(value.lastname);
|
||||
identity.username = this.getValueOrDefault(value.username);
|
||||
identity.company = this.getValueOrDefault(value.company);
|
||||
identity.ssn = this.getValueOrDefault(value.ssn);
|
||||
identity.address1 = this.getValueOrDefault(value.address1);
|
||||
identity.address2 = this.getValueOrDefault(value.address2);
|
||||
identity.address3 = this.getValueOrDefault(value.address3);
|
||||
identity.city = this.getValueOrDefault(value.city);
|
||||
identity.state = this.getValueOrDefault(value.state);
|
||||
identity.postalCode = this.getValueOrDefault(value.zip);
|
||||
identity.country = this.getValueOrDefault(value.country);
|
||||
identity.email = this.getValueOrDefault(value.email);
|
||||
identity.phone = this.getValueOrDefault(value.phone);
|
||||
|
||||
if (!this.isNullOrWhitespace(identity.title)) {
|
||||
identity.title = identity.title.charAt(0).toUpperCase() + identity.title.slice(1);
|
||||
}
|
||||
|
||||
return identity;
|
||||
}
|
||||
|
||||
private parseSecureNote(value: any, cipher: CipherView) {
|
||||
const extraParts = this.splitNewLine(value.extra);
|
||||
let processedNote = false;
|
||||
|
||||
if (extraParts.length) {
|
||||
const typeParts = extraParts[0].split(':');
|
||||
if (typeParts.length > 1 && typeParts[0] === 'NoteType' &&
|
||||
(typeParts[1] === 'Credit Card' || typeParts[1] === 'Address')) {
|
||||
if (typeParts[1] === 'Credit Card') {
|
||||
const mappedData = this.parseSecureNoteMapping<CardView>(cipher, extraParts, {
|
||||
'Number': 'number',
|
||||
'Name on Card': 'cardholderName',
|
||||
'Security Code': 'code',
|
||||
// LP provides date in a format like 'June,2020'
|
||||
// Store in expMonth, then parse and modify
|
||||
'Expiration Date': 'expMonth',
|
||||
});
|
||||
|
||||
if (this.isNullOrWhitespace(mappedData.expMonth) || mappedData.expMonth === ',') {
|
||||
// No expiration data
|
||||
mappedData.expMonth = undefined;
|
||||
} else {
|
||||
const [monthString, year] = mappedData.expMonth.split(',');
|
||||
// Parse month name into number
|
||||
if (!this.isNullOrWhitespace(monthString)) {
|
||||
const month = new Date(Date.parse(monthString.trim() + ' 1, 2012')).getMonth() + 1;
|
||||
if (isNaN(month)) {
|
||||
mappedData.expMonth = undefined;
|
||||
} else {
|
||||
mappedData.expMonth = month.toString();
|
||||
}
|
||||
} else {
|
||||
mappedData.expMonth = undefined;
|
||||
}
|
||||
if (!this.isNullOrWhitespace(year)) {
|
||||
mappedData.expYear = year;
|
||||
}
|
||||
}
|
||||
|
||||
cipher.type = CipherType.Card;
|
||||
cipher.card = mappedData;
|
||||
} else if (typeParts[1] === 'Address') {
|
||||
const mappedData = this.parseSecureNoteMapping<IdentityView>(cipher, extraParts, {
|
||||
'Title': 'title',
|
||||
'First Name': 'firstName',
|
||||
'Last Name': 'lastName',
|
||||
'Middle Name': 'middleName',
|
||||
'Company': 'company',
|
||||
'Address 1': 'address1',
|
||||
'Address 2': 'address2',
|
||||
'Address 3': 'address3',
|
||||
'City / Town': 'city',
|
||||
'State': 'state',
|
||||
'Zip / Postal Code': 'postalCode',
|
||||
'Country': 'country',
|
||||
'Email Address': 'email',
|
||||
'Username': 'username',
|
||||
});
|
||||
cipher.type = CipherType.Identity;
|
||||
cipher.identity = mappedData;
|
||||
}
|
||||
processedNote = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!processedNote) {
|
||||
cipher.secureNote = new SecureNoteView();
|
||||
cipher.secureNote.type = SecureNoteType.Generic;
|
||||
cipher.notes = this.getValueOrDefault(value.extra);
|
||||
}
|
||||
}
|
||||
|
||||
private parseSecureNoteMapping<T>(cipher: CipherView, extraParts: string[], map: any): T {
|
||||
const dataObj: any = {};
|
||||
|
||||
let processingNotes = false;
|
||||
extraParts.forEach(extraPart => {
|
||||
let key: string = null;
|
||||
let val: string = null;
|
||||
if (!processingNotes) {
|
||||
if (this.isNullOrWhitespace(extraPart)) {
|
||||
return;
|
||||
}
|
||||
const colonIndex = extraPart.indexOf(':');
|
||||
if (colonIndex === -1) {
|
||||
key = extraPart;
|
||||
} else {
|
||||
key = extraPart.substring(0, colonIndex);
|
||||
if (extraPart.length > colonIndex) {
|
||||
val = extraPart.substring(colonIndex + 1);
|
||||
}
|
||||
}
|
||||
if (this.isNullOrWhitespace(key) || this.isNullOrWhitespace(val) || key === 'NoteType') {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (processingNotes) {
|
||||
cipher.notes += ('\n' + extraPart);
|
||||
} else if (key === 'Notes') {
|
||||
if (!this.isNullOrWhitespace(cipher.notes)) {
|
||||
cipher.notes += ('\n' + val);
|
||||
} else {
|
||||
cipher.notes = val;
|
||||
}
|
||||
processingNotes = true;
|
||||
} else if (map.hasOwnProperty(key)) {
|
||||
dataObj[map[key]] = val;
|
||||
} else {
|
||||
this.processKvp(cipher, key, val);
|
||||
}
|
||||
});
|
||||
|
||||
return dataObj;
|
||||
}
|
||||
}
|
||||
31
common/src/importers/logMeOnceCsvImporter.ts
Normal file
31
common/src/importers/logMeOnceCsvImporter.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
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();
|
||||
const results = this.parseCsv(data, false);
|
||||
if (results == null) {
|
||||
result.success = false;
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
|
||||
results.forEach(value => {
|
||||
if (value.length < 4) {
|
||||
return;
|
||||
}
|
||||
const cipher = this.initLoginCipher();
|
||||
cipher.name = this.getValueOrDefault(value[0], '--');
|
||||
cipher.login.username = this.getValueOrDefault(value[2]);
|
||||
cipher.login.password = this.getValueOrDefault(value[3]);
|
||||
cipher.login.uris = this.makeUriArray(value[1]);
|
||||
this.cleanupCipher(cipher);
|
||||
result.ciphers.push(cipher);
|
||||
});
|
||||
|
||||
result.success = true;
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
}
|
||||
29
common/src/importers/meldiumCsvImporter.ts
Normal file
29
common/src/importers/meldiumCsvImporter.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
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();
|
||||
const results = this.parseCsv(data, true);
|
||||
if (results == null) {
|
||||
result.success = false;
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
|
||||
results.forEach(value => {
|
||||
const cipher = this.initLoginCipher();
|
||||
cipher.name = this.getValueOrDefault(value.DisplayName, '--');
|
||||
cipher.notes = this.getValueOrDefault(value.Notes);
|
||||
cipher.login.username = this.getValueOrDefault(value.UserName);
|
||||
cipher.login.password = this.getValueOrDefault(value.Password);
|
||||
cipher.login.uris = this.makeUriArray(value.Url);
|
||||
this.cleanupCipher(cipher);
|
||||
result.ciphers.push(cipher);
|
||||
});
|
||||
|
||||
result.success = true;
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
}
|
||||
62
common/src/importers/msecureCsvImporter.ts
Normal file
62
common/src/importers/msecureCsvImporter.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
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 { SecureNoteView } from '../models/view/secureNoteView';
|
||||
|
||||
export class MSecureCsvImporter extends BaseImporter implements Importer {
|
||||
parse(data: string): Promise<ImportResult> {
|
||||
const result = new ImportResult();
|
||||
const results = this.parseCsv(data, false);
|
||||
if (results == null) {
|
||||
result.success = false;
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
|
||||
results.forEach(value => {
|
||||
if (value.length < 3) {
|
||||
return;
|
||||
}
|
||||
|
||||
const folderName = this.getValueOrDefault(value[0], 'Unassigned') !== 'Unassigned' ? value[0] : null;
|
||||
this.processFolder(result, folderName);
|
||||
|
||||
const cipher = this.initLoginCipher();
|
||||
cipher.name = this.getValueOrDefault(value[2], '--');
|
||||
|
||||
if (value[1] === 'Web Logins' || value[1] === 'Login') {
|
||||
cipher.login.uris = this.makeUriArray(value[4]);
|
||||
cipher.login.username = this.getValueOrDefault(value[5]);
|
||||
cipher.login.password = this.getValueOrDefault(value[6]);
|
||||
cipher.notes = !this.isNullOrWhitespace(value[3]) ? value[3].split('\\n').join('\n') : null;
|
||||
} else if (value.length > 3) {
|
||||
cipher.type = CipherType.SecureNote;
|
||||
cipher.secureNote = new SecureNoteView();
|
||||
cipher.secureNote.type = SecureNoteType.Generic;
|
||||
for (let i = 3; i < value.length; i++) {
|
||||
if (!this.isNullOrWhitespace(value[i])) {
|
||||
cipher.notes += (value[i] + '\n');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.isNullOrWhitespace(value[1]) && cipher.type !== CipherType.Login) {
|
||||
cipher.name = value[1] + ': ' + cipher.name;
|
||||
}
|
||||
|
||||
this.cleanupCipher(cipher);
|
||||
result.ciphers.push(cipher);
|
||||
});
|
||||
|
||||
if (this.organization) {
|
||||
this.moveFoldersToCollections(result);
|
||||
}
|
||||
|
||||
result.success = true;
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
}
|
||||
76
common/src/importers/mykiCsvImporter.ts
Normal file
76
common/src/importers/mykiCsvImporter.ts
Normal file
@@ -0,0 +1,76 @@
|
||||
import { BaseImporter } from './baseImporter';
|
||||
import { Importer } from './importer';
|
||||
|
||||
import { CipherType } from '../enums/cipherType';
|
||||
import { SecureNoteType } from '../enums/secureNoteType';
|
||||
|
||||
import { CardView } from '../models/view/cardView';
|
||||
import { IdentityView } from '../models/view/identityView';
|
||||
import { SecureNoteView } from '../models/view/secureNoteView';
|
||||
|
||||
import { ImportResult } from '../models/domain/importResult';
|
||||
|
||||
export class MykiCsvImporter 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);
|
||||
}
|
||||
|
||||
results.forEach(value => {
|
||||
const cipher = this.initLoginCipher();
|
||||
cipher.name = this.getValueOrDefault(value.nickname, '--');
|
||||
cipher.notes = this.getValueOrDefault(value.additionalInfo);
|
||||
|
||||
if (value.url !== undefined) {
|
||||
// Accounts
|
||||
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);
|
||||
} else if (value.cardNumber !== undefined) {
|
||||
// Cards
|
||||
cipher.card = new CardView();
|
||||
cipher.type = CipherType.Card;
|
||||
cipher.card.cardholderName = this.getValueOrDefault(value.cardName);
|
||||
cipher.card.number = this.getValueOrDefault(value.cardNumber);
|
||||
cipher.card.brand = this.getCardBrand(cipher.card.number);
|
||||
cipher.card.expMonth = this.getValueOrDefault(value.exp_month);
|
||||
cipher.card.expYear = this.getValueOrDefault(value.exp_year);
|
||||
cipher.card.code = this.getValueOrDefault(value.cvv);
|
||||
} else if (value.firstName !== undefined) {
|
||||
// Identities
|
||||
cipher.identity = new IdentityView();
|
||||
cipher.type = CipherType.Identity;
|
||||
cipher.identity.title = this.getValueOrDefault(value.title);
|
||||
cipher.identity.firstName = this.getValueOrDefault(value.firstName);
|
||||
cipher.identity.middleName = this.getValueOrDefault(value.middleName);
|
||||
cipher.identity.lastName = this.getValueOrDefault(value.lastName);
|
||||
cipher.identity.phone = this.getValueOrDefault(value.number);
|
||||
cipher.identity.email = this.getValueOrDefault(value.email);
|
||||
cipher.identity.address1 = this.getValueOrDefault(value.firstAddressLine);
|
||||
cipher.identity.address2 = this.getValueOrDefault(value.secondAddressLine);
|
||||
cipher.identity.city = this.getValueOrDefault(value.city);
|
||||
cipher.identity.country = this.getValueOrDefault(value.country);
|
||||
cipher.identity.postalCode = this.getValueOrDefault(value.zipCode);
|
||||
} 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);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
this.cleanupCipher(cipher);
|
||||
result.ciphers.push(cipher);
|
||||
});
|
||||
|
||||
result.success = true;
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
}
|
||||
149
common/src/importers/nordpassCsvImporter.ts
Normal file
149
common/src/importers/nordpassCsvImporter.ts
Normal file
@@ -0,0 +1,149 @@
|
||||
import { BaseImporter } from './baseImporter';
|
||||
import { Importer } from './importer';
|
||||
|
||||
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';
|
||||
|
||||
type nodePassCsvParsed = {
|
||||
name: string;
|
||||
url: string;
|
||||
username: string;
|
||||
password: string;
|
||||
note: string;
|
||||
cardholdername: string;
|
||||
cardnumber: string;
|
||||
cvc: string;
|
||||
expirydate: string;
|
||||
zipcode: string;
|
||||
folder: string;
|
||||
full_name: string;
|
||||
phone_number: string;
|
||||
email: string;
|
||||
address1: string;
|
||||
address2: string;
|
||||
city: string;
|
||||
country: string;
|
||||
state: string;
|
||||
};
|
||||
|
||||
export class NordPassCsvImporter extends BaseImporter implements Importer {
|
||||
parse(data: string): Promise<ImportResult> {
|
||||
const result = new ImportResult();
|
||||
const results: nodePassCsvParsed[] = this.parseCsv(data, true);
|
||||
if (results == null) {
|
||||
result.success = false;
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
|
||||
results.forEach(record => {
|
||||
|
||||
const recordType = this.evaluateType(record);
|
||||
if (recordType === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.organization) {
|
||||
this.processFolder(result, record.folder);
|
||||
}
|
||||
|
||||
const cipher = new CipherView();
|
||||
cipher.name = this.getValueOrDefault(record.name, '--');
|
||||
cipher.notes = this.getValueOrDefault(record.note);
|
||||
|
||||
switch (recordType) {
|
||||
case CipherType.Login:
|
||||
cipher.type = CipherType.Login;
|
||||
cipher.login = new LoginView();
|
||||
cipher.login.username = this.getValueOrDefault(record.username);
|
||||
cipher.login.password = this.getValueOrDefault(record.password);
|
||||
cipher.login.uris = this.makeUriArray(record.url);
|
||||
break;
|
||||
case CipherType.Card:
|
||||
cipher.type = CipherType.Card;
|
||||
cipher.card.cardholderName = this.getValueOrDefault(record.cardholdername);
|
||||
cipher.card.number = this.getValueOrDefault(record.cardnumber);
|
||||
cipher.card.code = this.getValueOrDefault(record.cvc);
|
||||
cipher.card.brand = this.getCardBrand(cipher.card.number);
|
||||
this.setCardExpiration(cipher, record.expirydate);
|
||||
break;
|
||||
|
||||
case CipherType.Identity:
|
||||
cipher.type = CipherType.Identity;
|
||||
|
||||
this.processName(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);
|
||||
cipher.identity.state = this.getValueOrDefault(record.state);
|
||||
cipher.identity.postalCode = this.getValueOrDefault(record.zipcode);
|
||||
cipher.identity.country = this.getValueOrDefault(record.country);
|
||||
if (cipher.identity.country != null) {
|
||||
cipher.identity.country = cipher.identity.country.toUpperCase();
|
||||
}
|
||||
cipher.identity.email = this.getValueOrDefault(record.email);
|
||||
cipher.identity.phone = this.getValueOrDefault(record.phone_number);
|
||||
break;
|
||||
case CipherType.SecureNote:
|
||||
cipher.type = CipherType.SecureNote;
|
||||
cipher.secureNote.type = SecureNoteType.Generic;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
this.cleanupCipher(cipher);
|
||||
result.ciphers.push(cipher);
|
||||
});
|
||||
|
||||
if (this.organization) {
|
||||
this.moveFoldersToCollections(result);
|
||||
}
|
||||
|
||||
result.success = true;
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
|
||||
private evaluateType(record: nodePassCsvParsed): CipherType {
|
||||
|
||||
if (!this.isNullOrWhitespace(record.username)) {
|
||||
return CipherType.Login;
|
||||
}
|
||||
|
||||
if (!this.isNullOrWhitespace(record.cardnumber)) {
|
||||
return CipherType.Card;
|
||||
}
|
||||
|
||||
if (!this.isNullOrWhitespace(record.full_name)) {
|
||||
return CipherType.Identity;
|
||||
}
|
||||
|
||||
if (!this.isNullOrWhitespace(record.note)) {
|
||||
return CipherType.SecureNote;
|
||||
}
|
||||
|
||||
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(' ');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import { CipherView } from '../../models/view';
|
||||
|
||||
export class CipherImportContext {
|
||||
lowerProperty: string;
|
||||
constructor(public importRecord: any, public property: string, public cipher: CipherView) {
|
||||
this.lowerProperty = property.toLowerCase();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,248 @@
|
||||
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 { 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';
|
||||
|
||||
export class OnePassword1PifImporter extends BaseImporter implements Importer {
|
||||
result = new ImportResult();
|
||||
|
||||
parse(data: string): Promise<ImportResult> {
|
||||
data.split(this.newLineRegex).forEach(line => {
|
||||
if (this.isNullOrWhitespace(line) || line[0] !== '{') {
|
||||
return;
|
||||
}
|
||||
const item = JSON.parse(line);
|
||||
if (item.trashed === true) {
|
||||
return;
|
||||
}
|
||||
const cipher = this.initLoginCipher();
|
||||
|
||||
if (this.isNullOrWhitespace(item.hmac)) {
|
||||
this.processStandardItem(item, cipher);
|
||||
} else {
|
||||
this.processWinOpVaultItem(item, cipher);
|
||||
}
|
||||
|
||||
this.convertToNoteIfNeeded(cipher);
|
||||
this.cleanupCipher(cipher);
|
||||
this.result.ciphers.push(cipher);
|
||||
});
|
||||
|
||||
this.result.success = true;
|
||||
return Promise.resolve(this.result);
|
||||
}
|
||||
|
||||
private processWinOpVaultItem(item: any, cipher: CipherView) {
|
||||
if (item.overview != null) {
|
||||
cipher.name = this.getValueOrDefault(item.overview.title);
|
||||
if (item.overview.URLs != null) {
|
||||
const urls: string[] = [];
|
||||
item.overview.URLs.forEach((url: any) => {
|
||||
if (!this.isNullOrWhitespace(url.u)) {
|
||||
urls.push(url.u);
|
||||
}
|
||||
});
|
||||
cipher.login.uris = this.makeUriArray(urls);
|
||||
}
|
||||
}
|
||||
|
||||
if (item.details != null) {
|
||||
if (item.details.passwordHistory != null) {
|
||||
this.parsePasswordHistory(item.details.passwordHistory, cipher);
|
||||
}
|
||||
if (!this.isNullOrWhitespace(item.details.ccnum) || !this.isNullOrWhitespace(item.details.cvv)) {
|
||||
cipher.type = CipherType.Card;
|
||||
cipher.card = new CardView();
|
||||
} else if (!this.isNullOrWhitespace(item.details.firstname) ||
|
||||
!this.isNullOrWhitespace(item.details.address1)) {
|
||||
cipher.type = CipherType.Identity;
|
||||
cipher.identity = new IdentityView();
|
||||
}
|
||||
if (cipher.type === CipherType.Login && !this.isNullOrWhitespace(item.details.password)) {
|
||||
cipher.login.password = item.details.password;
|
||||
}
|
||||
if (!this.isNullOrWhitespace(item.details.notesPlain)) {
|
||||
cipher.notes = item.details.notesPlain.split(this.newLineRegex).join('\n') + '\n';
|
||||
}
|
||||
if (item.details.fields != null) {
|
||||
this.parseFields(item.details.fields, cipher, 'designation', 'value', 'name');
|
||||
}
|
||||
if (item.details.sections != null) {
|
||||
item.details.sections.forEach((section: any) => {
|
||||
if (section.fields != null) {
|
||||
this.parseFields(section.fields, cipher, 'n', 'v', 't');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private processStandardItem(item: any, cipher: CipherView) {
|
||||
cipher.favorite = item.openContents && item.openContents.faveIndex ? true : false;
|
||||
cipher.name = this.getValueOrDefault(item.title);
|
||||
|
||||
if (item.typeName === 'securenotes.SecureNote') {
|
||||
cipher.type = CipherType.SecureNote;
|
||||
cipher.secureNote = new SecureNoteView();
|
||||
cipher.secureNote.type = SecureNoteType.Generic;
|
||||
} else if (item.typeName === 'wallet.financial.CreditCard') {
|
||||
cipher.type = CipherType.Card;
|
||||
cipher.card = new CardView();
|
||||
} else if (item.typeName === 'identities.Identity') {
|
||||
cipher.type = CipherType.Identity;
|
||||
cipher.identity = new IdentityView();
|
||||
} else {
|
||||
cipher.login.uris = this.makeUriArray(item.location);
|
||||
}
|
||||
|
||||
if (item.secureContents != null) {
|
||||
if (item.secureContents.passwordHistory != null) {
|
||||
this.parsePasswordHistory(item.secureContents.passwordHistory, cipher);
|
||||
}
|
||||
if (!this.isNullOrWhitespace(item.secureContents.notesPlain)) {
|
||||
cipher.notes = item.secureContents.notesPlain.split(this.newLineRegex).join('\n') + '\n';
|
||||
}
|
||||
if (cipher.type === CipherType.Login) {
|
||||
if (!this.isNullOrWhitespace(item.secureContents.password)) {
|
||||
cipher.login.password = item.secureContents.password;
|
||||
}
|
||||
if (item.secureContents.URLs != null) {
|
||||
const urls: string[] = [];
|
||||
item.secureContents.URLs.forEach((u: any) => {
|
||||
if (!this.isNullOrWhitespace(u.url)) {
|
||||
urls.push(u.url);
|
||||
}
|
||||
});
|
||||
if (urls.length > 0) {
|
||||
cipher.login.uris = this.makeUriArray(urls);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (item.secureContents.fields != null) {
|
||||
this.parseFields(item.secureContents.fields, cipher, 'designation', 'value', 'name');
|
||||
}
|
||||
if (item.secureContents.sections != null) {
|
||||
item.secureContents.sections.forEach((section: any) => {
|
||||
if (section.fields != null) {
|
||||
this.parseFields(section.fields, cipher, 'n', 'v', 't');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private parsePasswordHistory(items: any[], cipher: CipherView) {
|
||||
const maxSize = items.length > 5 ? 5 : items.length;
|
||||
cipher.passwordHistory = items
|
||||
.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;
|
||||
});
|
||||
}
|
||||
|
||||
private parseFields(fields: any[], cipher: CipherView, designationKey: string, valueKey: string, nameKey: string) {
|
||||
fields.forEach((field: any) => {
|
||||
if (field[valueKey] == null || field[valueKey].toString().trim() === '') {
|
||||
return;
|
||||
}
|
||||
|
||||
const fieldValue = field[valueKey].toString();
|
||||
const fieldDesignation = field[designationKey] != null ? field[designationKey].toString() : null;
|
||||
|
||||
if (cipher.type === CipherType.Login) {
|
||||
if (this.isNullOrWhitespace(cipher.login.username) && fieldDesignation === 'username') {
|
||||
cipher.login.username = fieldValue;
|
||||
return;
|
||||
} else if (this.isNullOrWhitespace(cipher.login.password) && fieldDesignation === 'password') {
|
||||
cipher.login.password = fieldValue;
|
||||
return;
|
||||
} else if (this.isNullOrWhitespace(cipher.login.totp) && fieldDesignation != null &&
|
||||
fieldDesignation.startsWith('TOTP_')) {
|
||||
cipher.login.totp = fieldValue;
|
||||
return;
|
||||
}
|
||||
} else if (cipher.type === CipherType.Card) {
|
||||
if (this.isNullOrWhitespace(cipher.card.number) && fieldDesignation === 'ccnum') {
|
||||
cipher.card.number = fieldValue;
|
||||
cipher.card.brand = this.getCardBrand(fieldValue);
|
||||
return;
|
||||
} else if (this.isNullOrWhitespace(cipher.card.code) && fieldDesignation === 'cvv') {
|
||||
cipher.card.code = fieldValue;
|
||||
return;
|
||||
} else if (this.isNullOrWhitespace(cipher.card.cardholderName) && fieldDesignation === 'cardholder') {
|
||||
cipher.card.cardholderName = fieldValue;
|
||||
return;
|
||||
} else if (this.isNullOrWhitespace(cipher.card.expiration) && fieldDesignation === 'expiry' &&
|
||||
fieldValue.length === 6) {
|
||||
cipher.card.expMonth = (fieldValue as string).substr(4, 2);
|
||||
if (cipher.card.expMonth[0] === '0') {
|
||||
cipher.card.expMonth = cipher.card.expMonth.substr(1, 1);
|
||||
}
|
||||
cipher.card.expYear = (fieldValue as string).substr(0, 4);
|
||||
return;
|
||||
} else if (fieldDesignation === 'type') {
|
||||
// Skip since brand was determined from number above
|
||||
return;
|
||||
}
|
||||
} else if (cipher.type === CipherType.Identity) {
|
||||
const identity = cipher.identity;
|
||||
if (this.isNullOrWhitespace(identity.firstName) && fieldDesignation === 'firstname') {
|
||||
identity.firstName = fieldValue;
|
||||
return;
|
||||
} else if (this.isNullOrWhitespace(identity.lastName) && fieldDesignation === 'lastname') {
|
||||
identity.lastName = fieldValue;
|
||||
return;
|
||||
} else if (this.isNullOrWhitespace(identity.middleName) && fieldDesignation === 'initial') {
|
||||
identity.middleName = fieldValue;
|
||||
return;
|
||||
} else if (this.isNullOrWhitespace(identity.phone) && fieldDesignation === 'defphone') {
|
||||
identity.phone = fieldValue;
|
||||
return;
|
||||
} else if (this.isNullOrWhitespace(identity.company) && fieldDesignation === 'company') {
|
||||
identity.company = fieldValue;
|
||||
return;
|
||||
} else if (this.isNullOrWhitespace(identity.email) && fieldDesignation === 'email') {
|
||||
identity.email = fieldValue;
|
||||
return;
|
||||
} else if (this.isNullOrWhitespace(identity.username) && fieldDesignation === 'username') {
|
||||
identity.username = fieldValue;
|
||||
return;
|
||||
} else if (fieldDesignation === 'address') {
|
||||
// fieldValue is an object casted into a string, so access the plain value instead
|
||||
const { street, city, country, zip } = field[valueKey];
|
||||
identity.address1 = this.getValueOrDefault(street);
|
||||
identity.city = this.getValueOrDefault(city);
|
||||
if (!this.isNullOrWhitespace(country)) {
|
||||
identity.country = country.toUpperCase();
|
||||
}
|
||||
identity.postalCode = this.getValueOrDefault(zip);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const fieldName = this.isNullOrWhitespace(field[nameKey]) ? 'no_name' : field[nameKey];
|
||||
if (fieldName === 'password' && cipher.passwordHistory != null &&
|
||||
cipher.passwordHistory.some(h => h.password === fieldValue)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const fieldType = field.k === 'concealed' ? FieldType.Hidden : FieldType.Text;
|
||||
this.processKvp(cipher, fieldName, fieldValue, fieldType);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,288 @@
|
||||
import { ImportResult } from '../../models/domain/importResult';
|
||||
import { BaseImporter } from '../baseImporter';
|
||||
import { Importer } from '../importer';
|
||||
|
||||
import { CipherType } from '../../enums/cipherType';
|
||||
import { FieldType } from '../../enums/fieldType';
|
||||
import { CipherView } from '../../models/view';
|
||||
import { CipherImportContext } from './cipherImportContext';
|
||||
|
||||
export const IgnoredProperties = ['ainfo', 'autosubmit', 'notesplain', 'ps', 'scope', 'tags', 'title', 'uuid', 'notes'];
|
||||
|
||||
export abstract class OnePasswordCsvImporter extends BaseImporter implements Importer {
|
||||
protected loginPropertyParsers = [this.setLoginUsername, this.setLoginPassword, this.setLoginUris];
|
||||
protected creditCardPropertyParsers = [this.setCreditCardNumber, this.setCreditCardVerification, this.setCreditCardCardholderName, this.setCreditCardExpiry];
|
||||
protected identityPropertyParsers = [this.setIdentityFirstName, this.setIdentityInitial, this.setIdentityLastName, this.setIdentityUserName, this.setIdentityEmail, this.setIdentityPhone, this.setIdentityCompany];
|
||||
|
||||
abstract setCipherType(value: any, cipher: CipherView): void;
|
||||
|
||||
parse(data: string): Promise<ImportResult> {
|
||||
const result = new ImportResult();
|
||||
const results = this.parseCsv(data, true, {
|
||||
quoteChar: '"',
|
||||
escapeChar: '\\',
|
||||
});
|
||||
if (results == null) {
|
||||
result.success = false;
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
|
||||
results.forEach(value => {
|
||||
if (this.isNullOrWhitespace(this.getProp(value, 'title'))) {
|
||||
return;
|
||||
}
|
||||
|
||||
const cipher = this.initLoginCipher();
|
||||
cipher.name = this.getValueOrDefault(this.getProp(value, 'title'), '--');
|
||||
|
||||
this.setNotes(value, cipher);
|
||||
|
||||
this.setCipherType(value, cipher);
|
||||
|
||||
let altUsername: string = null;
|
||||
for (const property in value) {
|
||||
if (!value.hasOwnProperty(property) || this.isNullOrWhitespace(value[property])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const context = new CipherImportContext(value, property, cipher);
|
||||
if (cipher.type === CipherType.Login && this.setKnownLoginValue(context)) {
|
||||
continue;
|
||||
} else if (cipher.type === CipherType.Card && this.setKnownCreditCardValue(context)) {
|
||||
continue;
|
||||
} else if (cipher.type === CipherType.Identity && this.setKnownIdentityValue(context)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
altUsername = this.setUnknownValue(context, altUsername);
|
||||
}
|
||||
|
||||
if (cipher.type === CipherType.Login && !this.isNullOrWhitespace(altUsername) &&
|
||||
this.isNullOrWhitespace(cipher.login.username) && altUsername.indexOf('://') === -1) {
|
||||
cipher.login.username = altUsername;
|
||||
}
|
||||
|
||||
this.convertToNoteIfNeeded(cipher);
|
||||
this.cleanupCipher(cipher);
|
||||
result.ciphers.push(cipher);
|
||||
});
|
||||
|
||||
result.success = true;
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
|
||||
protected getProp(obj: any, name: string): any {
|
||||
const lowerObj = Object.entries(obj).reduce((agg: any, entry: [string, any]) => {
|
||||
agg[entry[0].toLowerCase()] = entry[1];
|
||||
return agg;
|
||||
}, {});
|
||||
return lowerObj[name.toLowerCase()];
|
||||
}
|
||||
|
||||
protected getPropByRegexp(obj: any, regexp: RegExp): any {
|
||||
const matchingKeys = Object.keys(obj).reduce((agg: string[], key: string) => {
|
||||
if (key.match(regexp)) {
|
||||
agg.push(key);
|
||||
}
|
||||
return agg;
|
||||
}, []);
|
||||
if (matchingKeys.length === 0) {
|
||||
return null;
|
||||
} else {
|
||||
return obj[matchingKeys[0]];
|
||||
}
|
||||
}
|
||||
|
||||
protected getPropIncluding(obj: any, name: string): any {
|
||||
const includesMap = Object.keys(obj).reduce((agg: string[], entry: string) => {
|
||||
if (entry.toLowerCase().includes(name.toLowerCase())) {
|
||||
agg.push(entry);
|
||||
}
|
||||
return agg;
|
||||
}, []);
|
||||
if (includesMap.length === 0) {
|
||||
return null;
|
||||
} else {
|
||||
return obj[includesMap[0]];
|
||||
}
|
||||
}
|
||||
|
||||
protected setNotes(importRecord: any, cipher: CipherView) {
|
||||
cipher.notes = this.getValueOrDefault(this.getProp(importRecord, 'notesPlain'), '') + '\n' +
|
||||
this.getValueOrDefault(this.getProp(importRecord, 'notes'), '') + '\n';
|
||||
cipher.notes.trim();
|
||||
|
||||
}
|
||||
|
||||
protected setKnownLoginValue(context: CipherImportContext): boolean {
|
||||
return this.loginPropertyParsers.reduce((agg: boolean, func) => {
|
||||
if (!agg) {
|
||||
agg = func.bind(this)(context);
|
||||
}
|
||||
return agg;
|
||||
}, false);
|
||||
}
|
||||
|
||||
protected setKnownCreditCardValue(context: CipherImportContext): boolean {
|
||||
return this.creditCardPropertyParsers.reduce((agg: boolean, func) => {
|
||||
if (!agg) {
|
||||
agg = func.bind(this)(context);
|
||||
}
|
||||
return agg;
|
||||
}, false);
|
||||
}
|
||||
|
||||
protected setKnownIdentityValue(context: CipherImportContext): boolean {
|
||||
return this.identityPropertyParsers.reduce((agg: boolean, func) => {
|
||||
if (!agg) {
|
||||
agg = func.bind(this)(context);
|
||||
}
|
||||
return agg;
|
||||
}, false);
|
||||
}
|
||||
|
||||
protected setUnknownValue(context: CipherImportContext, altUsername: string): string {
|
||||
if (IgnoredProperties.indexOf(context.lowerProperty) === -1 && !context.lowerProperty.startsWith('section:') &&
|
||||
!context.lowerProperty.startsWith('section ')) {
|
||||
if (altUsername == null && context.lowerProperty === 'email') {
|
||||
return context.importRecord[context.property];
|
||||
}
|
||||
else if (context.lowerProperty === 'created date' || context.lowerProperty === 'modified date') {
|
||||
const readableDate = new Date(parseInt(context.importRecord[context.property], 10) * 1000).toUTCString();
|
||||
this.processKvp(context.cipher, '1Password ' + context.property, readableDate);
|
||||
return null;
|
||||
}
|
||||
if (context.lowerProperty.includes('password') || context.lowerProperty.includes('key') || context.lowerProperty.includes('secret')) {
|
||||
this.processKvp(context.cipher, context.property, context.importRecord[context.property], FieldType.Hidden);
|
||||
} else {
|
||||
this.processKvp(context.cipher, context.property, context.importRecord[context.property]);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected setIdentityFirstName(context: CipherImportContext) {
|
||||
if (this.isNullOrWhitespace(context.cipher.identity.firstName) && context.lowerProperty.includes('first name')) {
|
||||
context.cipher.identity.firstName = context.importRecord[context.property];
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected setIdentityInitial(context: CipherImportContext) {
|
||||
if (this.isNullOrWhitespace(context.cipher.identity.middleName) && context.lowerProperty.includes('initial')) {
|
||||
context.cipher.identity.middleName = context.importRecord[context.property];
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected setIdentityLastName(context: CipherImportContext) {
|
||||
if (this.isNullOrWhitespace(context.cipher.identity.lastName) && context.lowerProperty.includes('last name')) {
|
||||
context.cipher.identity.lastName = context.importRecord[context.property];
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected setIdentityUserName(context: CipherImportContext) {
|
||||
if (this.isNullOrWhitespace(context.cipher.identity.username) && context.lowerProperty.includes('username')) {
|
||||
context.cipher.identity.username = context.importRecord[context.property];
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected setIdentityCompany(context: CipherImportContext) {
|
||||
if (this.isNullOrWhitespace(context.cipher.identity.company) && context.lowerProperty.includes('company')) {
|
||||
context.cipher.identity.company = context.importRecord[context.property];
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected setIdentityPhone(context: CipherImportContext) {
|
||||
if (this.isNullOrWhitespace(context.cipher.identity.phone) && context.lowerProperty.includes('default phone')) {
|
||||
context.cipher.identity.phone = context.importRecord[context.property];
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected setIdentityEmail(context: CipherImportContext) {
|
||||
if (this.isNullOrWhitespace(context.cipher.identity.email) && context.lowerProperty.includes('email')) {
|
||||
context.cipher.identity.email = context.importRecord[context.property];
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected setCreditCardNumber(context: CipherImportContext): boolean {
|
||||
if (this.isNullOrWhitespace(context.cipher.card.number) && context.lowerProperty.includes('number')) {
|
||||
context.cipher.card.number = context.importRecord[context.property];
|
||||
context.cipher.card.brand = this.getCardBrand(context.cipher.card.number);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected setCreditCardVerification(context: CipherImportContext) {
|
||||
if (this.isNullOrWhitespace(context.cipher.card.code) && context.lowerProperty.includes('verification number')) {
|
||||
context.cipher.card.code = context.importRecord[context.property];
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected setCreditCardCardholderName(context: CipherImportContext) {
|
||||
if (this.isNullOrWhitespace(context.cipher.card.cardholderName) && context.lowerProperty.includes('cardholder name')) {
|
||||
context.cipher.card.cardholderName = context.importRecord[context.property];
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected setCreditCardExpiry(context: CipherImportContext) {
|
||||
if (this.isNullOrWhitespace(context.cipher.card.expiration) && context.lowerProperty.includes('expiry date') &&
|
||||
context.importRecord[context.property].length === 7) {
|
||||
context.cipher.card.expMonth = (context.importRecord[context.property] as string).substr(0, 2);
|
||||
if (context.cipher.card.expMonth[0] === '0') {
|
||||
context.cipher.card.expMonth = context.cipher.card.expMonth.substr(1, 1);
|
||||
}
|
||||
context.cipher.card.expYear = (context.importRecord[context.property] as string).substr(3, 4);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected setLoginPassword(context: CipherImportContext) {
|
||||
if (this.isNullOrWhitespace(context.cipher.login.password) && context.lowerProperty === 'password') {
|
||||
context.cipher.login.password = context.importRecord[context.property];
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected setLoginUsername(context: CipherImportContext) {
|
||||
if (this.isNullOrWhitespace(context.cipher.login.username) && context.lowerProperty === 'username') {
|
||||
context.cipher.login.username = context.importRecord[context.property];
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected setLoginUris(context: CipherImportContext) {
|
||||
if ((context.cipher.login.uris == null || context.cipher.login.uris.length === 0) && context.lowerProperty === 'urls') {
|
||||
const urls = context.importRecord[context.property].split(this.newLineRegex);
|
||||
context.cipher.login.uris = this.makeUriArray(urls);
|
||||
return true;
|
||||
} else if ((context.lowerProperty === 'url')) {
|
||||
if (context.cipher.login.uris == null) {
|
||||
context.cipher.login.uris = [];
|
||||
}
|
||||
context.cipher.login.uris.concat(this.makeUriArray(context.importRecord[context.property]));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import { Importer } from '../importer';
|
||||
import { IgnoredProperties, OnePasswordCsvImporter } from './onepasswordCsvImporter';
|
||||
|
||||
import { CipherType } from '../../enums/cipherType';
|
||||
import { CardView, CipherView, IdentityView } from '../../models/view';
|
||||
|
||||
export class OnePasswordMacCsvImporter extends OnePasswordCsvImporter implements Importer {
|
||||
setCipherType(value: any, cipher: CipherView) {
|
||||
const onePassType = this.getValueOrDefault(this.getProp(value, 'type'), 'Login');
|
||||
switch (onePassType) {
|
||||
case 'Credit Card':
|
||||
cipher.type = CipherType.Card;
|
||||
cipher.card = new CardView();
|
||||
IgnoredProperties.push('type');
|
||||
break;
|
||||
case 'Identity':
|
||||
cipher.type = CipherType.Identity;
|
||||
cipher.identity = new IdentityView();
|
||||
IgnoredProperties.push('type');
|
||||
break;
|
||||
case 'Login':
|
||||
case 'Secure Note':
|
||||
IgnoredProperties.push('type');
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user