From 1acdebf6866158be45ff9c4be56fa933162b3e41 Mon Sep 17 00:00:00 2001 From: tamarafinogina Date: Tue, 14 Jan 2025 11:26:23 +0100 Subject: [PATCH] fix index tests and add more test cases --- src/app/share/services/share.service.ts | 3 + .../store/slices/sharedLinks/index.test.ts | 306 +++++++++++++++++- src/app/store/slices/sharedLinks/index.ts | 20 +- 3 files changed, 311 insertions(+), 18 deletions(-) diff --git a/src/app/share/services/share.service.ts b/src/app/share/services/share.service.ts index a47c6c42c..8a9c21d93 100644 --- a/src/app/share/services/share.service.ts +++ b/src/app/share/services/share.service.ts @@ -867,6 +867,9 @@ const shareService = { validateSharingInvitation, getPublicSharedItemInfo, getSharedFolderSize, + inviteUserToSharedFolder, + getSharedFolderInvitationsAsInvitedUser, + getSharingRoles, }; export default shareService; diff --git a/src/app/store/slices/sharedLinks/index.test.ts b/src/app/store/slices/sharedLinks/index.test.ts index c5969c613..b9c1426b6 100644 --- a/src/app/store/slices/sharedLinks/index.test.ts +++ b/src/app/store/slices/sharedLinks/index.test.ts @@ -4,13 +4,17 @@ import { describe, expect, it, vi, beforeEach, beforeAll } from 'vitest'; import { sharedThunks, ShareFileWithUserPayload } from './index'; const { shareItemWithUser } = sharedThunks; -import { generateNewKeys, hybridDecryptMessageWithPrivateKey } from '../../../crypto/services/pgp.service'; +import { + generateNewKeys, + hybridDecryptMessageWithPrivateKey, + decryptMessageWithPrivateKey, +} from '../../../crypto/services/pgp.service'; import navigationService from 'app/core/services/navigation.service'; import { RootState } from '../..'; import { Buffer } from 'buffer'; import { UserSettings } from '@internxt/sdk/dist/shared/types/userSettings'; import userService from '../../../auth/services/user.service'; -import * as shareService from 'app/share/services/share.service'; +import shareService from 'app/share/services/share.service'; describe('Encryption and Decryption', () => { beforeAll(() => { @@ -21,9 +25,11 @@ describe('Encryption and Decryption', () => { default: { push: vi.fn() }, })); vi.mock('app/share/services/share.service', () => ({ - inviteUserToSharedFolder: vi.fn(), - getSharedFolderInvitationsAsInvitedUser: vi.fn(), - getSharingRoles: vi.fn(), + default: { + inviteUserToSharedFolder: vi.fn(), + getSharedFolderInvitationsAsInvitedUser: vi.fn(), + getSharingRoles: vi.fn(), + }, })); vi.mock('../../../auth/services/user.service', () => ({ default: { @@ -52,7 +58,7 @@ describe('Encryption and Decryption', () => { vi.clearAllMocks(); }); - it('should setup workspace and encrypt mnemonic with kyber', async () => { + it('shareItemWithUser encrypts with kyber for an existing user', async () => { const keys = await generateNewKeys(); const mockPayload: ShareFileWithUserPayload = { publicKey: keys.publicKeyArmored, @@ -94,7 +100,7 @@ describe('Encryption and Decryption', () => { getSharedFolderInvitationsAsInvitedUser: vi.fn(), getSharingRoles: vi.fn(), }; - + vi.spyOn(shareService, 'getSharedFolderInvitationsAsInvitedUser').mockImplementation( mockShareService.getSharedFolderInvitationsAsInvitedUser, ); @@ -102,7 +108,196 @@ describe('Encryption and Decryption', () => { vi.spyOn(shareService, 'inviteUserToSharedFolder').mockImplementation(mockShareService.inviteUserToSharedFolder); vi.spyOn(userService, 'getPublicKeyByEmail').mockReturnValue( - Promise.resolve({ publicKey: user.keys.ecc.publicKey, publicKyberKey: user.keys?.kyber.publicKey }), + Promise.resolve({ publicKey: '', publicKyberKey: '' }), + ); + vi.spyOn(userService, 'preCreateUser').mockReturnValue( + Promise.resolve({ + publicKey: '', + publicKyberKey: '', + user: { + uuid: '', + email: '', + }, + }), + ); + + const getStateMock = vi.fn(() => mockRootState as RootState); + const dispatchMock = vi.fn(); + + const thunk = shareItemWithUser(mockPayload); + await thunk(dispatchMock, getStateMock, undefined); + + const [inviteUserToSharedFolderInput] = mockShareService.inviteUserToSharedFolder.mock.calls[0]; + expect(inviteUserToSharedFolderInput.encryptionKey).toBeDefined(); + + const { encryptionKey = '' } = inviteUserToSharedFolderInput; + const decryptedMessage = await hybridDecryptMessageWithPrivateKey({ + encryptedMessageInBase64: encryptionKey, + privateKeyInBase64: Buffer.from(keys.privateKeyArmored).toString('base64'), + privateKyberKeyInBase64: keys.privateKyberKeyBase64, + }); + + expect(decryptedMessage).toEqual(mockUser.mnemonic); + expect(mockShareService.inviteUserToSharedFolder).toHaveBeenCalledWith( + expect.objectContaining({ + itemId: mockPayload.itemId, + itemType: mockPayload.itemType, + sharedWith: mockPayload.sharedWith, + notifyUser: mockPayload.notifyUser, + notificationMessage: mockPayload.notificationMessage, + encryptionKey: encryptionKey, + encryptionAlgorithm: mockPayload.encryptionAlgorithm, + roleId: mockPayload.roleId, + persistPreviousSharing: true, + }), + ); + }); + + it('shareItemWithUser encrypts without kyber for an existing user', async () => { + const keys = await generateNewKeys(); + const mockPayload: ShareFileWithUserPayload = { + publicKey: keys.publicKeyArmored, + publicKyberKey: '', + isNewUser: false, + itemId: 'mock-itemId', + itemType: 'file', + notifyUser: false, + notificationMessage: 'mock-notificationMessage', + sharedWith: 'mock-sharedWith', + encryptionAlgorithm: 'mock-encryptionAlgorithm', + roleId: 'mock-roleId', + }; + + const mockUser: Partial = { + mnemonic: + 'truck arch rather sell tilt return warm nurse rack vacuum rubber tribe unfold scissors copper sock panel ozone harsh ahead danger soda legal state', + keys: { + ecc: { + publicKey: keys.publicKeyArmored, + privateKeyEncrypted: Buffer.from(keys.privateKeyArmored).toString('base64'), + }, + kyber: { + publicKey: keys.publicKyberKeyBase64, + privateKeyEncrypted: keys.privateKyberKeyBase64, + }, + }, + }; + + const mockRootState: Partial = { + user: { user: mockUser as UserSettings, isInitializing: false, isAuthenticated: false, isInitialized: false }, + }; + + const user = mockUser as UserSettings; + vi.spyOn(navigationService, 'push').mockImplementation(() => {}); + + const mockShareService = { + inviteUserToSharedFolder: vi.fn(), + getSharedFolderInvitationsAsInvitedUser: vi.fn(), + getSharingRoles: vi.fn(), + }; + + vi.spyOn(shareService, 'getSharedFolderInvitationsAsInvitedUser').mockImplementation( + mockShareService.getSharedFolderInvitationsAsInvitedUser, + ); + vi.spyOn(shareService, 'getSharingRoles').mockImplementation(mockShareService.getSharingRoles); + vi.spyOn(shareService, 'inviteUserToSharedFolder').mockImplementation(mockShareService.inviteUserToSharedFolder); + + vi.spyOn(userService, 'getPublicKeyByEmail').mockReturnValue( + Promise.resolve({ publicKey: '', publicKyberKey: '' }), + ); + vi.spyOn(userService, 'preCreateUser').mockReturnValue( + Promise.resolve({ + publicKey: '', + publicKyberKey: '', + user: { + uuid: '', + email: '', + }, + }), + ); + + const getStateMock = vi.fn(() => mockRootState as RootState); + const dispatchMock = vi.fn(); + + const thunk = shareItemWithUser(mockPayload); + await thunk(dispatchMock, getStateMock, undefined); + + const [inviteUserToSharedFolderInput] = mockShareService.inviteUserToSharedFolder.mock.calls[0]; + expect(inviteUserToSharedFolderInput.encryptionKey).toBeDefined(); + + const { encryptionKey = '' } = inviteUserToSharedFolderInput; + const decryptedMessage = await decryptMessageWithPrivateKey({ + encryptedMessage: atob(encryptionKey), + privateKeyInBase64: Buffer.from(keys.privateKeyArmored).toString('base64'), + }); + + expect(decryptedMessage).toEqual(mockUser.mnemonic); + expect(mockShareService.inviteUserToSharedFolder).toHaveBeenCalledWith( + expect.objectContaining({ + itemId: mockPayload.itemId, + itemType: mockPayload.itemType, + sharedWith: mockPayload.sharedWith, + notifyUser: mockPayload.notifyUser, + notificationMessage: mockPayload.notificationMessage, + encryptionKey: encryptionKey, + encryptionAlgorithm: mockPayload.encryptionAlgorithm, + roleId: mockPayload.roleId, + persistPreviousSharing: true, + }), + ); + }); + + it('shareItemWithUser encrypts with kyber for an new user', async () => { + const keys = await generateNewKeys(); + const mockPayload: ShareFileWithUserPayload = { + publicKey: '', + publicKyberKey: '', + isNewUser: true, + itemId: 'mock-itemId', + itemType: 'file', + notifyUser: false, + notificationMessage: 'mock-notificationMessage', + sharedWith: 'mock-sharedWith', + encryptionAlgorithm: 'mock-encryptionAlgorithm', + roleId: 'mock-roleId', + }; + + const mockUser: Partial = { + mnemonic: + 'truck arch rather sell tilt return warm nurse rack vacuum rubber tribe unfold scissors copper sock panel ozone harsh ahead danger soda legal state', + keys: { + ecc: { + publicKey: keys.publicKeyArmored, + privateKeyEncrypted: Buffer.from(keys.privateKeyArmored).toString('base64'), + }, + kyber: { + publicKey: keys.publicKyberKeyBase64, + privateKeyEncrypted: keys.privateKyberKeyBase64, + }, + }, + }; + + const mockRootState: Partial = { + user: { user: mockUser as UserSettings, isInitializing: false, isAuthenticated: false, isInitialized: false }, + }; + + const user = mockUser as UserSettings; + vi.spyOn(navigationService, 'push').mockImplementation(() => {}); + + const mockShareService = { + inviteUserToSharedFolder: vi.fn(), + getSharedFolderInvitationsAsInvitedUser: vi.fn(), + getSharingRoles: vi.fn(), + }; + + vi.spyOn(shareService, 'getSharedFolderInvitationsAsInvitedUser').mockImplementation( + mockShareService.getSharedFolderInvitationsAsInvitedUser, + ); + vi.spyOn(shareService, 'getSharingRoles').mockImplementation(mockShareService.getSharingRoles); + vi.spyOn(shareService, 'inviteUserToSharedFolder').mockImplementation(mockShareService.inviteUserToSharedFolder); + + vi.spyOn(userService, 'getPublicKeyByEmail').mockReturnValue( + Promise.resolve({ publicKey: '', publicKyberKey: '' }), ); vi.spyOn(userService, 'preCreateUser').mockReturnValue( Promise.resolve({ @@ -146,4 +341,99 @@ describe('Encryption and Decryption', () => { }), ); }); + + it('shareItemWithUser encrypts with kyber key obtained via getPublicKeyByEmail ', async () => { + const keys = await generateNewKeys(); + const mockPayload: ShareFileWithUserPayload = { + publicKey: '', + publicKyberKey: '', + isNewUser: false, + itemId: 'mock-itemId', + itemType: 'file', + notifyUser: false, + notificationMessage: 'mock-notificationMessage', + sharedWith: 'mock-sharedWith', + encryptionAlgorithm: 'mock-encryptionAlgorithm', + roleId: 'mock-roleId', + }; + + const mockUser: Partial = { + mnemonic: + 'truck arch rather sell tilt return warm nurse rack vacuum rubber tribe unfold scissors copper sock panel ozone harsh ahead danger soda legal state', + keys: { + ecc: { + publicKey: keys.publicKeyArmored, + privateKeyEncrypted: Buffer.from(keys.privateKeyArmored).toString('base64'), + }, + kyber: { + publicKey: keys.publicKyberKeyBase64, + privateKeyEncrypted: keys.privateKyberKeyBase64, + }, + }, + }; + + const mockRootState: Partial = { + user: { user: mockUser as UserSettings, isInitializing: false, isAuthenticated: false, isInitialized: false }, + }; + + const user = mockUser as UserSettings; + vi.spyOn(navigationService, 'push').mockImplementation(() => {}); + + const mockShareService = { + inviteUserToSharedFolder: vi.fn(), + getSharedFolderInvitationsAsInvitedUser: vi.fn(), + getSharingRoles: vi.fn(), + }; + + vi.spyOn(shareService, 'getSharedFolderInvitationsAsInvitedUser').mockImplementation( + mockShareService.getSharedFolderInvitationsAsInvitedUser, + ); + vi.spyOn(shareService, 'getSharingRoles').mockImplementation(mockShareService.getSharingRoles); + vi.spyOn(shareService, 'inviteUserToSharedFolder').mockImplementation(mockShareService.inviteUserToSharedFolder); + + vi.spyOn(userService, 'getPublicKeyByEmail').mockReturnValue( + Promise.resolve({ publicKey: user.keys.ecc.publicKey, publicKyberKey: user.keys?.kyber.publicKey }), + ); + vi.spyOn(userService, 'preCreateUser').mockReturnValue( + Promise.resolve({ + publicKey: '', + publicKyberKey: '', + user: { + uuid: '', + email: '', + }, + }), + ); + + const getStateMock = vi.fn(() => mockRootState as RootState); + const dispatchMock = vi.fn(); + + const thunk = shareItemWithUser(mockPayload); + await thunk(dispatchMock, getStateMock, undefined); + + const [inviteUserToSharedFolderInput] = mockShareService.inviteUserToSharedFolder.mock.calls[0]; + expect(inviteUserToSharedFolderInput.encryptionKey).toBeDefined(); + + const { encryptionKey = '' } = inviteUserToSharedFolderInput; + const decryptedMessage = await hybridDecryptMessageWithPrivateKey({ + encryptedMessageInBase64: encryptionKey, + privateKeyInBase64: Buffer.from(keys.privateKeyArmored).toString('base64'), + privateKyberKeyInBase64: keys.privateKyberKeyBase64, + }); + + expect(decryptedMessage).toEqual(mockUser.mnemonic); + expect(mockShareService.inviteUserToSharedFolder).toHaveBeenCalledWith( + expect.objectContaining({ + itemId: mockPayload.itemId, + itemType: mockPayload.itemType, + sharedWith: mockPayload.sharedWith, + notifyUser: mockPayload.notifyUser, + notificationMessage: mockPayload.notificationMessage, + encryptionKey: encryptionKey, + encryptionAlgorithm: mockPayload.encryptionAlgorithm, + roleId: mockPayload.roleId, + persistPreviousSharing: true, + }), + ); + }); }); diff --git a/src/app/store/slices/sharedLinks/index.ts b/src/app/store/slices/sharedLinks/index.ts index db5d3ca2d..1436fb3d6 100644 --- a/src/app/store/slices/sharedLinks/index.ts +++ b/src/app/store/slices/sharedLinks/index.ts @@ -1,5 +1,5 @@ import { PayloadAction, createAsyncThunk, createSlice } from '@reduxjs/toolkit'; -import * as shareService from 'app/share/services/share.service'; +import shareService from 'app/share/services/share.service'; import { RootState } from '../..'; import { Role, SharedFoldersInvitationsAsInvitedUserResponse } from '@internxt/sdk/dist/drive/share/types'; @@ -103,7 +103,7 @@ const shareItemWithUser = createAsyncThunk => { try { - const newRoles = await getSharingRoles(); + const newRoles = await shareService.getSharingRoles(); if (newRoles.length > 0) { dispatch(sharedActions.setSharedFolderUserRoles(newRoles)); @@ -185,7 +185,7 @@ const getPendingInvitations = createAsyncThunk => { try { - const pendingInvitations = await getSharedFolderInvitationsAsInvitedUser({}); + const pendingInvitations = await shareService.getSharedFolderInvitationsAsInvitedUser({}); dispatch(sharedActions.setPendingInvitations(pendingInvitations.invites)); } catch (err: unknown) { @@ -241,14 +241,14 @@ export const sharedSelectors = { }, }; -export const sharedActions = sharedSlice.actions; */ +export const sharedActions = sharedSlice.actions; export const sharedThunks = { shareItemWithUser, - //stopSharingItem, - //removeUserFromSharedFolder, - //getSharedFolderRoles, - //getPendingInvitations, + stopSharingItem, + removeUserFromSharedFolder, + getSharedFolderRoles, + getPendingInvitations, }; -//export default sharedSlice.reducer; +export default sharedSlice.reducer;