diff --git a/jslib/angular/src/services/jslib-services.module.ts b/jslib/angular/src/services/jslib-services.module.ts index be8c7b32..53a4e82d 100644 --- a/jslib/angular/src/services/jslib-services.module.ts +++ b/jslib/angular/src/services/jslib-services.module.ts @@ -1,10 +1,9 @@ -import { Injector, LOCALE_ID, NgModule } from "@angular/core"; +import { LOCALE_ID, NgModule } from "@angular/core"; import { ApiService as ApiServiceAbstraction } from "@/jslib/common/src/abstractions/api.service"; import { AppIdService as AppIdServiceAbstraction } from "@/jslib/common/src/abstractions/appId.service"; import { AuthService as AuthServiceAbstraction } from "@/jslib/common/src/abstractions/auth.service"; import { BroadcasterService as BroadcasterServiceAbstraction } from "@/jslib/common/src/abstractions/broadcaster.service"; -import { CipherService as CipherServiceAbstraction } from "@/jslib/common/src/abstractions/cipher.service"; import { CollectionService as CollectionServiceAbstraction } from "@/jslib/common/src/abstractions/collection.service"; import { CryptoService as CryptoServiceAbstraction } from "@/jslib/common/src/abstractions/crypto.service"; import { CryptoFunctionService as CryptoFunctionServiceAbstraction } from "@/jslib/common/src/abstractions/cryptoFunction.service"; @@ -39,7 +38,6 @@ import { GlobalState } from "@/jslib/common/src/models/domain/globalState"; import { ApiService } from "@/jslib/common/src/services/api.service"; import { AppIdService } from "@/jslib/common/src/services/appId.service"; import { AuthService } from "@/jslib/common/src/services/auth.service"; -import { CipherService } from "@/jslib/common/src/services/cipher.service"; import { CollectionService } from "@/jslib/common/src/services/collection.service"; import { ConsoleLogService } from "@/jslib/common/src/services/consoleLog.service"; import { CryptoService } from "@/jslib/common/src/services/crypto.service"; @@ -108,39 +106,6 @@ import { ValidationService } from "./validation.service"; I18nServiceAbstraction, ], }, - { - provide: CipherServiceAbstraction, - useFactory: ( - cryptoService: CryptoServiceAbstraction, - settingsService: SettingsServiceAbstraction, - apiService: ApiServiceAbstraction, - fileUploadService: FileUploadServiceAbstraction, - i18nService: I18nServiceAbstraction, - injector: Injector, - logService: LogService, - stateService: StateServiceAbstraction, - ) => - new CipherService( - cryptoService, - settingsService, - apiService, - fileUploadService, - i18nService, - () => injector.get(SearchServiceAbstraction), - logService, - stateService, - ), - deps: [ - CryptoServiceAbstraction, - SettingsServiceAbstraction, - ApiServiceAbstraction, - FileUploadServiceAbstraction, - I18nServiceAbstraction, - Injector, // TODO: Get rid of this circular dependency! - LogService, - StateServiceAbstraction, - ], - }, { provide: FolderServiceAbstraction, useClass: FolderService, @@ -148,7 +113,6 @@ import { ValidationService } from "./validation.service"; CryptoServiceAbstraction, ApiServiceAbstraction, I18nServiceAbstraction, - CipherServiceAbstraction, StateServiceAbstraction, ], }, @@ -206,7 +170,7 @@ import { ValidationService } from "./validation.service"; { provide: FileUploadServiceAbstraction, useClass: FileUploadService, - deps: [LogServiceAbstraction, ApiServiceAbstraction], + deps: [LogService, ApiServiceAbstraction], }, { provide: SyncServiceAbstraction, @@ -214,7 +178,6 @@ import { ValidationService } from "./validation.service"; apiService: ApiServiceAbstraction, settingsService: SettingsServiceAbstraction, folderService: FolderServiceAbstraction, - cipherService: CipherServiceAbstraction, cryptoService: CryptoServiceAbstraction, collectionService: CollectionServiceAbstraction, messagingService: MessagingServiceAbstraction, @@ -229,7 +192,6 @@ import { ValidationService } from "./validation.service"; apiService, settingsService, folderService, - cipherService, cryptoService, collectionService, messagingService, @@ -245,7 +207,6 @@ import { ValidationService } from "./validation.service"; ApiServiceAbstraction, SettingsServiceAbstraction, FolderServiceAbstraction, - CipherServiceAbstraction, CryptoServiceAbstraction, CollectionServiceAbstraction, MessagingServiceAbstraction, @@ -266,7 +227,6 @@ import { ValidationService } from "./validation.service"; { provide: VaultTimeoutServiceAbstraction, useFactory: ( - cipherService: CipherServiceAbstraction, folderService: FolderServiceAbstraction, collectionService: CollectionServiceAbstraction, cryptoService: CryptoServiceAbstraction, @@ -279,7 +239,6 @@ import { ValidationService } from "./validation.service"; stateService: StateServiceAbstraction, ) => new VaultTimeoutService( - cipherService, folderService, collectionService, cryptoService, @@ -295,7 +254,6 @@ import { ValidationService } from "./validation.service"; messagingService.send("logout", { expired: false, userId: userId }), ), deps: [ - CipherServiceAbstraction, FolderServiceAbstraction, CollectionServiceAbstraction, CryptoServiceAbstraction, @@ -346,7 +304,7 @@ import { ValidationService } from "./validation.service"; { provide: SearchServiceAbstraction, useClass: SearchService, - deps: [CipherServiceAbstraction, LogService, I18nServiceAbstraction], + deps: [LogService, I18nServiceAbstraction], }, { provide: NotificationsServiceAbstraction, @@ -386,7 +344,6 @@ import { ValidationService } from "./validation.service"; useClass: EventService, deps: [ ApiServiceAbstraction, - CipherServiceAbstraction, StateServiceAbstraction, LogService, OrganizationServiceAbstraction, diff --git a/jslib/common/spec/services/cipher.service.spec.ts b/jslib/common/spec/services/cipher.service.spec.ts deleted file mode 100644 index 5ab62d4f..00000000 --- a/jslib/common/spec/services/cipher.service.spec.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { Arg, Substitute, SubstituteOf } from "@fluffy-spoon/substitute"; - -import { ApiService } from "@/jslib/common/src/abstractions/api.service"; -import { CryptoService } from "@/jslib/common/src/abstractions/crypto.service"; -import { FileUploadService } from "@/jslib/common/src/abstractions/fileUpload.service"; -import { I18nService } from "@/jslib/common/src/abstractions/i18n.service"; -import { LogService } from "@/jslib/common/src/abstractions/log.service"; -import { SearchService } from "@/jslib/common/src/abstractions/search.service"; -import { SettingsService } from "@/jslib/common/src/abstractions/settings.service"; -import { StateService } from "@/jslib/common/src/abstractions/state.service"; -import { Utils } from "@/jslib/common/src/misc/utils"; -import { Cipher } from "@/jslib/common/src/models/domain/cipher"; -import { EncArrayBuffer } from "@/jslib/common/src/models/domain/encArrayBuffer"; -import { EncString } from "@/jslib/common/src/models/domain/encString"; -import { SymmetricCryptoKey } from "@/jslib/common/src/models/domain/symmetricCryptoKey"; -import { CipherService } from "@/jslib/common/src/services/cipher.service"; - -const ENCRYPTED_TEXT = "This data has been encrypted"; -const ENCRYPTED_BYTES = new EncArrayBuffer(Utils.fromUtf8ToArray(ENCRYPTED_TEXT).buffer); - -describe("Cipher Service", () => { - let cryptoService: SubstituteOf; - let stateService: SubstituteOf; - let settingsService: SubstituteOf; - let apiService: SubstituteOf; - let fileUploadService: SubstituteOf; - let i18nService: SubstituteOf; - let searchService: SubstituteOf; - let logService: SubstituteOf; - - let cipherService: CipherService; - - beforeEach(() => { - cryptoService = Substitute.for(); - stateService = Substitute.for(); - settingsService = Substitute.for(); - apiService = Substitute.for(); - fileUploadService = Substitute.for(); - i18nService = Substitute.for(); - searchService = Substitute.for(); - logService = Substitute.for(); - - cryptoService.encryptToBytes(Arg.any(), Arg.any()).resolves(ENCRYPTED_BYTES); - cryptoService.encrypt(Arg.any(), Arg.any()).resolves(new EncString(ENCRYPTED_TEXT)); - - cipherService = new CipherService( - cryptoService, - settingsService, - apiService, - fileUploadService, - i18nService, - () => searchService, - logService, - stateService, - ); - }); - - it("attachments upload encrypted file contents", async () => { - const fileName = "filename"; - const fileData = new Uint8Array(10).buffer; - cryptoService.getOrgKey(Arg.any()).resolves(new SymmetricCryptoKey(new Uint8Array(32).buffer)); - - await cipherService.saveAttachmentRawWithServer(new Cipher(), fileName, fileData); - - fileUploadService - .received(1) - .uploadCipherAttachment(Arg.any(), Arg.any(), new EncString(ENCRYPTED_TEXT), ENCRYPTED_BYTES); - }); -}); diff --git a/jslib/common/src/abstractions/cipher.service.ts b/jslib/common/src/abstractions/cipher.service.ts deleted file mode 100644 index 5ae5a006..00000000 --- a/jslib/common/src/abstractions/cipher.service.ts +++ /dev/null @@ -1,79 +0,0 @@ -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 { - clearCache: (userId?: string) => Promise; - encrypt: ( - model: CipherView, - key?: SymmetricCryptoKey, - originalCipher?: Cipher, - ) => Promise; - encryptFields: (fieldsModel: FieldView[], key: SymmetricCryptoKey) => Promise; - encryptField: (fieldModel: FieldView, key: SymmetricCryptoKey) => Promise; - get: (id: string) => Promise; - getAll: () => Promise; - getAllDecrypted: () => Promise; - getAllDecryptedForGrouping: (groupingId: string, folder?: boolean) => Promise; - getAllDecryptedForUrl: ( - url: string, - includeOtherTypes?: CipherType[], - defaultMatch?: UriMatchType, - ) => Promise; - getAllFromApiForOrganization: (organizationId: string) => Promise; - getLastUsedForUrl: (url: string, autofillOnPageLoad: boolean) => Promise; - getLastLaunchedForUrl: (url: string, autofillOnPageLoad: boolean) => Promise; - getNextCipherForUrl: (url: string) => Promise; - updateLastUsedIndexForUrl: (url: string) => void; - updateLastUsedDate: (id: string) => Promise; - updateLastLaunchedDate: (id: string) => Promise; - saveNeverDomain: (domain: string) => Promise; - saveWithServer: (cipher: Cipher) => Promise; - shareWithServer: ( - cipher: CipherView, - organizationId: string, - collectionIds: string[], - ) => Promise; - shareManyWithServer: ( - ciphers: CipherView[], - organizationId: string, - collectionIds: string[], - ) => Promise; - saveAttachmentWithServer: ( - cipher: Cipher, - unencryptedFile: any, - admin?: boolean, - ) => Promise; - saveAttachmentRawWithServer: ( - cipher: Cipher, - filename: string, - data: ArrayBuffer, - admin?: boolean, - ) => Promise; - saveCollectionsWithServer: (cipher: Cipher) => Promise; - upsert: (cipher: CipherData | CipherData[]) => Promise; - replace: (ciphers: { [id: string]: CipherData }) => Promise; - clear: (userId: string) => Promise; - moveManyWithServer: (ids: string[], folderId: string) => Promise; - delete: (id: string | string[]) => Promise; - deleteWithServer: (id: string) => Promise; - deleteManyWithServer: (ids: string[]) => Promise; - deleteAttachment: (id: string, attachmentId: string) => Promise; - deleteAttachmentWithServer: (id: string, attachmentId: string) => Promise; - sortCiphersByLastUsed: (a: any, b: any) => number; - sortCiphersByLastUsedThenName: (a: any, b: any) => number; - getLocaleSortingFunction: () => (a: CipherView, b: CipherView) => number; - softDelete: (id: string | string[]) => Promise; - softDeleteWithServer: (id: string) => Promise; - softDeleteManyWithServer: (ids: string[]) => Promise; - restore: ( - cipher: { id: string; revisionDate: string } | { id: string; revisionDate: string }[], - ) => Promise; - restoreWithServer: (id: string) => Promise; - restoreManyWithServer: (ids: string[]) => Promise; -} diff --git a/jslib/common/src/abstractions/sync.service.ts b/jslib/common/src/abstractions/sync.service.ts index 9c6923de..7fcea1cf 100644 --- a/jslib/common/src/abstractions/sync.service.ts +++ b/jslib/common/src/abstractions/sync.service.ts @@ -1,5 +1,4 @@ import { - SyncCipherNotification, SyncFolderNotification, } from "../models/response/notificationResponse"; @@ -11,6 +10,4 @@ export abstract class SyncService { fullSync: (forceSync: boolean, allowThrowOnError?: boolean) => Promise; syncUpsertFolder: (notification: SyncFolderNotification, isEdit: boolean) => Promise; syncDeleteFolder: (notification: SyncFolderNotification) => Promise; - syncUpsertCipher: (notification: SyncCipherNotification, isEdit: boolean) => Promise; - syncDeleteCipher: (notification: SyncFolderNotification) => Promise; } diff --git a/jslib/common/src/services/cipher.service.ts b/jslib/common/src/services/cipher.service.ts deleted file mode 100644 index 876d2d35..00000000 --- a/jslib/common/src/services/cipher.service.ts +++ /dev/null @@ -1,1282 +0,0 @@ -import { ApiService } from "../abstractions/api.service"; -import { CipherService as CipherServiceAbstraction } from "../abstractions/cipher.service"; -import { CryptoService } from "../abstractions/crypto.service"; -import { FileUploadService } from "../abstractions/fileUpload.service"; -import { I18nService } from "../abstractions/i18n.service"; -import { LogService } from "../abstractions/log.service"; -import { SearchService } from "../abstractions/search.service"; -import { SettingsService } from "../abstractions/settings.service"; -import { StateService } from "../abstractions/state.service"; -import { CipherType } from "../enums/cipherType"; -import { FieldType } from "../enums/fieldType"; -import { UriMatchType } from "../enums/uriMatchType"; -import { sequentialize } from "../misc/sequentialize"; -import { Utils } from "../misc/utils"; -import { CipherData } from "../models/data/cipherData"; -import { Attachment } from "../models/domain/attachment"; -import { Card } from "../models/domain/card"; -import { Cipher } from "../models/domain/cipher"; -import Domain from "../models/domain/domainBase"; -import { EncArrayBuffer } from "../models/domain/encArrayBuffer"; -import { EncString } from "../models/domain/encString"; -import { Field } from "../models/domain/field"; -import { Identity } from "../models/domain/identity"; -import { Login } from "../models/domain/login"; -import { LoginUri } from "../models/domain/loginUri"; -import { Password } from "../models/domain/password"; -import { SecureNote } from "../models/domain/secureNote"; -import { SortedCiphersCache } from "../models/domain/sortedCiphersCache"; -import { SymmetricCryptoKey } from "../models/domain/symmetricCryptoKey"; -import { AttachmentRequest } from "../models/request/attachmentRequest"; -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 { CipherResponse } from "../models/response/cipherResponse"; -import { ErrorResponse } from "../models/response/errorResponse"; -import { AttachmentView } from "../models/view/attachmentView"; -import { CipherView } from "../models/view/cipherView"; -import { FieldView } from "../models/view/fieldView"; -import { PasswordHistoryView } from "../models/view/passwordHistoryView"; -import { View } from "../models/view/view"; - -const DomainMatchBlacklist = new Map>([ - ["google.com", new Set(["script.google.com"])], -]); - -export class CipherService implements CipherServiceAbstraction { - private sortedCiphersCache: SortedCiphersCache = new SortedCiphersCache( - this.sortCiphersByLastUsed, - ); - - constructor( - private cryptoService: CryptoService, - private settingsService: SettingsService, - private apiService: ApiService, - private fileUploadService: FileUploadService, - private i18nService: I18nService, - private searchService: () => SearchService, - private logService: LogService, - private stateService: StateService, - ) {} - - async getDecryptedCipherCache(): Promise { - const decryptedCiphers = await this.stateService.getDecryptedCiphers(); - return decryptedCiphers; - } - - async setDecryptedCipherCache(value: CipherView[]) { - await this.stateService.setDecryptedCiphers(value); - if (this.searchService != null) { - if (value == null) { - this.searchService().clearIndex(); - } else { - this.searchService().indexCiphers(); - } - } - } - - async clearCache(userId?: string): Promise { - await this.clearDecryptedCiphersState(userId); - } - - async encrypt( - model: CipherView, - key?: SymmetricCryptoKey, - originalCipher: Cipher = null, - ): Promise { - // Adjust password history - if (model.id != null) { - if (originalCipher == null) { - originalCipher = await this.get(model.id); - } - if (originalCipher != null) { - const existingCipher = await originalCipher.decrypt(); - model.passwordHistory = existingCipher.passwordHistory || []; - if (model.type === CipherType.Login && existingCipher.type === CipherType.Login) { - if ( - existingCipher.login.password != null && - existingCipher.login.password !== "" && - existingCipher.login.password !== model.login.password - ) { - const ph = new PasswordHistoryView(); - ph.password = existingCipher.login.password; - ph.lastUsedDate = model.login.passwordRevisionDate = new Date(); - model.passwordHistory.splice(0, 0, ph); - } else { - model.login.passwordRevisionDate = existingCipher.login.passwordRevisionDate; - } - } - if (existingCipher.hasFields) { - const existingHiddenFields = existingCipher.fields.filter( - (f) => - f.type === FieldType.Hidden && - f.name != null && - f.name !== "" && - f.value != null && - f.value !== "", - ); - const hiddenFields = - model.fields == null - ? [] - : model.fields.filter( - (f) => f.type === FieldType.Hidden && f.name != null && f.name !== "", - ); - existingHiddenFields.forEach((ef) => { - const matchedField = hiddenFields.find((f) => f.name === ef.name); - if (matchedField == null || matchedField.value !== ef.value) { - const ph = new PasswordHistoryView(); - ph.password = ef.name + ": " + ef.value; - ph.lastUsedDate = new Date(); - model.passwordHistory.splice(0, 0, ph); - } - }); - } - } - if (model.passwordHistory != null && model.passwordHistory.length === 0) { - model.passwordHistory = null; - } else if (model.passwordHistory != null && model.passwordHistory.length > 5) { - // only save last 5 history - model.passwordHistory = model.passwordHistory.slice(0, 5); - } - } - - const cipher = new Cipher(); - cipher.id = model.id; - cipher.folderId = model.folderId; - cipher.favorite = model.favorite; - cipher.organizationId = model.organizationId; - cipher.type = model.type; - cipher.collectionIds = model.collectionIds; - cipher.revisionDate = model.revisionDate; - cipher.reprompt = model.reprompt; - - if (key == null && cipher.organizationId != null) { - key = await this.cryptoService.getOrgKey(cipher.organizationId); - if (key == null) { - throw new Error("Cannot encrypt cipher for organization. No key."); - } - } - await Promise.all([ - this.encryptObjProperty( - model, - cipher, - { - name: null, - notes: null, - }, - key, - ), - this.encryptCipherData(cipher, model, key), - this.encryptFields(model.fields, key).then((fields) => { - cipher.fields = fields; - }), - this.encryptPasswordHistories(model.passwordHistory, key).then((ph) => { - cipher.passwordHistory = ph; - }), - this.encryptAttachments(model.attachments, key).then((attachments) => { - cipher.attachments = attachments; - }), - ]); - - return cipher; - } - - async encryptAttachments( - attachmentsModel: AttachmentView[], - key: SymmetricCryptoKey, - ): Promise { - if (attachmentsModel == null || attachmentsModel.length === 0) { - return null; - } - - const promises: Promise[] = []; - const encAttachments: Attachment[] = []; - attachmentsModel.forEach(async (model) => { - const attachment = new Attachment(); - attachment.id = model.id; - attachment.size = model.size; - attachment.sizeName = model.sizeName; - attachment.url = model.url; - const promise = this.encryptObjProperty( - model, - attachment, - { - fileName: null, - }, - key, - ).then(async () => { - if (model.key != null) { - attachment.key = await this.cryptoService.encrypt(model.key.key, key); - } - encAttachments.push(attachment); - }); - promises.push(promise); - }); - - await Promise.all(promises); - return encAttachments; - } - - async encryptFields(fieldsModel: FieldView[], key: SymmetricCryptoKey): Promise { - if (!fieldsModel || !fieldsModel.length) { - return null; - } - - const self = this; - const encFields: Field[] = []; - await fieldsModel.reduce(async (promise, field) => { - await promise; - const encField = await self.encryptField(field, key); - encFields.push(encField); - }, Promise.resolve()); - - return encFields; - } - - async encryptField(fieldModel: FieldView, key: SymmetricCryptoKey): Promise { - const field = new Field(); - field.type = fieldModel.type; - field.linkedId = fieldModel.linkedId; - // normalize boolean type field values - if (fieldModel.type === FieldType.Boolean && fieldModel.value !== "true") { - fieldModel.value = "false"; - } - - await this.encryptObjProperty( - fieldModel, - field, - { - name: null, - value: null, - }, - key, - ); - - return field; - } - - async encryptPasswordHistories( - phModels: PasswordHistoryView[], - key: SymmetricCryptoKey, - ): Promise { - if (!phModels || !phModels.length) { - return null; - } - - const self = this; - const encPhs: Password[] = []; - await phModels.reduce(async (promise, ph) => { - await promise; - const encPh = await self.encryptPasswordHistory(ph, key); - encPhs.push(encPh); - }, Promise.resolve()); - - return encPhs; - } - - async encryptPasswordHistory( - phModel: PasswordHistoryView, - key: SymmetricCryptoKey, - ): Promise { - const ph = new Password(); - ph.lastUsedDate = phModel.lastUsedDate; - - await this.encryptObjProperty( - phModel, - ph, - { - password: null, - }, - key, - ); - - return ph; - } - - async get(id: string): Promise { - const ciphers = await this.stateService.getEncryptedCiphers(); - // eslint-disable-next-line - if (ciphers == null || !ciphers.hasOwnProperty(id)) { - return null; - } - - const localData = await this.stateService.getLocalData(); - return new Cipher(ciphers[id], localData ? localData[id] : null); - } - - async getAll(): Promise { - const localData = await this.stateService.getLocalData(); - const ciphers = await this.stateService.getEncryptedCiphers(); - const response: Cipher[] = []; - for (const id in ciphers) { - // eslint-disable-next-line - if (ciphers.hasOwnProperty(id)) { - response.push(new Cipher(ciphers[id], localData ? localData[id] : null)); - } - } - return response; - } - - @sequentialize(() => "getAllDecrypted") - async getAllDecrypted(): Promise { - const userId = await this.stateService.getUserId(); - if ((await this.getDecryptedCipherCache()) != null) { - if ( - this.searchService != null && - (this.searchService().indexedEntityId ?? userId) !== userId - ) { - await this.searchService().indexCiphers(userId, await this.getDecryptedCipherCache()); - } - return await this.getDecryptedCipherCache(); - } - - const decCiphers: CipherView[] = []; - const hasKey = await this.cryptoService.hasKey(); - if (!hasKey) { - throw new Error("No key."); - } - - const promises: any[] = []; - const ciphers = await this.getAll(); - ciphers.forEach(async (cipher) => { - promises.push(cipher.decrypt().then((c) => decCiphers.push(c))); - }); - - await Promise.all(promises); - decCiphers.sort(this.getLocaleSortingFunction()); - await this.setDecryptedCipherCache(decCiphers); - return decCiphers; - } - - async getAllDecryptedForGrouping(groupingId: string, folder = true): Promise { - const ciphers = await this.getAllDecrypted(); - - return ciphers.filter((cipher) => { - if (cipher.isDeleted) { - return false; - } - if (folder && cipher.folderId === groupingId) { - return true; - } else if ( - !folder && - cipher.collectionIds != null && - cipher.collectionIds.indexOf(groupingId) > -1 - ) { - return true; - } - - return false; - }); - } - - async getAllDecryptedForUrl( - url: string, - includeOtherTypes?: CipherType[], - defaultMatch: UriMatchType = null, - ): Promise { - if (url == null && includeOtherTypes == null) { - return Promise.resolve([]); - } - - const domain = Utils.getDomain(url); - const eqDomainsPromise = - domain == null - ? Promise.resolve([]) - : this.settingsService.getEquivalentDomains().then((eqDomains: any[][]) => { - let matches: any[] = []; - eqDomains.forEach((eqDomain) => { - if (eqDomain.length && eqDomain.indexOf(domain) >= 0) { - matches = matches.concat(eqDomain); - } - }); - - if (!matches.length) { - matches.push(domain); - } - - return matches; - }); - - const result = await Promise.all([eqDomainsPromise, this.getAllDecrypted()]); - const matchingDomains = result[0]; - const ciphers = result[1]; - - if (defaultMatch == null) { - defaultMatch = await this.stateService.getDefaultUriMatch(); - if (defaultMatch == null) { - defaultMatch = UriMatchType.Domain; - } - } - - return ciphers.filter((cipher) => { - if (cipher.deletedDate != null) { - return false; - } - if (includeOtherTypes != null && includeOtherTypes.indexOf(cipher.type) > -1) { - return true; - } - - if (url != null && cipher.type === CipherType.Login && cipher.login.uris != null) { - for (let i = 0; i < cipher.login.uris.length; i++) { - const u = cipher.login.uris[i]; - if (u.uri == null) { - continue; - } - - const match = u.match == null ? defaultMatch : u.match; - switch (match) { - case UriMatchType.Domain: - if (domain != null && u.domain != null && matchingDomains.indexOf(u.domain) > -1) { - if (DomainMatchBlacklist.has(u.domain)) { - const domainUrlHost = Utils.getHost(url); - if (!DomainMatchBlacklist.get(u.domain).has(domainUrlHost)) { - return true; - } - } else { - return true; - } - } - break; - case UriMatchType.Host: { - const urlHost = Utils.getHost(url); - if (urlHost != null && urlHost === Utils.getHost(u.uri)) { - return true; - } - break; - } - case UriMatchType.Exact: - if (url === u.uri) { - return true; - } - break; - case UriMatchType.StartsWith: - if (url.startsWith(u.uri)) { - return true; - } - break; - case UriMatchType.RegularExpression: - try { - const regex = new RegExp(u.uri, "i"); - if (regex.test(url)) { - return true; - } - } catch (e) { - this.logService.error(e); - } - break; - case UriMatchType.Never: - default: - break; - } - } - } - - return false; - }); - } - - async getAllFromApiForOrganization(organizationId: string): Promise { - const ciphers = await this.apiService.getCiphersOrganization(organizationId); - if (ciphers != null && ciphers.data != null && ciphers.data.length) { - const decCiphers: CipherView[] = []; - const promises: any[] = []; - ciphers.data.forEach((r) => { - const data = new CipherData(r); - const cipher = new Cipher(data); - promises.push(cipher.decrypt().then((c) => decCiphers.push(c))); - }); - await Promise.all(promises); - decCiphers.sort(this.getLocaleSortingFunction()); - return decCiphers; - } else { - return []; - } - } - - async getLastUsedForUrl(url: string, autofillOnPageLoad = false): Promise { - return this.getCipherForUrl(url, true, false, autofillOnPageLoad); - } - - async getLastLaunchedForUrl(url: string, autofillOnPageLoad = false): Promise { - return this.getCipherForUrl(url, false, true, autofillOnPageLoad); - } - - async getNextCipherForUrl(url: string): Promise { - return this.getCipherForUrl(url, false, false, false); - } - - updateLastUsedIndexForUrl(url: string) { - this.sortedCiphersCache.updateLastUsedIndex(url); - } - - async updateLastUsedDate(id: string): Promise { - let ciphersLocalData = await this.stateService.getLocalData(); - if (!ciphersLocalData) { - ciphersLocalData = {}; - } - - if (ciphersLocalData[id]) { - ciphersLocalData[id].lastUsedDate = new Date().getTime(); - } else { - ciphersLocalData[id] = { - lastUsedDate: new Date().getTime(), - }; - } - - await this.stateService.setLocalData(ciphersLocalData); - - const decryptedCipherCache = await this.stateService.getDecryptedCiphers(); - if (!decryptedCipherCache) { - return; - } - - for (let i = 0; i < decryptedCipherCache.length; i++) { - const cached = decryptedCipherCache[i]; - if (cached.id === id) { - cached.localData = ciphersLocalData[id]; - break; - } - } - await this.stateService.setDecryptedCiphers(decryptedCipherCache); - } - - async updateLastLaunchedDate(id: string): Promise { - let ciphersLocalData = await this.stateService.getLocalData(); - if (!ciphersLocalData) { - ciphersLocalData = {}; - } - - if (ciphersLocalData[id]) { - ciphersLocalData[id].lastLaunched = new Date().getTime(); - } else { - ciphersLocalData[id] = { - lastUsedDate: new Date().getTime(), - }; - } - - await this.stateService.setLocalData(ciphersLocalData); - - const decryptedCipherCache = await this.stateService.getDecryptedCiphers(); - if (!decryptedCipherCache) { - return; - } - - for (let i = 0; i < decryptedCipherCache.length; i++) { - const cached = decryptedCipherCache[i]; - if (cached.id === id) { - cached.localData = ciphersLocalData[id]; - break; - } - } - await this.stateService.setDecryptedCiphers(decryptedCipherCache); - } - - async saveNeverDomain(domain: string): Promise { - if (domain == null) { - return; - } - - let domains = await this.stateService.getNeverDomains(); - if (!domains) { - domains = {}; - } - domains[domain] = null; - await this.stateService.setNeverDomains(domains); - } - - async saveWithServer(cipher: Cipher): Promise { - let response: CipherResponse; - if (cipher.id == null) { - if (cipher.collectionIds != null) { - const request = new CipherCreateRequest(cipher); - response = await this.apiService.postCipherCreate(request); - } else { - const request = new CipherRequest(cipher); - response = await this.apiService.postCipher(request); - } - cipher.id = response.id; - } else { - const request = new CipherRequest(cipher); - response = await this.apiService.putCipher(cipher.id, request); - } - - const data = new CipherData( - response, - await this.stateService.getUserId(), - cipher.collectionIds, - ); - await this.upsert(data); - } - - async shareWithServer( - cipher: CipherView, - organizationId: string, - collectionIds: string[], - ): Promise { - const attachmentPromises: Promise[] = []; - if (cipher.attachments != null) { - cipher.attachments.forEach((attachment) => { - if (attachment.key == null) { - attachmentPromises.push( - this.shareAttachmentWithServer(attachment, cipher.id, organizationId), - ); - } - }); - } - await Promise.all(attachmentPromises); - - cipher.organizationId = organizationId; - cipher.collectionIds = collectionIds; - const encCipher = await this.encrypt(cipher); - const request = new CipherShareRequest(encCipher); - const response = await this.apiService.putShareCipher(cipher.id, request); - const data = new CipherData(response, await this.stateService.getUserId(), collectionIds); - await this.upsert(data); - } - - async shareManyWithServer( - ciphers: CipherView[], - organizationId: string, - collectionIds: string[], - ): Promise { - const promises: Promise[] = []; - const encCiphers: Cipher[] = []; - for (const cipher of ciphers) { - cipher.organizationId = organizationId; - cipher.collectionIds = collectionIds; - promises.push( - this.encrypt(cipher).then((c) => { - encCiphers.push(c); - }), - ); - } - await Promise.all(promises); - const request = new CipherBulkShareRequest(encCiphers, collectionIds); - try { - await this.apiService.putShareCiphers(request); - } catch (e) { - for (const cipher of ciphers) { - cipher.organizationId = null; - cipher.collectionIds = null; - } - throw e; - } - const userId = await this.stateService.getUserId(); - await this.upsert(encCiphers.map((c) => c.toCipherData(userId))); - } - - saveAttachmentWithServer(cipher: Cipher, unencryptedFile: any, admin = false): Promise { - return new Promise((resolve, reject) => { - const reader = new FileReader(); - reader.readAsArrayBuffer(unencryptedFile); - reader.onload = async (evt: any) => { - try { - const cData = await this.saveAttachmentRawWithServer( - cipher, - unencryptedFile.name, - evt.target.result, - admin, - ); - resolve(cData); - } catch (e) { - reject(e); - } - }; - reader.onerror = () => { - reject("Error reading file."); - }; - }); - } - - async saveAttachmentRawWithServer( - cipher: Cipher, - filename: string, - data: ArrayBuffer, - admin = false, - ): Promise { - const key = await this.cryptoService.getOrgKey(cipher.organizationId); - const encFileName = await this.cryptoService.encrypt(filename, key); - - const dataEncKey = await this.cryptoService.makeEncKey(key); - const encData = await this.cryptoService.encryptToBytes(data, dataEncKey[0]); - - const request: AttachmentRequest = { - key: dataEncKey[1].encryptedString, - fileName: encFileName.encryptedString, - fileSize: encData.buffer.byteLength, - adminRequest: admin, - }; - - let response: CipherResponse; - try { - const uploadDataResponse = await this.apiService.postCipherAttachment(cipher.id, request); - response = admin ? uploadDataResponse.cipherMiniResponse : uploadDataResponse.cipherResponse; - await this.fileUploadService.uploadCipherAttachment( - admin, - uploadDataResponse, - encFileName, - encData, - ); - } catch (e) { - if ( - (e instanceof ErrorResponse && (e as ErrorResponse).statusCode === 404) || - (e as ErrorResponse).statusCode === 405 - ) { - response = await this.legacyServerAttachmentFileUpload( - admin, - cipher.id, - encFileName, - encData, - dataEncKey[1], - ); - } else if (e instanceof ErrorResponse) { - throw new Error((e as ErrorResponse).getSingleMessage()); - } else { - throw e; - } - } - - const cData = new CipherData( - response, - await this.stateService.getUserId(), - cipher.collectionIds, - ); - if (!admin) { - await this.upsert(cData); - } - return new Cipher(cData); - } - - /** - * @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. - */ - async legacyServerAttachmentFileUpload( - admin: boolean, - cipherId: string, - encFileName: EncString, - encData: EncArrayBuffer, - key: EncString, - ) { - const fd = new FormData(); - try { - const blob = new Blob([encData.buffer], { type: "application/octet-stream" }); - fd.append("key", key.encryptedString); - fd.append("data", blob, encFileName.encryptedString); - } catch (e) { - if (Utils.isNode && !Utils.isBrowser) { - fd.append("key", key.encryptedString); - fd.append( - "data", - Buffer.from(encData.buffer) as any, - { - filepath: encFileName.encryptedString, - contentType: "application/octet-stream", - } as any, - ); - } else { - throw e; - } - } - - let response: CipherResponse; - try { - if (admin) { - response = await this.apiService.postCipherAttachmentAdminLegacy(cipherId, fd); - } else { - response = await this.apiService.postCipherAttachmentLegacy(cipherId, fd); - } - } catch (e) { - throw new Error((e as ErrorResponse).getSingleMessage()); - } - - return response; - } - - async saveCollectionsWithServer(cipher: Cipher): Promise { - const request = new CipherCollectionsRequest(cipher.collectionIds); - await this.apiService.putCipherCollections(cipher.id, request); - const data = cipher.toCipherData(await this.stateService.getUserId()); - await this.upsert(data); - } - - async upsert(cipher: CipherData | CipherData[]): Promise { - let ciphers = await this.stateService.getEncryptedCiphers(); - if (ciphers == null) { - ciphers = {}; - } - - if (cipher instanceof CipherData) { - const c = cipher as CipherData; - ciphers[c.id] = c; - } else { - (cipher as CipherData[]).forEach((c) => { - ciphers[c.id] = c; - }); - } - - await this.replace(ciphers); - } - - async replace(ciphers: { [id: string]: CipherData }): Promise { - await this.clearDecryptedCiphersState(); - await this.stateService.setEncryptedCiphers(ciphers); - } - - async clear(userId?: string): Promise { - await this.clearEncryptedCiphersState(userId); - await this.clearCache(userId); - } - - async moveManyWithServer(ids: string[], folderId: string): Promise { - await this.apiService.putMoveCiphers(new CipherBulkMoveRequest(ids, folderId)); - - let ciphers = await this.stateService.getEncryptedCiphers(); - if (ciphers == null) { - ciphers = {}; - } - - ids.forEach((id) => { - // eslint-disable-next-line - if (ciphers.hasOwnProperty(id)) { - ciphers[id].folderId = folderId; - } - }); - - await this.clearCache(); - await this.stateService.setEncryptedCiphers(ciphers); - } - - async delete(id: string | string[]): Promise { - const ciphers = await this.stateService.getEncryptedCiphers(); - if (ciphers == null) { - return; - } - - if (typeof id === "string") { - if (ciphers[id] == null) { - return; - } - delete ciphers[id]; - } else { - (id as string[]).forEach((i) => { - delete ciphers[i]; - }); - } - - await this.clearCache(); - await this.stateService.setEncryptedCiphers(ciphers); - } - - async deleteWithServer(id: string): Promise { - await this.apiService.deleteCipher(id); - await this.delete(id); - } - - async deleteManyWithServer(ids: string[]): Promise { - await this.apiService.deleteManyCiphers(new CipherBulkDeleteRequest(ids)); - await this.delete(ids); - } - - async deleteAttachment(id: string, attachmentId: string): Promise { - const ciphers = await this.stateService.getEncryptedCiphers(); - - // eslint-disable-next-line - if (ciphers == null || !ciphers.hasOwnProperty(id) || ciphers[id].attachments == null) { - return; - } - - for (let i = 0; i < ciphers[id].attachments.length; i++) { - if (ciphers[id].attachments[i].id === attachmentId) { - ciphers[id].attachments.splice(i, 1); - } - } - - await this.clearCache(); - await this.stateService.setEncryptedCiphers(ciphers); - } - - async deleteAttachmentWithServer(id: string, attachmentId: string): Promise { - try { - await this.apiService.deleteCipherAttachment(id, attachmentId); - } catch (e) { - return Promise.reject((e as ErrorResponse).getSingleMessage()); - } - await this.deleteAttachment(id, attachmentId); - } - - sortCiphersByLastUsed(a: CipherView, b: CipherView): number { - const aLastUsed = - a.localData && a.localData.lastUsedDate ? (a.localData.lastUsedDate as number) : null; - const bLastUsed = - b.localData && b.localData.lastUsedDate ? (b.localData.lastUsedDate as number) : null; - - const bothNotNull = aLastUsed != null && bLastUsed != null; - if (bothNotNull && aLastUsed < bLastUsed) { - return 1; - } - if (aLastUsed != null && bLastUsed == null) { - return -1; - } - - if (bothNotNull && aLastUsed > bLastUsed) { - return -1; - } - if (bLastUsed != null && aLastUsed == null) { - return 1; - } - - return 0; - } - - sortCiphersByLastUsedThenName(a: CipherView, b: CipherView): number { - const result = this.sortCiphersByLastUsed(a, b); - if (result !== 0) { - return result; - } - - return this.getLocaleSortingFunction()(a, b); - } - - getLocaleSortingFunction(): (a: CipherView, b: CipherView) => number { - return (a, b) => { - let aName = a.name; - let bName = b.name; - - if (aName == null && bName != null) { - return -1; - } - if (aName != null && bName == null) { - return 1; - } - if (aName == null && bName == null) { - return 0; - } - - const result = this.i18nService.collator - ? this.i18nService.collator.compare(aName, bName) - : aName.localeCompare(bName); - - if (result !== 0 || a.type !== CipherType.Login || b.type !== CipherType.Login) { - return result; - } - - if (a.login.username != null) { - aName += a.login.username; - } - - if (b.login.username != null) { - bName += b.login.username; - } - - return this.i18nService.collator - ? this.i18nService.collator.compare(aName, bName) - : aName.localeCompare(bName); - }; - } - - async softDelete(id: string | string[]): Promise { - const ciphers = await this.stateService.getEncryptedCiphers(); - if (ciphers == null) { - return; - } - - const setDeletedDate = (cipherId: string) => { - if (ciphers[cipherId] == null) { - return; - } - ciphers[cipherId].deletedDate = new Date().toISOString(); - }; - - if (typeof id === "string") { - setDeletedDate(id); - } else { - (id as string[]).forEach(setDeletedDate); - } - - await this.clearCache(); - await this.stateService.setEncryptedCiphers(ciphers); - } - - async softDeleteWithServer(id: string): Promise { - await this.apiService.putDeleteCipher(id); - await this.softDelete(id); - } - - async softDeleteManyWithServer(ids: string[]): Promise { - await this.apiService.putDeleteManyCiphers(new CipherBulkDeleteRequest(ids)); - await this.softDelete(ids); - } - - async restore( - cipher: { id: string; revisionDate: string } | { id: string; revisionDate: string }[], - ) { - const ciphers = await this.stateService.getEncryptedCiphers(); - if (ciphers == null) { - return; - } - - const clearDeletedDate = (c: { id: string; revisionDate: string }) => { - if (ciphers[c.id] == null) { - return; - } - ciphers[c.id].deletedDate = null; - ciphers[c.id].revisionDate = c.revisionDate; - }; - - if (cipher.constructor.name === Array.name) { - (cipher as { id: string; revisionDate: string }[]).forEach(clearDeletedDate); - } else { - clearDeletedDate(cipher as { id: string; revisionDate: string }); - } - - await this.clearCache(); - await this.stateService.setEncryptedCiphers(ciphers); - } - - async restoreWithServer(id: string): Promise { - const response = await this.apiService.putRestoreCipher(id); - await this.restore({ id: id, revisionDate: response.revisionDate }); - } - - async restoreManyWithServer(ids: string[]): Promise { - const response = await this.apiService.putRestoreManyCiphers(new CipherBulkRestoreRequest(ids)); - const restores: { id: string; revisionDate: string }[] = []; - for (const cipher of response.data) { - restores.push({ id: cipher.id, revisionDate: cipher.revisionDate }); - } - await this.restore(restores); - } - - // Helpers - - private async shareAttachmentWithServer( - attachmentView: AttachmentView, - cipherId: string, - organizationId: string, - ): Promise { - const attachmentResponse = await this.apiService.nativeFetch( - new Request(attachmentView.url, { cache: "no-store" }), - ); - if (attachmentResponse.status !== 200) { - throw Error("Failed to download attachment: " + attachmentResponse.status.toString()); - } - - const buf = await attachmentResponse.arrayBuffer(); - const decBuf = await this.cryptoService.decryptFromBytes(buf, null); - const key = await this.cryptoService.getOrgKey(organizationId); - const encFileName = await this.cryptoService.encrypt(attachmentView.fileName, key); - - const dataEncKey = await this.cryptoService.makeEncKey(key); - const encData = await this.cryptoService.encryptToBytes(decBuf, dataEncKey[0]); - - const fd = new FormData(); - try { - const blob = new Blob([encData.buffer], { type: "application/octet-stream" }); - fd.append("key", dataEncKey[1].encryptedString); - fd.append("data", blob, encFileName.encryptedString); - } catch (e) { - if (Utils.isNode && !Utils.isBrowser) { - fd.append("key", dataEncKey[1].encryptedString); - fd.append( - "data", - Buffer.from(encData.buffer) as any, - { - filepath: encFileName.encryptedString, - contentType: "application/octet-stream", - } as any, - ); - } else { - throw e; - } - } - - try { - await this.apiService.postShareCipherAttachment( - cipherId, - attachmentView.id, - fd, - organizationId, - ); - } catch (e) { - throw new Error((e as ErrorResponse).getSingleMessage()); - } - } - - private async encryptObjProperty( - model: V, - obj: D, - map: any, - key: SymmetricCryptoKey, - ): Promise { - const promises = []; - const self = this; - - for (const prop in map) { - // eslint-disable-next-line - if (!map.hasOwnProperty(prop)) { - continue; - } - - (function (theProp, theObj) { - const p = Promise.resolve() - .then(() => { - const modelProp = (model as any)[map[theProp] || theProp]; - if (modelProp && modelProp !== "") { - return self.cryptoService.encrypt(modelProp, key); - } - return null; - }) - .then((val: EncString) => { - (theObj as any)[theProp] = val; - }); - promises.push(p); - })(prop, obj); - } - - await Promise.all(promises); - } - - private async encryptCipherData(cipher: Cipher, model: CipherView, key: SymmetricCryptoKey) { - switch (cipher.type) { - case CipherType.Login: - cipher.login = new Login(); - cipher.login.passwordRevisionDate = model.login.passwordRevisionDate; - cipher.login.autofillOnPageLoad = model.login.autofillOnPageLoad; - await this.encryptObjProperty( - model.login, - cipher.login, - { - username: null, - password: null, - totp: null, - }, - key, - ); - - if (model.login.uris != null) { - cipher.login.uris = []; - for (let i = 0; i < model.login.uris.length; i++) { - const loginUri = new LoginUri(); - loginUri.match = model.login.uris[i].match; - await this.encryptObjProperty( - model.login.uris[i], - loginUri, - { - uri: null, - }, - key, - ); - cipher.login.uris.push(loginUri); - } - } - return; - case CipherType.SecureNote: - cipher.secureNote = new SecureNote(); - cipher.secureNote.type = model.secureNote.type; - return; - case CipherType.Card: - cipher.card = new Card(); - await this.encryptObjProperty( - model.card, - cipher.card, - { - cardholderName: null, - brand: null, - number: null, - expMonth: null, - expYear: null, - code: null, - }, - key, - ); - return; - case CipherType.Identity: - cipher.identity = new Identity(); - await this.encryptObjProperty( - model.identity, - cipher.identity, - { - title: null, - firstName: null, - middleName: null, - lastName: null, - address1: null, - address2: null, - address3: null, - city: null, - state: null, - postalCode: null, - country: null, - company: null, - email: null, - phone: null, - ssn: null, - username: null, - passportNumber: null, - licenseNumber: null, - }, - key, - ); - return; - default: - throw new Error("Unknown cipher type."); - } - } - - private async getCipherForUrl( - url: string, - lastUsed: boolean, - lastLaunched: boolean, - autofillOnPageLoad: boolean, - ): Promise { - const cacheKey = autofillOnPageLoad ? "autofillOnPageLoad-" + url : url; - - if (!this.sortedCiphersCache.isCached(cacheKey)) { - let ciphers = await this.getAllDecryptedForUrl(url); - if (!ciphers) { - return null; - } - - if (autofillOnPageLoad) { - const autofillOnPageLoadDefault = await this.stateService.getAutoFillOnPageLoadDefault(); - ciphers = ciphers.filter( - (cipher) => - cipher.login.autofillOnPageLoad || - (cipher.login.autofillOnPageLoad == null && autofillOnPageLoadDefault !== false), - ); - if (ciphers.length === 0) { - return null; - } - } - - this.sortedCiphersCache.addCiphers(cacheKey, ciphers); - } - - if (lastLaunched) { - return this.sortedCiphersCache.getLastLaunched(cacheKey); - } else if (lastUsed) { - return this.sortedCiphersCache.getLastUsed(cacheKey); - } else { - return this.sortedCiphersCache.getNext(cacheKey); - } - } - - private async clearEncryptedCiphersState(userId?: string) { - await this.stateService.setEncryptedCiphers(null, { userId: userId }); - } - - private async clearDecryptedCiphersState(userId?: string) { - await this.stateService.setDecryptedCiphers(null, { userId: userId }); - this.clearSortedCiphers(); - } - - private clearSortedCiphers() { - this.sortedCiphersCache.clear(); - } -} diff --git a/jslib/common/src/services/event.service.ts b/jslib/common/src/services/event.service.ts index 00f928b5..a74e3e80 100644 --- a/jslib/common/src/services/event.service.ts +++ b/jslib/common/src/services/event.service.ts @@ -1,5 +1,4 @@ import { ApiService } from "../abstractions/api.service"; -import { CipherService } from "../abstractions/cipher.service"; import { EventService as EventServiceAbstraction } from "../abstractions/event.service"; import { LogService } from "../abstractions/log.service"; import { OrganizationService } from "../abstractions/organization.service"; @@ -13,7 +12,6 @@ export class EventService implements EventServiceAbstraction { constructor( private apiService: ApiService, - private cipherService: CipherService, private stateService: StateService, private logService: LogService, private organizationService: OrganizationService, @@ -48,12 +46,6 @@ export class EventService implements EventServiceAbstraction { if (orgIds.size === 0) { return; } - if (cipherId != null) { - const cipher = await this.cipherService.get(cipherId); - if (cipher == null || cipher.organizationId == null || !orgIds.has(cipher.organizationId)) { - return; - } - } let eventCollection = await this.stateService.getEventCollection(); if (eventCollection == null) { eventCollection = []; diff --git a/jslib/common/src/services/folder.service.ts b/jslib/common/src/services/folder.service.ts index 7c0b2a08..075de3fa 100644 --- a/jslib/common/src/services/folder.service.ts +++ b/jslib/common/src/services/folder.service.ts @@ -1,5 +1,4 @@ import { ApiService } from "../abstractions/api.service"; -import { CipherService } from "../abstractions/cipher.service"; import { CryptoService } from "../abstractions/crypto.service"; import { FolderService as FolderServiceAbstraction } from "../abstractions/folder.service"; import { I18nService } from "../abstractions/i18n.service"; @@ -22,7 +21,6 @@ export class FolderService implements FolderServiceAbstraction { private cryptoService: CryptoService, private apiService: ApiService, private i18nService: I18nService, - private cipherService: CipherService, private stateService: StateService, ) {} @@ -181,9 +179,6 @@ export class FolderService implements FolderServiceAbstraction { updates.push(ciphers[cId]); } } - if (updates.length > 0) { - this.cipherService.upsert(updates); - } } } diff --git a/jslib/common/src/services/notifications.service.ts b/jslib/common/src/services/notifications.service.ts index b033ca2a..940d852b 100644 --- a/jslib/common/src/services/notifications.service.ts +++ b/jslib/common/src/services/notifications.service.ts @@ -12,7 +12,6 @@ import { VaultTimeoutService } from "../abstractions/vaultTimeout.service"; import { NotificationType } from "../enums/notificationType"; import { NotificationResponse, - SyncCipherNotification, SyncFolderNotification, } from "../models/response/notificationResponse"; @@ -131,17 +130,6 @@ export class NotificationsService implements NotificationsServiceAbstraction { } switch (notification.type) { - case NotificationType.SyncCipherCreate: - case NotificationType.SyncCipherUpdate: - await this.syncService.syncUpsertCipher( - notification.payload as SyncCipherNotification, - notification.type === NotificationType.SyncCipherUpdate, - ); - break; - case NotificationType.SyncCipherDelete: - case NotificationType.SyncLoginDelete: - await this.syncService.syncDeleteCipher(notification.payload as SyncCipherNotification); - break; case NotificationType.SyncFolderCreate: case NotificationType.SyncFolderUpdate: await this.syncService.syncUpsertFolder( diff --git a/jslib/common/src/services/search.service.ts b/jslib/common/src/services/search.service.ts index a5134614..a3411bd0 100644 --- a/jslib/common/src/services/search.service.ts +++ b/jslib/common/src/services/search.service.ts @@ -1,6 +1,5 @@ import * as lunr from "lunr"; -import { CipherService } from "../abstractions/cipher.service"; import { I18nService } from "../abstractions/i18n.service"; import { LogService } from "../abstractions/log.service"; import { SearchService as SearchServiceAbstraction } from "../abstractions/search.service"; @@ -17,7 +16,6 @@ export class SearchService implements SearchServiceAbstraction { private searchableMinLength = 2; constructor( - private cipherService: CipherService, private logService: LogService, private i18nService: I18nService, ) { @@ -76,7 +74,6 @@ export class SearchService implements SearchServiceAbstraction { extractor: (c: CipherView) => this.attachmentExtractor(c, true), }); builder.field("organizationid", { extractor: (c: CipherView) => c.organizationId }); - ciphers = ciphers || (await this.cipherService.getAllDecrypted()); ciphers.forEach((c) => builder.add(c)); this.index = builder.build(); @@ -97,11 +94,6 @@ export class SearchService implements SearchServiceAbstraction { if (query === "") { query = null; } - - if (ciphers == null) { - ciphers = await this.cipherService.getAllDecrypted(); - } - if (filter != null && Array.isArray(filter) && filter.length > 0) { ciphers = ciphers.filter((c) => filter.every((f) => f == null || f(c))); } else if (filter != null) { diff --git a/jslib/common/src/services/sync.service.ts b/jslib/common/src/services/sync.service.ts index 04418408..26837adb 100644 --- a/jslib/common/src/services/sync.service.ts +++ b/jslib/common/src/services/sync.service.ts @@ -1,5 +1,4 @@ import { ApiService } from "../abstractions/api.service"; -import { CipherService } from "../abstractions/cipher.service"; import { CollectionService } from "../abstractions/collection.service"; import { CryptoService } from "../abstractions/crypto.service"; import { FolderService } from "../abstractions/folder.service"; @@ -13,18 +12,15 @@ import { SettingsService } from "../abstractions/settings.service"; import { StateService } from "../abstractions/state.service"; import { SyncService as SyncServiceAbstraction } from "../abstractions/sync.service"; import { sequentialize } from "../misc/sequentialize"; -import { CipherData } from "../models/data/cipherData"; import { CollectionData } from "../models/data/collectionData"; import { FolderData } from "../models/data/folderData"; import { OrganizationData } from "../models/data/organizationData"; import { PolicyData } from "../models/data/policyData"; import { ProviderData } from "../models/data/providerData"; -import { CipherResponse } from "../models/response/cipherResponse"; import { CollectionDetailsResponse } from "../models/response/collectionResponse"; import { DomainsResponse } from "../models/response/domainsResponse"; import { FolderResponse } from "../models/response/folderResponse"; import { - SyncCipherNotification, SyncFolderNotification, } from "../models/response/notificationResponse"; import { PolicyResponse } from "../models/response/policyResponse"; @@ -37,7 +33,6 @@ export class SyncService implements SyncServiceAbstraction { private apiService: ApiService, private settingsService: SettingsService, private folderService: FolderService, - private cipherService: CipherService, private cryptoService: CryptoService, private collectionService: CollectionService, private messagingService: MessagingService, @@ -98,7 +93,6 @@ export class SyncService implements SyncServiceAbstraction { await this.syncProfile(response.profile); await this.syncFolders(userId, response.folders); await this.syncCollections(response.collections); - await this.syncCiphers(userId, response.ciphers); await this.syncSettings(response.domains); await this.syncPolicies(response.policies); @@ -148,79 +142,6 @@ export class SyncService implements SyncServiceAbstraction { return this.syncCompleted(false); } - async syncUpsertCipher(notification: SyncCipherNotification, isEdit: boolean): Promise { - this.syncStarted(); - if (await this.stateService.getIsAuthenticated()) { - try { - let shouldUpdate = true; - const localCipher = await this.cipherService.get(notification.id); - if (localCipher != null && localCipher.revisionDate >= notification.revisionDate) { - shouldUpdate = false; - } - - let checkCollections = false; - if (shouldUpdate) { - if (isEdit) { - shouldUpdate = localCipher != null; - checkCollections = true; - } else { - if (notification.collectionIds == null || notification.organizationId == null) { - shouldUpdate = localCipher == null; - } else { - shouldUpdate = false; - checkCollections = true; - } - } - } - - if ( - !shouldUpdate && - checkCollections && - notification.organizationId != null && - notification.collectionIds != null && - notification.collectionIds.length > 0 - ) { - const collections = await this.collectionService.getAll(); - if (collections != null) { - for (let i = 0; i < collections.length; i++) { - if (notification.collectionIds.indexOf(collections[i].id) > -1) { - shouldUpdate = true; - break; - } - } - } - } - - if (shouldUpdate) { - const remoteCipher = await this.apiService.getCipher(notification.id); - if (remoteCipher != null) { - const userId = await this.stateService.getUserId(); - await this.cipherService.upsert(new CipherData(remoteCipher, userId)); - this.messagingService.send("syncedUpsertedCipher", { cipherId: notification.id }); - return this.syncCompleted(true); - } - } - } catch (e) { - if (e != null && e.statusCode === 404 && isEdit) { - await this.cipherService.delete(notification.id); - this.messagingService.send("syncedDeletedCipher", { cipherId: notification.id }); - return this.syncCompleted(true); - } - } - } - return this.syncCompleted(false); - } - - async syncDeleteCipher(notification: SyncCipherNotification): Promise { - this.syncStarted(); - if (await this.stateService.getIsAuthenticated()) { - await this.cipherService.delete(notification.id); - this.messagingService.send("syncedDeletedCipher", { cipherId: notification.id }); - return this.syncCompleted(true); - } - return this.syncCompleted(false); - } - // Helpers private syncStarted() { @@ -314,14 +235,6 @@ export class SyncService implements SyncServiceAbstraction { return await this.collectionService.replace(collections); } - private async syncCiphers(userId: string, response: CipherResponse[]) { - const ciphers: { [id: string]: CipherData } = {}; - response.forEach((c) => { - ciphers[c.id] = new CipherData(c, userId); - }); - return await this.cipherService.replace(ciphers); - } - private async syncSettings(response: DomainsResponse) { let eqDomains: string[][] = []; if (response != null && response.equivalentDomains != null) { diff --git a/jslib/common/src/services/vaultTimeout.service.ts b/jslib/common/src/services/vaultTimeout.service.ts index a4468711..2b45215e 100644 --- a/jslib/common/src/services/vaultTimeout.service.ts +++ b/jslib/common/src/services/vaultTimeout.service.ts @@ -1,6 +1,5 @@ import { firstValueFrom } from "rxjs"; -import { CipherService } from "../abstractions/cipher.service"; import { CollectionService } from "../abstractions/collection.service"; import { CryptoService } from "../abstractions/crypto.service"; import { FolderService } from "../abstractions/folder.service"; @@ -19,7 +18,6 @@ export class VaultTimeoutService implements VaultTimeoutServiceAbstraction { private inited = false; constructor( - private cipherService: CipherService, private folderService: FolderService, private collectionService: CollectionService, private cryptoService: CryptoService, @@ -107,7 +105,6 @@ export class VaultTimeoutService implements VaultTimeoutServiceAbstraction { await this.cryptoService.clearEncKey(true, userId); await this.folderService.clearCache(userId); - await this.cipherService.clearCache(userId); await this.collectionService.clearCache(userId); this.messagingService.send("locked", { userId: userId }); diff --git a/src/bwdc.ts b/src/bwdc.ts index e8f55f05..4e97c3c7 100644 --- a/src/bwdc.ts +++ b/src/bwdc.ts @@ -8,7 +8,6 @@ import { LogLevelType } from "@/jslib/common/src/enums/logLevelType"; import { StateFactory } from "@/jslib/common/src/factories/stateFactory"; import { GlobalState } from "@/jslib/common/src/models/domain/globalState"; import { AppIdService } from "@/jslib/common/src/services/appId.service"; -import { CipherService } from "@/jslib/common/src/services/cipher.service"; import { CollectionService } from "@/jslib/common/src/services/collection.service"; import { ContainerService } from "@/jslib/common/src/services/container.service"; import { CryptoService } from "@/jslib/common/src/services/crypto.service"; @@ -61,7 +60,6 @@ export class Main { cryptoFunctionService: NodeCryptoFunctionService; authService: AuthService; collectionService: CollectionService; - cipherService: CipherService; fileUploadService: FileUploadService; folderService: FolderService; searchService: SearchService; @@ -218,24 +216,12 @@ export class Main { this.fileUploadService = new FileUploadService(this.logService, this.apiService); - this.cipherService = new CipherService( - this.cryptoService, - this.settingsService, - this.apiService, - this.fileUploadService, - this.i18nService, - () => searchService, - this.logService, - this.stateService, - ); - - this.searchService = new SearchService(this.cipherService, this.logService, this.i18nService); + this.searchService = new SearchService(this.logService, this.i18nService); this.folderService = new FolderService( this.cryptoService, this.apiService, this.i18nService, - this.cipherService, this.stateService, );