Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix/using custom protocol multiple times causes wrong invitation codes being stored #1847 #2068

Merged
merged 12 commits into from
Nov 15, 2023
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
[unreleased]

* Fix network data proceeding when using custom protocol multiple times #1847

* Backward incompatible change: use pre shared key as connection protector in libp2p. Add libp2p psk to invitation link

[2.0.3-alpha.1]
Expand Down
41 changes: 27 additions & 14 deletions packages/common/src/tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,32 @@ import { InvitationData } from '@quiet/types'
import { composeInvitationDeepUrl, composeInvitationShareUrl } from './invitationCode'
import { QUIET_JOIN_PAGE } from './static'

const validInvitationCodeTestData: InvitationData = {
pairs: [
{
onionAddress: 'y7yczmugl2tekami7sbdz5pfaemvx7bahwthrdvcbzw5vex2crsr26qd',
peerId: 'QmZoiJNAvCffeEHBjk766nLuKVdkxkAT7wfFJDPPLsbKSE',
},
],
psk: 'BNlxfE2WBF7LrlpIX0CvECN5o1oZtA16PkAb7GYiwYw=',
}
export const validInvitationCodeTestData: InvitationData[] = [
{
pairs: [
{
onionAddress: 'y7yczmugl2tekami7sbdz5pfaemvx7bahwthrdvcbzw5vex2crsr26qd',
peerId: 'QmZoiJNAvCffeEHBjk766nLuKVdkxkAT7wfFJDPPLsbKSE',
},
],
psk: 'BNlxfE2WBF7LrlpIX0CvECN5o1oZtA16PkAb7GYiwYw=',
},
{
pairs: [
{
onionAddress: 'pgzlcstu4ljvma7jqyalimcxlvss5bwlbba3c3iszgtwxee4qjdlgeqd',
peerId: 'QmaRchXhkPWq8iLiMZwFfd2Yi4iESWhAYYJt8cTCVXSwpG',
},
],
psk: '5T9GBVpDoRpKJQK4caDTz5e5nym2zprtoySL2oLrzr4=',
},
]

export const validInvitationUrlTestData = {
shareUrl: () => composeInvitationShareUrl(validInvitationCodeTestData),
deepUrl: () => composeInvitationDeepUrl(validInvitationCodeTestData),
code: () => composeInvitationShareUrl(validInvitationCodeTestData).split(QUIET_JOIN_PAGE + '#')[1],
data: validInvitationCodeTestData,
export const getValidInvitationUrlTestData = (data: InvitationData) => {
return {
shareUrl: () => composeInvitationShareUrl(data),
deepUrl: () => composeInvitationDeepUrl(data),
code: () => composeInvitationShareUrl(data).split(QUIET_JOIN_PAGE + '#')[1],
data: data,
}
}
4 changes: 2 additions & 2 deletions packages/desktop/src/main/main.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { autoUpdater } from 'electron-updater'
import { BrowserWindow, app, ipcMain, Menu } from 'electron'
import { waitFor } from '@testing-library/dom'
import path from 'path'
import { composeInvitationDeepUrl, validInvitationUrlTestData } from '@quiet/common'
import { composeInvitationDeepUrl, getValidInvitationUrlTestData, validInvitationCodeTestData } from '@quiet/common'
import { InvitationData } from '@quiet/types'

// eslint-disable-next-line
Expand Down Expand Up @@ -241,7 +241,7 @@ describe('Invitation code', () => {
let codes: InvitationData

beforeEach(() => {
codes = { ...validInvitationUrlTestData.data }
codes = { ...getValidInvitationUrlTestData(validInvitationCodeTestData[0]).data }
})

it('handles invitation code on open-url event (on macos)', async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,18 @@ import PerformCommunityActionComponent from '../PerformCommunityActionComponent'
import { inviteLinkField } from '../../../forms/fields/communityFields'
import { InviteLinkErrors } from '../../../forms/fieldsErrors'
import { CommunityOwnership } from '@quiet/types'
import { Site, QUIET_JOIN_PAGE, validInvitationUrlTestData, PSK_PARAM_KEY } from '@quiet/common'
import {
Site,
QUIET_JOIN_PAGE,
validInvitationCodeTestData,
getValidInvitationUrlTestData,
PSK_PARAM_KEY,
} from '@quiet/common'

describe('join community', () => {
const validCode = validInvitationUrlTestData.code()
const validData = validInvitationUrlTestData.data
const psk = validInvitationUrlTestData.data.psk
const { code, data } = getValidInvitationUrlTestData(validInvitationCodeTestData[0])

const validCode = code()

it('users switches from join to create', async () => {
const { store } = await prepareStore({
Expand Down Expand Up @@ -127,7 +133,7 @@ describe('join community', () => {
expect(submitButton).toBeEnabled()
await userEvent.click(submitButton)

await waitFor(() => expect(handleCommunityAction).toBeCalledWith(validData))
await waitFor(() => expect(handleCommunityAction).toBeCalledWith(data))
})

it.each([[`${QUIET_JOIN_PAGE}#${validCode}`], [`${QUIET_JOIN_PAGE}/#${validCode}`]])(
Expand Down Expand Up @@ -161,7 +167,7 @@ describe('join community', () => {
expect(submitButton).toBeEnabled()
await userEvent.click(submitButton)

await waitFor(() => expect(handleCommunityAction).toBeCalledWith(validData))
await waitFor(() => expect(handleCommunityAction).toBeCalledWith(data))
}
)

Expand Down Expand Up @@ -194,12 +200,12 @@ describe('join community', () => {
expect(submitButton).toBeEnabled()
await userEvent.click(submitButton)

await waitFor(() => expect(handleCommunityAction).toBeCalledWith(validData))
await waitFor(() => expect(handleCommunityAction).toBeCalledWith(data))
})

it.each([
[`http://${validCode}`, InviteLinkErrors.InvalidCode],
[`QmZoiJNAvCffeEHBjk766nLuKVdkxkAT7wfFJDPPLsbKSE=bbb&${PSK_PARAM_KEY}=${psk}`, InviteLinkErrors.InvalidCode],
[`QmZoiJNAvCffeEHBjk766nLuKVdkxkAT7wfFJDPPLsbKSE=bbb&${PSK_PARAM_KEY}=${data.psk}`, InviteLinkErrors.InvalidCode],
['bbb=y7yczmugl2tekami7sbdz5pfaemvx7bahwthrdvcbzw5vex2crsr26qd', InviteLinkErrors.InvalidCode],
['QmZoiJNAvCffeEHBjk766nLuKVdkxkAT7wfFJDPPLsbKSE= ', InviteLinkErrors.InvalidCode],
['nqnw4kc4c77fb47lk52m5l57h4tc', InviteLinkErrors.InvalidCode],
Expand Down
2 changes: 1 addition & 1 deletion packages/desktop/src/renderer/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ ipcRenderer.on('force-save-state', async _event => {
ipcRenderer.on('invitation', (_event, invitation: { data: InvitationData }) => {
if (!invitation.data) return
console.log('invitation', invitation.data.pairs, 'dispatching action')
store.dispatch(communities.actions.handleInvitationCodes(invitation.data))
store.dispatch(communities.actions.customProtocol(invitation.data))
})

const container = document.getElementById('root')
Expand Down
4 changes: 2 additions & 2 deletions packages/desktop/src/renderer/sagas/index.saga.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { communities } from '@quiet/state-manager'
import { all, takeEvery } from 'redux-saga/effects'
import { handleInvitationCodeSaga } from './invitation/handleInvitationCode.saga'
import { customProtocolSaga } from './invitation/customProtocol.saga'
import { startConnectionSaga } from './socket/socket.saga'
import { socketActions } from './socket/socket.slice'

export default function* root(): Generator {
const dataPort = new URLSearchParams(window.location.search).get('dataPort') || ''
yield all([
takeEvery(communities.actions.handleInvitationCodes.type, handleInvitationCodeSaga),
takeEvery(communities.actions.customProtocol.type, customProtocolSaga),
startConnectionSaga(
socketActions.startConnection({
dataPort: parseInt(dataPort),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ import { communities, getFactory, Store } from '@quiet/state-manager'
import { Community, CommunityOwnership, CreateNetworkPayload, InvitationData } from '@quiet/types'
import { FactoryGirl } from 'factory-girl'
import { expectSaga } from 'redux-saga-test-plan'
import { handleInvitationCodeSaga } from './handleInvitationCode.saga'
import { customProtocolSaga } from './customProtocol.saga'
import { SocketState } from '../socket/socket.slice'
import { prepareStore } from '../../testUtils/prepareStore'
import { StoreKeys } from '../../store/store.keys'
import { modalsActions } from '../modals/modals.slice'
import { ModalName } from '../modals/modals.types'
import { validInvitationUrlTestData } from '@quiet/common'
import { validInvitationCodeTestData, getValidInvitationUrlTestData } from '@quiet/common'

describe('Handle invitation code', () => {
let store: Store
Expand All @@ -27,7 +27,8 @@ describe('Handle invitation code', () => {
).store

factory = await getFactory(store)
validInvitationData = validInvitationUrlTestData.data

validInvitationData = getValidInvitationUrlTestData(validInvitationCodeTestData[0]).data
})

it('creates network if code is valid', async () => {
Expand All @@ -36,7 +37,7 @@ describe('Handle invitation code', () => {
peers: validInvitationData.pairs,
psk: validInvitationData.psk,
}
await expectSaga(handleInvitationCodeSaga, communities.actions.handleInvitationCodes(validInvitationData))
await expectSaga(customProtocolSaga, communities.actions.customProtocol(validInvitationData))
.withState(store.getState())
.put(communities.actions.createNetwork(payload))
.run()
Expand All @@ -50,7 +51,7 @@ describe('Handle invitation code', () => {
psk: validInvitationData.psk,
}

await expectSaga(handleInvitationCodeSaga, communities.actions.handleInvitationCodes(validInvitationData))
await expectSaga(customProtocolSaga, communities.actions.customProtocol(validInvitationData))
.withState(store.getState())
.put(
modalsActions.openModal({
Expand All @@ -72,8 +73,8 @@ describe('Handle invitation code', () => {
}

await expectSaga(
handleInvitationCodeSaga,
communities.actions.handleInvitationCodes({
customProtocolSaga,
communities.actions.customProtocol({
pairs: [],
psk: '12345',
})
Expand All @@ -100,8 +101,8 @@ describe('Handle invitation code', () => {
}

await expectSaga(
handleInvitationCodeSaga,
communities.actions.handleInvitationCodes({
customProtocolSaga,
communities.actions.customProtocol({
pairs: validInvitationData.pairs,
psk: '',
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import { socketSelectors } from '../socket/socket.selectors'
import { ModalName } from '../modals/modals.types'
import { modalsActions } from '../modals/modals.slice'

export function* handleInvitationCodeSaga(
action: PayloadAction<ReturnType<typeof communities.actions.handleInvitationCodes>['payload']>
export function* customProtocolSaga(
action: PayloadAction<ReturnType<typeof communities.actions.customProtocol>['payload']>
): Generator {
while (true) {
const connected = yield* select(socketSelectors.isConnected)
Expand All @@ -17,8 +17,8 @@ export function* handleInvitationCodeSaga(
yield* delay(500)
}

const currentCommunityId = yield* select(communities.selectors.currentCommunityId)
if (currentCommunityId) {
const community = yield* select(communities.selectors.currentCommunity)
if (community) {
yield* put(
modalsActions.openModal({
name: ModalName.warningModal,
Expand Down
2 changes: 1 addition & 1 deletion packages/desktop/src/rtl-tests/customProtocol.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ describe('Opening app through custom protocol', () => {
psk: '12345',
}

store.dispatch(communities.actions.handleInvitationCodes(invitationCodes))
store.dispatch(communities.actions.customProtocol(invitationCodes))

store.dispatch(modalsActions.openModal({ name: ModalName.joinCommunityModal }))

Expand Down
61 changes: 61 additions & 0 deletions packages/desktop/src/rtl-tests/deep.linking.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import React from 'react'
import '@testing-library/jest-dom/extend-expect'
import { act } from 'react-dom/test-utils'
import { AnyAction } from 'redux'
import { take } from 'typed-redux-saga'
import MockedSocket from 'socket.io-mock'
import { ioMock } from '../shared/setupTests'
import { prepareStore } from '../renderer/testUtils/prepareStore'
import { renderComponent } from '../renderer/testUtils/renderComponent'
import { validInvitationCodeTestData } from '@quiet/common'
import { communities } from '@quiet/state-manager'

describe('Deep linking', () => {
let socket: MockedSocket

beforeEach(async () => {
socket = new MockedSocket()
ioMock.mockImplementation(() => socket)
})

test('does not override network data if triggered twice', async () => {
const { store, runSaga } = await prepareStore({}, socket)

// Log all the dispatched actions in order
const actions: AnyAction[] = []
runSaga(function* (): Generator {
while (true) {
const action = yield* take()
actions.push(action.type)
}
})

renderComponent(<></>, store)

store.dispatch(communities.actions.customProtocol(validInvitationCodeTestData[0]))
await act(async () => {})

const originalPair = communities.selectors.invitationCodes(store.getState())

// Redo the action to provoke renewed saga runs
store.dispatch(communities.actions.customProtocol(validInvitationCodeTestData[1]))
await act(async () => {})

const currentPair = communities.selectors.invitationCodes(store.getState())

expect(originalPair).toEqual(currentPair)

expect(actions).toMatchInlineSnapshot(`
Array [
"Communities/customProtocol",
"Communities/createNetwork",
"Communities/setInvitationCodes",
"Communities/savePSK",
"Communities/addNewCommunity",
"Communities/setCurrentCommunity",
"Communities/customProtocol",
"Modals/openModal",
]
`)
})
})
10 changes: 7 additions & 3 deletions packages/mobile/src/store/init/deepLink/deepLink.saga.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,18 @@ import { navigationActions } from '../../navigation/navigation.slice'
import { ScreenNames } from '../../../const/ScreenNames.enum'
import { deepLinkSaga } from './deepLink.saga'
import { type Community, CommunityOwnership, ConnectionProcessInfo, type Identity, InvitationData } from '@quiet/types'
import { composeInvitationShareUrl, validInvitationUrlTestData } from '@quiet/common'
import { composeInvitationShareUrl, validInvitationCodeTestData, getValidInvitationUrlTestData } from '@quiet/common'

describe('deepLinkSaga', () => {
let store: Store

const { code, data } = getValidInvitationUrlTestData(validInvitationCodeTestData[0])

const validCode = code()
const validData = data

const id = '00d045ab'
const validData: InvitationData = validInvitationUrlTestData.data
const validCode = validInvitationUrlTestData.code()

const community: Community = {
id,
name: '',
Expand Down
Loading
Loading