mirror of
https://github.com/standardnotes/server
synced 2026-10-20 21:03:36 -04:00
feat: asymmetric messages
This commit is contained in:
@@ -7,7 +7,7 @@ import '../src/Controller/ItemsController'
|
||||
import '../src/Controller/SharedVaultsController'
|
||||
import '../src/Controller/SharedVaultUsersController'
|
||||
import '../src/Controller/SharedVaultInvitesController'
|
||||
import '../src/Controller/ContactsController'
|
||||
import '../src/Controller/AsymmetricMessagesController'
|
||||
|
||||
import helmet from 'helmet'
|
||||
import * as cors from 'cors'
|
||||
|
||||
+1
-1
@@ -26,7 +26,7 @@ export class CreateSharedVaultInvites1684528646896 implements MigrationInterface
|
||||
isNullable: false,
|
||||
}),
|
||||
new TableColumn({
|
||||
name: 'inviter_uuid',
|
||||
name: 'sender_uuid',
|
||||
type: 'varchar',
|
||||
length: '255',
|
||||
isNullable: false,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { TypeORMSharedVaultInviteRepository } from '../Domain/SharedVaultInvite/Repository/TypeORMSharedVaultInviteRepository'
|
||||
import { Contact } from './../Domain/Contact/Model/Contact'
|
||||
import { AsymmetricMessage } from './../Domain/AsymmetricMessage/Model/AsymmetricMessage'
|
||||
import { SharedVaultInvite } from '../Domain/SharedVaultInvite/Model/SharedVaultInvite'
|
||||
import { SharedVaultUser } from '../Domain/SharedVaultUser/Model/SharedVaultUser'
|
||||
import { SharedVault } from '../Domain/SharedVault/Model/SharedVault'
|
||||
@@ -33,16 +33,16 @@ import { SharedVaultProjection } from '../Projection/SharedVaultProjection'
|
||||
import { SharedVaultProjector } from '../Projection/SharedVaultProjector'
|
||||
import { SharedVaultInviteProjection } from '../Projection/SharedVaultInviteProjection'
|
||||
import { SharedVaultInviteProjector } from '../Projection/SharedVaultInviteProjector'
|
||||
import { ContactProjection } from '../Projection/ContactProjection'
|
||||
import { ContactProjector } from '../Projection/ContactProjector'
|
||||
import { AsymmetricMessageProjection } from '../Projection/AsymmetricMessageProjection'
|
||||
import { AsymmetricMessageProjector } from '../Projection/AsymmetricMessageProjector'
|
||||
import { SharedVaultInviteRepositoryInterface } from '../Domain/SharedVaultInvite/Repository/SharedVaultInviteRepositoryInterface'
|
||||
import { ContactsRepositoryInterface } from '../Domain/Contact/Repository/ContactRepositoryInterface'
|
||||
import { TypeORMContactRepository } from '../Domain/Contact/Repository/TypeORMContactRepository'
|
||||
import { TypeORMAsymmetricMessageRepository } from '../Domain/AsymmetricMessage/Repository/TypeORMAsymmetricMessageRepository'
|
||||
import { UserEventsRepositoryInterface } from '../Domain/UserEvent/Repository/UserEventRepositoryInterface'
|
||||
import { TypeORMUserEventRepository } from '../Domain/UserEvent/Repository/TypeORMUserEventRepository'
|
||||
import { UserEvent } from '../Domain/UserEvent/Model/UserEvent'
|
||||
import { UserEventProjection } from '../Projection/UserEventProjection'
|
||||
import { UserEventProjector } from '../Projection/UserEventProjector'
|
||||
import { AsymmetricMessageRepositoryInterface } from '../Domain/AsymmetricMessage/Repository/AsymmetricMessageRepositoryInterface'
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const newrelicFormatter = require('@newrelic/winston-enricher')
|
||||
|
||||
@@ -117,9 +117,9 @@ export class CommonContainerConfigLoader {
|
||||
return new TypeORMSharedVaultInviteRepository(context.container.get(TYPES.ORMSharedVaultInviteRepository))
|
||||
})
|
||||
container
|
||||
.bind<ContactsRepositoryInterface>(TYPES.ContactRepository)
|
||||
.bind<AsymmetricMessageRepositoryInterface>(TYPES.AsymmetricMessageRepository)
|
||||
.toDynamicValue((context: interfaces.Context) => {
|
||||
return new TypeORMContactRepository(context.container.get(TYPES.ORMContactRepository))
|
||||
return new TypeORMAsymmetricMessageRepository(context.container.get(TYPES.ORMAsymmetricMessageRepository))
|
||||
})
|
||||
container
|
||||
.bind<UserEventsRepositoryInterface>(TYPES.UserEventRepository)
|
||||
@@ -139,8 +139,8 @@ export class CommonContainerConfigLoader {
|
||||
.bind<Repository<SharedVaultInvite>>(TYPES.ORMSharedVaultInviteRepository)
|
||||
.toDynamicValue(() => AppDataSource.getRepository(SharedVaultInvite))
|
||||
container
|
||||
.bind<Repository<Contact>>(TYPES.ORMContactRepository)
|
||||
.toDynamicValue(() => AppDataSource.getRepository(Contact))
|
||||
.bind<Repository<AsymmetricMessage>>(TYPES.ORMAsymmetricMessageRepository)
|
||||
.toDynamicValue(() => AppDataSource.getRepository(AsymmetricMessage))
|
||||
container
|
||||
.bind<Repository<UserEvent>>(TYPES.ORMUserEventRepository)
|
||||
.toDynamicValue(() => AppDataSource.getRepository(UserEvent))
|
||||
@@ -166,9 +166,11 @@ export class CommonContainerConfigLoader {
|
||||
.toDynamicValue(() => {
|
||||
return new SharedVaultInviteProjector()
|
||||
})
|
||||
container.bind<ProjectorInterface<Contact, ContactProjection>>(TYPES.ContactProjector).toDynamicValue(() => {
|
||||
return new ContactProjector()
|
||||
})
|
||||
container
|
||||
.bind<ProjectorInterface<AsymmetricMessage, AsymmetricMessageProjection>>(TYPES.AsymmetricMessageProjector)
|
||||
.toDynamicValue(() => {
|
||||
return new AsymmetricMessageProjector()
|
||||
})
|
||||
container.bind<ProjectorInterface<UserEvent, UserEventProjection>>(TYPES.UserEventProjector).toDynamicValue(() => {
|
||||
return new UserEventProjector()
|
||||
})
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { AsymmetricMessage } from './../Domain/AsymmetricMessage/Model/AsymmetricMessage'
|
||||
import { UserEvent } from './../Domain/UserEvent/Model/UserEvent'
|
||||
import { Contact } from './../Domain/Contact/Model/Contact'
|
||||
import { SharedVaultInvite } from '../Domain/SharedVaultInvite/Model/SharedVaultInvite'
|
||||
import { SharedVaultUser } from '../Domain/SharedVaultUser/Model/SharedVaultUser'
|
||||
import { DataSource, LoggerOptions } from 'typeorm'
|
||||
@@ -43,7 +43,7 @@ const replicationConfig = {
|
||||
|
||||
const commonDataSourceOptions = {
|
||||
maxQueryExecutionTime,
|
||||
entities: [Item, SharedVault, SharedVaultUser, SharedVaultInvite, Contact, UserEvent],
|
||||
entities: [Item, SharedVault, SharedVaultUser, SharedVaultInvite, AsymmetricMessage, UserEvent],
|
||||
migrations: [`dist/migrations/${isConfiguredForMySQL ? 'mysql' : 'sqlite'}/*.js`],
|
||||
migrationsRun: true,
|
||||
logging: <LoggerOptions>env.get('DB_DEBUG_LEVEL'),
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { SaveItem } from './../Domain/UseCase/SaveItem/SaveItem'
|
||||
import { ContactFactory } from './../Domain/Contact/Factory/ContactFactory'
|
||||
import { AsymmetricMessageFactory } from './../Domain/AsymmetricMessage/Factory/AsymmetricMessageFactory'
|
||||
|
||||
import { SharedVaultServiceInterface } from '../Domain/SharedVault/Service/SharedVaultServiceInterface'
|
||||
import { SharedVaultUserFactory } from '../Domain/SharedVaultUser/Factory/SharedVaultUserFactory'
|
||||
@@ -44,9 +44,9 @@ import { SharedVaultUserServiceInterface } from '../Domain/SharedVaultUser/Servi
|
||||
import { SharedVaultInviteServiceInterface } from '../Domain/SharedVaultInvite/Service/SharedVaultInviteServiceInterface'
|
||||
import { SharedVaultInviteService } from '../Domain/SharedVaultInvite/Service/SharedVaultInviteService'
|
||||
import { SharedVaultInviteFactory } from '../Domain/SharedVaultInvite/Factory/SharedVaultInviteFactory'
|
||||
import { ContactServiceInterface } from '../Domain/Contact/Service/ContactServiceInterface'
|
||||
import { ContactService } from '../Domain/Contact/Service/ContactService'
|
||||
import { ContactFactoryInterface } from '../Domain/Contact/Factory/ContactFactoryInterface'
|
||||
import { AsymmetricMessageServiceInterface } from '../Domain/AsymmetricMessage/Service/AsymmetricMessageServiceInterface'
|
||||
import { AsymmetricMessageService } from '../Domain/AsymmetricMessage/Service/AsymmetricMessageService'
|
||||
import { AsymmetricMessageFactoryInterface } from '../Domain/AsymmetricMessage/Factory/AsymmetricMessageFactoryInterface'
|
||||
import { SharedVaultSnjsVersionFilter } from '../Domain/Item/SaveRule/SharedVaultSnjsVersionFilter'
|
||||
import { CreateSharedVaultFileValetToken } from '../Domain/UseCase/CreateSharedVaultFileValetToken/CreateSharedVaultFileValetToken'
|
||||
import { UserEventServiceInterface } from '../Domain/UserEvent/Service/UserEventServiceInterface'
|
||||
@@ -106,7 +106,7 @@ export class ServerContainerConfigLoader extends CommonContainerConfigLoader {
|
||||
context.container.get(TYPES.ItemService),
|
||||
context.container.get(TYPES.SharedVaultService),
|
||||
context.container.get(TYPES.SharedVaultInviteService),
|
||||
context.container.get(TYPES.ContactService),
|
||||
context.container.get(TYPES.AsymmetricMessageService),
|
||||
context.container.get(TYPES.UserEventService),
|
||||
)
|
||||
})
|
||||
@@ -189,13 +189,15 @@ export class ServerContainerConfigLoader extends CommonContainerConfigLoader {
|
||||
context.container.get(TYPES.Timer),
|
||||
)
|
||||
})
|
||||
container.bind<ContactServiceInterface>(TYPES.ContactService).toDynamicValue((context: interfaces.Context) => {
|
||||
return new ContactService(
|
||||
context.container.get(TYPES.ContactRepository),
|
||||
context.container.get(TYPES.ContactFactory),
|
||||
context.container.get(TYPES.Timer),
|
||||
)
|
||||
})
|
||||
container
|
||||
.bind<AsymmetricMessageServiceInterface>(TYPES.AsymmetricMessageService)
|
||||
.toDynamicValue((context: interfaces.Context) => {
|
||||
return new AsymmetricMessageService(
|
||||
context.container.get(TYPES.AsymmetricMessageRepository),
|
||||
context.container.get(TYPES.AsymmetricMessageFactory),
|
||||
context.container.get(TYPES.Timer),
|
||||
)
|
||||
})
|
||||
container.bind<UserEventServiceInterface>(TYPES.UserEventService).toDynamicValue((context: interfaces.Context) => {
|
||||
return new UserEventService(
|
||||
context.container.get(TYPES.UserEventRepository),
|
||||
@@ -217,7 +219,7 @@ export class ServerContainerConfigLoader extends CommonContainerConfigLoader {
|
||||
context.container.get(TYPES.ItemConflictProjector),
|
||||
context.container.get(TYPES.SavedItemProjector),
|
||||
context.container.get(TYPES.SharedVaultInviteProjector),
|
||||
context.container.get(TYPES.ContactProjector),
|
||||
context.container.get(TYPES.AsymmetricMessageProjector),
|
||||
context.container.get(TYPES.SharedVaultProjector),
|
||||
context.container.get(TYPES.UserEventProjector),
|
||||
)
|
||||
@@ -249,9 +251,11 @@ export class ServerContainerConfigLoader extends CommonContainerConfigLoader {
|
||||
.toDynamicValue((context: interfaces.Context) => {
|
||||
return new SharedVaultInviteFactory(context.container.get(TYPES.Timer))
|
||||
})
|
||||
container.bind<ContactFactoryInterface>(TYPES.ContactFactory).toDynamicValue((context: interfaces.Context) => {
|
||||
return new ContactFactory(context.container.get(TYPES.Timer))
|
||||
})
|
||||
container
|
||||
.bind<AsymmetricMessageFactoryInterface>(TYPES.AsymmetricMessageFactory)
|
||||
.toDynamicValue((context: interfaces.Context) => {
|
||||
return new AsymmetricMessageFactory(context.container.get(TYPES.Timer))
|
||||
})
|
||||
container.bind<UserEventFactoryInterface>(TYPES.UserEventFactory).toDynamicValue((context: interfaces.Context) => {
|
||||
return new UserEventFactory(context.container.get(TYPES.Timer))
|
||||
})
|
||||
|
||||
@@ -11,7 +11,7 @@ const TYPES = {
|
||||
ItemRepository: Symbol.for('ItemRepository'),
|
||||
SharedVaultRepository: Symbol.for('SharedVaultRepository'),
|
||||
SharedVaultUserRepository: Symbol.for('SharedVaultUserRepository'),
|
||||
ContactRepository: Symbol.for('ContactRepository'),
|
||||
AsymmetricMessageRepository: Symbol.for('AsymmetricMessageRepository'),
|
||||
SharedVaultInviteRepository: Symbol.for('SharedVaultInviteRepository'),
|
||||
UserEventRepository: Symbol.for('UserEventRepository'),
|
||||
|
||||
@@ -19,7 +19,7 @@ const TYPES = {
|
||||
ORMItemRepository: Symbol.for('ORMItemRepository'),
|
||||
ORMSharedVaultRepository: Symbol.for('ORMSharedVaultRepository'),
|
||||
ORMSharedVaultUserRepository: Symbol.for('ORMSharedVaultUserRepository'),
|
||||
ORMContactRepository: Symbol.for('ORMContactRepository'),
|
||||
ORMAsymmetricMessageRepository: Symbol.for('ORMAsymmetricMessageRepository'),
|
||||
ORMSharedVaultInviteRepository: Symbol.for('ORMSharedVaultInviteRepository'),
|
||||
ORMUserEventRepository: Symbol.for('ORMUserEventRepository'),
|
||||
|
||||
@@ -32,14 +32,14 @@ const TYPES = {
|
||||
ItemConflictProjector: Symbol.for('ItemConflictProjector'),
|
||||
SharedVaultProjector: Symbol.for('SharedVaultProjector'),
|
||||
SharedVaultUserProjector: Symbol.for('SharedVaultUserProjector'),
|
||||
ContactProjector: Symbol.for('ContactProjector'),
|
||||
AsymmetricMessageProjector: Symbol.for('AsymmetricMessageProjector'),
|
||||
SharedVaultInviteProjector: Symbol.for('SharedVaultInviteProjector'),
|
||||
UserEventProjector: Symbol.for('UserEventProjector'),
|
||||
|
||||
// Services
|
||||
SharedVaultService: Symbol.for('SharedVaultService'),
|
||||
SharedVaultUserService: Symbol.for('SharedVaultUserService'),
|
||||
ContactService: Symbol.for('ContactService'),
|
||||
AsymmetricMessageService: Symbol.for('AsymmetricMessageService'),
|
||||
SharedVaultInviteService: Symbol.for('SharedVaultInviteService'),
|
||||
UserEventService: Symbol.for('UserEventService'),
|
||||
|
||||
@@ -47,7 +47,7 @@ const TYPES = {
|
||||
ItemFactory: Symbol.for('ItemFactory'),
|
||||
SharedVaultFactory: Symbol.for('SharedVaultFactory'),
|
||||
SharedVaultUserFactory: Symbol.for('SharedVaultUserFactory'),
|
||||
ContactFactory: Symbol.for('ContactFactory'),
|
||||
AsymmetricMessageFactory: Symbol.for('AsymmetricMessageFactory'),
|
||||
SharedVaultInviteFactory: Symbol.for('SharedVaultInviteFactory'),
|
||||
UserEventFactory: Symbol.for('UserEventFactory'),
|
||||
|
||||
@@ -78,7 +78,6 @@ const TYPES = {
|
||||
SaveItem: Symbol.for('SaveItem'),
|
||||
CreateSharedVaultFileReadValetToken: Symbol.for('CreateSharedVaultFileReadValetToken'),
|
||||
// Handlers
|
||||
UserCredentialsChangedEventHandler: Symbol.for('UserCredentialsChangedEventHandler'),
|
||||
AccountDeletionRequestedEventHandler: Symbol.for('AccountDeletionRequestedEventHandler'),
|
||||
DuplicateItemSyncedEventHandler: Symbol.for('DuplicateItemSyncedEventHandler'),
|
||||
EmailBackupRequestedEventHandler: Symbol.for('EmailBackupRequestedEventHandler'),
|
||||
|
||||
@@ -30,7 +30,6 @@ import { EmailBackupRequestedEventHandler } from '../Domain/Handler/EmailBackupR
|
||||
import { ItemRevisionCreationRequestedEventHandler } from '../Domain/Handler/ItemRevisionCreationRequestedEventHandler'
|
||||
import { FSItemBackupService } from '../Infra/FS/FSItemBackupService'
|
||||
import { CommonContainerConfigLoader } from './CommonContainerConfigLoader'
|
||||
import { UserCredentialsChangedEventHandler } from '../Domain/Handler/UserCredentialsChangedEventHandler'
|
||||
import { SharedVaultFileUploadedEventHandler } from '../Domain/Handler/SharedVaultFileUploadedEventHandler'
|
||||
import { SharedVaultFileRemovedEventHandler } from '../Domain/Handler/SharedVaultFileRemovedEventHandler'
|
||||
|
||||
@@ -135,16 +134,6 @@ export class WorkerContainerConfigLoader extends CommonContainerConfigLoader {
|
||||
)
|
||||
})
|
||||
|
||||
container
|
||||
.bind<UserCredentialsChangedEventHandler>(TYPES.UserCredentialsChangedEventHandler)
|
||||
.toDynamicValue((context: interfaces.Context) => {
|
||||
return new UserCredentialsChangedEventHandler(
|
||||
context.container.get(TYPES.ContactRepository),
|
||||
context.container.get(TYPES.SharedVaultInviteRepository),
|
||||
context.container.get(TYPES.Timer),
|
||||
)
|
||||
})
|
||||
|
||||
container
|
||||
.bind<SharedVaultFileUploadedEventHandler>(TYPES.SharedVaultFileUploadedEventHandler)
|
||||
.to(SharedVaultFileUploadedEventHandler)
|
||||
@@ -202,7 +191,6 @@ export class WorkerContainerConfigLoader extends CommonContainerConfigLoader {
|
||||
['ACCOUNT_DELETION_REQUESTED', context.container.get(TYPES.AccountDeletionRequestedEventHandler)],
|
||||
['EMAIL_BACKUP_REQUESTED', context.container.get(TYPES.EmailBackupRequestedEventHandler)],
|
||||
['ITEM_REVISION_CREATION_REQUESTED', context.container.get(TYPES.ItemRevisionCreationRequestedEventHandler)],
|
||||
['USER_CREDENTIALS_CHANGED', container.get(TYPES.UserCredentialsChangedEventHandler)],
|
||||
['SHARED_VAULT_FILE_REMOVED', container.get(TYPES.SharedVaultFileRemovedEventHandler)],
|
||||
['SHARED_VAULT_FILE_UPLOADED', container.get(TYPES.SharedVaultFileUploadedEventHandler)],
|
||||
])
|
||||
|
||||
@@ -0,0 +1,136 @@
|
||||
import { Request, Response } from 'express'
|
||||
import {
|
||||
BaseHttpController,
|
||||
controller,
|
||||
httpPost,
|
||||
results,
|
||||
httpDelete,
|
||||
httpGet,
|
||||
httpPatch,
|
||||
} from 'inversify-express-utils'
|
||||
import TYPES from '../Bootstrap/Types'
|
||||
import { inject } from 'inversify'
|
||||
import { AsymmetricMessageServiceInterface } from '../Domain/AsymmetricMessage/Service/AsymmetricMessageServiceInterface'
|
||||
import { AsymmetricMessageProjector } from '../Projection/AsymmetricMessageProjector'
|
||||
|
||||
@controller('/asymmetric-messages')
|
||||
export class AsymmetricMessagesController extends BaseHttpController {
|
||||
constructor(
|
||||
@inject(TYPES.AsymmetricMessageService) private asymmetricMessageService: AsymmetricMessageServiceInterface,
|
||||
@inject(TYPES.AsymmetricMessageProjector) private asymmetricMessageProjector: AsymmetricMessageProjector,
|
||||
) {
|
||||
super()
|
||||
}
|
||||
|
||||
@httpGet('/', TYPES.AuthMiddleware)
|
||||
public async getAsymmetricMessages(
|
||||
_request: Request,
|
||||
response: Response,
|
||||
): Promise<results.NotFoundResult | results.JsonResult> {
|
||||
const result = await this.asymmetricMessageService.getMessagesForUser({
|
||||
userUuid: response.locals.user.uuid,
|
||||
})
|
||||
|
||||
if (!result) {
|
||||
return this.errorResponse(400, 'Could not get shared vault messages')
|
||||
}
|
||||
|
||||
const projected = result.map((message) => this.asymmetricMessageProjector.projectFull(message))
|
||||
|
||||
return this.json({ messages: projected })
|
||||
}
|
||||
|
||||
@httpPost('/', TYPES.AuthMiddleware)
|
||||
public async createAsymmetricMessage(
|
||||
request: Request,
|
||||
response: Response,
|
||||
): Promise<results.NotFoundResult | results.JsonResult> {
|
||||
const result = await this.asymmetricMessageService.createMessage({
|
||||
senderUuid: response.locals.user.uuid,
|
||||
userUuid: request.body.messagee_uuid,
|
||||
senderPublicKey: request.body.sender_public_key,
|
||||
encryptedMessage: request.body.encrypted_message,
|
||||
})
|
||||
|
||||
if (!result) {
|
||||
return this.errorResponse(400, 'Could not create message')
|
||||
}
|
||||
|
||||
return this.json({ message: this.asymmetricMessageProjector.projectFull(result) })
|
||||
}
|
||||
|
||||
@httpPatch('/:messageUuid', TYPES.AuthMiddleware)
|
||||
public async updateAsymmetricMessage(
|
||||
request: Request,
|
||||
response: Response,
|
||||
): Promise<results.NotFoundResult | results.JsonResult> {
|
||||
const result = await this.asymmetricMessageService.updateMessage({
|
||||
senderUuid: response.locals.user.uuid,
|
||||
messageUuid: request.params.messageUuid,
|
||||
senderPublicKey: request.body.sender_public_key,
|
||||
encryptedMessage: request.body.encrypted_message,
|
||||
})
|
||||
|
||||
if (!result) {
|
||||
return this.errorResponse(400, 'Could not update message')
|
||||
}
|
||||
|
||||
return this.json({ message: this.asymmetricMessageProjector.projectFull(result) })
|
||||
}
|
||||
|
||||
@httpDelete('/inbound', TYPES.AuthMiddleware)
|
||||
public async deleteInboundAsymmetricMessages(
|
||||
_request: Request,
|
||||
response: Response,
|
||||
): Promise<results.NotFoundResult | results.JsonResult> {
|
||||
await this.asymmetricMessageService.deleteAllInboundMessages({
|
||||
userUuid: response.locals.user.uuid,
|
||||
})
|
||||
|
||||
return this.json({ success: true })
|
||||
}
|
||||
|
||||
@httpGet('/outbound', TYPES.AuthMiddleware)
|
||||
public async getOutboundAsymmetricMessages(
|
||||
_request: Request,
|
||||
response: Response,
|
||||
): Promise<results.NotFoundResult | results.JsonResult> {
|
||||
const result = await this.asymmetricMessageService.getOutboundMessagesForUser({
|
||||
userUuid: response.locals.user.uuid,
|
||||
})
|
||||
|
||||
if (!result) {
|
||||
return this.errorResponse(400, 'Could not get shared vault messages')
|
||||
}
|
||||
|
||||
const projected = result.map((message) => this.asymmetricMessageProjector.projectFull(message))
|
||||
|
||||
return this.json({ messages: projected })
|
||||
}
|
||||
|
||||
@httpDelete('/:messageUuid', TYPES.AuthMiddleware)
|
||||
public async deleteAsymmetricMessage(
|
||||
request: Request,
|
||||
response: Response,
|
||||
): Promise<results.NotFoundResult | results.JsonResult> {
|
||||
const result = await this.asymmetricMessageService.deleteMessage({
|
||||
originatorUuid: response.locals.user.uuid,
|
||||
messageUuid: request.params.messageUuid,
|
||||
})
|
||||
|
||||
if (!result) {
|
||||
return this.errorResponse(400, 'Could not delete message')
|
||||
}
|
||||
|
||||
return this.json({ success: true })
|
||||
}
|
||||
|
||||
private errorResponse(status: number, message?: string, tag?: string) {
|
||||
return this.json(
|
||||
{
|
||||
error: { message, tag },
|
||||
},
|
||||
status,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -28,11 +28,11 @@ export class SharedVaultInvitesController extends BaseHttpController {
|
||||
response: Response,
|
||||
): Promise<results.NotFoundResult | results.JsonResult> {
|
||||
const result = await this.sharedVaultInviteService.createInvite({
|
||||
originatorUuid: response.locals.user.uuid,
|
||||
senderUuid: response.locals.user.uuid,
|
||||
sharedVaultUuid: request.params.sharedVaultUuid,
|
||||
userUuid: request.body.invitee_uuid,
|
||||
inviterPublicKey: request.body.sender_public_key,
|
||||
encryptedVaultKeyContent: request.body.encrypted_message,
|
||||
senderPublicKey: request.body.sender_public_key,
|
||||
encryptedMessage: request.body.encrypted_message,
|
||||
permissions: request.body.permissions,
|
||||
})
|
||||
|
||||
@@ -49,10 +49,10 @@ export class SharedVaultInvitesController extends BaseHttpController {
|
||||
response: Response,
|
||||
): Promise<results.NotFoundResult | results.JsonResult> {
|
||||
const result = await this.sharedVaultInviteService.updateInvite({
|
||||
originatorUuid: response.locals.user.uuid,
|
||||
senderUuid: response.locals.user.uuid,
|
||||
inviteUuid: request.params.inviteUuid,
|
||||
inviterPublicKey: request.body.sender_public_key,
|
||||
encryptedVaultKeyContent: request.body.encrypted_message,
|
||||
senderPublicKey: request.body.sender_public_key,
|
||||
encryptedMessage: request.body.encrypted_message,
|
||||
permissions: request.body.permissions,
|
||||
})
|
||||
|
||||
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
import { TimerInterface } from '@standardnotes/time'
|
||||
import { AsymmetricMessageFactoryInterface } from './AsymmetricMessageFactoryInterface'
|
||||
import { AsymmetricMessageHash } from './AsymmetricMessageHash'
|
||||
import { AsymmetricMessage } from '../Model/AsymmetricMessage'
|
||||
|
||||
export class AsymmetricMessageFactory implements AsymmetricMessageFactoryInterface {
|
||||
constructor(private timer: TimerInterface) {}
|
||||
|
||||
create(dto: AsymmetricMessageHash): AsymmetricMessage {
|
||||
const newAsymmetricMessage = new AsymmetricMessage()
|
||||
newAsymmetricMessage.uuid = dto.uuid
|
||||
newAsymmetricMessage.userUuid = dto.user_uuid
|
||||
newAsymmetricMessage.senderUuid = dto.sender_uuid
|
||||
newAsymmetricMessage.senderPublicKey = dto.sender_public_key
|
||||
newAsymmetricMessage.encryptedMessage = dto.encrypted_message
|
||||
|
||||
const now = this.timer.getTimestampInMicroseconds()
|
||||
newAsymmetricMessage.updatedAtTimestamp = now
|
||||
newAsymmetricMessage.createdAtTimestamp = now
|
||||
|
||||
if (dto.created_at_timestamp) {
|
||||
newAsymmetricMessage.createdAtTimestamp = dto.created_at_timestamp
|
||||
}
|
||||
|
||||
return newAsymmetricMessage
|
||||
}
|
||||
|
||||
createStub(dto: AsymmetricMessageHash): AsymmetricMessage {
|
||||
const item = this.create(dto)
|
||||
return item
|
||||
}
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
import { AsymmetricMessage } from '../Model/AsymmetricMessage'
|
||||
import { AsymmetricMessageHash } from './AsymmetricMessageHash'
|
||||
|
||||
export interface AsymmetricMessageFactoryInterface {
|
||||
create(dto: AsymmetricMessageHash): AsymmetricMessage
|
||||
createStub(dto: AsymmetricMessageHash): AsymmetricMessage
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
export type AsymmetricMessageHash = {
|
||||
uuid: string
|
||||
user_uuid: string
|
||||
sender_uuid: string
|
||||
sender_public_key: string
|
||||
encrypted_message: string
|
||||
created_at_timestamp?: number
|
||||
updated_at_timestamp?: number
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'
|
||||
|
||||
@Entity({ name: 'asymmetric_messages' })
|
||||
export class AsymmetricMessage {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
declare uuid: string
|
||||
|
||||
@Column({
|
||||
name: 'user_uuid',
|
||||
length: 36,
|
||||
})
|
||||
declare userUuid: string
|
||||
|
||||
@Column({
|
||||
name: 'sender_uuid',
|
||||
})
|
||||
declare senderUuid: string
|
||||
|
||||
@Column({
|
||||
name: 'sender_public_key',
|
||||
})
|
||||
declare senderPublicKey: string
|
||||
|
||||
@Column({
|
||||
name: 'encrypted_message',
|
||||
})
|
||||
declare encryptedMessage: string
|
||||
|
||||
@Column({
|
||||
name: 'created_at_timestamp',
|
||||
type: 'bigint',
|
||||
})
|
||||
declare createdAtTimestamp: number
|
||||
|
||||
@Column({
|
||||
name: 'updated_at_timestamp',
|
||||
type: 'bigint',
|
||||
})
|
||||
declare updatedAtTimestamp: number
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
import { AsymmetricMessage } from '../Model/AsymmetricMessage'
|
||||
|
||||
export type AsymmetricMessageQuery = {
|
||||
userUuid: string
|
||||
}
|
||||
|
||||
export type AsymmetricMessageFindAllForUserQuery = {
|
||||
userUuid?: string
|
||||
lastSyncTime?: number
|
||||
senderUuid?: string
|
||||
}
|
||||
|
||||
export interface AsymmetricMessageRepositoryInterface {
|
||||
findByUuid(asymmetricMessageUuid: string): Promise<AsymmetricMessage | null>
|
||||
create(sharedVault: AsymmetricMessage): Promise<AsymmetricMessage>
|
||||
save(asymmetricMessage: AsymmetricMessage): Promise<AsymmetricMessage>
|
||||
remove(sharedVault: AsymmetricMessage): Promise<AsymmetricMessage>
|
||||
findAll(query: AsymmetricMessageFindAllForUserQuery): Promise<AsymmetricMessage[]>
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
import { AsymmetricMessage } from '../Model/AsymmetricMessage'
|
||||
import { Repository, SelectQueryBuilder } from 'typeorm'
|
||||
import {
|
||||
AsymmetricMessageFindAllForUserQuery,
|
||||
AsymmetricMessageRepositoryInterface,
|
||||
} from './AsymmetricMessageRepositoryInterface'
|
||||
|
||||
export class TypeORMAsymmetricMessageRepository implements AsymmetricMessageRepositoryInterface {
|
||||
constructor(private ormRepository: Repository<AsymmetricMessage>) {}
|
||||
|
||||
async create(asymmetricMessage: AsymmetricMessage): Promise<AsymmetricMessage> {
|
||||
return this.ormRepository.save(asymmetricMessage)
|
||||
}
|
||||
|
||||
async save(asymmetricMessage: AsymmetricMessage): Promise<AsymmetricMessage> {
|
||||
return this.ormRepository.save(asymmetricMessage)
|
||||
}
|
||||
|
||||
findByUuid(uuid: string): Promise<AsymmetricMessage | null> {
|
||||
return this.ormRepository
|
||||
.createQueryBuilder('asymmetric_message')
|
||||
.where('asymmetric_message.uuid = :uuid', {
|
||||
uuid,
|
||||
})
|
||||
.getOne()
|
||||
}
|
||||
|
||||
async remove(asymmetricMessage: AsymmetricMessage): Promise<AsymmetricMessage> {
|
||||
return this.ormRepository.remove(asymmetricMessage)
|
||||
}
|
||||
|
||||
async findAll(query: AsymmetricMessageFindAllForUserQuery): Promise<AsymmetricMessage[]> {
|
||||
return this.createFindAllQueryBuilder(query).getMany()
|
||||
}
|
||||
|
||||
private createFindAllQueryBuilder(
|
||||
query: AsymmetricMessageFindAllForUserQuery,
|
||||
): SelectQueryBuilder<AsymmetricMessage> {
|
||||
const queryBuilder = this.ormRepository.createQueryBuilder('asymmetric_message')
|
||||
|
||||
if (query.userUuid) {
|
||||
queryBuilder.where('asymmetric_message.user_uuid = :userUuid', { userUuid: query.userUuid })
|
||||
} else if (query.senderUuid) {
|
||||
queryBuilder.where('asymmetric_message.sender_uuid = :senderUuid', {
|
||||
senderUuid: query.senderUuid,
|
||||
})
|
||||
}
|
||||
|
||||
if (query.lastSyncTime) {
|
||||
queryBuilder.andWhere('asymmetric_message.updated_at_timestamp > :lastSyncTime', {
|
||||
lastSyncTime: query.lastSyncTime,
|
||||
})
|
||||
}
|
||||
|
||||
return queryBuilder
|
||||
}
|
||||
}
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
import { UpdateAsymmetricMessageDTO } from './UpdateAsymmetricMessageDTO'
|
||||
import { TimerInterface } from '@standardnotes/time'
|
||||
import { AsymmetricMessage } from '../Model/AsymmetricMessage'
|
||||
import { AsymmetricMessageFactoryInterface } from '../Factory/AsymmetricMessageFactoryInterface'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import { AsymmetricMessageRepositoryInterface } from '../Repository/AsymmetricMessageRepositoryInterface'
|
||||
import { AsymmetricMessageServiceInterface } from './AsymmetricMessageServiceInterface'
|
||||
import { GetUserAsymmetricMessagesDTO } from './GetUserAsymmetricMessagesDTO'
|
||||
import { CreateAsymmetricMessageDTO } from './CreateAsymmetricMessageDTO'
|
||||
|
||||
export class AsymmetricMessageService implements AsymmetricMessageServiceInterface {
|
||||
constructor(
|
||||
private asymmetricMessageRepository: AsymmetricMessageRepositoryInterface,
|
||||
private asymmetricMessageFactory: AsymmetricMessageFactoryInterface,
|
||||
private timer: TimerInterface,
|
||||
) {}
|
||||
|
||||
async createMessage(dto: CreateAsymmetricMessageDTO): Promise<AsymmetricMessage | null> {
|
||||
const timestamp = this.timer.getTimestampInMicroseconds()
|
||||
|
||||
const asymmetricMessage = this.asymmetricMessageFactory.create({
|
||||
uuid: uuidv4(),
|
||||
user_uuid: dto.userUuid,
|
||||
sender_uuid: dto.senderUuid,
|
||||
sender_public_key: dto.senderPublicKey,
|
||||
encrypted_message: dto.encryptedMessage,
|
||||
created_at_timestamp: timestamp,
|
||||
updated_at_timestamp: timestamp,
|
||||
})
|
||||
|
||||
return this.asymmetricMessageRepository.create(asymmetricMessage)
|
||||
}
|
||||
|
||||
async updateMessage(dto: UpdateAsymmetricMessageDTO): Promise<AsymmetricMessage | null> {
|
||||
const asymmetricMessage = await this.asymmetricMessageRepository.findByUuid(dto.messageUuid)
|
||||
if (!asymmetricMessage || asymmetricMessage.senderUuid !== dto.senderUuid) {
|
||||
return null
|
||||
}
|
||||
|
||||
asymmetricMessage.senderPublicKey = dto.senderPublicKey
|
||||
asymmetricMessage.encryptedMessage = dto.encryptedMessage
|
||||
asymmetricMessage.updatedAtTimestamp = this.timer.getTimestampInMicroseconds()
|
||||
|
||||
return this.asymmetricMessageRepository.save(asymmetricMessage)
|
||||
}
|
||||
|
||||
getMessagesForUser(dto: GetUserAsymmetricMessagesDTO): Promise<AsymmetricMessage[]> {
|
||||
return this.asymmetricMessageRepository.findAll(dto)
|
||||
}
|
||||
|
||||
getOutboundMessagesForUser(dto: { userUuid: string }): Promise<AsymmetricMessage[]> {
|
||||
return this.asymmetricMessageRepository.findAll({
|
||||
senderUuid: dto.userUuid,
|
||||
})
|
||||
}
|
||||
|
||||
async deleteAllInboundMessages(dto: { userUuid: string }): Promise<void> {
|
||||
const inboundMessages = await this.asymmetricMessageRepository.findAll({
|
||||
userUuid: dto.userUuid,
|
||||
})
|
||||
|
||||
for (const message of inboundMessages) {
|
||||
await this.asymmetricMessageRepository.remove(message)
|
||||
}
|
||||
}
|
||||
|
||||
async deleteMessage(dto: { originatorUuid: string; messageUuid: string }): Promise<boolean> {
|
||||
const message = await this.asymmetricMessageRepository.findByUuid(dto.messageUuid)
|
||||
if (!message) {
|
||||
return false
|
||||
}
|
||||
|
||||
const isAuthorized = message.senderUuid === dto.originatorUuid || message.userUuid === dto.originatorUuid
|
||||
if (!isAuthorized) {
|
||||
return false
|
||||
}
|
||||
|
||||
await this.asymmetricMessageRepository.remove(message)
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
import { UpdateAsymmetricMessageDTO } from './UpdateAsymmetricMessageDTO'
|
||||
import { AsymmetricMessage } from '../Model/AsymmetricMessage'
|
||||
import { GetUserAsymmetricMessagesDTO } from './GetUserAsymmetricMessagesDTO'
|
||||
import { CreateAsymmetricMessageDTO } from './CreateAsymmetricMessageDTO'
|
||||
|
||||
export interface AsymmetricMessageServiceInterface {
|
||||
createMessage(dto: CreateAsymmetricMessageDTO): Promise<AsymmetricMessage | null>
|
||||
updateMessage(dto: UpdateAsymmetricMessageDTO): Promise<AsymmetricMessage | null>
|
||||
|
||||
getMessagesForUser(dto: GetUserAsymmetricMessagesDTO): Promise<AsymmetricMessage[]>
|
||||
getOutboundMessagesForUser(dto: { userUuid: string }): Promise<AsymmetricMessage[]>
|
||||
|
||||
deleteAllInboundMessages(dto: { userUuid: string }): Promise<void>
|
||||
deleteMessage(dto: { originatorUuid: string; messageUuid: string }): Promise<boolean>
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
export type CreateAsymmetricMessageDTO = {
|
||||
userUuid: string
|
||||
senderUuid: string
|
||||
senderPublicKey: string
|
||||
encryptedMessage: string
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
export interface GetUserAsymmetricMessagesDTO {
|
||||
userUuid: string
|
||||
lastSyncTime?: number
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
export type UpdateAsymmetricMessageDTO = {
|
||||
messageUuid: string
|
||||
senderUuid: string
|
||||
senderPublicKey: string
|
||||
encryptedMessage: string
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
import {
|
||||
DomainEventHandlerInterface,
|
||||
UserCredentialsChangedEvent,
|
||||
UserCredentialsChangedEventPayload,
|
||||
} from '@standardnotes/domain-events'
|
||||
import { TimerInterface } from '@standardnotes/time'
|
||||
import { ContactsRepositoryInterface } from '../Contact/Repository/ContactRepositoryInterface'
|
||||
import { SharedVaultInviteRepositoryInterface } from '../SharedVaultInvite/Repository/SharedVaultInviteRepositoryInterface'
|
||||
|
||||
export class UserCredentialsChangedEventHandler implements DomainEventHandlerInterface {
|
||||
constructor(
|
||||
private contactRepository: ContactsRepositoryInterface,
|
||||
private sharedVaultInviteRepository: SharedVaultInviteRepositoryInterface,
|
||||
private timer: TimerInterface,
|
||||
) {}
|
||||
|
||||
async handle(event: UserCredentialsChangedEvent): Promise<void> {
|
||||
await this.updatePublicKeyOfAllContacts(event.payload)
|
||||
|
||||
await this.nullifyPendingInboundSharedVaultInvites(event.payload)
|
||||
}
|
||||
|
||||
private async updatePublicKeyOfAllContacts(payload: UserCredentialsChangedEventPayload): Promise<void> {
|
||||
const contacts = await this.contactRepository.findAll({ contactUuid: payload.userUuid })
|
||||
for (const contact of contacts) {
|
||||
if (contact.contactPublicKey === payload.newPublicKey) {
|
||||
continue
|
||||
}
|
||||
|
||||
contact.contactPublicKey = payload.newPublicKey
|
||||
contact.contactSigningPublicKey = payload.newSigningPublicKey
|
||||
|
||||
contact.updatedAtTimestamp = this.timer.getTimestampInMicroseconds()
|
||||
|
||||
await this.contactRepository.save(contact)
|
||||
}
|
||||
}
|
||||
|
||||
private async nullifyPendingInboundSharedVaultInvites(payload: UserCredentialsChangedEventPayload): Promise<void> {
|
||||
const inboundInvites = await this.sharedVaultInviteRepository.findAll({
|
||||
userUuid: payload.userUuid,
|
||||
})
|
||||
|
||||
for (const invite of inboundInvites) {
|
||||
await this.sharedVaultInviteRepository.remove(invite)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ContactProjection } from '../../../Projection/ContactProjection'
|
||||
import { AsymmetricMessageProjection } from './../../../Projection/AsymmetricMessageProjection'
|
||||
import { SharedVaultInviteProjection } from '../../../Projection/SharedVaultInviteProjection'
|
||||
import { SharedVaultProjection } from '../../../Projection/SharedVaultProjection'
|
||||
import { ItemConflictProjection } from '../../../Projection/ItemConflictProjection'
|
||||
@@ -12,7 +12,7 @@ export type SyncResponse20200115 = {
|
||||
conflicts: Array<ItemConflictProjection>
|
||||
sync_token: string
|
||||
cursor_token?: string
|
||||
contacts: Array<ContactProjection>
|
||||
asymmetric_messages: Array<AsymmetricMessageProjection>
|
||||
shared_vaults: Array<SharedVaultProjection>
|
||||
shared_vault_invites: Array<SharedVaultInviteProjection>
|
||||
user_events: Array<UserEventProjection>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { AsymmetricMessageProjection } from './../../../Projection/AsymmetricMessageProjection'
|
||||
import { AsymmetricMessage } from './../../AsymmetricMessage/Model/AsymmetricMessage'
|
||||
import { SharedVaultInvite } from '../../SharedVaultInvite/Model/SharedVaultInvite'
|
||||
import { Contact } from './../../Contact/Model/Contact'
|
||||
import { ProjectorInterface } from '../../../Projection/ProjectorInterface'
|
||||
import { SyncItemsResponse } from '../../UseCase/SyncItemsResponse'
|
||||
import { Item } from '../Item'
|
||||
@@ -9,7 +10,6 @@ import { ItemProjection } from '../../../Projection/ItemProjection'
|
||||
import { SyncResponse20200115 } from './SyncResponse20200115'
|
||||
import { SyncResponseFactoryInterface } from './SyncResponseFactoryInterface'
|
||||
import { SavedItemProjection } from '../../../Projection/SavedItemProjection'
|
||||
import { ContactProjection } from '../../../Projection/ContactProjection'
|
||||
import { SharedVaultInviteProjection } from '../../../Projection/SharedVaultInviteProjection'
|
||||
import { SharedVaultProjection } from '../../../Projection/SharedVaultProjection'
|
||||
import { SharedVault } from '../../SharedVault/Model/SharedVault'
|
||||
@@ -22,7 +22,7 @@ export class SyncResponseFactory20200115 implements SyncResponseFactoryInterface
|
||||
private itemConflictProjector: ProjectorInterface<ItemConflict, ItemConflictProjection>,
|
||||
private savedItemProjector: ProjectorInterface<Item, SavedItemProjection>,
|
||||
private sharedVaultInviteProjector: ProjectorInterface<SharedVaultInvite, SharedVaultInviteProjection>,
|
||||
private contactProjector: ProjectorInterface<Contact, ContactProjection>,
|
||||
private asymmetricMessageProjector: ProjectorInterface<AsymmetricMessage, AsymmetricMessageProjection>,
|
||||
private sharedVaultProjector: ProjectorInterface<SharedVault, SharedVaultProjection>,
|
||||
private userEventProjector: ProjectorInterface<UserEvent, UserEventProjection>,
|
||||
) {}
|
||||
@@ -53,9 +53,9 @@ export class SyncResponseFactory20200115 implements SyncResponseFactoryInterface
|
||||
sharedVaultInvites.push(this.sharedVaultInviteProjector.projectFull(sharedVaultInvite))
|
||||
}
|
||||
|
||||
const contacts = []
|
||||
for (const contact of syncItemsResponse.contacts) {
|
||||
contacts.push(this.contactProjector.projectFull(contact))
|
||||
const asymmetricMessages = []
|
||||
for (const contact of syncItemsResponse.asymmetricMessages) {
|
||||
asymmetricMessages.push(this.asymmetricMessageProjector.projectFull(contact))
|
||||
}
|
||||
|
||||
const userEvents = []
|
||||
@@ -69,10 +69,10 @@ export class SyncResponseFactory20200115 implements SyncResponseFactoryInterface
|
||||
conflicts,
|
||||
sync_token: syncItemsResponse.syncToken,
|
||||
cursor_token: syncItemsResponse.cursorToken,
|
||||
asymmetric_messages: asymmetricMessages,
|
||||
user_events: userEvents,
|
||||
shared_vaults: sharedVaults,
|
||||
shared_vault_invites: sharedVaultInvites,
|
||||
user_events: userEvents,
|
||||
contacts,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -11,9 +11,9 @@ export class SharedVaultInviteFactory implements SharedVaultInviteFactoryInterfa
|
||||
newSharedVaultInvite.uuid = dto.uuid
|
||||
newSharedVaultInvite.userUuid = dto.user_uuid
|
||||
newSharedVaultInvite.sharedVaultUuid = dto.shared_vault_uuid
|
||||
newSharedVaultInvite.inviterUuid = dto.inviter_uuid
|
||||
newSharedVaultInvite.inviterPublicKey = dto.sender_public_key
|
||||
newSharedVaultInvite.encryptedVaultKeyContent = dto.encrypted_message
|
||||
newSharedVaultInvite.senderUuid = dto.sender_uuid
|
||||
newSharedVaultInvite.senderPublicKey = dto.sender_public_key
|
||||
newSharedVaultInvite.encryptedMessage = dto.encrypted_message
|
||||
newSharedVaultInvite.permissions = dto.permissions
|
||||
|
||||
const now = this.timer.getTimestampInMicroseconds()
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ export type SharedVaultInviteHash = {
|
||||
uuid: string
|
||||
user_uuid: string
|
||||
shared_vault_uuid: string
|
||||
inviter_uuid: string
|
||||
sender_uuid: string
|
||||
sender_public_key: string
|
||||
encrypted_message: string
|
||||
permissions: SharedVaultUserPermission
|
||||
|
||||
@@ -19,19 +19,19 @@ export class SharedVaultInvite {
|
||||
declare userUuid: string
|
||||
|
||||
@Column({
|
||||
name: 'inviter_uuid',
|
||||
name: 'sender_uuid',
|
||||
})
|
||||
declare inviterUuid: string
|
||||
declare senderUuid: string
|
||||
|
||||
@Column({
|
||||
name: 'sender_public_key',
|
||||
})
|
||||
declare inviterPublicKey: string
|
||||
declare senderPublicKey: string
|
||||
|
||||
@Column({
|
||||
name: 'encrypted_message',
|
||||
})
|
||||
declare encryptedVaultKeyContent: string
|
||||
declare encryptedMessage: string
|
||||
|
||||
@Column({
|
||||
name: 'permissions',
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ export type SharedVaultInviteQuery = {
|
||||
export type SharedVaultInviteFindAllForUserQuery = {
|
||||
userUuid?: string
|
||||
lastSyncTime?: number
|
||||
inviterUuid?: string
|
||||
senderUuid?: string
|
||||
}
|
||||
|
||||
export type SharedVaultInviteFindAllForSharedVault = {
|
||||
|
||||
+3
-3
@@ -54,9 +54,9 @@ export class TypeORMSharedVaultInviteRepository implements SharedVaultInviteRepo
|
||||
|
||||
if (query.userUuid) {
|
||||
queryBuilder.where('shared_vault_invite.user_uuid = :userUuid', { userUuid: query.userUuid })
|
||||
} else if (query.inviterUuid) {
|
||||
queryBuilder.where('shared_vault_invite.inviter_uuid = :inviterUuid', {
|
||||
inviterUuid: query.inviterUuid,
|
||||
} else if (query.senderUuid) {
|
||||
queryBuilder.where('shared_vault_invite.sender_uuid = :senderUuid', {
|
||||
senderUuid: query.senderUuid,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { SharedVaultUserPermission } from '../../SharedVaultUser/Model/SharedVaultUserPermission'
|
||||
|
||||
export type CreateInviteDTO = {
|
||||
originatorUuid: string
|
||||
sharedVaultUuid: string
|
||||
userUuid: string
|
||||
inviterPublicKey: string
|
||||
encryptedVaultKeyContent: string
|
||||
sharedVaultUuid: string
|
||||
senderUuid: string
|
||||
senderPublicKey: string
|
||||
encryptedMessage: string
|
||||
permissions: SharedVaultUserPermission
|
||||
}
|
||||
|
||||
+9
-9
@@ -21,7 +21,7 @@ export class SharedVaultInviteService implements SharedVaultInviteServiceInterfa
|
||||
|
||||
async createInvite(dto: CreateInviteDTO): Promise<SharedVaultInvite | null> {
|
||||
const sharedVault = await this.sharedVaultRepository.findByUuid(dto.sharedVaultUuid)
|
||||
if (!sharedVault || sharedVault.userUuid !== dto.originatorUuid) {
|
||||
if (!sharedVault || sharedVault.userUuid !== dto.senderUuid) {
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -31,9 +31,9 @@ export class SharedVaultInviteService implements SharedVaultInviteServiceInterfa
|
||||
uuid: uuidv4(),
|
||||
user_uuid: dto.userUuid,
|
||||
shared_vault_uuid: dto.sharedVaultUuid,
|
||||
inviter_uuid: dto.originatorUuid,
|
||||
sender_public_key: dto.inviterPublicKey,
|
||||
encrypted_message: dto.encryptedVaultKeyContent,
|
||||
sender_uuid: dto.senderUuid,
|
||||
sender_public_key: dto.senderPublicKey,
|
||||
encrypted_message: dto.encryptedMessage,
|
||||
permissions: dto.permissions,
|
||||
created_at_timestamp: timestamp,
|
||||
updated_at_timestamp: timestamp,
|
||||
@@ -44,12 +44,12 @@ export class SharedVaultInviteService implements SharedVaultInviteServiceInterfa
|
||||
|
||||
async updateInvite(dto: UpdateInviteDTO): Promise<SharedVaultInvite | null> {
|
||||
const sharedVaultInvite = await this.sharedVaultInviteRepository.findByUuid(dto.inviteUuid)
|
||||
if (!sharedVaultInvite || sharedVaultInvite.inviterUuid !== dto.originatorUuid) {
|
||||
if (!sharedVaultInvite || sharedVaultInvite.senderUuid !== dto.senderUuid) {
|
||||
return null
|
||||
}
|
||||
|
||||
sharedVaultInvite.inviterPublicKey = dto.inviterPublicKey
|
||||
sharedVaultInvite.encryptedVaultKeyContent = dto.encryptedVaultKeyContent
|
||||
sharedVaultInvite.senderPublicKey = dto.senderPublicKey
|
||||
sharedVaultInvite.encryptedMessage = dto.encryptedMessage
|
||||
if (dto.permissions) {
|
||||
sharedVaultInvite.permissions = dto.permissions
|
||||
}
|
||||
@@ -64,7 +64,7 @@ export class SharedVaultInviteService implements SharedVaultInviteServiceInterfa
|
||||
|
||||
getOutboundInvitesForUser(dto: { userUuid: string }): Promise<SharedVaultInvite[]> {
|
||||
return this.sharedVaultInviteRepository.findAll({
|
||||
inviterUuid: dto.userUuid,
|
||||
senderUuid: dto.userUuid,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -145,7 +145,7 @@ export class SharedVaultInviteService implements SharedVaultInviteServiceInterfa
|
||||
return false
|
||||
}
|
||||
|
||||
const isAuthorized = invite.inviterUuid === dto.originatorUuid || invite.userUuid === dto.originatorUuid
|
||||
const isAuthorized = invite.senderUuid === dto.originatorUuid || invite.userUuid === dto.originatorUuid
|
||||
if (!isAuthorized) {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { SharedVaultUserPermission } from '../../SharedVaultUser/Model/SharedVaultUserPermission'
|
||||
|
||||
export type UpdateInviteDTO = {
|
||||
originatorUuid: string
|
||||
inviteUuid: string
|
||||
inviterPublicKey: string
|
||||
encryptedVaultKeyContent: string
|
||||
senderUuid: string
|
||||
senderPublicKey: string
|
||||
encryptedMessage: string
|
||||
permissions?: SharedVaultUserPermission
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { ContactServiceInterface } from './../Contact/Service/ContactServiceInterface'
|
||||
import { Item } from '../Item/Item'
|
||||
import { ItemConflict } from '../Item/ItemConflict'
|
||||
import { ItemServiceInterface } from '../Item/ItemServiceInterface'
|
||||
@@ -8,13 +7,14 @@ import { UseCaseInterface } from './UseCaseInterface'
|
||||
import { SharedVaultInviteServiceInterface } from '../SharedVaultInvite/Service/SharedVaultInviteServiceInterface'
|
||||
import { SharedVaultServiceInterface } from '../SharedVault/Service/SharedVaultServiceInterface'
|
||||
import { UserEventServiceInterface } from '../UserEvent/Service/UserEventServiceInterface'
|
||||
import { AsymmetricMessageServiceInterface } from '../AsymmetricMessage/Service/AsymmetricMessageServiceInterface'
|
||||
|
||||
export class SyncItems implements UseCaseInterface {
|
||||
constructor(
|
||||
private itemService: ItemServiceInterface,
|
||||
private sharedVaultService: SharedVaultServiceInterface,
|
||||
private sharedVaultInviteService: SharedVaultInviteServiceInterface,
|
||||
private contactService: ContactServiceInterface,
|
||||
private asymmetricMessageService: AsymmetricMessageServiceInterface,
|
||||
private userEventService: UserEventServiceInterface,
|
||||
) {}
|
||||
|
||||
@@ -58,7 +58,7 @@ export class SyncItems implements UseCaseInterface {
|
||||
lastSyncTime,
|
||||
})
|
||||
|
||||
const contacts = await this.contactService.getUserContacts({
|
||||
const asymmetricMessages = await this.asymmetricMessageService.getMessagesForUser({
|
||||
userUuid: dto.userUuid,
|
||||
lastSyncTime: lastSyncTime,
|
||||
})
|
||||
@@ -77,7 +77,7 @@ export class SyncItems implements UseCaseInterface {
|
||||
sharedVaults,
|
||||
sharedVaultInvites,
|
||||
userEvents,
|
||||
contacts,
|
||||
asymmetricMessages,
|
||||
}
|
||||
|
||||
return syncResponse
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { AsymmetricMessage } from './../AsymmetricMessage/Model/AsymmetricMessage'
|
||||
import { SharedVaultInvite } from '../SharedVaultInvite/Model/SharedVaultInvite'
|
||||
import { Contact } from '../Contact/Model/Contact'
|
||||
import { Item } from '../Item/Item'
|
||||
import { ItemConflict } from '../Item/ItemConflict'
|
||||
import { SharedVault } from '../SharedVault/Model/SharedVault'
|
||||
@@ -12,7 +12,7 @@ export type SyncItemsResponse = {
|
||||
sharedVaults: Array<SharedVault>
|
||||
sharedVaultInvites: Array<SharedVaultInvite>
|
||||
userEvents: Array<UserEvent>
|
||||
contacts: Array<Contact>
|
||||
asymmetricMessages: Array<AsymmetricMessage>
|
||||
syncToken: string
|
||||
cursorToken?: string
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
export type AsymmetricMessageProjection = {
|
||||
uuid: string
|
||||
user_uuid: string
|
||||
sender_uuid: string
|
||||
sender_public_key: string
|
||||
encrypted_message: string
|
||||
created_at_timestamp: number
|
||||
updated_at_timestamp: number
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
import { AsymmetricMessage } from '../Domain/AsymmetricMessage/Model/AsymmetricMessage'
|
||||
import { ProjectorInterface } from './ProjectorInterface'
|
||||
import { AsymmetricMessageProjection } from './AsymmetricMessageProjection'
|
||||
|
||||
export class AsymmetricMessageProjector implements ProjectorInterface<AsymmetricMessage, AsymmetricMessageProjection> {
|
||||
projectSimple(_asymmetricMessage: AsymmetricMessage): AsymmetricMessageProjection {
|
||||
throw Error('not implemented')
|
||||
}
|
||||
|
||||
projectCustom(_projectionType: string, asymmetricMessage: AsymmetricMessage): AsymmetricMessageProjection {
|
||||
const fullProjection = this.projectFull(asymmetricMessage)
|
||||
|
||||
return {
|
||||
...fullProjection,
|
||||
user_uuid: asymmetricMessage.userUuid,
|
||||
}
|
||||
}
|
||||
|
||||
projectFull(asymmetricMessage: AsymmetricMessage): AsymmetricMessageProjection {
|
||||
return {
|
||||
uuid: asymmetricMessage.uuid,
|
||||
user_uuid: asymmetricMessage.userUuid,
|
||||
sender_uuid: asymmetricMessage.senderUuid,
|
||||
sender_public_key: asymmetricMessage.senderPublicKey,
|
||||
encrypted_message: asymmetricMessage.encryptedMessage,
|
||||
created_at_timestamp: asymmetricMessage.createdAtTimestamp,
|
||||
updated_at_timestamp: asymmetricMessage.updatedAtTimestamp,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@ export type SharedVaultInviteProjection = {
|
||||
uuid: string
|
||||
shared_vault_uuid: string
|
||||
user_uuid: string
|
||||
inviter_uuid: string
|
||||
sender_uuid: string
|
||||
sender_public_key: string
|
||||
encrypted_message: string
|
||||
permissions: string
|
||||
|
||||
@@ -21,9 +21,9 @@ export class SharedVaultInviteProjector implements ProjectorInterface<SharedVaul
|
||||
uuid: sharedVaultInvite.uuid,
|
||||
shared_vault_uuid: sharedVaultInvite.sharedVaultUuid,
|
||||
user_uuid: sharedVaultInvite.userUuid,
|
||||
inviter_uuid: sharedVaultInvite.inviterUuid,
|
||||
sender_public_key: sharedVaultInvite.inviterPublicKey,
|
||||
encrypted_message: sharedVaultInvite.encryptedVaultKeyContent,
|
||||
sender_uuid: sharedVaultInvite.senderUuid,
|
||||
sender_public_key: sharedVaultInvite.senderPublicKey,
|
||||
encrypted_message: sharedVaultInvite.encryptedMessage,
|
||||
permissions: sharedVaultInvite.permissions,
|
||||
created_at_timestamp: sharedVaultInvite.createdAtTimestamp,
|
||||
updated_at_timestamp: sharedVaultInvite.updatedAtTimestamp,
|
||||
|
||||
Reference in New Issue
Block a user