tests: vaults (#2365)
* tests: signature tests * tests: asymmetric * feat: delete contact use case * chore: lint * chore: lint
This commit is contained in:
@@ -109,8 +109,14 @@ import {
|
||||
ItemsEncryptionService,
|
||||
DecryptBackupFile,
|
||||
VaultUserService,
|
||||
IsVaultAdmin,
|
||||
IsVaultOwner,
|
||||
VaultInviteService,
|
||||
VaultUserCache,
|
||||
GetVaults,
|
||||
GetSharedVaults,
|
||||
GetOwnedSharedVaults,
|
||||
ContactBelongsToVault,
|
||||
DeleteContact,
|
||||
} from '@standardnotes/services'
|
||||
import { ItemManager } from '../../Services/Items/ItemManager'
|
||||
import { PayloadManager } from '../../Services/Payloads/PayloadManager'
|
||||
@@ -207,8 +213,8 @@ export class Dependencies {
|
||||
)
|
||||
})
|
||||
|
||||
this.factory.set(TYPES.IsVaultAdmin, () => {
|
||||
return new IsVaultAdmin()
|
||||
this.factory.set(TYPES.IsVaultOwner, () => {
|
||||
return new IsVaultOwner()
|
||||
})
|
||||
|
||||
this.factory.set(TYPES.DecryptBackupFile, () => {
|
||||
@@ -223,6 +229,15 @@ export class Dependencies {
|
||||
return new FindContact(this.get(TYPES.ItemManager))
|
||||
})
|
||||
|
||||
this.factory.set(TYPES.DeleteContact, () => {
|
||||
return new DeleteContact(
|
||||
this.get(TYPES.MutatorService),
|
||||
this.get(TYPES.SyncService),
|
||||
this.get(TYPES.GetOwnedSharedVaults),
|
||||
this.get(TYPES.ContactBelongsToVault),
|
||||
)
|
||||
})
|
||||
|
||||
this.factory.set(TYPES.EditContact, () => {
|
||||
return new EditContact(this.get(TYPES.MutatorService), this.get(TYPES.SyncService))
|
||||
})
|
||||
@@ -248,6 +263,22 @@ export class Dependencies {
|
||||
return new GetVault(this.get(TYPES.ItemManager))
|
||||
})
|
||||
|
||||
this.factory.set(TYPES.GetVaults, () => {
|
||||
return new GetVaults(this.get(TYPES.ItemManager))
|
||||
})
|
||||
|
||||
this.factory.set(TYPES.GetSharedVaults, () => {
|
||||
return new GetSharedVaults(this.get(TYPES.GetVaults))
|
||||
})
|
||||
|
||||
this.factory.set(TYPES.GetOwnedSharedVaults, () => {
|
||||
return new GetOwnedSharedVaults(this.get(TYPES.GetSharedVaults), this.get(TYPES.IsVaultOwner))
|
||||
})
|
||||
|
||||
this.factory.set(TYPES.ContactBelongsToVault, () => {
|
||||
return new ContactBelongsToVault(this.get(TYPES.GetVaultUsers))
|
||||
})
|
||||
|
||||
this.factory.set(TYPES.ChangeVaultKeyOptions, () => {
|
||||
return new ChangeVaultKeyOptions(
|
||||
this.get(TYPES.MutatorService),
|
||||
@@ -453,7 +484,7 @@ export class Dependencies {
|
||||
})
|
||||
|
||||
this.factory.set(TYPES.GetVaultUsers, () => {
|
||||
return new GetVaultUsers(this.get(TYPES.SharedVaultUsersServer))
|
||||
return new GetVaultUsers(this.get(TYPES.SharedVaultUsersServer), this.get(TYPES.VaultUserCache))
|
||||
})
|
||||
|
||||
this.factory.set(TYPES.DecryptOwnMessage, () => {
|
||||
@@ -621,13 +652,17 @@ export class Dependencies {
|
||||
this.get(TYPES.VaultService),
|
||||
this.get(TYPES.GetVaultUsers),
|
||||
this.get(TYPES.RemoveVaultMember),
|
||||
this.get(TYPES.IsVaultAdmin),
|
||||
this.get(TYPES.IsVaultOwner),
|
||||
this.get(TYPES.GetVault),
|
||||
this.get(TYPES.LeaveVault),
|
||||
this.get(TYPES.InternalEventBus),
|
||||
)
|
||||
})
|
||||
|
||||
this.factory.set(TYPES.VaultUserCache, () => {
|
||||
return new VaultUserCache()
|
||||
})
|
||||
|
||||
this.factory.set(TYPES.VaultInviteService, () => {
|
||||
return new VaultInviteService(
|
||||
this.get(TYPES.ItemManager),
|
||||
@@ -650,9 +685,10 @@ export class Dependencies {
|
||||
|
||||
this.factory.set(TYPES.AsymmetricMessageService, () => {
|
||||
return new AsymmetricMessageService(
|
||||
this.get(TYPES.AsymmetricMessageServer),
|
||||
this.get(TYPES.EncryptionService),
|
||||
this.get(TYPES.MutatorService),
|
||||
this.get(TYPES.SessionManager),
|
||||
this.get(TYPES.AsymmetricMessageServer),
|
||||
this.get(TYPES.CreateOrEditContact),
|
||||
this.get(TYPES.FindContact),
|
||||
this.get(TYPES.GetAllContacts),
|
||||
@@ -673,8 +709,8 @@ export class Dependencies {
|
||||
this.get(TYPES.ItemManager),
|
||||
this.get(TYPES.EncryptionService),
|
||||
this.get(TYPES.SessionManager),
|
||||
this.get(TYPES.VaultService),
|
||||
this.get(TYPES.GetVault),
|
||||
this.get(TYPES.GetOwnedSharedVaults),
|
||||
this.get(TYPES.CreateSharedVault),
|
||||
this.get(TYPES.HandleKeyPairChange),
|
||||
this.get(TYPES.NotifyVaultUsersOfKeyRotation),
|
||||
@@ -684,7 +720,7 @@ export class Dependencies {
|
||||
this.get(TYPES.ShareContactWithVault),
|
||||
this.get(TYPES.ConvertToSharedVault),
|
||||
this.get(TYPES.DeleteSharedVault),
|
||||
this.get(TYPES.IsVaultAdmin),
|
||||
this.get(TYPES.IsVaultOwner),
|
||||
this.get(TYPES.InternalEventBus),
|
||||
)
|
||||
})
|
||||
@@ -698,6 +734,7 @@ export class Dependencies {
|
||||
this.get(TYPES.KeySystemKeyManager),
|
||||
this.get(TYPES.AlertService),
|
||||
this.get(TYPES.GetVault),
|
||||
this.get(TYPES.GetVaults),
|
||||
this.get(TYPES.ChangeVaultKeyOptions),
|
||||
this.get(TYPES.MoveItemsToVault),
|
||||
this.get(TYPES.CreateVault),
|
||||
@@ -727,6 +764,7 @@ export class Dependencies {
|
||||
this.get(TYPES.UserService),
|
||||
this.get(TYPES.SelfContactManager),
|
||||
this.get(TYPES.EncryptionService),
|
||||
this.get(TYPES.DeleteContact),
|
||||
this.get(TYPES.FindContact),
|
||||
this.get(TYPES.GetAllContacts),
|
||||
this.get(TYPES.CreateOrEditContact),
|
||||
|
||||
@@ -61,6 +61,7 @@ export const TYPES = {
|
||||
ItemsEncryptionService: Symbol.for('ItemsEncryptionService'),
|
||||
VaultUserService: Symbol.for('VaultUserService'),
|
||||
VaultInviteService: Symbol.for('VaultInviteService'),
|
||||
VaultUserCache: Symbol.for('VaultUserCache'),
|
||||
|
||||
// Servers
|
||||
RevisionServer: Symbol.for('RevisionServer'),
|
||||
@@ -94,9 +95,14 @@ export const TYPES = {
|
||||
EditContact: Symbol.for('EditContact'),
|
||||
ValidateItemSigner: Symbol.for('ValidateItemSigner'),
|
||||
GetVault: Symbol.for('GetVault'),
|
||||
GetVaults: Symbol.for('GetVaults'),
|
||||
GetSharedVaults: Symbol.for('GetSharedVaults'),
|
||||
GetOwnedSharedVaults: Symbol.for('GetOwnedSharedVaults'),
|
||||
ChangeVaultKeyOptions: Symbol.for('ChangeVaultKeyOptions'),
|
||||
MoveItemsToVault: Symbol.for('MoveItemsToVault'),
|
||||
CreateVault: Symbol.for('CreateVault'),
|
||||
DeleteContact: Symbol.for('DeleteContact'),
|
||||
ContactBelongsToVault: Symbol.for('ContactBelongsToVault'),
|
||||
RemoveItemFromVault: Symbol.for('RemoveItemFromVault'),
|
||||
DeleteVault: Symbol.for('DeleteVault'),
|
||||
RotateVaultKey: Symbol.for('RotateVaultKey'),
|
||||
@@ -142,7 +148,7 @@ export const TYPES = {
|
||||
EncryptTypeAPayload: Symbol.for('EncryptTypeAPayload'),
|
||||
EncryptTypeAPayloadWithKeyLookup: Symbol.for('EncryptTypeAPayloadWithKeyLookup'),
|
||||
DecryptBackupFile: Symbol.for('DecryptBackupFile'),
|
||||
IsVaultAdmin: Symbol.for('IsVaultAdmin'),
|
||||
IsVaultOwner: Symbol.for('IsVaultOwner'),
|
||||
|
||||
// Mappers
|
||||
SessionStorageMapper: Symbol.for('SessionStorageMapper'),
|
||||
|
||||
@@ -1,18 +1,21 @@
|
||||
|
||||
export const VaultTests = [
|
||||
'vaults/vaults.test.js',
|
||||
'vaults/pkc.test.js',
|
||||
'vaults/contacts.test.js',
|
||||
'vaults/crypto.test.js',
|
||||
'vaults/asymmetric-messages.test.js',
|
||||
'vaults/keypair-change.test.js',
|
||||
'vaults/signatures.test.js',
|
||||
'vaults/shared_vaults.test.js',
|
||||
'vaults/invites.test.js',
|
||||
'vaults/items.test.js',
|
||||
'vaults/conflicts.test.js',
|
||||
'vaults/deletion.test.js',
|
||||
'vaults/permissions.test.js',
|
||||
'vaults/key_rotation.test.js',
|
||||
'vaults/files.test.js',
|
||||
];
|
||||
export const VaultTests = {
|
||||
enabled: false,
|
||||
exclusive: false,
|
||||
files: [
|
||||
'vaults/vaults.test.js',
|
||||
'vaults/pkc.test.js',
|
||||
'vaults/contacts.test.js',
|
||||
'vaults/crypto.test.js',
|
||||
'vaults/asymmetric-messages.test.js',
|
||||
'vaults/keypair-change.test.js',
|
||||
'vaults/signatures.test.js',
|
||||
'vaults/shared_vaults.test.js',
|
||||
'vaults/invites.test.js',
|
||||
'vaults/items.test.js',
|
||||
'vaults/conflicts.test.js',
|
||||
'vaults/deletion.test.js',
|
||||
'vaults/permissions.test.js',
|
||||
'vaults/key_rotation.test.js',
|
||||
'vaults/files.test.js',
|
||||
],
|
||||
}
|
||||
|
||||
@@ -578,9 +578,11 @@ export class AppContext {
|
||||
}
|
||||
|
||||
async changeNoteTitle(note, title) {
|
||||
return this.application.mutator.changeNote(note, (mutator) => {
|
||||
await this.application.mutator.changeNote(note, (mutator) => {
|
||||
mutator.title = title
|
||||
})
|
||||
|
||||
return this.findItem(note.uuid)
|
||||
}
|
||||
|
||||
async changeNoteTitleAndSync(note, title) {
|
||||
|
||||
@@ -38,10 +38,6 @@
|
||||
<script type="module">
|
||||
import MainRegistry from './TestRegistry/MainRegistry.js'
|
||||
|
||||
const InternalFeatureStatus = {
|
||||
[InternalFeature.Vaults]: { enabled: false, exclusive: false },
|
||||
}
|
||||
|
||||
const loadTest = (fileName) => {
|
||||
return new Promise((resolve) => {
|
||||
const script = document.createElement('script');
|
||||
@@ -60,12 +56,12 @@
|
||||
}
|
||||
}
|
||||
|
||||
if (InternalFeatureStatus[InternalFeature.Vaults].enabled) {
|
||||
if (MainRegistry.VaultTests.enabled) {
|
||||
InternalFeatureService.get().enableFeature(InternalFeature.Vaults);
|
||||
await loadTests(MainRegistry.VaultTests);
|
||||
await loadTests(MainRegistry.VaultTests.files);
|
||||
}
|
||||
|
||||
if (!InternalFeatureStatus[InternalFeature.Vaults].exclusive) {
|
||||
if (!MainRegistry.VaultTests.enabled.exclusive) {
|
||||
await loadTests(MainRegistry.BaseTests);
|
||||
}
|
||||
|
||||
@@ -77,4 +73,4 @@
|
||||
<div id="mocha"></div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
</html>
|
||||
@@ -27,7 +27,32 @@ describe('asymmetric messages', function () {
|
||||
})
|
||||
|
||||
it('should not trust message if the trusted payload data recipientUuid does not match the message user uuid', async () => {
|
||||
console.error('TODO: implement')
|
||||
const { sharedVault, contactContext, deinitContactContext } =
|
||||
await Collaboration.createSharedVaultWithAcceptedInvite(context)
|
||||
|
||||
contactContext.lockSyncing()
|
||||
|
||||
await context.vaults.changeVaultNameAndDescription(sharedVault, {
|
||||
name: 'new vault name',
|
||||
description: 'new vault description',
|
||||
})
|
||||
|
||||
Object.defineProperty(contactContext.asymmetric.sessions, 'userUuid', {
|
||||
get: () => 'invalid user uuid',
|
||||
})
|
||||
|
||||
const completedProcessingMessagesPromise = contactContext.resolveWhenAsymmetricMessageProcessingCompletes()
|
||||
|
||||
contactContext.unlockSyncing()
|
||||
await contactContext.sync()
|
||||
await completedProcessingMessagesPromise
|
||||
|
||||
const updatedVault = contactContext.vaults.getVault({ keySystemIdentifier: sharedVault.systemIdentifier })
|
||||
|
||||
expect(updatedVault.name).to.not.equal('new vault name')
|
||||
expect(updatedVault.description).to.not.equal('new vault description')
|
||||
|
||||
await deinitContactContext()
|
||||
})
|
||||
|
||||
it('should delete message after processing it', async () => {
|
||||
@@ -89,7 +114,7 @@ describe('asymmetric messages', function () {
|
||||
await contactContext.sync()
|
||||
await completedProcessingMessagesPromise
|
||||
|
||||
const updatedContact = contactContext.contacts.findTrustedContact(thirdPartyContext.userUuid)
|
||||
const updatedContact = contactContext.contacts.findContact(thirdPartyContext.userUuid)
|
||||
expect(updatedContact.name).to.equal('Changed 3rd Party Name')
|
||||
|
||||
await deinitContactContext()
|
||||
@@ -228,7 +253,7 @@ describe('asymmetric messages', function () {
|
||||
expect(firstPartySpy.callCount).to.equal(0)
|
||||
expect(secondPartySpy.callCount).to.equal(1)
|
||||
|
||||
const contact = contactContext.contacts.findTrustedContact(context.userUuid)
|
||||
const contact = contactContext.contacts.findContact(context.userUuid)
|
||||
expect(contact.publicKeySet.encryption).to.equal(context.publicKey)
|
||||
expect(contact.publicKeySet.signing).to.equal(context.signingPublicKey)
|
||||
|
||||
@@ -294,7 +319,7 @@ describe('asymmetric messages', function () {
|
||||
const { contactContext, deinitContactContext } = await Collaboration.createContactContext()
|
||||
await Collaboration.createTrustedContactForUserOfContext(context, contactContext)
|
||||
await Collaboration.createTrustedContactForUserOfContext(contactContext, context)
|
||||
const originalContact = contactContext.contacts.findTrustedContact(context.userUuid)
|
||||
const originalContact = contactContext.contacts.findContact(context.userUuid)
|
||||
|
||||
await context.changePassword('new_password')
|
||||
|
||||
@@ -302,7 +327,7 @@ describe('asymmetric messages', function () {
|
||||
await contactContext.sync()
|
||||
await completedProcessingMessagesPromise
|
||||
|
||||
const updatedContact = contactContext.contacts.findTrustedContact(context.userUuid)
|
||||
const updatedContact = contactContext.contacts.findContact(context.userUuid)
|
||||
|
||||
expect(updatedContact.publicKeySet.encryption).to.not.equal(originalContact.publicKeySet.encryption)
|
||||
expect(updatedContact.publicKeySet.signing).to.not.equal(originalContact.publicKeySet.signing)
|
||||
@@ -352,7 +377,7 @@ describe('asymmetric messages', function () {
|
||||
await contactContext.sync()
|
||||
await completedProcessingMessagesPromise
|
||||
|
||||
const updatedContact = contactContext.contacts.findTrustedContact(context.userUuid)
|
||||
const updatedContact = contactContext.contacts.findContact(context.userUuid)
|
||||
expect(updatedContact.publicKeySet.encryption).to.equal(newKeyPair.publicKey)
|
||||
expect(updatedContact.publicKeySet.signing).to.equal(newSigningKeyPair.publicKey)
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import * as Factory from '../lib/factory.js'
|
||||
import * as Collaboration from '../lib/Collaboration.js'
|
||||
|
||||
chai.use(chaiAsPromised)
|
||||
const expect = chai.expect
|
||||
@@ -90,7 +91,14 @@ describe('contacts', function () {
|
||||
})
|
||||
|
||||
it('should not be able to delete a trusted contact if it belongs to a vault I administer', async () => {
|
||||
console.error('TODO: implement test')
|
||||
const { contact, deinitContactContext } = await Collaboration.createSharedVaultWithAcceptedInvite(context)
|
||||
|
||||
const result = await context.contacts.deleteContact(contact)
|
||||
|
||||
expect(result.isFailed()).to.be.true
|
||||
expect(result.getError()).to.equal('Cannot delete contact that belongs to an owned vault')
|
||||
|
||||
await deinitContactContext()
|
||||
})
|
||||
|
||||
it('should be able to refresh a contact using a collaborationID that includes full chain of previouos public keys', async () => {
|
||||
|
||||
@@ -167,11 +167,11 @@ describe('shared vault crypto', function () {
|
||||
const { note, contactContext, deinitContactContext } =
|
||||
await Collaboration.createSharedVaultWithAcceptedInviteAndNote(context)
|
||||
|
||||
expect(context.contacts.isItemAuthenticallySigned(note)).to.equal(ItemSignatureValidationResult.NotApplicable)
|
||||
expect(context.contacts.getItemSignatureStatus(note)).to.equal(ItemSignatureValidationResult.NotApplicable)
|
||||
|
||||
const contactNote = contactContext.items.findItem(note.uuid)
|
||||
|
||||
expect(contactContext.contacts.isItemAuthenticallySigned(contactNote)).to.equal(
|
||||
expect(contactContext.contacts.getItemSignatureStatus(contactNote)).to.equal(
|
||||
ItemSignatureValidationResult.Trusted,
|
||||
)
|
||||
|
||||
@@ -181,7 +181,7 @@ describe('shared vault crypto', function () {
|
||||
|
||||
let updatedNote = context.items.findItem(note.uuid)
|
||||
|
||||
expect(context.contacts.isItemAuthenticallySigned(updatedNote)).to.equal(ItemSignatureValidationResult.Trusted)
|
||||
expect(context.contacts.getItemSignatureStatus(updatedNote)).to.equal(ItemSignatureValidationResult.Trusted)
|
||||
|
||||
await deinitContactContext()
|
||||
})
|
||||
|
||||
@@ -23,11 +23,106 @@ describe('signatures', function () {
|
||||
await context.register()
|
||||
})
|
||||
|
||||
it('signatures should be marked as of questionable integrity when signed with non root contact public key', async () => {
|
||||
console.error('TODO: implement test')
|
||||
describe('item decryption signature verification', () => {
|
||||
it('should have failing signature if contact public key does not match', async () => {
|
||||
const { note, contactContext, deinitContactContext } =
|
||||
await Collaboration.createSharedVaultWithAcceptedInviteAndNote(context)
|
||||
|
||||
const regularContact = contactContext.contacts.findContact(context.userUuid)
|
||||
const decoyContact = new TrustedContact(
|
||||
regularContact.payload.copy({
|
||||
content: {
|
||||
...regularContact.payload.content,
|
||||
publicKeySet: ContactPublicKeySet.FromJson({
|
||||
...regularContact.payload.content.publicKeySet,
|
||||
encryption: 'invalid public key',
|
||||
signing: 'invalid signing public key',
|
||||
}),
|
||||
},
|
||||
}),
|
||||
)
|
||||
|
||||
contactContext.items.collection.onChange({
|
||||
changed: [decoyContact],
|
||||
inserted: [],
|
||||
discarded: [],
|
||||
ignored: [],
|
||||
unerrored: [],
|
||||
})
|
||||
|
||||
await context.changeNoteTitle(note, 'new title')
|
||||
|
||||
await contactContext.sync()
|
||||
|
||||
const contactNote = contactContext.items.findItem(note.uuid)
|
||||
|
||||
/** Signature data only verifies whether the embedded signature and embedded signature public key match up */
|
||||
expect(contactNote.signatureData.required).to.be.true
|
||||
expect(contactNote.signatureData.result.passes).to.be.true
|
||||
|
||||
const status = contactContext.contacts.getItemSignatureStatus(contactNote)
|
||||
expect(status).to.equal(ItemSignatureValidationResult.NotTrusted)
|
||||
|
||||
await deinitContactContext()
|
||||
})
|
||||
})
|
||||
|
||||
it('items marked with questionable integrity should have option to trust the item which would resync it', async () => {
|
||||
console.error('TODO: implement test')
|
||||
describe('UI signature status check', () => {
|
||||
it('signatures should be trusted with root public key', async () => {
|
||||
const { note, contactContext, deinitContactContext } =
|
||||
await Collaboration.createSharedVaultWithAcceptedInviteAndNote(context)
|
||||
|
||||
const contactNote = contactContext.items.findItem(note.uuid)
|
||||
|
||||
const status = contactContext.contacts.getItemSignatureStatus(contactNote)
|
||||
|
||||
expect(status).to.equal(ItemSignatureValidationResult.Trusted)
|
||||
|
||||
await deinitContactContext()
|
||||
})
|
||||
|
||||
it('signatures return SignedWithNonCurrentKey when signed with non root contact public key', async () => {
|
||||
const { note, contactContext, deinitContactContext } =
|
||||
await Collaboration.createSharedVaultWithAcceptedInviteAndNote(context)
|
||||
|
||||
await context.changePassword('new password')
|
||||
|
||||
await contactContext.sync()
|
||||
|
||||
const contactNote = contactContext.items.findItem(note.uuid)
|
||||
|
||||
const status = contactContext.contacts.getItemSignatureStatus(contactNote)
|
||||
|
||||
expect(status).to.equal(ItemSignatureValidationResult.SignedWithNonCurrentKey)
|
||||
|
||||
await deinitContactContext()
|
||||
})
|
||||
|
||||
it('syncing a SignedWithNonCurrentKey item should reset its status', async () => {
|
||||
const { note, contactContext, deinitContactContext } =
|
||||
await Collaboration.createSharedVaultWithAcceptedInviteAndNote(context)
|
||||
|
||||
await context.changePassword('new password')
|
||||
|
||||
await contactContext.sync()
|
||||
|
||||
const contactNote = contactContext.items.findItem(note.uuid)
|
||||
|
||||
const latestNote = await contactContext.changeNoteTitle(contactNote, 'new title')
|
||||
|
||||
const status = contactContext.contacts.getItemSignatureStatus(latestNote)
|
||||
|
||||
expect(status).to.equal(ItemSignatureValidationResult.NotApplicable)
|
||||
|
||||
await deinitContactContext()
|
||||
})
|
||||
|
||||
it('should return NotApplicable if item does not belong to shared vault', async () => {
|
||||
const item = await context.createSyncedNote()
|
||||
|
||||
const status = context.contacts.getItemSignatureStatus(item)
|
||||
|
||||
expect(status).to.equal(ItemSignatureValidationResult.NotApplicable)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user