tests: vault tests 3 (#2373)

This commit is contained in:
Mo
2023-07-27 07:35:38 -05:00
committed by GitHub
parent 1fef36d601
commit 14bae5e895
26 changed files with 350 additions and 283 deletions

View File

@@ -119,6 +119,7 @@ import {
DeleteContact,
VaultLockService,
RemoveItemsFromMemory,
ReencryptTypeAItems,
} from '@standardnotes/services'
import { ItemManager } from '../../Services/Items/ItemManager'
import { PayloadManager } from '../../Services/Payloads/PayloadManager'
@@ -202,6 +203,10 @@ export class Dependencies {
}
private registerUseCaseMakers() {
this.factory.set(TYPES.ReencryptTypeAItems, () => {
return new ReencryptTypeAItems(this.get(TYPES.ItemManager), this.get(TYPES.MutatorService))
})
this.factory.set(TYPES.ImportDataUseCase, () => {
return new ImportDataUseCase(
this.get(TYPES.ItemManager),
@@ -616,10 +621,9 @@ export class Dependencies {
return new RootKeyManager(
this.get(TYPES.DeviceInterface),
this.get(TYPES.DiskStorageService),
this.get(TYPES.ItemManager),
this.get(TYPES.MutatorService),
this.get(TYPES.EncryptionOperators),
this.options.identifier,
this.get(TYPES.ReencryptTypeAItems),
this.get(TYPES.InternalEventBus),
)
})
@@ -1086,6 +1090,7 @@ export class Dependencies {
this.get(TYPES.ChallengeService),
this.get(TYPES.ProtectionService),
this.get(TYPES.UserApiService),
this.get(TYPES.ReencryptTypeAItems),
this.get(TYPES.InternalEventBus),
)
})

View File

@@ -151,6 +151,7 @@ export const TYPES = {
DecryptBackupFile: Symbol.for('DecryptBackupFile'),
IsVaultOwner: Symbol.for('IsVaultOwner'),
RemoveItemsFromMemory: Symbol.for('RemoveItemsFromMemory'),
ReencryptTypeAItems: Symbol.for('ReencryptTypeAItems'),
// Mappers
SessionStorageMapper: Symbol.for('SessionStorageMapper'),

View File

@@ -14,7 +14,6 @@ export class Migration2_202_1 extends Migration {
this.registerStageHandler(ApplicationStage.FullSyncCompleted_13, async () => {
await this.migrateComponentDataToUserPreferences()
await this.migrateActiveComponentsToUserPreferences()
await this.deleteComponentsWhichAreNativeFeatures()
this.markDone()
})
@@ -70,29 +69,4 @@ export class Migration2_202_1 extends Migration {
await this.services.preferences.setValueDetached(PrefKey.ActiveThemes, Uuids(activeThemes))
await this.services.preferences.setValueDetached(PrefKey.ActiveComponents, Uuids(activeComponents))
}
private async deleteComponentsWhichAreNativeFeatures(): Promise<void> {
const componentsToDelete = [
...this.services.itemManager.getItems<ComponentInterface>(ContentType.TYPES.Component),
...this.services.itemManager.getItems<ComponentInterface>(ContentType.TYPES.Theme),
].filter((candidate) => {
const nativeFeature = FindNativeFeature(candidate.identifier)
if (!nativeFeature) {
return false
}
const isDeprecatedAndThusShouldNotDeleteComponentSinceUserHasItRetained = nativeFeature.deprecated
if (isDeprecatedAndThusShouldNotDeleteComponentSinceUserHasItRetained) {
return false
}
return true
})
if (componentsToDelete.length === 0) {
return
}
await this.services.mutator.setItemsToBeDeleted(componentsToDelete)
}
}

View File

@@ -286,13 +286,11 @@ export class PayloadManager extends AbstractService implements PayloadManagerInt
/**
* Imports an array of payloads from an external source (such as a backup file)
* and marks the items as dirty.
* @returns Resulting items
*/
public async importPayloads(payloads: DecryptedPayloadInterface[], historyMap: HistoryMap): Promise<string[]> {
public async importPayloads(payloads: FullyFormedPayloadInterface[], historyMap: HistoryMap): Promise<string[]> {
const sourcedPayloads = payloads.map((p) => p.copy(undefined, PayloadSource.FileImport))
const delta = new DeltaFileImport(this.getMasterCollection(), sourcedPayloads, historyMap)
const emit = delta.result()
await this.emitDeltaEmit(emit)

View File

@@ -6,6 +6,7 @@ export const VaultTests = {
'vaults/pkc.test.js',
'vaults/contacts.test.js',
'vaults/crypto.test.js',
'vaults/importing.test.js',
'vaults/asymmetric-messages.test.js',
'vaults/keypair-change.test.js',
'vaults/signatures.test.js',
@@ -16,7 +17,7 @@ export const VaultTests = {
'vaults/conflicts.test.js',
'vaults/deletion.test.js',
'vaults/permissions.test.js',
'vaults/key_rotation.test.js',
'vaults/key-rotation.test.js',
'vaults/files.test.js',
],
}

View File

@@ -755,7 +755,7 @@ describe('keys', function () {
currentServerPassword: currentRootKey.serverPassword,
newRootKey,
})
await this.application.encryption.reencryptApplicableItemsAfterUserRootKeyChange()
await this.application.dependencies.get(TYPES.ReencryptTypeAItems).execute()
/** Note: this may result in a deadlock if features_service syncs and results in an error */
await this.application.sync.sync({ awaitAll: true })

View File

@@ -369,6 +369,17 @@ export class AppContext {
})
}
spyOnFunctionResult(object, functionName) {
return new Promise((resolve) => {
sinon.stub(object, functionName).callsFake(async (params) => {
object[functionName].restore()
const result = await object[functionName](params)
resolve(result)
return result
})
})
}
resolveWhenAsymmetricMessageProcessingCompletes() {
return this.resolveWhenAsyncFunctionCompletes(this.asymmetric, 'handleRemoteReceivedAsymmetricMessages')
}

View File

@@ -121,71 +121,4 @@ describe('migrations', () => {
await Factory.safeDeinit(application)
})
describe('2.202.1', () => {
let application
beforeEach(async () => {
application = await Factory.createAppWithRandNamespace()
await application.prepareForLaunch({
receiveChallenge: () => {},
})
await application.launch(true)
})
afterEach(async () => {
await Factory.safeDeinit(application)
})
it('remove components that are available as native features', async function () {
const editor = CreateDecryptedItemFromPayload(
new DecryptedPayload({
uuid: '123',
content_type: ContentType.TYPES.Component,
content: FillItemContent({
package_info: {
identifier: NativeFeatureIdentifier.TYPES.MarkdownProEditor,
},
}),
}),
)
await application.mutator.insertItem(editor)
await application.sync.sync()
expect(application.items.getItems(ContentType.TYPES.Component).length).to.equal(1)
/** Run migration */
const migration = new Migration2_202_1(application.migrations.services)
await migration.handleStage(ApplicationStage.FullSyncCompleted_13)
await application.sync.sync()
expect(application.items.getItems(ContentType.TYPES.Component).length).to.equal(0)
})
it('do not remove components that are available as native features but deprecated', async function () {
const editor = CreateDecryptedItemFromPayload(
new DecryptedPayload({
uuid: '123',
content_type: ContentType.TYPES.Component,
content: FillItemContent({
package_info: {
identifier: NativeFeatureIdentifier.TYPES.DeprecatedBoldEditor,
},
}),
}),
)
await application.mutator.insertItem(editor)
await application.sync.sync()
expect(application.items.getItems(ContentType.TYPES.Component).length).to.equal(1)
/** Run migration */
const migration = new Migration2_202_1(application.migrations.services)
await migration.handleStage(ApplicationStage.FullSyncCompleted_13)
await application.sync.sync()
expect(application.items.getItems(ContentType.TYPES.Component).length).to.equal(1)
})
})
})

View File

@@ -882,8 +882,4 @@ describe('importing', function () {
expect(application.items.referencesForItem(importedTag).length).to.equal(1)
expect(application.items.itemsReferencingItem(importedNote).length).to.equal(1)
})
it('should decrypt backup file which contains a vaulted note without a synced key system root key', async () => {
console.error('TODO: Implement this test')
})
})

View File

@@ -101,7 +101,7 @@ describe('contacts', function () {
await deinitContactContext()
})
it('should be able to refresh a contact using a collaborationID that includes full chain of previouos public keys', async () => {
it('should be able to refresh a contact using a collaborationID that includes full chain of previous public keys', async () => {
console.error('TODO: implement test')
})
})

View File

@@ -35,12 +35,25 @@ describe('shared vault crypto', function () {
expect(recreatedContext.encryption.getSigningKeyPair()).to.not.be.undefined
})
it('changing user password should re-encrypt all key system root keys', async () => {
console.error('TODO: implement')
})
it('changing user password should re-encrypt all key system root keys and contacts with new user root key', async () => {
await Collaboration.createPrivateVault(context)
const spy = context.spyOnFunctionResult(context.application.sync, 'payloadsByPreparingForServer')
await context.changePassword('new_password')
it('changing user password should re-encrypt all trusted contacts', async () => {
console.error('TODO: implement')
const payloads = await spy
const keyPayloads = payloads.filter(
(payload) =>
payload.content_type === ContentType.TYPES.KeySystemRootKey ||
payload.content_type === ContentType.TYPES.TrustedContact,
)
expect(keyPayloads.length).to.equal(2)
for (const payload of payloads) {
const keyParams = context.encryption.getEmbeddedPayloadAuthenticatedData(new EncryptedPayload(payload)).kp
const userKeyParams = context.encryption.getRootKeyParams().content
expect(keyParams).to.eql(userKeyParams)
}
})
})

View File

@@ -0,0 +1,58 @@
import * as Factory from '../lib/factory.js'
import * as Collaboration from '../lib/Collaboration.js'
chai.use(chaiAsPromised)
const expect = chai.expect
describe.skip('vault importing', function () {
this.timeout(Factory.TwentySecondTimeout)
let context
afterEach(async function () {
await context.deinit()
localStorage.clear()
})
beforeEach(async function () {
localStorage.clear()
context = await Factory.createAppContextWithRealCrypto()
await context.launch()
await context.register()
})
it('should import vaulted items with synced root key', async () => {
console.error('TODO: implement')
})
it('should import vaulted items with non-present root key', async () => {
const vault = await context.vaults.createUserInputtedPasswordVault({
name: 'test vault',
userInputtedPassword: 'test password',
storagePreference: KeySystemRootKeyStorageMode.Ephemeral,
})
const note = await context.createSyncedNote('foo', 'bar')
await Collaboration.moveItemToVault(context, vault, note)
const backupData = await context.application.createEncryptedBackupFileForAutomatedDesktopBackups()
const otherContext = await Factory.createAppContextWithRealCrypto()
await otherContext.launch()
await otherContext.application.importData(backupData)
const expectedImportedItems = ['vault-items-key', 'note']
const invalidItems = otherContext.items.invalidItems
expect(invalidItems.length).to.equal(expectedImportedItems.length)
const encryptedItem = invalidItems[0]
expect(encryptedItem.key_system_identifier).to.equal(vault.systemIdentifier)
expect(encryptedItem.errorDecrypting).to.be.true
expect(encryptedItem.uuid).to.equal(note.uuid)
await otherContext.deinit()
})
})

View File

@@ -4,7 +4,7 @@ import * as Collaboration from '../lib/Collaboration.js'
chai.use(chaiAsPromised)
const expect = chai.expect
describe('shared vault key rotation', function () {
describe('vault key rotation', function () {
this.timeout(Factory.TwentySecondTimeout)
let context
@@ -29,17 +29,66 @@ describe('shared vault key rotation', function () {
contactContext.lockSyncing()
const spy = sinon.spy(context.keys, 'queueVaultItemsKeysForReencryption')
const callSpy = sinon.spy(context.keys, 'queueVaultItemsKeysForReencryption')
const syncSpy = context.spyOnFunctionResult(context.application.sync, 'payloadsByPreparingForServer')
const promise = context.resolveWhenSharedVaultKeyRotationInvitesGetSent(sharedVault)
await context.vaults.rotateVaultRootKey(sharedVault)
await promise
await syncSpy
expect(spy.callCount).to.equal(1)
expect(callSpy.callCount).to.equal(1)
const payloads = await syncSpy
const keyPayloads = payloads.filter((payload) => payload.content_type === ContentType.TYPES.KeySystemItemsKey)
expect(keyPayloads.length).to.equal(2)
const vaultRootKey = context.keys.getPrimaryKeySystemRootKey(sharedVault.systemIdentifier)
for (const payload of keyPayloads) {
const keyParams = context.encryption.getEmbeddedPayloadAuthenticatedData(new EncryptedPayload(payload)).kp
expect(keyParams).to.eql(vaultRootKey.keyParams)
}
deinitContactContext()
})
it('should update value of local storage mode key', async () => {
const vault = await context.vaults.createUserInputtedPasswordVault({
name: 'test vault',
userInputtedPassword: 'test password',
storagePreference: KeySystemRootKeyStorageMode.Local,
})
const beforeKey = context.keys.getRootKeyFromStorageForVault(vault.systemIdentifier)
await context.vaults.rotateVaultRootKey(vault, 'test password')
const afterKey = context.keys.getRootKeyFromStorageForVault(vault.systemIdentifier)
expect(afterKey.keyParams.creationTimestamp).to.be.greaterThan(beforeKey.keyParams.creationTimestamp)
expect(afterKey.key).to.not.equal(beforeKey.key)
expect(afterKey.itemsKey).to.not.equal(beforeKey.itemsKey)
})
it('should update value of mem storage mode key', async () => {
const vault = await context.vaults.createUserInputtedPasswordVault({
name: 'test vault',
userInputtedPassword: 'test password',
storagePreference: KeySystemRootKeyStorageMode.Ephemeral,
})
const beforeKey = context.keys.getMemCachedRootKey(vault.systemIdentifier)
await context.vaults.rotateVaultRootKey(vault, 'test password')
const afterKey = context.keys.getMemCachedRootKey(vault.systemIdentifier)
expect(afterKey.keyParams.creationTimestamp).to.be.greaterThan(beforeKey.keyParams.creationTimestamp)
expect(afterKey.key).to.not.equal(beforeKey.key)
expect(afterKey.itemsKey).to.not.equal(beforeKey.itemsKey)
})
it("rotating a vault's key should send an asymmetric message to all members", async () => {
const { sharedVault, contactContext, deinitContactContext } =
await Collaboration.createSharedVaultWithAcceptedInvite(context)

View File

@@ -104,10 +104,27 @@ describe('shared vaults', function () {
})
it('should convert a vault to a shared vault', async () => {
console.error('TODO')
})
const privateVault = await context.vaults.createRandomizedVault({
name: 'My Private Vault',
})
it('should send metadata change message when changing name or description', async () => {
console.error('TODO')
const note = await context.createSyncedNote('foo', 'bar')
await context.vaults.moveItemToVault(privateVault, note)
const sharedVault = await context.sharedVaults.convertVaultToSharedVault(privateVault)
const { thirdPartyContext, deinitThirdPartyContext } = await Collaboration.inviteNewPartyToSharedVault(
context,
sharedVault,
)
await Collaboration.acceptAllInvites(thirdPartyContext)
const contextNote = thirdPartyContext.items.findItem(note.uuid)
expect(contextNote).to.not.be.undefined
expect(contextNote.title).to.equal('foo')
expect(contextNote.text).to.equal(note.text)
await deinitThirdPartyContext()
})
})