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

Update Quiet client to detect a v2 invite link format #2330

Merged
merged 31 commits into from
Apr 12, 2024
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
28dc343
refactor: adjust customProtocolSaga code to be similar to deepLinkSag…
EmiM Mar 5, 2024
b8aa768
fix: customProtocolSaga tests
EmiM Mar 5, 2024
928a412
Merge branch 'develop' into feature/2310
EmiM Mar 5, 2024
1460d57
fix: tests; add missing file
EmiM Mar 5, 2024
b4e8f45
fix: main.ts test
EmiM Mar 6, 2024
7d0b19a
feat: handle old (psk, orbitdbIdentity, addresses) and new (cid, toke…
EmiM Mar 7, 2024
d386067
chore: adjust types
EmiM Mar 11, 2024
d5f7b66
chore: add joinNetwork saga that gathers data for createNetwork; mock…
EmiM Mar 11, 2024
67268d2
Merge branch 'develop' into feature/2310
EmiM Mar 11, 2024
a4d674b
refactor: invitation link parsers
EmiM Mar 14, 2024
8cc6fd3
fix: deepLink saga tests
EmiM Mar 14, 2024
b8203f9
refactor: simplify deepLink and customProtocol sagas
EmiM Mar 15, 2024
7d75c3a
fix: invitation code utils tests
EmiM Mar 15, 2024
d333cfe
chore: add missing github workflow for running common package tests
EmiM Mar 15, 2024
de8533c
refactor: invitation code tests
EmiM Mar 18, 2024
4895b29
fix: unit tests
EmiM Mar 18, 2024
3b5cf43
Merge branch 'develop' into feature/2310
EmiM Mar 18, 2024
54b925c
Merge branch 'develop' into feature/2310
EmiM Mar 19, 2024
6e82f71
feat: add one script for preparing AppImage for e2e tests
EmiM Mar 19, 2024
5f80ac1
fix: 'copy app image for e2e' script
EmiM Mar 19, 2024
7471a24
fix: getting env file name in e2e tests
EmiM Mar 19, 2024
967665d
chore: remove unused code responsible for locking invitation link for…
EmiM Mar 20, 2024
9f84ffe
chore: update changelog
EmiM Mar 20, 2024
bc2430d
Merge branch 'develop' into feature/2310
EmiM Mar 21, 2024
066a201
Merge branch 'develop' into feature/2310
EmiM Mar 25, 2024
beaec60
Merge branch 'develop' into feature/2310
EmiM Apr 4, 2024
cd863db
Merge branch 'develop' into feature/2310
EmiM Apr 9, 2024
a529c7f
fix: mobile tests
EmiM Apr 9, 2024
fcd8b2b
fix: long failing backend test
EmiM Apr 10, 2024
7b5b688
Merge branch 'develop' into feature/2310
EmiM Apr 10, 2024
8ac27e3
Merge branch 'develop' into feature/2310
EmiM Apr 12, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions .github/workflows/utils-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: Common package tests

on:
pull_request:
paths:
- packages/common/**

jobs:
utils-tests:
timeout-minutes: 25
runs-on: ${{ matrix.os }}

strategy:
matrix:
os: [ubuntu-20.04, macos-latest, windows-2019]

steps:
- name: "Print OS"
run: echo ${{ matrix.os }}

- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1

- name: "Setup environment"
uses: ./.github/actions/setup-env
with:
bootstrap-packages: "@quiet/eslint-config,@quiet/logger,@quiet/types,@quiet/common"

- name: "Unit tests"
run: lerna run test --scope @quiet/common --stream
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
"start:desktop": "lerna run --scope @quiet/desktop start",
"lint:all": "lerna run lint",
"distAndRunE2ETests:mac:local": "lerna run --scope @quiet/desktop distMac:local && lerna run --scope e2e-tests test:localBinary --",
"e2e:linux:build": "lerna run --scope @quiet/backend webpack:prod && lerna run --scope @quiet/desktop distUbuntu && lerna run --scope e2e-tests linux:copy",
"e2e:linux:run": "lerna run --scope e2e-tests test --",
"prepare": "husky",
"lint-staged": "lerna run lint-staged"
},
Expand Down
17 changes: 17 additions & 0 deletions packages/backend/src/nest/socket/socket.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import {
type DeleteChannelResponse,
type MessagesLoadedPayload,
type NetworkInfo,
CreateNetworkPayload,
CommunityOwnership,
} from '@quiet/types'
import EventEmitter from 'events'
import { CONFIG_OPTIONS, SERVER_IO_PROVIDER } from '../const'
Expand Down Expand Up @@ -170,6 +172,21 @@ export class SocketService extends EventEmitter implements OnModuleInit {
}
)

socket.on(
SocketActionTypes.DOWNLOAD_INVITE_DATA,
async (payload: { serverAddress: string; cid: string }, callback: (response: CreateNetworkPayload) => void) => {
// this.emit(SocketActionTypes.DOWNLOAD_INVITE_DATA, payload, callback)
console.log('download invite data', payload)
// Mock it for now
callback({
ownership: CommunityOwnership.User,
peers: [],
psk: '',
ownerOrbitDbIdentity: '',
})
}
)

socket.on(SocketActionTypes.LEAVE_COMMUNITY, async () => {
this.logger('Leaving community')
this.emit(SocketActionTypes.LEAVE_COMMUNITY)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export const areObjectsEqual = (obj1: any, obj2: any): boolean => {
// Using this only makes sense for small objects whose properties are in the same order
return JSON.stringify(obj1) === JSON.stringify(obj2)
}
1 change: 1 addition & 0 deletions packages/common/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ export * from './libp2p'
export * from './tests'
export * from './auth'
export * from './messages'
export * from './compare'
224 changes: 138 additions & 86 deletions packages/common/src/invitationCode.test.ts
Original file line number Diff line number Diff line change
@@ -1,120 +1,172 @@
import { InvitationData } from '@quiet/types'
import { InvitationDataV1, InvitationDataVersion, InvitationPair } from '@quiet/types'
import {
argvInvitationCode,
composeInvitationDeepUrl,
invitationShareUrl,
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 { validInvitationDatav1, validInvitationDatav2 } from './tests'
import { createLibp2pAddress } from './libp2p'

describe('Invitation code helper', () => {
const peerId1 = 'QmZoiJNAvCffeEHBjk766nLuKVdkxkAT7wfFJDPPLsbKSA'
const address1 = 'gloao6h5plwjy4tdlze24zzgcxll6upq2ex2fmu2ohhyu4gtys4nrjad'
const peerId2 = 'QmZoiJNAvCffeEHBjk766nLuKVdkxkAT7wfFJDPPLsbKSE'
const address2 = 'y7yczmugl2tekami7sbdz5pfaemvx7bahwthrdvcbzw5vex2crsr26qd'
const psk = 'BNlxfE2WBF7LrlpIX0CvECN5o1oZtA16PkAb7GYiwYw%3D'
const pskDecoded = 'BNlxfE2WBF7LrlpIX0CvECN5o1oZtA16PkAb7GYiwYw='
const ownerOrbitDbIdentity = 'testOwnerOrbitDbIdentity'
describe(`Invitation code helper ${InvitationDataVersion.v1}`, () => {
const address = 'gloao6h5plwjy4tdlze24zzgcxll6upq2ex2fmu2ohhyu4gtys4nrjad'
const peerId = 'QmZoiJNAvCffeEHBjk766nLuKVdkxkAT7wfFJDPPLsbKSE'
const data: InvitationDataV1 = {
...validInvitationDatav1[0],
pairs: [...validInvitationDatav1[0].pairs, { peerId: peerId, onionAddress: address }],
}
const urlParams = [
[data.pairs[0].peerId, data.pairs[0].onionAddress],
[data.pairs[1].peerId, data.pairs[1].onionAddress],
[PSK_PARAM_KEY, data.psk],
[OWNER_ORBIT_DB_IDENTITY_PARAM_KEY, data.ownerOrbitDbIdentity],
]

it('retrieves invitation code from argv', () => {
const expectedCodes: InvitationData = {
pairs: [
{ peerId: peerId1, onionAddress: address1 },
{ peerId: peerId2, onionAddress: address2 },
],
psk: pskDecoded,
ownerOrbitDbIdentity,
}
const result = argvInvitationCode([
'something',
'quiet:/invalid',
'zbay://invalid',
'quiet://invalid',
'quiet://?param=invalid',
composeInvitationDeepUrl(expectedCodes),
])
expect(result).toEqual(expectedCodes)
const result = argvInvitationCode(['something', 'quiet:/invalid', 'zbay://invalid', composeInvitationDeepUrl(data)])
expect(result).toEqual(data)
})

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({
pairs: [
{ peerId: 'peerID1', onionAddress: 'address1' },
{ peerId: 'peerID2', onionAddress: 'address2' },
],
psk: pskDecoded,
ownerOrbitDbIdentity,
})
).toEqual(`quiet://?peerID1=address1&peerID2=address2&${PSK_PARAM_KEY}=${psk}`)
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('creates invitation share url based on invitation data', () => {
const pairs: InvitationData = {
pairs: [
{ peerId: 'peerID1', onionAddress: 'address1' },
{ peerId: 'peerID2', onionAddress: 'address2' },
],
psk: pskDecoded,
ownerOrbitDbIdentity,
}
const expected = `${QUIET_JOIN_PAGE}#peerID1=address1&peerID2=address2&${PSK_PARAM_KEY}=${psk}`
expect(composeInvitationShareUrl(pairs)).toEqual(expected)
const url = new URL(QUIET_JOIN_PAGE)
urlParams.forEach(([key, value]) => url.searchParams.append(key, value))
expect(composeInvitationShareUrl(data)).toEqual(url.href.replace('?', '#'))
})

it('builds proper invitation share url from peers addresses', () => {
it('converts list of p2p addresses to invitation pairs', () => {
const pair: InvitationPair = {
peerId: 'QmZoiJNAvCffeEHBjk766nLuKVdkxkAT7wfFJDPPLsbKSE',
onionAddress: 'gloao6h5plwjy4tdlze24zzgcxll6upq2ex2fmu2ohhyu4gtys4nrjad',
}
const peerList = [
'/dns4/gloao6h5plwjy4tdlze24zzgcxll6upq2ex2fmu2ohhyu4gtys4nrjad.onion/tcp/443/wss/p2p/QmZoiJNAvCffeEHBjk766nLuKVdkxkAT7wfFJDPPLsbKSE',
createLibp2pAddress(pair.onionAddress, pair.peerId),
'invalidAddress',
'/dns4/somethingElse.onion/tcp/443/wss/p2p/QmZoiJNAvCffeEHBjk766nLuKVdkxkAT7wfFJDPPLsbKSA',
createLibp2pAddress('somethingElse.onion', 'QmZoiJNAvCffeEHBjk766nLuKVdkxkAT7wfFJDPPLsbKSA'),
]
expect(invitationShareUrl(peerList, pskDecoded, ownerOrbitDbIdentity)).toEqual(
`${QUIET_JOIN_PAGE}#QmZoiJNAvCffeEHBjk766nLuKVdkxkAT7wfFJDPPLsbKSE=gloao6h5plwjy4tdlze24zzgcxll6upq2ex2fmu2ohhyu4gtys4nrjad&QmZoiJNAvCffeEHBjk766nLuKVdkxkAT7wfFJDPPLsbKSA=somethingElse&${PSK_PARAM_KEY}=${psk}`
)
expect(p2pAddressesToPairs(peerList)).toEqual([pair])
})

it('retrieves invitation codes from deep url', () => {
const codes = parseInvitationCodeDeepUrl(
`quiet://?${peerId1}=${address1}&${peerId2}=${address2}&${PSK_PARAM_KEY}=${psk}`
)
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({
pairs: [
{ peerId: peerId1, onionAddress: address1 },
{ peerId: peerId2, onionAddress: address2 },
],
psk: pskDecoded,
ownerOrbitDbIdentity,
version: InvitationDataVersion.v1,
...data,
})
})

it.each([['12345'], ['a2FzemE='], 'a2FycGllIHcgZ2FsYXJlY2llIGVjaWUgcGVjaWUgYWxlIGkgdGFrIHpqZWNpZQ=='])(
'parsing invitation code throws error if psk is invalid: (%s)',
(psk: string) => {
expect(() => {
parseInvitationCodeDeepUrl(`quiet://?${peerId1}=${address1}&${peerId2}=${address2}&${PSK_PARAM_KEY}=${psk}`)
}).toThrow()
}
)

it('retrieves invitation codes from deep url with partly invalid codes', () => {
const peerId2 = 'QmZoiJNAvCffeEHBjk766nLuKVdkxkAT7wfFJDPPLs'
const address2 = 'y7yczmugl2tekami7sbdz5pfaemvx7bahwthrdvcbzw5vex2crsr26qd'
const parsed = parseInvitationCodeDeepUrl(
`quiet://?${peerId1}=${address1}&${peerId2}=${address2}&${PSK_PARAM_KEY}=${psk}`
)
expect(parsed).toEqual({ pairs: [{ peerId: peerId1, onionAddress: address1 }], psk: pskDecoded })
it.each([
[PSK_PARAM_KEY, '12345'],
[PSK_PARAM_KEY, 'a2FzemE='],
[PSK_PARAM_KEY, 'a2FycGllIHcgZ2FsYXJlY2llIGVjaWUgcGVjaWUgYWxlIGkgdGFrIHpqZWNpZQ=='],
])('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()
})

it('retrieves invitation codes from deep url with partly invalid addresses', () => {
const urlParamsWithInvalidAddress = [
[data.pairs[0].peerId, data.pairs[0].onionAddress],
[data.pairs[1].peerId, data.pairs[1].onionAddress],
['QmZoiJNAvCffeEHBjk766nLuKVdkxkAT7wf', 'y7yczmugl2tekami7sbdz5pfaemvx7bahwthrdv'],
[PSK_PARAM_KEY, data.psk],
[OWNER_ORBIT_DB_IDENTITY_PARAM_KEY, data.ownerOrbitDbIdentity],
]

const url = new URL(DEEP_URL_SCHEME_WITH_SEPARATOR)
urlParamsWithInvalidAddress.forEach(([key, value]) => url.searchParams.append(key, value))

const parsed = parseInvitationCodeDeepUrl(url.href)
expect(parsed).toEqual({
version: InvitationDataVersion.v1,
...data,
})
})
})

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()
})
})
Loading
Loading