internal: incomplete vault systems behind feature flag (#2340)
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
import { MutatorClientInterface } from './../Mutator/MutatorClientInterface'
|
||||
import { HttpServiceInterface } from '@standardnotes/api'
|
||||
import { AsymmetricMessageService } from './AsymmetricMessageService'
|
||||
import { ContactServiceInterface } from './../Contacts/ContactServiceInterface'
|
||||
import { InternalEventBusInterface } from '../Internal/InternalEventBusInterface'
|
||||
import { EncryptionProviderInterface } from '@standardnotes/encryption'
|
||||
import { ItemManagerInterface } from '../Item/ItemManagerInterface'
|
||||
import { SyncServiceInterface } from '../Sync/SyncServiceInterface'
|
||||
import { AsymmetricMessageServerHash } from '@standardnotes/responses'
|
||||
import { AsymmetricMessagePayloadType } from '@standardnotes/models'
|
||||
|
||||
describe('AsymmetricMessageService', () => {
|
||||
let service: AsymmetricMessageService
|
||||
|
||||
beforeEach(() => {
|
||||
const http = {} as jest.Mocked<HttpServiceInterface>
|
||||
http.delete = jest.fn()
|
||||
|
||||
const encryption = {} as jest.Mocked<EncryptionProviderInterface>
|
||||
const contacts = {} as jest.Mocked<ContactServiceInterface>
|
||||
const items = {} as jest.Mocked<ItemManagerInterface>
|
||||
const sync = {} as jest.Mocked<SyncServiceInterface>
|
||||
const mutator = {} as jest.Mocked<MutatorClientInterface>
|
||||
|
||||
const eventBus = {} as jest.Mocked<InternalEventBusInterface>
|
||||
eventBus.addEventHandler = jest.fn()
|
||||
|
||||
service = new AsymmetricMessageService(http, encryption, contacts, items, mutator, sync, eventBus)
|
||||
})
|
||||
|
||||
it('should process incoming messages oldest first', async () => {
|
||||
const messages: AsymmetricMessageServerHash[] = [
|
||||
{
|
||||
uuid: 'newer-message',
|
||||
user_uuid: '1',
|
||||
sender_uuid: '2',
|
||||
encrypted_message: 'encrypted_message',
|
||||
created_at_timestamp: 2,
|
||||
updated_at_timestamp: 2,
|
||||
},
|
||||
{
|
||||
uuid: 'older-message',
|
||||
user_uuid: '1',
|
||||
sender_uuid: '2',
|
||||
encrypted_message: 'encrypted_message',
|
||||
created_at_timestamp: 1,
|
||||
updated_at_timestamp: 1,
|
||||
},
|
||||
]
|
||||
|
||||
const trustedPayloadMock = { type: AsymmetricMessagePayloadType.ContactShare, data: { recipientUuid: '1' } }
|
||||
|
||||
service.getTrustedMessagePayload = jest.fn().mockReturnValue(trustedPayloadMock)
|
||||
|
||||
const handleTrustedContactShareMessageMock = jest.fn()
|
||||
service.handleTrustedContactShareMessage = handleTrustedContactShareMessageMock
|
||||
|
||||
await service.handleRemoteReceivedAsymmetricMessages(messages)
|
||||
|
||||
expect(handleTrustedContactShareMessageMock.mock.calls[0][0]).toEqual(messages[1])
|
||||
expect(handleTrustedContactShareMessageMock.mock.calls[1][0]).toEqual(messages[0])
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,187 @@
|
||||
import { MutatorClientInterface } from './../Mutator/MutatorClientInterface'
|
||||
import { ContactServiceInterface } from './../Contacts/ContactServiceInterface'
|
||||
import { AsymmetricMessageServerHash, ClientDisplayableError } from '@standardnotes/responses'
|
||||
import { SyncEvent, SyncEventReceivedAsymmetricMessagesData } from '../Event/SyncEvent'
|
||||
import { InternalEventBusInterface } from '../Internal/InternalEventBusInterface'
|
||||
import { InternalEventHandlerInterface } from '../Internal/InternalEventHandlerInterface'
|
||||
import { InternalEventInterface } from '../Internal/InternalEventInterface'
|
||||
import { AbstractService } from '../Service/AbstractService'
|
||||
import { GetAsymmetricMessageTrustedPayload } from './UseCase/GetAsymmetricMessageTrustedPayload'
|
||||
import { EncryptionProviderInterface } from '@standardnotes/encryption'
|
||||
import {
|
||||
AsymmetricMessageSharedVaultRootKeyChanged,
|
||||
AsymmetricMessagePayloadType,
|
||||
AsymmetricMessageSenderKeypairChanged,
|
||||
AsymmetricMessageTrustedContactShare,
|
||||
AsymmetricMessagePayload,
|
||||
AsymmetricMessageSharedVaultMetadataChanged,
|
||||
VaultListingMutator,
|
||||
} from '@standardnotes/models'
|
||||
import { HandleTrustedSharedVaultRootKeyChangedMessage } from './UseCase/HandleTrustedSharedVaultRootKeyChangedMessage'
|
||||
import { ItemManagerInterface } from '../Item/ItemManagerInterface'
|
||||
import { SyncServiceInterface } from '../Sync/SyncServiceInterface'
|
||||
import { SessionEvent } from '../Session/SessionEvent'
|
||||
import { AsymmetricMessageServer, HttpServiceInterface } from '@standardnotes/api'
|
||||
import { UserKeyPairChangedEventData } from '../Session/UserKeyPairChangedEventData'
|
||||
import { SendOwnContactChangeMessage } from './UseCase/SendOwnContactChangeMessage'
|
||||
import { GetOutboundAsymmetricMessages } from './UseCase/GetOutboundAsymmetricMessages'
|
||||
import { GetInboundAsymmetricMessages } from './UseCase/GetInboundAsymmetricMessages'
|
||||
import { GetVaultUseCase } from '../Vaults/UseCase/GetVault'
|
||||
|
||||
export class AsymmetricMessageService extends AbstractService implements InternalEventHandlerInterface {
|
||||
private messageServer: AsymmetricMessageServer
|
||||
|
||||
constructor(
|
||||
http: HttpServiceInterface,
|
||||
private encryption: EncryptionProviderInterface,
|
||||
private contacts: ContactServiceInterface,
|
||||
private items: ItemManagerInterface,
|
||||
private mutator: MutatorClientInterface,
|
||||
private sync: SyncServiceInterface,
|
||||
eventBus: InternalEventBusInterface,
|
||||
) {
|
||||
super(eventBus)
|
||||
|
||||
this.messageServer = new AsymmetricMessageServer(http)
|
||||
|
||||
eventBus.addEventHandler(this, SyncEvent.ReceivedAsymmetricMessages)
|
||||
eventBus.addEventHandler(this, SessionEvent.UserKeyPairChanged)
|
||||
}
|
||||
|
||||
async handleEvent(event: InternalEventInterface): Promise<void> {
|
||||
if (event.type === SessionEvent.UserKeyPairChanged) {
|
||||
void this.messageServer.deleteAllInboundMessages()
|
||||
void this.sendOwnContactChangeEventToAllContacts(event.payload as UserKeyPairChangedEventData)
|
||||
}
|
||||
|
||||
if (event.type === SyncEvent.ReceivedAsymmetricMessages) {
|
||||
void this.handleRemoteReceivedAsymmetricMessages(event.payload as SyncEventReceivedAsymmetricMessagesData)
|
||||
}
|
||||
}
|
||||
|
||||
public async getOutboundMessages(): Promise<AsymmetricMessageServerHash[] | ClientDisplayableError> {
|
||||
const usecase = new GetOutboundAsymmetricMessages(this.messageServer)
|
||||
return usecase.execute()
|
||||
}
|
||||
|
||||
public async getInboundMessages(): Promise<AsymmetricMessageServerHash[] | ClientDisplayableError> {
|
||||
const usecase = new GetInboundAsymmetricMessages(this.messageServer)
|
||||
return usecase.execute()
|
||||
}
|
||||
|
||||
async sendOwnContactChangeEventToAllContacts(data: UserKeyPairChangedEventData): Promise<void> {
|
||||
if (!data.oldKeyPair || !data.oldSigningKeyPair) {
|
||||
return
|
||||
}
|
||||
|
||||
const useCase = new SendOwnContactChangeMessage(this.encryption, this.messageServer)
|
||||
|
||||
const contacts = this.contacts.getAllContacts()
|
||||
|
||||
for (const contact of contacts) {
|
||||
if (contact.isMe) {
|
||||
continue
|
||||
}
|
||||
|
||||
await useCase.execute({
|
||||
senderOldKeyPair: data.oldKeyPair,
|
||||
senderOldSigningKeyPair: data.oldSigningKeyPair,
|
||||
senderNewKeyPair: data.newKeyPair,
|
||||
senderNewSigningKeyPair: data.newSigningKeyPair,
|
||||
contact,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
async handleRemoteReceivedAsymmetricMessages(messages: AsymmetricMessageServerHash[]): Promise<void> {
|
||||
if (messages.length === 0) {
|
||||
return
|
||||
}
|
||||
|
||||
const sortedMessages = messages.slice().sort((a, b) => a.created_at_timestamp - b.created_at_timestamp)
|
||||
|
||||
for (const message of sortedMessages) {
|
||||
const trustedMessagePayload = this.getTrustedMessagePayload(message)
|
||||
if (!trustedMessagePayload) {
|
||||
continue
|
||||
}
|
||||
|
||||
if (trustedMessagePayload.data.recipientUuid !== message.user_uuid) {
|
||||
continue
|
||||
}
|
||||
|
||||
if (trustedMessagePayload.type === AsymmetricMessagePayloadType.ContactShare) {
|
||||
await this.handleTrustedContactShareMessage(message, trustedMessagePayload)
|
||||
} else if (trustedMessagePayload.type === AsymmetricMessagePayloadType.SenderKeypairChanged) {
|
||||
await this.handleTrustedSenderKeypairChangedMessage(message, trustedMessagePayload)
|
||||
} else if (trustedMessagePayload.type === AsymmetricMessagePayloadType.SharedVaultRootKeyChanged) {
|
||||
await this.handleTrustedSharedVaultRootKeyChangedMessage(message, trustedMessagePayload)
|
||||
} else if (trustedMessagePayload.type === AsymmetricMessagePayloadType.SharedVaultMetadataChanged) {
|
||||
await this.handleVaultMetadataChangedMessage(message, trustedMessagePayload)
|
||||
} else if (trustedMessagePayload.type === AsymmetricMessagePayloadType.SharedVaultInvite) {
|
||||
throw new Error('Shared vault invites payloads are not handled as part of asymmetric messages')
|
||||
}
|
||||
|
||||
await this.deleteMessageAfterProcessing(message)
|
||||
}
|
||||
}
|
||||
|
||||
getTrustedMessagePayload(message: AsymmetricMessageServerHash): AsymmetricMessagePayload | undefined {
|
||||
const useCase = new GetAsymmetricMessageTrustedPayload(this.encryption, this.contacts)
|
||||
|
||||
return useCase.execute({
|
||||
privateKey: this.encryption.getKeyPair().privateKey,
|
||||
message,
|
||||
})
|
||||
}
|
||||
|
||||
private async deleteMessageAfterProcessing(message: AsymmetricMessageServerHash): Promise<void> {
|
||||
await this.messageServer.deleteMessage({ messageUuid: message.uuid })
|
||||
}
|
||||
|
||||
async handleVaultMetadataChangedMessage(
|
||||
_message: AsymmetricMessageServerHash,
|
||||
trustedPayload: AsymmetricMessageSharedVaultMetadataChanged,
|
||||
): Promise<void> {
|
||||
const vault = new GetVaultUseCase(this.items).execute({ sharedVaultUuid: trustedPayload.data.sharedVaultUuid })
|
||||
if (!vault) {
|
||||
return
|
||||
}
|
||||
|
||||
await this.mutator.changeItem<VaultListingMutator>(vault, (mutator) => {
|
||||
mutator.name = trustedPayload.data.name
|
||||
mutator.description = trustedPayload.data.description
|
||||
})
|
||||
}
|
||||
|
||||
async handleTrustedContactShareMessage(
|
||||
_message: AsymmetricMessageServerHash,
|
||||
trustedPayload: AsymmetricMessageTrustedContactShare,
|
||||
): Promise<void> {
|
||||
await this.contacts.createOrUpdateTrustedContactFromContactShare(trustedPayload.data.trustedContact)
|
||||
}
|
||||
|
||||
private async handleTrustedSenderKeypairChangedMessage(
|
||||
message: AsymmetricMessageServerHash,
|
||||
trustedPayload: AsymmetricMessageSenderKeypairChanged,
|
||||
): Promise<void> {
|
||||
await this.contacts.createOrEditTrustedContact({
|
||||
contactUuid: message.sender_uuid,
|
||||
publicKey: trustedPayload.data.newEncryptionPublicKey,
|
||||
signingPublicKey: trustedPayload.data.newSigningPublicKey,
|
||||
})
|
||||
}
|
||||
|
||||
private async handleTrustedSharedVaultRootKeyChangedMessage(
|
||||
_message: AsymmetricMessageServerHash,
|
||||
trustedPayload: AsymmetricMessageSharedVaultRootKeyChanged,
|
||||
): Promise<void> {
|
||||
const useCase = new HandleTrustedSharedVaultRootKeyChangedMessage(
|
||||
this.mutator,
|
||||
this.items,
|
||||
this.sync,
|
||||
this.encryption,
|
||||
)
|
||||
await useCase.execute(trustedPayload)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import { EncryptionProviderInterface } from '@standardnotes/encryption'
|
||||
import { ContactServiceInterface } from '../../Contacts/ContactServiceInterface'
|
||||
import { AsymmetricMessageServerHash } from '@standardnotes/responses'
|
||||
import { AsymmetricMessagePayload } from '@standardnotes/models'
|
||||
|
||||
export class GetAsymmetricMessageTrustedPayload<M extends AsymmetricMessagePayload> {
|
||||
constructor(private encryption: EncryptionProviderInterface, private contacts: ContactServiceInterface) {}
|
||||
|
||||
execute(dto: { privateKey: string; message: AsymmetricMessageServerHash }): M | undefined {
|
||||
const trustedContact = this.contacts.findTrustedContact(dto.message.sender_uuid)
|
||||
if (!trustedContact) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
const decryptionResult = this.encryption.asymmetricallyDecryptMessage<M>({
|
||||
encryptedString: dto.message.encrypted_message,
|
||||
trustedSender: trustedContact,
|
||||
privateKey: dto.privateKey,
|
||||
})
|
||||
|
||||
return decryptionResult
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { EncryptionProviderInterface } from '@standardnotes/encryption'
|
||||
import { AsymmetricMessageServerHash } from '@standardnotes/responses'
|
||||
import { AsymmetricMessagePayload } from '@standardnotes/models'
|
||||
|
||||
export class GetAsymmetricMessageUntrustedPayload<M extends AsymmetricMessagePayload> {
|
||||
constructor(private encryption: EncryptionProviderInterface) {}
|
||||
|
||||
execute(dto: { privateKey: string; message: AsymmetricMessageServerHash }): M | undefined {
|
||||
const decryptionResult = this.encryption.asymmetricallyDecryptMessage<M>({
|
||||
encryptedString: dto.message.encrypted_message,
|
||||
trustedSender: undefined,
|
||||
privateKey: dto.privateKey,
|
||||
})
|
||||
|
||||
return decryptionResult
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { ClientDisplayableError, isErrorResponse, AsymmetricMessageServerHash } from '@standardnotes/responses'
|
||||
import { AsymmetricMessageServerInterface } from '@standardnotes/api'
|
||||
|
||||
export class GetInboundAsymmetricMessages {
|
||||
constructor(private messageServer: AsymmetricMessageServerInterface) {}
|
||||
|
||||
async execute(): Promise<AsymmetricMessageServerHash[] | ClientDisplayableError> {
|
||||
const response = await this.messageServer.getMessages()
|
||||
|
||||
if (isErrorResponse(response)) {
|
||||
return ClientDisplayableError.FromError(response.data.error)
|
||||
}
|
||||
|
||||
return response.data.messages
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { ClientDisplayableError, isErrorResponse, AsymmetricMessageServerHash } from '@standardnotes/responses'
|
||||
import { AsymmetricMessageServerInterface } from '@standardnotes/api'
|
||||
|
||||
export class GetOutboundAsymmetricMessages {
|
||||
constructor(private messageServer: AsymmetricMessageServerInterface) {}
|
||||
|
||||
async execute(): Promise<AsymmetricMessageServerHash[] | ClientDisplayableError> {
|
||||
const response = await this.messageServer.getOutboundUserMessages()
|
||||
|
||||
if (isErrorResponse(response)) {
|
||||
return ClientDisplayableError.FromError(response.data.error)
|
||||
}
|
||||
|
||||
return response.data.messages
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
import { MutatorClientInterface } from './../../Mutator/MutatorClientInterface'
|
||||
import { HandleTrustedSharedVaultInviteMessage } from './HandleTrustedSharedVaultInviteMessage'
|
||||
import { SyncServiceInterface } from '../../Sync/SyncServiceInterface'
|
||||
import { ContactServiceInterface } from '../../Contacts/ContactServiceInterface'
|
||||
import { ContentType } from '@standardnotes/common'
|
||||
import {
|
||||
AsymmetricMessagePayloadType,
|
||||
AsymmetricMessageSharedVaultInvite,
|
||||
KeySystemRootKeyContent,
|
||||
} from '@standardnotes/models'
|
||||
|
||||
describe('HandleTrustedSharedVaultInviteMessage', () => {
|
||||
let mutatorMock: jest.Mocked<MutatorClientInterface>
|
||||
let syncServiceMock: jest.Mocked<SyncServiceInterface>
|
||||
let contactServiceMock: jest.Mocked<ContactServiceInterface>
|
||||
|
||||
beforeEach(() => {
|
||||
mutatorMock = {
|
||||
createItem: jest.fn(),
|
||||
} as any
|
||||
|
||||
syncServiceMock = {
|
||||
sync: jest.fn(),
|
||||
} as any
|
||||
|
||||
contactServiceMock = {
|
||||
createOrEditTrustedContact: jest.fn(),
|
||||
} as any
|
||||
})
|
||||
|
||||
it('should create root key before creating vault listing so that propagated vault listings do not appear as locked', async () => {
|
||||
const handleTrustedSharedVaultInviteMessage = new HandleTrustedSharedVaultInviteMessage(
|
||||
mutatorMock,
|
||||
syncServiceMock,
|
||||
contactServiceMock,
|
||||
)
|
||||
|
||||
const testMessage = {
|
||||
type: AsymmetricMessagePayloadType.SharedVaultInvite,
|
||||
data: {
|
||||
recipientUuid: 'test-recipient-uuid',
|
||||
rootKey: {
|
||||
systemIdentifier: 'test-system-identifier',
|
||||
} as jest.Mocked<KeySystemRootKeyContent>,
|
||||
metadata: {
|
||||
name: 'test-name',
|
||||
},
|
||||
trustedContacts: [],
|
||||
},
|
||||
} as jest.Mocked<AsymmetricMessageSharedVaultInvite>
|
||||
|
||||
const sharedVaultUuid = 'test-shared-vault-uuid'
|
||||
const senderUuid = 'test-sender-uuid'
|
||||
|
||||
await handleTrustedSharedVaultInviteMessage.execute(testMessage, sharedVaultUuid, senderUuid)
|
||||
|
||||
const keySystemRootKeyCallIndex = mutatorMock.createItem.mock.calls.findIndex(
|
||||
([contentType]) => contentType === ContentType.KeySystemRootKey,
|
||||
)
|
||||
|
||||
const vaultListingCallIndex = mutatorMock.createItem.mock.calls.findIndex(
|
||||
([contentType]) => contentType === ContentType.VaultListing,
|
||||
)
|
||||
|
||||
expect(keySystemRootKeyCallIndex).toBeLessThan(vaultListingCallIndex)
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,64 @@
|
||||
import { ContactServiceInterface } from './../../Contacts/ContactServiceInterface'
|
||||
import { SyncServiceInterface } from '../../Sync/SyncServiceInterface'
|
||||
import {
|
||||
KeySystemRootKeyInterface,
|
||||
AsymmetricMessageSharedVaultInvite,
|
||||
KeySystemRootKeyContent,
|
||||
FillItemContent,
|
||||
FillItemContentSpecialized,
|
||||
VaultListingContentSpecialized,
|
||||
KeySystemRootKeyStorageMode,
|
||||
} from '@standardnotes/models'
|
||||
import { ContentType } from '@standardnotes/common'
|
||||
import { MutatorClientInterface } from '../../Mutator/MutatorClientInterface'
|
||||
|
||||
export class HandleTrustedSharedVaultInviteMessage {
|
||||
constructor(
|
||||
private mutator: MutatorClientInterface,
|
||||
private sync: SyncServiceInterface,
|
||||
private contacts: ContactServiceInterface,
|
||||
) {}
|
||||
|
||||
async execute(
|
||||
message: AsymmetricMessageSharedVaultInvite,
|
||||
sharedVaultUuid: string,
|
||||
senderUuid: string,
|
||||
): Promise<void> {
|
||||
const { rootKey: rootKeyContent, trustedContacts, metadata } = message.data
|
||||
|
||||
const content: VaultListingContentSpecialized = {
|
||||
systemIdentifier: rootKeyContent.systemIdentifier,
|
||||
rootKeyParams: rootKeyContent.keyParams,
|
||||
keyStorageMode: KeySystemRootKeyStorageMode.Synced,
|
||||
name: metadata.name,
|
||||
description: metadata.description,
|
||||
sharing: {
|
||||
sharedVaultUuid: sharedVaultUuid,
|
||||
ownerUserUuid: senderUuid,
|
||||
},
|
||||
}
|
||||
|
||||
await this.mutator.createItem<KeySystemRootKeyInterface>(
|
||||
ContentType.KeySystemRootKey,
|
||||
FillItemContent<KeySystemRootKeyContent>(rootKeyContent),
|
||||
true,
|
||||
)
|
||||
|
||||
await this.mutator.createItem(ContentType.VaultListing, FillItemContentSpecialized(content), true)
|
||||
|
||||
for (const contact of trustedContacts) {
|
||||
if (contact.isMe) {
|
||||
throw new Error('Should not receive isMe contact from invite')
|
||||
}
|
||||
|
||||
await this.contacts.createOrEditTrustedContact({
|
||||
name: contact.name,
|
||||
contactUuid: contact.contactUuid,
|
||||
publicKey: contact.publicKeySet.encryption,
|
||||
signingPublicKey: contact.publicKeySet.signing,
|
||||
})
|
||||
}
|
||||
|
||||
void this.sync.sync({ sourceDescription: 'Not awaiting due to this event handler running from sync response' })
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
import { ItemManagerInterface } from './../../Item/ItemManagerInterface'
|
||||
import { MutatorClientInterface } from './../../Mutator/MutatorClientInterface'
|
||||
import { SyncServiceInterface } from '../../Sync/SyncServiceInterface'
|
||||
import {
|
||||
KeySystemRootKeyInterface,
|
||||
AsymmetricMessageSharedVaultRootKeyChanged,
|
||||
FillItemContent,
|
||||
KeySystemRootKeyContent,
|
||||
VaultListingMutator,
|
||||
} from '@standardnotes/models'
|
||||
|
||||
import { ContentType } from '@standardnotes/common'
|
||||
import { GetVaultUseCase } from '../../Vaults/UseCase/GetVault'
|
||||
import { EncryptionProviderInterface } from '@standardnotes/encryption'
|
||||
|
||||
export class HandleTrustedSharedVaultRootKeyChangedMessage {
|
||||
constructor(
|
||||
private mutator: MutatorClientInterface,
|
||||
private items: ItemManagerInterface,
|
||||
private sync: SyncServiceInterface,
|
||||
private encryption: EncryptionProviderInterface,
|
||||
) {}
|
||||
|
||||
async execute(message: AsymmetricMessageSharedVaultRootKeyChanged): Promise<void> {
|
||||
const rootKeyContent = message.data.rootKey
|
||||
|
||||
await this.mutator.createItem<KeySystemRootKeyInterface>(
|
||||
ContentType.KeySystemRootKey,
|
||||
FillItemContent<KeySystemRootKeyContent>(rootKeyContent),
|
||||
true,
|
||||
)
|
||||
|
||||
const vault = new GetVaultUseCase(this.items).execute({ keySystemIdentifier: rootKeyContent.systemIdentifier })
|
||||
if (vault) {
|
||||
await this.mutator.changeItem<VaultListingMutator>(vault, (mutator) => {
|
||||
mutator.rootKeyParams = rootKeyContent.keyParams
|
||||
})
|
||||
}
|
||||
|
||||
await this.encryption.decryptErroredPayloads()
|
||||
|
||||
void this.sync.sync({ sourceDescription: 'Not awaiting due to this event handler running from sync response' })
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
import { ClientDisplayableError, isErrorResponse, AsymmetricMessageServerHash } from '@standardnotes/responses'
|
||||
import { AsymmetricMessageServerInterface } from '@standardnotes/api'
|
||||
|
||||
export class SendAsymmetricMessageUseCase {
|
||||
constructor(private messageServer: AsymmetricMessageServerInterface) {}
|
||||
|
||||
async execute(params: {
|
||||
recipientUuid: string
|
||||
encryptedMessage: string
|
||||
replaceabilityIdentifier: string | undefined
|
||||
}): Promise<AsymmetricMessageServerHash | ClientDisplayableError> {
|
||||
const response = await this.messageServer.createMessage({
|
||||
recipientUuid: params.recipientUuid,
|
||||
encryptedMessage: params.encryptedMessage,
|
||||
replaceabilityIdentifier: params.replaceabilityIdentifier,
|
||||
})
|
||||
|
||||
if (isErrorResponse(response)) {
|
||||
return ClientDisplayableError.FromError(response.data.error)
|
||||
}
|
||||
|
||||
return response.data.message
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
import { EncryptionProviderInterface } from '@standardnotes/encryption'
|
||||
import { AsymmetricMessageServerHash, ClientDisplayableError } from '@standardnotes/responses'
|
||||
import {
|
||||
TrustedContactInterface,
|
||||
AsymmetricMessagePayloadType,
|
||||
AsymmetricMessageSenderKeypairChanged,
|
||||
} from '@standardnotes/models'
|
||||
import { AsymmetricMessageServer } from '@standardnotes/api'
|
||||
import { PkcKeyPair } from '@standardnotes/sncrypto-common'
|
||||
import { SendAsymmetricMessageUseCase } from './SendAsymmetricMessageUseCase'
|
||||
|
||||
export class SendOwnContactChangeMessage {
|
||||
constructor(private encryption: EncryptionProviderInterface, private messageServer: AsymmetricMessageServer) {}
|
||||
|
||||
async execute(params: {
|
||||
senderOldKeyPair: PkcKeyPair
|
||||
senderOldSigningKeyPair: PkcKeyPair
|
||||
senderNewKeyPair: PkcKeyPair
|
||||
senderNewSigningKeyPair: PkcKeyPair
|
||||
contact: TrustedContactInterface
|
||||
}): Promise<AsymmetricMessageServerHash | ClientDisplayableError> {
|
||||
const message: AsymmetricMessageSenderKeypairChanged = {
|
||||
type: AsymmetricMessagePayloadType.SenderKeypairChanged,
|
||||
data: {
|
||||
recipientUuid: params.contact.contactUuid,
|
||||
newEncryptionPublicKey: params.senderNewKeyPair.publicKey,
|
||||
newSigningPublicKey: params.senderNewSigningKeyPair.publicKey,
|
||||
},
|
||||
}
|
||||
|
||||
const encryptedMessage = this.encryption.asymmetricallyEncryptMessage({
|
||||
message: message,
|
||||
senderKeyPair: params.senderOldKeyPair,
|
||||
senderSigningKeyPair: params.senderOldSigningKeyPair,
|
||||
recipientPublicKey: params.contact.publicKeySet.encryption,
|
||||
})
|
||||
|
||||
const sendMessageUseCase = new SendAsymmetricMessageUseCase(this.messageServer)
|
||||
const sendMessageResult = await sendMessageUseCase.execute({
|
||||
recipientUuid: params.contact.contactUuid,
|
||||
encryptedMessage,
|
||||
replaceabilityIdentifier: undefined,
|
||||
})
|
||||
|
||||
return sendMessageResult
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user