diff --git a/packages/auth/migrations/mysql/1684172607219-add_pkc_fields.ts b/packages/auth/migrations/mysql/1684172607219-add_pkc_fields.ts new file mode 100644 index 000000000..621d0b792 --- /dev/null +++ b/packages/auth/migrations/mysql/1684172607219-add_pkc_fields.ts @@ -0,0 +1,26 @@ +import { MigrationInterface, QueryRunner, TableColumn } from 'typeorm' + +export class AddPkcFields1684172607219 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.addColumn( + 'users', + new TableColumn({ + name: 'public_key', + type: 'text', + }), + ) + + await queryRunner.addColumn( + 'users', + new TableColumn({ + name: 'encrypted_private_key', + type: 'text', + }), + ) + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.dropColumn('users', 'public_key') + await queryRunner.dropColumn('users', 'encrypted_private_key') + } +} diff --git a/packages/auth/src/Controller/UsersController.ts b/packages/auth/src/Controller/UsersController.ts index 7efdcb788..895b9bfd8 100644 --- a/packages/auth/src/Controller/UsersController.ts +++ b/packages/auth/src/Controller/UsersController.ts @@ -72,6 +72,8 @@ export class UsersController extends BaseHttpController { kpOrigination: request.body.origination, kpCreated: request.body.created, version: request.body.version, + publicKey: request.body.public_key, + encryptedPrivateKey: request.body.private_key, }) if (updateResult.success) { @@ -208,6 +210,8 @@ export class UsersController extends BaseHttpController { kpOrigination: request.body.origination, updatedWithUserAgent: request.headers['user-agent'], protocolVersion: request.body.version, + publicKey: request.body.new_public_key, + encryptedPrivateKey: request.body.new_encrypted_private_key, }) if (!changeCredentialsResult.success) { diff --git a/packages/auth/src/Domain/Auth/AuthResponse.ts b/packages/auth/src/Domain/Auth/AuthResponse.ts index cb61d0dd1..f6210d414 100644 --- a/packages/auth/src/Domain/Auth/AuthResponse.ts +++ b/packages/auth/src/Domain/Auth/AuthResponse.ts @@ -1,9 +1,5 @@ -import { ProtocolVersion } from '@standardnotes/common' +import { SimpleUserProjection } from '../../Projection/SimpleUserProjection' export interface AuthResponse { - user: { - uuid: string - email: string - protocolVersion: ProtocolVersion - } + user: SimpleUserProjection } diff --git a/packages/auth/src/Domain/Auth/AuthResponseFactory20200115.ts b/packages/auth/src/Domain/Auth/AuthResponseFactory20200115.ts index f62faa5d4..2f06c9bfd 100644 --- a/packages/auth/src/Domain/Auth/AuthResponseFactory20200115.ts +++ b/packages/auth/src/Domain/Auth/AuthResponseFactory20200115.ts @@ -4,7 +4,6 @@ import { TokenEncoderInterface, } from '@standardnotes/security' import { DomainEventPublisherInterface } from '@standardnotes/domain-events' -import { ProtocolVersion } from '@standardnotes/common' import { SessionBody } from '@standardnotes/responses' import { inject, injectable } from 'inversify' import { Logger } from 'winston' @@ -19,6 +18,7 @@ import { DomainEventFactoryInterface } from '../Event/DomainEventFactoryInterfac import { AuthResponse20161215 } from './AuthResponse20161215' import { AuthResponse20200115 } from './AuthResponse20200115' +import { SimpleUserProjection } from '../../Projection/SimpleUserProjection' @injectable() export class AuthResponseFactory20200115 extends AuthResponseFactory20190520 { @@ -54,11 +54,7 @@ export class AuthResponseFactory20200115 extends AuthResponseFactory20190520 { return { session: sessionPayload, key_params: this.keyParamsFactory.create(dto.user, true), - user: this.userProjector.projectSimple(dto.user) as { - uuid: string - email: string - protocolVersion: ProtocolVersion - }, + user: this.userProjector.projectSimple(dto.user) as SimpleUserProjection, } } diff --git a/packages/auth/src/Domain/UseCase/ChangeCredentials/ChangeCredentials.ts b/packages/auth/src/Domain/UseCase/ChangeCredentials/ChangeCredentials.ts index b88c50b53..52371505d 100644 --- a/packages/auth/src/Domain/UseCase/ChangeCredentials/ChangeCredentials.ts +++ b/packages/auth/src/Domain/UseCase/ChangeCredentials/ChangeCredentials.ts @@ -62,6 +62,13 @@ export class ChangeCredentials implements UseCaseInterface { dto.user.email = newUsername.value } + if (dto.publicKey) { + dto.user.publicKey = dto.publicKey + } + if (dto.encryptedPrivateKey) { + dto.user.encryptedPrivateKey = dto.encryptedPrivateKey + } + dto.user.pwNonce = dto.pwNonce if (dto.protocolVersion) { dto.user.version = dto.protocolVersion diff --git a/packages/auth/src/Domain/UseCase/ChangeCredentials/ChangeCredentialsDTO.ts b/packages/auth/src/Domain/UseCase/ChangeCredentials/ChangeCredentialsDTO.ts index 31a82363e..774607858 100644 --- a/packages/auth/src/Domain/UseCase/ChangeCredentials/ChangeCredentialsDTO.ts +++ b/packages/auth/src/Domain/UseCase/ChangeCredentials/ChangeCredentialsDTO.ts @@ -11,4 +11,6 @@ export type ChangeCredentialsDTO = { protocolVersion?: string kpOrigination?: string kpCreated?: string + publicKey?: string + encryptedPrivateKey?: string } diff --git a/packages/auth/src/Domain/UseCase/Register.ts b/packages/auth/src/Domain/UseCase/Register.ts index 639198108..06a088321 100644 --- a/packages/auth/src/Domain/UseCase/Register.ts +++ b/packages/auth/src/Domain/UseCase/Register.ts @@ -71,6 +71,8 @@ export class Register implements UseCaseInterface { user.encryptedPassword = await bcrypt.hash(password, User.PASSWORD_HASH_COST) user.encryptedServerKey = await this.crypter.generateEncryptedUserServerKey() user.serverEncryptionVersion = User.DEFAULT_ENCRYPTION_VERSION + user.publicKey = dto.publicKey ?? null + user.encryptedPrivateKey = dto.encryptedPrivateKey ?? null const defaultRole = await this.roleRepository.findOneByName(RoleName.NAMES.CoreUser) if (defaultRole) { diff --git a/packages/auth/src/Domain/UseCase/RegisterDTO.ts b/packages/auth/src/Domain/UseCase/RegisterDTO.ts index 85bc1276e..0f207aa01 100644 --- a/packages/auth/src/Domain/UseCase/RegisterDTO.ts +++ b/packages/auth/src/Domain/UseCase/RegisterDTO.ts @@ -10,4 +10,6 @@ export type RegisterDTO = { kpOrigination?: string kpCreated?: string version?: string + publicKey?: string + encryptedPrivateKey?: string } diff --git a/packages/auth/src/Domain/UseCase/UpdateUserDTO.ts b/packages/auth/src/Domain/UseCase/UpdateUserDTO.ts index 90b3bf468..6c1467302 100644 --- a/packages/auth/src/Domain/UseCase/UpdateUserDTO.ts +++ b/packages/auth/src/Domain/UseCase/UpdateUserDTO.ts @@ -15,4 +15,6 @@ export type UpdateUserDTO = { kpOrigination?: string kpCreated?: Date version?: string + publicKey?: string + encryptedPrivateKey?: string } diff --git a/packages/auth/src/Domain/User/User.ts b/packages/auth/src/Domain/User/User.ts index 81b119c82..038fc7a25 100644 --- a/packages/auth/src/Domain/User/User.ts +++ b/packages/auth/src/Domain/User/User.ts @@ -42,6 +42,22 @@ export class User { }) declare encryptedServerKey: string | null + @Column({ + name: 'public_key', + length: 255, + type: 'varchar', + nullable: true, + }) + declare publicKey: string | null + + @Column({ + name: 'encrypted_private_key', + length: 255, + type: 'varchar', + nullable: true, + }) + declare encryptedPrivateKey: string | null + @Column({ name: 'server_encryption_version', type: 'tinyint', diff --git a/packages/auth/src/Projection/SimpleUserProjection.ts b/packages/auth/src/Projection/SimpleUserProjection.ts new file mode 100644 index 000000000..54c633ae0 --- /dev/null +++ b/packages/auth/src/Projection/SimpleUserProjection.ts @@ -0,0 +1,7 @@ +export type SimpleUserProjection = { + uuid: string + email: string + protocolVersion: string + publicKey?: string + encryptedPrivateKey?: string +} diff --git a/packages/auth/src/Projection/UserProjector.ts b/packages/auth/src/Projection/UserProjector.ts index 10abb40ae..d88ae51a0 100644 --- a/packages/auth/src/Projection/UserProjector.ts +++ b/packages/auth/src/Projection/UserProjector.ts @@ -2,14 +2,17 @@ import { injectable } from 'inversify' import { User } from '../Domain/User/User' import { ProjectorInterface } from './ProjectorInterface' +import { SimpleUserProjection } from './SimpleUserProjection' @injectable() export class UserProjector implements ProjectorInterface { - projectSimple(user: User): Record { + projectSimple(user: User): SimpleUserProjection { return { uuid: user.uuid, email: user.email, protocolVersion: user.version, + publicKey: user.publicKey ?? undefined, + encryptedPrivateKey: user.encryptedPrivateKey ?? undefined, } } diff --git a/packages/syncing-server/migrations/mysql/1684173017359-create_group.ts b/packages/syncing-server/migrations/mysql/1684173017359-create_group.ts new file mode 100644 index 000000000..18274209c --- /dev/null +++ b/packages/syncing-server/migrations/mysql/1684173017359-create_group.ts @@ -0,0 +1,37 @@ +import { MigrationInterface, QueryRunner, Table, TableColumn } from 'typeorm' + +export class CreateGroup1684173017359 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + const table = new Table({ + name: 'groups', + columns: [ + new TableColumn({ + name: 'uuid', + type: 'varchar', + length: '36', + isPrimary: true, + }), + new TableColumn({ + name: 'user_uuid', + type: 'varchar', + length: '36', + isNullable: false, + }), + new TableColumn({ + name: 'created_at_timestamp', + type: 'bigint', + }), + new TableColumn({ + name: 'updated_at_timestamp', + type: 'bigint', + }), + ], + }) + + await queryRunner.createTable(table) + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.dropTable('groups') + } +} diff --git a/packages/syncing-server/migrations/mysql/1684173023883-create_group_user.ts b/packages/syncing-server/migrations/mysql/1684173023883-create_group_user.ts new file mode 100644 index 000000000..1113bf5cc --- /dev/null +++ b/packages/syncing-server/migrations/mysql/1684173023883-create_group_user.ts @@ -0,0 +1,53 @@ +import { MigrationInterface, QueryRunner, Table, TableColumn } from 'typeorm' + +export class CreateGroupUser1684173023883 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + const table = new Table({ + name: 'group_users', + columns: [ + new TableColumn({ + name: 'uuid', + type: 'varchar', + length: '36', + isPrimary: true, + }), + new TableColumn({ + name: 'group_uuid', + type: 'varchar', + length: '36', + isNullable: false, + }), + new TableColumn({ + name: 'user_uuid', + type: 'varchar', + length: '36', + isNullable: false, + }), + new TableColumn({ + name: 'encrypted_group_key', + type: 'text', + isNullable: false, + }), + new TableColumn({ + name: 'sender_public_key', + type: 'text', + isNullable: false, + }), + new TableColumn({ + name: 'created_at_timestamp', + type: 'bigint', + }), + new TableColumn({ + name: 'updated_at_timestamp', + type: 'bigint', + }), + ], + }) + + await queryRunner.createTable(table) + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.dropTable('group_users') + } +} diff --git a/packages/syncing-server/src/Controller/GroupsController.ts b/packages/syncing-server/src/Controller/GroupsController.ts index 504a7e62f..f12458bc2 100644 --- a/packages/syncing-server/src/Controller/GroupsController.ts +++ b/packages/syncing-server/src/Controller/GroupsController.ts @@ -2,10 +2,11 @@ import { GroupServiceInterface } from './../Domain/Group/Service/GroupServiceInt import { Request, Response } from 'express' import { BaseHttpController, controller, httpGet, httpPost, results } from 'inversify-express-utils' import TYPES from '../Bootstrap/Types' +import { GroupUserServiceInterface } from '../Domain/GroupUser/Service/GroupUserService' @controller('/groups') export class GroupsController extends BaseHttpController { - constructor(private groupsService: GroupServiceInterface) { + constructor(private groupsService: GroupServiceInterface, private groupUserService: GroupUserServiceInterface) { super() } @@ -23,6 +24,20 @@ export class GroupsController extends BaseHttpController { return this.json({ group: result }) } + @httpGet('/', TYPES.AuthMiddleware) + public async getUserGroupKeys( + _request: Request, + response: Response, + ): Promise { + const result = await this.groupUserService.getUserGroupKeys({ userUuid: response.locals.user.uuid }) + + if (!result) { + return this.errorResponse(500, 'Could not get user groups') + } + + return this.json({ groups: result }) + } + @httpPost('/:groupUuid/users', TYPES.AuthMiddleware) public async addUserToGroup( request: Request, @@ -31,8 +46,9 @@ export class GroupsController extends BaseHttpController { const result = await this.groupsService.addUserToGroup({ groupUuid: request.params.groupUuid, ownerUuid: response.locals.user.uuid, - inviteeUuid: request.body.inviteeUuid, - encryptedGroupKey: request.body.encryptedGroupKey, + inviteeUuid: request.body.invitee_uuid, + encryptedGroupKey: request.body.encrypted_group_key, + senderPublicKey: request.body.sender_public_key, }) if (!result) { @@ -42,20 +58,6 @@ export class GroupsController extends BaseHttpController { return this.json({ groupUser: result }) } - @httpGet('/', TYPES.AuthMiddleware) - public async getUserGroups( - _request: Request, - response: Response, - ): Promise { - const result = await this.groupsService.getUserGroups(response.locals.user.uuid) - - if (!result) { - return this.errorResponse(500, 'Could not get user groups') - } - - return this.json({ groups: result }) - } - private errorResponse(status: number, message?: string, tag?: string) { return this.json( { diff --git a/packages/syncing-server/src/Domain/Group/Service/GroupService.ts b/packages/syncing-server/src/Domain/Group/Service/GroupService.ts index 7480a8e5a..3552f3ff4 100644 --- a/packages/syncing-server/src/Domain/Group/Service/GroupService.ts +++ b/packages/syncing-server/src/Domain/Group/Service/GroupService.ts @@ -1,4 +1,3 @@ -import { ItemHash } from './../../Item/ItemHash' import { GroupUser } from '../../GroupUser/Model/GroupUser' import { Group } from '../Model/Group' @@ -10,20 +9,13 @@ import { TimerInterface } from '@standardnotes/time' import { GroupUserServiceInterface } from '../../GroupUser/Service/GroupUserService' import { v4 as uuidv4 } from 'uuid' -import { GetItem } from '../../UseCase/GetItem/GetItem' -import { SaveItemUseCase } from '../../UseCase/SaveItem' -import { ProjectorInterface } from '../../../Projection/ProjectorInterface' -import { Item } from '../../Item/Item' -import { ItemProjection } from '../../../Projection/ItemProjection' export class GroupService implements GroupServiceInterface { constructor( private groupRepository: GroupsRepositoryInterface, private groupFactory: GroupFactoryInterface, - private groupUserSerivce: GroupUserServiceInterface, - private getItem: GetItem, - private saveItem: SaveItemUseCase, - private itemProjector: ProjectorInterface, + private groupUserService: GroupUserServiceInterface, + private timer: TimerInterface, ) {} @@ -48,45 +40,15 @@ export class GroupService implements GroupServiceInterface { ownerUuid: string inviteeUuid: string encryptedGroupKey: string + senderPublicKey: string }): Promise { - const user = await this.groupUserSerivce.createGroupUser(dto.groupUuid, dto.inviteeUuid, dto.encryptedGroupKey) + const user = await this.groupUserService.createGroupUser({ + groupUuid: dto.groupUuid, + userUuid: dto.inviteeUuid, + encryptedGroupKey: dto.encryptedGroupKey, + senderPublicKey: dto.senderPublicKey, + }) return user } - - async addItemToGroup(dto: { - groupUuid: string - userUuid: string - itemUuid: string - apiVersion: string - readOnlyAccess: boolean - sessionUuid: string | null - }): Promise<{ success: boolean }> { - const getItemResult = await this.getItem.execute({ itemUuid: dto.itemUuid, userUuid: dto.userUuid }) - - if (!getItemResult.success) { - return { success: false } - } - - const itemHash = await this.itemProjector.projectFull(getItemResult.item) - itemHash.group_uuid = dto.groupUuid - - const saveItemResult = await this.saveItem.execute({ - itemHash: itemHash, - userUuid: dto.userUuid, - apiVersion: dto.apiVersion, - readOnlyAccess: dto.readOnlyAccess, - sessionUuid: dto.sessionUuid, - }) - - return { success: saveItemResult.success } - } - - getGroup(groupUuid: string): Promise { - return this.groupRepository.findByUuid(groupUuid) - } - - async getUserGroups(userUuid: string): Promise { - return this.groupRepository.findAll({ userUuid }) - } } diff --git a/packages/syncing-server/src/Domain/Group/Service/GroupServiceInterface.ts b/packages/syncing-server/src/Domain/Group/Service/GroupServiceInterface.ts index 53ac1abf4..bb50e60f3 100644 --- a/packages/syncing-server/src/Domain/Group/Service/GroupServiceInterface.ts +++ b/packages/syncing-server/src/Domain/Group/Service/GroupServiceInterface.ts @@ -9,18 +9,6 @@ export interface GroupServiceInterface { ownerUuid: string inviteeUuid: string encryptedGroupKey: string + senderPublicKey: string }): Promise - - addItemToGroup(dto: { - groupUuid: string - userUuid: string - itemUuid: string - apiVersion: string - readOnlyAccess: boolean - sessionUuid: string | null - }): Promise<{ success: boolean }> - - getGroup(groupUuid: string): Promise - - getUserGroups(userUuid: string): Promise } diff --git a/packages/syncing-server/src/Domain/GroupUser/Model/GroupUser.ts b/packages/syncing-server/src/Domain/GroupUser/Model/GroupUser.ts index 898262936..0cbc23144 100644 --- a/packages/syncing-server/src/Domain/GroupUser/Model/GroupUser.ts +++ b/packages/syncing-server/src/Domain/GroupUser/Model/GroupUser.ts @@ -22,6 +22,11 @@ export class GroupUser { }) declare encryptedGroupKey: string + @Column({ + name: 'sender_public_key', + }) + declare senderPublicKey: string + @Column({ name: 'created_at_timestamp', type: 'bigint', diff --git a/packages/syncing-server/src/Domain/GroupUser/Service/GetUserGroupKeysDTO.ts b/packages/syncing-server/src/Domain/GroupUser/Service/GetUserGroupKeysDTO.ts new file mode 100644 index 000000000..61c3a7079 --- /dev/null +++ b/packages/syncing-server/src/Domain/GroupUser/Service/GetUserGroupKeysDTO.ts @@ -0,0 +1,4 @@ +export interface GetUserGroupKeysDTO { + userUuid: string + syncToken?: string | null +} diff --git a/packages/syncing-server/src/Domain/GroupUser/Service/GroupUserService.ts b/packages/syncing-server/src/Domain/GroupUser/Service/GroupUserService.ts index 671bdbf33..0b80ca033 100644 --- a/packages/syncing-server/src/Domain/GroupUser/Service/GroupUserService.ts +++ b/packages/syncing-server/src/Domain/GroupUser/Service/GroupUserService.ts @@ -1,12 +1,26 @@ +import { GetUserGroupKeysDTO } from './GetUserGroupKeysDTO' import { GroupUser } from '../Model/GroupUser' export interface GroupUserServiceInterface { - createGroupUser(groupUuid: string, userUuid: string, encryptedGroupKey: string): Promise - getGroupUsers(groupUuid: string): Promise + createGroupUser(dto: { + groupUuid: string + userUuid: string + encryptedGroupKey: string + senderPublicKey: string + }): Promise + getUsersForGroup(groupUuid: string): Promise + getUserGroupKeys(dto: GetUserGroupKeysDTO): Promise } export class GroupUserService implements GroupUserServiceInterface { - createGroupUser(groupUuid: string, userUuid: string, encryptedGroupKey: string): Promise {} + createGroupUser(dto: { + groupUuid: string + userUuid: string + encryptedGroupKey: string + senderPublicKey: string + }): Promise {} - getGroupUsers(groupUuid: string): Promise {} + getUsersForGroup(groupUuid: string): Promise {} + + getUserGroupKeys(dto: GetUserGroupKeysDTO): Promise {} } diff --git a/packages/syncing-server/src/Domain/Item/SaveRule/OwnershipFilter.ts b/packages/syncing-server/src/Domain/Item/SaveRule/OwnershipFilter.ts index ecfa683df..980acb754 100644 --- a/packages/syncing-server/src/Domain/Item/SaveRule/OwnershipFilter.ts +++ b/packages/syncing-server/src/Domain/Item/SaveRule/OwnershipFilter.ts @@ -2,11 +2,10 @@ import { ItemSaveValidationDTO } from '../SaveValidator/ItemSaveValidationDTO' import { ItemSaveRuleResult } from './ItemSaveRuleResult' import { ItemSaveRuleInterface } from './ItemSaveRuleInterface' import { ConflictType } from '@standardnotes/responses' -import { GetUserGroupsUseCase } from '../../UseCase/Groups/GetUserGroupsUseCase' -import { GetGroupItemsUseCase } from '../../UseCase/Groups/GetGroupItemsUseCase' +import { GetUserGroupKeysUseCase } from '../../UseCase/Groups/GetUserGroupKeysUseCase' export class OwnershipFilter implements ItemSaveRuleInterface { - constructor(private getUserGroupsUseCase: GetUserGroupsUseCase, private getGroupItemsUseCase: GetGroupItemsUseCase) {} + constructor(private getUserGroupsUseCase: GetUserGroupKeysUseCase) {} async check(dto: ItemSaveValidationDTO): Promise { const itemBelongsToADifferentUser = dto.existingItem !== null && dto.existingItem.userUuid !== dto.userUuid diff --git a/packages/syncing-server/src/Domain/Item/SyncResponse/SyncResponse20200115.ts b/packages/syncing-server/src/Domain/Item/SyncResponse/SyncResponse20200115.ts index c53e4ae6f..f59871e8a 100644 --- a/packages/syncing-server/src/Domain/Item/SyncResponse/SyncResponse20200115.ts +++ b/packages/syncing-server/src/Domain/Item/SyncResponse/SyncResponse20200115.ts @@ -1,3 +1,4 @@ +import { GroupUserProjection } from '../../../Projection/GroupUserProjection' import { ItemConflictProjection } from '../../../Projection/ItemConflictProjection' import { ItemProjection } from '../../../Projection/ItemProjection' import { SavedItemProjection } from '../../../Projection/SavedItemProjection' @@ -8,4 +9,5 @@ export type SyncResponse20200115 = { conflicts: Array sync_token: string cursor_token?: string + group_keys: Array } diff --git a/packages/syncing-server/src/Domain/Item/SyncResponse/SyncResponseFactory20200115.ts b/packages/syncing-server/src/Domain/Item/SyncResponse/SyncResponseFactory20200115.ts index 67ece6f7a..a7d0860ce 100644 --- a/packages/syncing-server/src/Domain/Item/SyncResponse/SyncResponseFactory20200115.ts +++ b/packages/syncing-server/src/Domain/Item/SyncResponse/SyncResponseFactory20200115.ts @@ -7,12 +7,15 @@ import { ItemProjection } from '../../../Projection/ItemProjection' import { SyncResponse20200115 } from './SyncResponse20200115' import { SyncResponseFactoryInterface } from './SyncResponseFactoryInterface' import { SavedItemProjection } from '../../../Projection/SavedItemProjection' +import { GroupUser } from '../../GroupUser/Model/GroupUser' +import { GroupUserProjection } from '../../../Projection/GroupUserProjection' export class SyncResponseFactory20200115 implements SyncResponseFactoryInterface { constructor( private itemProjector: ProjectorInterface, private itemConflictProjector: ProjectorInterface, private savedItemProjector: ProjectorInterface, + private groupKeyProjector: ProjectorInterface, ) {} async createResponse(syncItemsResponse: SyncItemsResponse): Promise { @@ -31,12 +34,18 @@ export class SyncResponseFactory20200115 implements SyncResponseFactoryInterface conflicts.push(await this.itemConflictProjector.projectFull(itemConflict)) } + const groupKeys = [] + for (const groupKey of syncItemsResponse.groupKeys) { + groupKeys.push(await this.groupKeyProjector.projectFull(groupKey)) + } + return { retrieved_items: retrievedItems, saved_items: savedItems, conflicts, sync_token: syncItemsResponse.syncToken, cursor_token: syncItemsResponse.cursorToken, + group_keys: groupKeys, } } } diff --git a/packages/syncing-server/src/Domain/UseCase/Groups/GetGroupUseCase.ts b/packages/syncing-server/src/Domain/UseCase/Groups/GetGroupUseCase.ts deleted file mode 100644 index b81c289a3..000000000 --- a/packages/syncing-server/src/Domain/UseCase/Groups/GetGroupUseCase.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { GroupServiceInterface } from '../../Group/Service/GroupServiceInterface' -import { UseCaseInterface } from '../UseCaseInterface' -import { Group } from '../../Group/Model/Group' - -export type GetGroupUseCaseResult = - | { - success: true - group: Group - } - | { - success: false - message: string - } - -export class GetGroupUseCase implements UseCaseInterface { - constructor(private groupService: GroupServiceInterface) {} - - async execute(dto: { groupUuid: string }): Promise { - const result = await this.groupService.getGroup(dto.groupUuid) - - if (!result) { - return { - success: false, - message: `Could not get group ${dto.groupUuid}`, - } - } - - return { - success: true, - group: result, - } - } -} diff --git a/packages/syncing-server/src/Domain/UseCase/Groups/GetUserGroupKeysUseCase.ts b/packages/syncing-server/src/Domain/UseCase/Groups/GetUserGroupKeysUseCase.ts new file mode 100644 index 000000000..510329735 --- /dev/null +++ b/packages/syncing-server/src/Domain/UseCase/Groups/GetUserGroupKeysUseCase.ts @@ -0,0 +1,34 @@ +import { GetUserGroupKeysDTO } from './../../GroupUser/Service/GetUserGroupKeysDTO' +import { UseCaseInterface } from '../UseCaseInterface' +import { Group } from '../../Group/Model/Group' +import { GroupUserServiceInterface } from '../../GroupUser/Service/GroupUserService' + +export type GetUserGroupKeysUseCaseResult = + | { + success: true + groups: Group[] + } + | { + success: false + message: string + } + +export class GetUserGroupKeysUseCase implements UseCaseInterface { + constructor(private groupUserService: GroupUserServiceInterface) {} + + async execute(dto: GetUserGroupKeysDTO): Promise { + const result = await this.groupUserService.getUserGroupKeys(dto) + + if (!result) { + return { + success: false, + message: `Could not get user groups for user ${dto.userUuid}`, + } + } + + return { + success: true, + groups: result, + } + } +} diff --git a/packages/syncing-server/src/Domain/UseCase/Groups/GetUserGroupsUseCase.ts b/packages/syncing-server/src/Domain/UseCase/Groups/GetUserGroupsUseCase.ts deleted file mode 100644 index cc7741f6c..000000000 --- a/packages/syncing-server/src/Domain/UseCase/Groups/GetUserGroupsUseCase.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { GroupServiceInterface } from '../../Group/Service/GroupServiceInterface' -import { UseCaseInterface } from '../UseCaseInterface' -import { Group } from '../../Group/Model/Group' - -export type GetUserGroupsUseCaseResult = - | { - success: true - groups: Group[] - } - | { - success: false - message: string - } - -export class GetUserGroupsUseCase implements UseCaseInterface { - constructor(private groupService: GroupServiceInterface) {} - - async execute(dto: { userUuid: string }): Promise { - const result = await this.groupService.getUserGroups(dto.userUuid) - - if (!result) { - return { - success: false, - message: `Could not get user groups for user ${dto.userUuid}`, - } - } - - return { - success: true, - groups: result, - } - } -} diff --git a/packages/syncing-server/src/Domain/UseCase/SyncItems.ts b/packages/syncing-server/src/Domain/UseCase/SyncItems.ts index 641202fd4..4675bdfe3 100644 --- a/packages/syncing-server/src/Domain/UseCase/SyncItems.ts +++ b/packages/syncing-server/src/Domain/UseCase/SyncItems.ts @@ -1,3 +1,4 @@ +import { GroupUserServiceInterface } from '../GroupUser/Service/GroupUserService' import { Item } from '../Item/Item' import { ItemConflict } from '../Item/ItemConflict' import { ItemServiceInterface } from '../Item/ItemServiceInterface' @@ -6,7 +7,7 @@ import { SyncItemsResponse } from './SyncItemsResponse' import { UseCaseInterface } from './UseCaseInterface' export class SyncItems implements UseCaseInterface { - constructor(private itemService: ItemServiceInterface) {} + constructor(private itemService: ItemServiceInterface, private groupUserService: GroupUserServiceInterface) {} async execute(dto: SyncItemsDTO): Promise { const getItemsResult = await this.itemService.getItems({ @@ -30,12 +31,15 @@ export class SyncItems implements UseCaseInterface { retrievedItems = await this.itemService.frontLoadKeysItemsToTop(dto.userUuid, retrievedItems) } + const groupKeys = await this.groupUserService.getUserGroupKeys({ userUuid: dto.userUuid, syncToken: dto.syncToken }) + const syncResponse: SyncItemsResponse = { retrievedItems, syncToken: saveItemsResult.syncToken, savedItems: saveItemsResult.savedItems, conflicts: saveItemsResult.conflicts, cursorToken: getItemsResult.cursorToken, + groupKeys, } return syncResponse diff --git a/packages/syncing-server/src/Domain/UseCase/SyncItemsResponse.ts b/packages/syncing-server/src/Domain/UseCase/SyncItemsResponse.ts index fcc940ddd..8a3385e08 100644 --- a/packages/syncing-server/src/Domain/UseCase/SyncItemsResponse.ts +++ b/packages/syncing-server/src/Domain/UseCase/SyncItemsResponse.ts @@ -1,3 +1,4 @@ +import { GroupUser } from './../GroupUser/Model/GroupUser' import { Item } from '../Item/Item' import { ItemConflict } from '../Item/ItemConflict' @@ -5,6 +6,7 @@ export type SyncItemsResponse = { retrievedItems: Array savedItems: Array conflicts: Array + groupKeys: Array syncToken: string cursorToken?: string } diff --git a/packages/syncing-server/src/Projection/GroupUserProjection.ts b/packages/syncing-server/src/Projection/GroupUserProjection.ts new file mode 100644 index 000000000..f2b2bf740 --- /dev/null +++ b/packages/syncing-server/src/Projection/GroupUserProjection.ts @@ -0,0 +1,9 @@ +export type GroupUserProjection = { + uuid: string + group_uuid: string + user_uuid: string + encrypted_group_key: string + sender_public_key: string + created_at_timestamp: number + updated_at_timestamp: number +}