diff --git a/packages/common/src/invitationCode.test.ts b/packages/common/src/invitationCode.test.ts index 91eaa5d55d..2403312823 100644 --- a/packages/common/src/invitationCode.test.ts +++ b/packages/common/src/invitationCode.test.ts @@ -1,15 +1,22 @@ -import { InvitationData, InvitationPair } from '@quiet/types' +import { InvitationData, InvitationDataVersion, InvitationPair } from '@quiet/types' import { argvInvitationCode, composeInvitationDeepUrl, composeInvitationShareUrl, parseInvitationCodeDeepUrl, PSK_PARAM_KEY, + OWNER_ORBIT_DB_IDENTITY_PARAM_KEY, p2pAddressesToPairs, + CID_PARAM_KEY, + TOKEN_PARAM_KEY, + SERVER_ADDRESS_PARAM_KEY, + INVITER_ADDRESS_PARAM_KEY, + DEEP_URL_SCHEME_WITH_SEPARATOR, } from './invitationCode' import { QUIET_JOIN_PAGE } from './static' +import { validInvitationDatav2 } from './tests' -describe('Invitation code helper', () => { +describe(`Invitation code helper ${InvitationDataVersion.v1}`, () => { const peerId1 = 'QmZoiJNAvCffeEHBjk766nLuKVdkxkAT7wfFJDPPLsbKSA' const address1 = 'gloao6h5plwjy4tdlze24zzgcxll6upq2ex2fmu2ohhyu4gtys4nrjad' const peerId2 = 'QmZoiJNAvCffeEHBjk766nLuKVdkxkAT7wfFJDPPLsbKSE' @@ -31,24 +38,22 @@ describe('Invitation code helper', () => { 'something', 'quiet:/invalid', 'zbay://invalid', - 'quiet://invalid', - 'quiet://?param=invalid', composeInvitationDeepUrl(expectedCodes), ]) expect(result).toEqual(expectedCodes) }) - it('returns null if argv do not contain any valid invitation code', () => { - const result = argvInvitationCode([ - 'something', - 'quiet:/invalid', - 'zbay://invalid', - 'quiet://invalid', - 'quiet://?param=invalid', - ]) + it('returns null if argv do not contain any url with proper scheme', () => { + const result = argvInvitationCode(['something', 'quiet:/invalid', 'zbay://invalid']) expect(result).toBeNull() }) + it('throws error if argv contains invalid invitation url', () => { + expect(() => { + argvInvitationCode(['something', 'quiet:/invalid', 'quiet://?param=invalid']) + }).toThrow() + }) + it('composes proper invitation deep url', () => { expect( composeInvitationDeepUrl({ @@ -59,7 +64,9 @@ describe('Invitation code helper', () => { psk: pskDecoded, ownerOrbitDbIdentity, }) - ).toEqual(`quiet://?peerID1=address1&peerID2=address2&${PSK_PARAM_KEY}=${psk}`) + ).toEqual( + `quiet://?peerID1=address1&peerID2=address2&${PSK_PARAM_KEY}=${psk}&${OWNER_ORBIT_DB_IDENTITY_PARAM_KEY}=${ownerOrbitDbIdentity}` + ) }) it('creates invitation share url based on invitation data', () => { @@ -71,7 +78,7 @@ describe('Invitation code helper', () => { psk: pskDecoded, ownerOrbitDbIdentity, } - const expected = `${QUIET_JOIN_PAGE}#peerID1=address1&peerID2=address2&${PSK_PARAM_KEY}=${psk}` + const expected = `${QUIET_JOIN_PAGE}#peerID1=address1&peerID2=address2&${PSK_PARAM_KEY}=${psk}&${OWNER_ORBIT_DB_IDENTITY_PARAM_KEY}=${ownerOrbitDbIdentity}` expect(composeInvitationShareUrl(pairs)).toEqual(expected) }) @@ -85,14 +92,16 @@ describe('Invitation code helper', () => { 'invalidAddress', '/dns4/somethingElse.onion/tcp/443/wss/p2p/QmZoiJNAvCffeEHBjk766nLuKVdkxkAT7wfFJDPPLsbKSA', ] + console.log('p2pAddressesToPairs(peerList)', p2pAddressesToPairs(peerList)) expect(p2pAddressesToPairs(peerList)).toEqual([pair]) }) it('retrieves invitation codes from deep url', () => { const codes = parseInvitationCodeDeepUrl( - `quiet://?${peerId1}=${address1}&${peerId2}=${address2}&${PSK_PARAM_KEY}=${psk}` + `quiet://?${peerId1}=${address1}&${peerId2}=${address2}&${PSK_PARAM_KEY}=${psk}&${OWNER_ORBIT_DB_IDENTITY_PARAM_KEY}=${ownerOrbitDbIdentity}` ) expect(codes).toEqual({ + version: InvitationDataVersion.v1, pairs: [ { peerId: peerId1, onionAddress: address1 }, { peerId: peerId2, onionAddress: address2 }, @@ -106,7 +115,9 @@ describe('Invitation code helper', () => { 'parsing invitation code throws error if psk is invalid: (%s)', (psk: string) => { expect(() => { - parseInvitationCodeDeepUrl(`quiet://?${peerId1}=${address1}&${peerId2}=${address2}&${PSK_PARAM_KEY}=${psk}`) + parseInvitationCodeDeepUrl( + `quiet://?${peerId1}=${address1}&${peerId2}=${address2}&${PSK_PARAM_KEY}=${psk}&${OWNER_ORBIT_DB_IDENTITY_PARAM_KEY}=${ownerOrbitDbIdentity}` + ) }).toThrow() } ) @@ -115,8 +126,65 @@ describe('Invitation code helper', () => { const peerId2 = 'QmZoiJNAvCffeEHBjk766nLuKVdkxkAT7wfFJDPPLs' const address2 = 'y7yczmugl2tekami7sbdz5pfaemvx7bahwthrdvcbzw5vex2crsr26qd' const parsed = parseInvitationCodeDeepUrl( - `quiet://?${peerId1}=${address1}&${peerId2}=${address2}&${PSK_PARAM_KEY}=${psk}` + `${DEEP_URL_SCHEME_WITH_SEPARATOR}?${peerId1}=${address1}&${peerId2}=${address2}&${PSK_PARAM_KEY}=${psk}&${OWNER_ORBIT_DB_IDENTITY_PARAM_KEY}=${ownerOrbitDbIdentity}` ) - expect(parsed).toEqual({ pairs: [{ peerId: peerId1, onionAddress: address1 }], psk: pskDecoded }) + expect(parsed).toEqual({ + version: InvitationDataVersion.v1, + pairs: [{ peerId: peerId1, onionAddress: address1 }], + psk: pskDecoded, + ownerOrbitDbIdentity, + }) + }) +}) + +describe(`Invitation code helper ${InvitationDataVersion.v2}`, () => { + const data = validInvitationDatav2[0] + const urlParams = [ + [CID_PARAM_KEY, data.cid], + [TOKEN_PARAM_KEY, data.token], + [SERVER_ADDRESS_PARAM_KEY, data.serverAddress], + [INVITER_ADDRESS_PARAM_KEY, data.inviterAddress], + ] + + it('creates invitation share url based on invitation data', () => { + const url = new URL(QUIET_JOIN_PAGE) + urlParams.forEach(([key, value]) => url.searchParams.append(key, value)) + expect(composeInvitationShareUrl(data)).toEqual(url.href.replace('?', '#')) + }) + + it('composes proper invitation deep url', () => { + const url = new URL(DEEP_URL_SCHEME_WITH_SEPARATOR) + urlParams.forEach(([key, value]) => url.searchParams.append(key, value)) + expect(composeInvitationDeepUrl(data)).toEqual(url.href) + }) + + it('retrieves invitation codes from deep url v2', () => { + const url = new URL(DEEP_URL_SCHEME_WITH_SEPARATOR) + urlParams.forEach(([key, value]) => url.searchParams.append(key, value)) + const codes = parseInvitationCodeDeepUrl(url.href) + expect(codes).toEqual({ + version: InvitationDataVersion.v2, + cid: data.cid, + token: data.token, + serverAddress: data.serverAddress, + inviterAddress: data.inviterAddress, + }) + }) + + it.each([ + // TODO: add check for invalid token + [CID_PARAM_KEY, 'sth'], + [SERVER_ADDRESS_PARAM_KEY, 'website.com'], + [INVITER_ADDRESS_PARAM_KEY, 'abcd'], + ])('parsing deep url throws error if data is invalid: %s=%s', (paramKey: string, paramValue: string) => { + const url = new URL(DEEP_URL_SCHEME_WITH_SEPARATOR) + urlParams.forEach(([key, value]) => url.searchParams.append(key, value)) + + // Replace valid param value with invalid one + url.searchParams.set(paramKey, paramValue) + + expect(() => { + parseInvitationCodeDeepUrl(url.href) + }).toThrow() }) }) diff --git a/packages/common/src/invitationCode.ts b/packages/common/src/invitationCode.ts index 1fcfd016de..1b748a70e2 100644 --- a/packages/common/src/invitationCode.ts +++ b/packages/common/src/invitationCode.ts @@ -15,7 +15,7 @@ export const TOKEN_PARAM_KEY = 't' export const INVITER_ADDRESS_PARAM_KEY = 'i' export const SERVER_ADDRESS_PARAM_KEY = 's' -const DEEP_URL_SCHEME_WITH_SEPARATOR = 'quiet://' +export const DEEP_URL_SCHEME_WITH_SEPARATOR = 'quiet://' const DEEP_URL_SCHEME = 'quiet' const ONION_ADDRESS_REGEX = /^[a-z0-9]{56}$/g const PEER_ID_REGEX = /^[a-zA-Z0-9]{46}$/g @@ -153,6 +153,8 @@ export const p2pAddressesToPairs = (addresses: string[]): InvitationPair[] => { continue } const rawAddress = onionAddress.endsWith('.onion') ? onionAddress.split('.')[0] : onionAddress + if (!peerDataValid({ peerId, onionAddress: rawAddress })) continue + pairs.push({ peerId: peerId, onionAddress: rawAddress }) } return pairs @@ -265,7 +267,7 @@ const isParamValid = (param: string, value: string) => { // logger.error(e.message) // return false // } - return true + return Boolean(value.match(PEER_ID_REGEX)) case TOKEN_PARAM_KEY: // TODO: validate token format @@ -278,7 +280,7 @@ const isParamValid = (param: string, value: string) => { logger.error(e.message) return false } - break + return true case INVITER_ADDRESS_PARAM_KEY: return Boolean(value.trim().match(ONION_ADDRESS_REGEX)) diff --git a/packages/common/src/tests.ts b/packages/common/src/tests.ts index 5fd8e250de..6c4791f4bb 100644 --- a/packages/common/src/tests.ts +++ b/packages/common/src/tests.ts @@ -25,10 +25,10 @@ export const validInvitationDatav1: InvitationDataV1[] = [ }, ] -const validInvitationDatav2: InvitationDataV2[] = [ +export const validInvitationDatav2: InvitationDataV2[] = [ { version: InvitationDataVersion.v2, - cid: 'QmZoiJNAvCffeEHBjk766nLuKVdkxkAT7wfFJDPPL', + cid: 'QmaRchXhkPWq8iLiMZwFfd2Yi4iESWhAYYJt8cTCVXSwpG', token: 'BNlxfE2WBF7LrlpIX0CvECN5o1oZtA16PkAb7GYiwYw', serverAddress: 'https://tryquiet.org/api/', inviterAddress: 'pgzlcstu4ljvma7jqyalimcxlvss5bwlbba3c3iszgtwxee4qjdlgeqd',