diff --git a/.github/workflows/desktop-build.yml b/.github/workflows/desktop-build.yml index 84e6c79ab5..841c1c5776 100644 --- a/.github/workflows/desktop-build.yml +++ b/.github/workflows/desktop-build.yml @@ -138,8 +138,9 @@ jobs: GH_TOKEN: ${{ secrets.GH_TOKEN }} CSC_KEY_PASSWORD: ${{ secrets.MAC_CSC_KEY_PASSWORD }} CSC_LINK: ${{ secrets.MAC_CSC_LINK }} - APPLEID: ${{ secrets.APPLE_ID }} - APPLEIDPASS: ${{ secrets.APPLE_ID_PASS }} + APPLE_ID: ${{ secrets.APPLE_ID }} + APPLE_ID_PASS: ${{ secrets.APPLE_ID_PASS }} + APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} USE_HARD_LINKS: false diff --git a/packages/backend/CHANGELOG.md b/packages/backend/CHANGELOG.md index 850cd66787..8318c9d408 100644 --- a/packages/backend/CHANGELOG.md +++ b/packages/backend/CHANGELOG.md @@ -193,6 +193,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline # [1.10.0-alpha.0](https://github.com/TryQuiet/backend/compare/@quiet/backend@1.9.0...@quiet/backend@1.10.0-alpha.0) (2023-08-29) +## [1.9.5](https://github.com/TryQuiet/backend/compare/@quiet/backend@1.9.4...@quiet/backend@1.9.5) (2023-11-09) **Note:** Version bump only for package @quiet/backend diff --git a/packages/backend/src/backendManager.ts b/packages/backend/src/backendManager.ts index f7e80b5e4e..b0abe6066e 100644 --- a/packages/backend/src/backendManager.ts +++ b/packages/backend/src/backendManager.ts @@ -8,8 +8,10 @@ import { ConnectionsManagerService } from './nest/connections-manager/connection import { TorControl } from './nest/tor/tor-control.service' import { torBinForPlatform, torDirForPlatform } from './nest/common/utils' import initRnBridge from './rn-bridge' - +import { INestApplicationContext } from '@nestjs/common' import logger from './nest/common/logger' +import { OpenServices, validateOptions } from './options' + const log = logger('backendManager') const program = new Command() @@ -25,21 +27,13 @@ program .option('-a, --appDataPath ', 'Path of application data directory') .option('-d, --socketIOPort ', 'Socket io data server port') .option('-r, --resourcesPath ', 'Application resources path') + .option('-scrt, --socketIOSecret ', 'socketIO secret') program.parse(process.argv) const options = program.opts() console.log('options', options) -interface OpenServices { - torControlPort?: any - socketIOPort?: any - httpTunnelPort?: any - authCookie?: any -} - -import { INestApplicationContext } from '@nestjs/common' - export const runBackendDesktop = async () => { const isDev = process.env.NODE_ENV === 'development' @@ -48,11 +42,14 @@ export const runBackendDesktop = async () => { // @ts-ignore global.crypto = webcrypto + validateOptions(options) + const resourcesPath = isDev ? null : options.resourcesPath.trim() const app = await NestFactory.createApplicationContext( AppModule.forOptions({ socketIOPort: options.socketIOPort, + socketIOSecret: options.socketIOSecret, torBinaryPath: torBinForPlatform(resourcesPath), torResourcesPath: torDirForPlatform(resourcesPath), torControlPort: await getPort(), @@ -87,7 +84,7 @@ export const runBackendDesktop = async () => { }) } -export const runBackendMobile = async (): Promise => { +export const runBackendMobile = async () => { // Enable triggering push notifications process.env['BACKEND'] = 'mobile' process.env['CONNECTION_TIME'] = (new Date().getTime() / 1000).toString() // Get time in seconds @@ -97,6 +94,7 @@ export const runBackendMobile = async (): Promise => { const app: INestApplicationContext = await NestFactory.createApplicationContext( AppModule.forOptions({ socketIOPort: options.dataPort, + socketIOSecret: options.socketIOSecret, httpTunnelPort: options.httpTunnelPort ? options.httpTunnelPort : null, torAuthCookie: options.authCookie ? options.authCookie : null, torControlPort: options.controlPort ? options.controlPort : await getPort(), diff --git a/packages/backend/src/nest/app.module.ts b/packages/backend/src/nest/app.module.ts index bbc422bbbb..cf2fdc655d 100644 --- a/packages/backend/src/nest/app.module.ts +++ b/packages/backend/src/nest/app.module.ts @@ -32,7 +32,7 @@ import { Server as SocketIO } from 'socket.io' import { StorageModule } from './storage/storage.module' import { IpfsModule } from './ipfs/ipfs.module' import { Level } from 'level' -import { getCors } from './common/utils' +import { verifyToken } from '@quiet/common' @Global() @Module({ @@ -94,10 +94,40 @@ export class AppModule { _app.use(cors()) const server = createServer(_app) const io = new SocketIO(server, { - cors: getCors(), + cors: { + origin: '127.0.0.1', + allowedHeaders: ['authorization'], + credentials: true, + }, pingInterval: 1000_000, pingTimeout: 1000_000, }) + io.engine.use((req, res, next) => { + const authHeader = req.headers['authorization'] + if (!authHeader) { + console.error('No authorization header') + res.writeHead(401, 'No authorization header') + res.end() + return + } + + const token = authHeader && authHeader.split(' ')[1] + if (!token) { + console.error('No auth token') + res.writeHead(401, 'No authorization token') + res.end() + return + } + + if (verifyToken(options.socketIOSecret, token)) { + next() + } else { + console.error('Wrong basic token') + res.writeHead(401, 'Unauthorized') + res.end() + } + }) + return { server, io } }, inject: [EXPRESS_PROVIDER], @@ -122,7 +152,7 @@ export class AppModule { }, { provide: LEVEL_DB, - useFactory: (dbPath: string) => new Level(dbPath, { valueEncoding: 'json' }), + useFactory: (dbPath: string) => new Level(dbPath, { valueEncoding: 'json' }), inject: [DB_PATH], }, ], diff --git a/packages/backend/src/nest/types.ts b/packages/backend/src/nest/types.ts index fa2c4e3943..ec6302de1a 100644 --- a/packages/backend/src/nest/types.ts +++ b/packages/backend/src/nest/types.ts @@ -5,6 +5,7 @@ import { Server as SocketIO } from 'socket.io' export class ConnectionsManagerTypes { options: Partial socketIOPort: number + socketIOSecret: string httpTunnelPort?: number torAuthCookie?: string torControlPort?: number diff --git a/packages/backend/src/options.ts b/packages/backend/src/options.ts new file mode 100644 index 0000000000..a19d0342c4 --- /dev/null +++ b/packages/backend/src/options.ts @@ -0,0 +1,32 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import commander from 'commander' + +export interface OpenServices { + torControlPort?: any + socketIOPort?: any + socketIOSecret?: any + httpTunnelPort?: any + authCookie?: any +} + +interface Options { + platform?: any + dataPath?: any + dataPort?: any + torBinary?: any + authCookie?: any + controlPort?: any + httpTunnelPort?: any + appDataPath?: string + socketIOPort?: number + resourcesPath?: string + socketIOSecret: string +} + +// concept +export const validateOptions = (_options: commander.OptionValues) => { + const options = _options as Options + if (!options.socketIOSecret) { + throw new Error('socketIOSecret is missing in options') + } +} diff --git a/packages/common/CHANGELOG.md b/packages/common/CHANGELOG.md index 58019f1a5d..8e887a1e7b 100644 --- a/packages/common/CHANGELOG.md +++ b/packages/common/CHANGELOG.md @@ -5,10 +5,6 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline ## [2.0.2-alpha.1](https://github.com/TryQuiet/quiet/compare/@quiet/common@2.0.2-alpha.0...@quiet/common@2.0.2-alpha.1) (2023-11-14) -**Note:** Version bump only for package @quiet/common - - - ## [2.0.2-alpha.0](https://github.com/TryQuiet/quiet/compare/@quiet/common@2.0.1-alpha.4...@quiet/common@2.0.2-alpha.0) (2023-10-26) @@ -107,9 +103,17 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline +# [1.9.0-alpha.0](/compare/@quiet/common@1.8.0...@quiet/common@1.9.0-alpha.0) (2023-08-29) + + +## [1.8.2](https://github.com/TryQuiet/quiet/compare/@quiet/common@1.8.1...@quiet/common@1.8.2) (2023-11-09) + +**Note:** Version bump only for package @quiet/common + + + -# [1.9.0-alpha.0](/compare/@quiet/common@1.8.0...@quiet/common@1.9.0-alpha.0) (2023-08-29) ## [1.8.1](https://github.com/TryQuiet/quiet/compare/@quiet/common@1.8.0...@quiet/common@1.8.1) (2023-09-15) **Note:** Version bump only for package @quiet/common diff --git a/packages/common/package-lock.json b/packages/common/package-lock.json index 8e6302db19..f59a8c3632 100644 --- a/packages/common/package-lock.json +++ b/packages/common/package-lock.json @@ -983,6 +983,88 @@ "@jridgewell/sourcemap-codec": "1.4.14" } }, + "node_modules/@peculiar/webcrypto": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/@peculiar/webcrypto/-/webcrypto-1.4.3.tgz", + "integrity": "sha512-VtaY4spKTdN5LjJ04im/d/joXuvLbQdgy5Z4DXF4MFZhQ+MTrejbNMkfZBp1Bs3O5+bFqnJgyGdPuZQflvIa5A==", + "dependencies": { + "@peculiar/asn1-schema": "^2.3.6", + "@peculiar/json-schema": "^1.1.12", + "pvtsutils": "^1.3.2", + "tslib": "^2.5.0", + "webcrypto-core": "^1.7.7" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/@peculiar/webcrypto/node_modules/@peculiar/asn1-schema": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-schema/-/asn1-schema-2.3.8.tgz", + "integrity": "sha512-ULB1XqHKx1WBU/tTFIA+uARuRoBVZ4pNdOA878RDrRbBfBGcSzi5HBkdScC6ZbHn8z7L8gmKCgPC1LHRrP46tA==", + "dependencies": { + "asn1js": "^3.0.5", + "pvtsutils": "^1.3.5", + "tslib": "^2.6.2" + } + }, + "node_modules/@peculiar/webcrypto/node_modules/@peculiar/json-schema": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/@peculiar/json-schema/-/json-schema-1.1.12.tgz", + "integrity": "sha512-coUfuoMeIB7B8/NMekxaDzLhaYmp0HZNPEjYRm9goRou8UZIC3z21s0sL9AWoCw4EG876QyO3kYrc61WNF9B/w==", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@peculiar/webcrypto/node_modules/asn1js": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/asn1js/-/asn1js-3.0.5.tgz", + "integrity": "sha512-FVnvrKJwpt9LP2lAMl8qZswRNm3T4q9CON+bxldk2iwk3FFpuwhx2FfinyitizWHsVYyaY+y5JzDR0rCMV5yTQ==", + "dependencies": { + "pvtsutils": "^1.3.2", + "pvutils": "^1.1.3", + "tslib": "^2.4.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@peculiar/webcrypto/node_modules/pvtsutils": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/pvtsutils/-/pvtsutils-1.3.5.tgz", + "integrity": "sha512-ARvb14YB9Nm2Xi6nBq1ZX6dAM0FsJnuk+31aUp4TrcZEdKUlSqOqsxJHUPJDNE3qiIp+iUPEIeR6Je/tgV7zsA==", + "dependencies": { + "tslib": "^2.6.1" + } + }, + "node_modules/@peculiar/webcrypto/node_modules/pvutils": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/pvutils/-/pvutils-1.1.3.tgz", + "integrity": "sha512-pMpnA0qRdFp32b1sJl1wOJNxZLQ2cbQx+k6tjNtZ8CpvVhNqEPRgivZ2WOUev2YMajecdH7ctUPDvEe87nariQ==", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@peculiar/webcrypto/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@peculiar/webcrypto/node_modules/webcrypto-core": { + "version": "1.7.7", + "resolved": "https://registry.npmjs.org/webcrypto-core/-/webcrypto-core-1.7.7.tgz", + "integrity": "sha512-7FjigXNsBfopEj+5DV2nhNpfic2vumtjjgPmeDKk45z+MJwXKKfhPB7118Pfzrmh4jqOMST6Ch37iPAHoImg5g==", + "dependencies": { + "@peculiar/asn1-schema": "^2.3.6", + "@peculiar/json-schema": "^1.1.12", + "asn1js": "^3.0.1", + "pvtsutils": "^1.3.2", + "tslib": "^2.4.0" + } + }, "node_modules/@sinonjs/commons": { "version": "1.8.6", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.6.tgz", @@ -6982,6 +7064,78 @@ "@jridgewell/sourcemap-codec": "1.4.14" } }, + "@peculiar/webcrypto": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/@peculiar/webcrypto/-/webcrypto-1.4.3.tgz", + "integrity": "sha512-VtaY4spKTdN5LjJ04im/d/joXuvLbQdgy5Z4DXF4MFZhQ+MTrejbNMkfZBp1Bs3O5+bFqnJgyGdPuZQflvIa5A==", + "requires": { + "@peculiar/asn1-schema": "^2.3.6", + "@peculiar/json-schema": "^1.1.12", + "pvtsutils": "^1.3.2", + "tslib": "^2.5.0", + "webcrypto-core": "^1.7.7" + }, + "dependencies": { + "@peculiar/asn1-schema": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-schema/-/asn1-schema-2.3.8.tgz", + "integrity": "sha512-ULB1XqHKx1WBU/tTFIA+uARuRoBVZ4pNdOA878RDrRbBfBGcSzi5HBkdScC6ZbHn8z7L8gmKCgPC1LHRrP46tA==", + "requires": { + "asn1js": "^3.0.5", + "pvtsutils": "^1.3.5", + "tslib": "^2.6.2" + } + }, + "@peculiar/json-schema": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/@peculiar/json-schema/-/json-schema-1.1.12.tgz", + "integrity": "sha512-coUfuoMeIB7B8/NMekxaDzLhaYmp0HZNPEjYRm9goRou8UZIC3z21s0sL9AWoCw4EG876QyO3kYrc61WNF9B/w==", + "requires": { + "tslib": "^2.0.0" + } + }, + "asn1js": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/asn1js/-/asn1js-3.0.5.tgz", + "integrity": "sha512-FVnvrKJwpt9LP2lAMl8qZswRNm3T4q9CON+bxldk2iwk3FFpuwhx2FfinyitizWHsVYyaY+y5JzDR0rCMV5yTQ==", + "requires": { + "pvtsutils": "^1.3.2", + "pvutils": "^1.1.3", + "tslib": "^2.4.0" + } + }, + "pvtsutils": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/pvtsutils/-/pvtsutils-1.3.5.tgz", + "integrity": "sha512-ARvb14YB9Nm2Xi6nBq1ZX6dAM0FsJnuk+31aUp4TrcZEdKUlSqOqsxJHUPJDNE3qiIp+iUPEIeR6Je/tgV7zsA==", + "requires": { + "tslib": "^2.6.1" + } + }, + "pvutils": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/pvutils/-/pvutils-1.1.3.tgz", + "integrity": "sha512-pMpnA0qRdFp32b1sJl1wOJNxZLQ2cbQx+k6tjNtZ8CpvVhNqEPRgivZ2WOUev2YMajecdH7ctUPDvEe87nariQ==" + }, + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "webcrypto-core": { + "version": "1.7.7", + "resolved": "https://registry.npmjs.org/webcrypto-core/-/webcrypto-core-1.7.7.tgz", + "integrity": "sha512-7FjigXNsBfopEj+5DV2nhNpfic2vumtjjgPmeDKk45z+MJwXKKfhPB7118Pfzrmh4jqOMST6Ch37iPAHoImg5g==", + "requires": { + "@peculiar/asn1-schema": "^2.3.6", + "@peculiar/json-schema": "^1.1.12", + "asn1js": "^3.0.1", + "pvtsutils": "^1.3.2", + "tslib": "^2.4.0" + } + } + } + }, "@sinonjs/commons": { "version": "1.8.6", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.6.tgz", diff --git a/packages/common/src/auth.test.ts b/packages/common/src/auth.test.ts new file mode 100644 index 0000000000..3d00770d7b --- /dev/null +++ b/packages/common/src/auth.test.ts @@ -0,0 +1,19 @@ +import { encodeSecret, verifyToken } from './auth' + +describe('Auth', () => { + it('correctly create secret, encode and decode', () => { + const secret = 'secret' + const token = encodeSecret(secret) + const decodedSecret = verifyToken(secret, token) + + expect(decodedSecret).toBeTruthy() + }) + + it('create token with wrong secret', () => { + const secret = 'secret' + const token = encodeSecret('test') + const decodedSecret = verifyToken(secret, token) + + expect(decodedSecret).toBeFalsy() + }) +}) diff --git a/packages/common/src/auth.ts b/packages/common/src/auth.ts new file mode 100644 index 0000000000..41bd90449e --- /dev/null +++ b/packages/common/src/auth.ts @@ -0,0 +1,6 @@ +export const encodeSecret = (secret: string) => Buffer.from(secret).toString('base64') + +export const verifyToken = (secret: string, token: string): boolean => { + const decoded = Buffer.from(token, 'base64').toString('ascii') + return decoded === secret +} diff --git a/packages/common/src/index.ts b/packages/common/src/index.ts index 19dcfce623..f15cd34520 100644 --- a/packages/common/src/index.ts +++ b/packages/common/src/index.ts @@ -9,4 +9,5 @@ export * from './naming' export * from './fileData' export * from './libp2p' export * from './tests' +export * from './auth' export * from './messages' diff --git a/packages/desktop/CHANGELOG.md b/packages/desktop/CHANGELOG.md index 96cee6fc28..89971d9147 100644 --- a/packages/desktop/CHANGELOG.md +++ b/packages/desktop/CHANGELOG.md @@ -63,14 +63,6 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline ## [2.0.3-alpha.2](https://github.com/TryQuiet/quiet/compare/@quiet/desktop@2.0.3-alpha.1...@quiet/desktop@2.0.3-alpha.2) (2023-11-09) -### Bug Fixes - -* trigger desktop ([2898bee](https://github.com/TryQuiet/quiet/commit/2898bee80bbf2f16cbda67281a29e47716faa77c)) - - - - - ## [2.0.3-alpha.1](https://github.com/TryQuiet/quiet/compare/@quiet/desktop@2.0.3-alpha.0...@quiet/desktop@2.0.3-alpha.1) (2023-11-08) **Note:** Version bump only for package @quiet/desktop @@ -159,6 +151,19 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline ## [2.0.1-alpha.1](https://github.com/TryQuiet/quiet/compare/quiet@2.0.1-alpha.0...quiet@2.0.1-alpha.1) (2023-09-25) + +## [1.9.7](https://github.com/TryQuiet/quiet/compare/quiet@1.9.6...quiet@1.9.7) (2023-11-17) + + +### Bug Fixes + +* trigger desktop ([2898bee](https://github.com/TryQuiet/quiet/commit/2898bee80bbf2f16cbda67281a29e47716faa77c)) +* pass team id for notarization ([ab86fc0](https://github.com/TryQuiet/quiet/commit/ab86fc0cefd5d8b3715712a4dd234bbe45f18cc2)) + + + +## [1.9.6](https://github.com/TryQuiet/quiet/compare/quiet@1.9.5...quiet@1.9.6) (2023-11-09) + **Note:** Version bump only for package quiet diff --git a/packages/desktop/scripts/notarize.js b/packages/desktop/scripts/notarize.js index f60c6b64e1..59b99c1ea0 100644 --- a/packages/desktop/scripts/notarize.js +++ b/packages/desktop/scripts/notarize.js @@ -2,24 +2,23 @@ const { notarize } = require('@electron/notarize') exports.default = async function notarizing(context) { - const { electronPlatformName, appOutDir } = context + const { electronPlatformName } = context + if (electronPlatformName !== 'darwin' || process.env.IS_E2E) { console.log('skipping notarization') return } - const appName = context.packager.appInfo.productFilename - console.log('notarization start') - try { - const response = await notarize({ - appBundleId: 'com.yourcompany.yourAppId', - appPath: `${appOutDir}/${appName}.app`, - appleId: process.env.APPLEID, - appleIdPassword: process.env.APPLEIDPASS - }) - console.log('notarization done') - return response - } catch (e) { - console.error(e) - } + console.log('notarization started') + + const response = await notarize({ + tool: 'notarytool', + appleId: process.env.APPLE_ID, + appleIdPassword: process.env.APPLE_ID_PASS, + teamId: process.env.APPLE_TEAM_ID + }) + + console.log('notarization done') + + return response } diff --git a/packages/desktop/src/main/main.ts b/packages/desktop/src/main/main.ts index c6c74db976..24174c5dd9 100644 --- a/packages/desktop/src/main/main.ts +++ b/packages/desktop/src/main/main.ts @@ -102,6 +102,8 @@ setEngine( }) ) +const SOCKET_IO_SECRET = webcrypto.getRandomValues(new Uint32Array(5)).join('') + export const isBrowserWindow = (window: BrowserWindow | null): window is BrowserWindow => { return window instanceof BrowserWindow } @@ -208,7 +210,7 @@ export const createWindow = async () => { mainWindow.loadURL( url.format({ pathname: path.join(__dirname, './index.html'), - search: `dataPort=${ports.dataServer}`, + search: `dataPort=${ports.dataServer}&socketIOSecret=${SOCKET_IO_SECRET}`, protocol: 'file:', slashes: true, hash: '/', @@ -333,6 +335,7 @@ app.on('ready', async () => { await createWindow() mainWindow?.webContents.on('did-finish-load', () => { + mainWindow?.webContents.send('socketIOSecret', SOCKET_IO_SECRET) if (splash && !splash.isDestroyed()) { const [width, height] = splash.getSize() mainWindow?.setSize(width, height) @@ -365,6 +368,8 @@ app.on('ready', async () => { `${process.resourcesPath}`, '-p', 'desktop', + '-scrt', + `${SOCKET_IO_SECRET}`, ] const backendBundlePath = path.normalize(require.resolve('backend-bundle')) diff --git a/packages/desktop/src/renderer/Root.tsx b/packages/desktop/src/renderer/Root.tsx index 608060d374..3cd16b9876 100644 --- a/packages/desktop/src/renderer/Root.tsx +++ b/packages/desktop/src/renderer/Root.tsx @@ -33,6 +33,8 @@ import UnregisteredModalContainer from './components/widgets/userLabel/unregiste import DuplicateModalContainer from './components/widgets/userLabel/duplicate/DuplicateModal.container' import UsernameTakenModalContainer from './components/widgets/usernameTakenModal/UsernameTakenModal.container' import PossibleImpersonationAttackModalContainer from './components/widgets/possibleImpersonationAttackModal/PossibleImpersonationAttackModal.container' +import BreakingChangesWarning from './containers/widgets/breakingChangesWarning/BreakingChangesWarning' +// Trigger lerna export const persistor = persistStore(store) export default () => { @@ -61,6 +63,7 @@ export default () => { + diff --git a/packages/desktop/src/renderer/components/widgets/update/UpdateModalComponent.stories.tsx b/packages/desktop/src/renderer/components/widgets/update/UpdateModalComponent.stories.tsx new file mode 100644 index 0000000000..d8c17ce5b9 --- /dev/null +++ b/packages/desktop/src/renderer/components/widgets/update/UpdateModalComponent.stories.tsx @@ -0,0 +1,53 @@ +import React from 'react' +import { ComponentStory, ComponentMeta } from '@storybook/react' + +import UpdateModal, { UpdateModalProps } from './UpdateModalComponent' + +import { withTheme } from '../../../storybook/decorators' +import theme from '../../../theme' + +import Button from '@mui/material/Button' + +const Template: ComponentStory = args => { + return +} + +const args: UpdateModalProps = { + open: true, + handleClose: function (): void { + console.log('modal closed') + }, + buttons: [ + , + ], + title: 'Software update', + message: 'An update is available for Quiet.', +} + +export const Component = Template.bind({}) + +Component.args = args + +const component: ComponentMeta = { + title: 'Components/UpdateModalComponent', + decorators: [withTheme], + component: UpdateModal, +} + +export default component diff --git a/packages/desktop/src/renderer/components/widgets/update/UpdateModal.test.tsx b/packages/desktop/src/renderer/components/widgets/update/UpdateModalComponent.test.tsx similarity index 54% rename from packages/desktop/src/renderer/components/widgets/update/UpdateModal.test.tsx rename to packages/desktop/src/renderer/components/widgets/update/UpdateModalComponent.test.tsx index d29212be1e..1a968eca97 100644 --- a/packages/desktop/src/renderer/components/widgets/update/UpdateModal.test.tsx +++ b/packages/desktop/src/renderer/components/widgets/update/UpdateModalComponent.test.tsx @@ -1,11 +1,19 @@ import React from 'react' -import { UpdateModal } from './UpdateModal' import { renderComponent } from '../../../testUtils/renderComponent' +import UpdateModalComponent from './UpdateModalComponent' describe('UpdateModal', () => { it('renders component', () => { - const result = renderComponent() + const result = renderComponent( + + ) expect(result.baseElement).toMatchInlineSnapshot(` { style="width: 600px;" >
-
- -
+
-
-

- Software update -

-
+ Software update +
-
-

- A new update for Quiet is available and will be applied on your next restart. -

-
+ Update is available for Quiet. +

-
- -
-
-
- -
+ class="MuiGrid-root MuiGrid-container MuiGrid-spacing-xs-2 MuiGrid-direction-xs-column css-1bnhfwg-MuiGrid-root" + />
diff --git a/packages/desktop/src/renderer/components/widgets/update/UpdateModalComponent.tsx b/packages/desktop/src/renderer/components/widgets/update/UpdateModalComponent.tsx new file mode 100644 index 0000000000..0ffd86edbb --- /dev/null +++ b/packages/desktop/src/renderer/components/widgets/update/UpdateModalComponent.tsx @@ -0,0 +1,79 @@ +import React, { ReactElement } from 'react' + +import { styled } from '@mui/material/styles' + +import Typography from '@mui/material/Typography' +import Grid from '@mui/material/Grid' + +import Icon from '../../ui/Icon/Icon' +import updateIcon from '../../../static/images/updateIcon.svg' +import Modal from '../../ui/Modal/Modal' + +const PREFIX = 'UpdateModal' + +const classes = { + info: `${PREFIX}info`, + updateIcon: `${PREFIX}updateIcon`, + title: `${PREFIX}title`, + message: `${PREFIX}message`, +} + +const StyledModalContent = styled(Grid)(({ theme }) => ({ + backgroundColor: theme.palette.colors.white, + border: 'none', + + [`& .${classes.info}`]: { + marginTop: 38, + }, + + [`& .${classes.updateIcon}`]: { + width: 102, + height: 102, + }, + + [`& .${classes.title}`]: { + marginTop: 24, + marginBottom: 16, + textAlign: 'center', + }, + + [`& .${classes.message}`]: { + marginBottom: 32, + textAlign: 'center', + }, +})) + +export interface UpdateModalProps { + open: boolean + handleClose: () => void + buttons: ReactElement[] + title: string + message: string +} + +export const UpdateModalComponent: React.FC = ({ open, handleClose, buttons, title, message }) => { + return ( + + + + + + + {title} + + + {message} + + + {buttons.map((button, index) => ( + + {button} + + ))} + + + + ) +} + +export default UpdateModalComponent diff --git a/packages/desktop/src/renderer/containers/widgets/breakingChangesWarning/BreakingChangesWarning.tsx b/packages/desktop/src/renderer/containers/widgets/breakingChangesWarning/BreakingChangesWarning.tsx new file mode 100644 index 0000000000..9ef8817dca --- /dev/null +++ b/packages/desktop/src/renderer/containers/widgets/breakingChangesWarning/BreakingChangesWarning.tsx @@ -0,0 +1,63 @@ +import React, { useCallback, useEffect } from 'react' +import { ModalName } from '../../../sagas/modals/modals.types' +import { useModal } from '../../hooks' +import UpdateModalComponent from '../../../components/widgets/update/UpdateModalComponent' + +import Button from '@mui/material/Button' +import theme from '../../../theme' + +import { shell } from 'electron' +import { Site } from '@quiet/common' + +const BreakingChangesWarning = () => { + const modal = useModal(ModalName.breakingChangesWarning) + + const title = 'Update available' + const message = + 'Quiet’s next release makes joining communities faster and more reliable by letting people join when the owner is offline! 🎉 However, these changes are not backwards compatible, so you must re-install Quiet from tryquiet.org and re-create or re-join your community. 😥 This version of Quiet will no longer receive any updates or security fixes, so please re-install soon. We apologize for the inconvenience.' + + const updateAction = useCallback(() => { + shell.openExternal(`${Site.MAIN_PAGE}#Downloads`) + }, []) + + const updateButton = ( + + ) + + const dismissButton = ( + + ) + + return +} + +export default BreakingChangesWarning diff --git a/packages/desktop/src/renderer/containers/widgets/update/UpdateModal.test.tsx b/packages/desktop/src/renderer/containers/widgets/update/UpdateModal.test.tsx new file mode 100644 index 0000000000..25bbb22ed1 --- /dev/null +++ b/packages/desktop/src/renderer/containers/widgets/update/UpdateModal.test.tsx @@ -0,0 +1,35 @@ +import React from 'react' +import { jest } from '@jest/globals' +import { fireEvent, screen } from '@testing-library/dom' +import { prepareStore, renderComponent } from '../../../testUtils' +import { StoreKeys } from '@quiet/state-manager' +import { ModalsInitialState } from '../../../sagas/modals/modals.slice' +import { ModalName } from '../../../sagas/modals/modals.types' +import * as UpdateModal from './UpdateModal' + +describe('Update Modal', () => { + test('triggers app update on button click', async () => { + const { store } = await prepareStore({ + [StoreKeys.Modals]: { + ...new ModalsInitialState(), + [ModalName.applicationUpdate]: { open: true, args: {} }, + }, + }) + + const update = jest.fn() + + const modal = + + // @ts-expect-error + jest.spyOn(UpdateModal, 'mapDispatchToProps').mockImplementation(() => ({ + handleUpdate: update, + })) + + renderComponent(modal, store) + + const button = screen.getByText('Update now') + fireEvent.click(button) + + expect(update).toHaveBeenCalled() + }) +}) diff --git a/packages/desktop/src/renderer/containers/widgets/update/UpdateModal.tsx b/packages/desktop/src/renderer/containers/widgets/update/UpdateModal.tsx index 30096a2c84..b2a54ad345 100644 --- a/packages/desktop/src/renderer/containers/widgets/update/UpdateModal.tsx +++ b/packages/desktop/src/renderer/containers/widgets/update/UpdateModal.tsx @@ -1,11 +1,16 @@ import React from 'react' import { AnyAction, Dispatch, bindActionCreators } from 'redux' import { useDispatch } from 'react-redux' -import UpdateModal from '../../../components/widgets/update/UpdateModal' import updateHandlers from '../../../store/handlers/update' + import { useModal } from '../../hooks' import { ModalName } from '../../../sagas/modals/modals.types' +import UpdateModalComponent from '../../../components/widgets/update/UpdateModalComponent' + +import Button from '@mui/material/Button' +import theme from '../../../theme' + export const mapDispatchToProps = (dispatch: Dispatch) => bindActionCreators( { @@ -17,8 +22,32 @@ export const mapDispatchToProps = (dispatch: Dispatch) => const ApplicationUpdateModal: React.FC = () => { const dispatch = useDispatch() + const actions = mapDispatchToProps(dispatch) const modal = useModal(ModalName.applicationUpdate) - return + + const title = 'Software update' + const message = 'An update is availale for Quiet.' + + const button = ( + + ) + + return } + export default ApplicationUpdateModal diff --git a/packages/desktop/src/renderer/index.tsx b/packages/desktop/src/renderer/index.tsx index 49a82e3f13..3c478402b2 100644 --- a/packages/desktop/src/renderer/index.tsx +++ b/packages/desktop/src/renderer/index.tsx @@ -1,11 +1,10 @@ import React from 'react' import { createRoot } from 'react-dom/client' import { ipcRenderer } from 'electron' - import Root, { persistor } from './Root' import store from './store' import updateHandlers from './store/handlers/update' -import { communities } from '@quiet/state-manager' +import { communities, connection } from '@quiet/state-manager' import { InvitationData } from '@quiet/types' if (window && process.env.DEBUG) { @@ -27,6 +26,10 @@ ipcRenderer.on('invitation', (_event, invitation: { data: InvitationData }) => { store.dispatch(communities.actions.customProtocol(invitation.data)) }) +ipcRenderer.on('socketIOSecret', (_event, socketIOSecret) => { + store.dispatch(connection.actions.setSocketIOSecret(socketIOSecret)) +}) + const container = document.getElementById('root') if (!container) throw new Error('No root html element!') let root = createRoot(container) diff --git a/packages/desktop/src/renderer/sagas/modals/modals.slice.ts b/packages/desktop/src/renderer/sagas/modals/modals.slice.ts index fee515645f..31b217a520 100644 --- a/packages/desktop/src/renderer/sagas/modals/modals.slice.ts +++ b/packages/desktop/src/renderer/sagas/modals/modals.slice.ts @@ -37,7 +37,8 @@ export class ModalsInitialState { [ModalName.unregisteredUsernameModal] = { open: false, args: {} }; [ModalName.duplicatedUsernameModal] = { open: false, args: {} }; [ModalName.usernameTakenModal] = { open: false, args: {} }; - [ModalName.possibleImpersonationAttackModal] = { open: false, args: {} } + [ModalName.possibleImpersonationAttackModal] = { open: false, args: {} }; + [ModalName.breakingChangesWarning] = { open: false, args: {} }; } export const modalsSlice = createSlice({ diff --git a/packages/desktop/src/renderer/sagas/modals/modals.types.ts b/packages/desktop/src/renderer/sagas/modals/modals.types.ts index d79e2518e1..ddc4b9e8b3 100644 --- a/packages/desktop/src/renderer/sagas/modals/modals.types.ts +++ b/packages/desktop/src/renderer/sagas/modals/modals.types.ts @@ -1,5 +1,6 @@ export enum ModalName { applicationUpdate = 'applicationUpdate', + breakingChangesWarning = 'breakingChangesWarning', createChannel = 'createChannel', deleteChannel = 'deleteChannel', accountSettingsModal = 'accountSettingsModal', diff --git a/packages/desktop/src/renderer/sagas/socket/socket.saga.test.ts b/packages/desktop/src/renderer/sagas/socket/socket.saga.test.ts new file mode 100644 index 0000000000..d6c199291f --- /dev/null +++ b/packages/desktop/src/renderer/sagas/socket/socket.saga.test.ts @@ -0,0 +1,41 @@ +import { connection, getFactory, Store } from '@quiet/state-manager' +import { FactoryGirl } from 'factory-girl' +import { expectSaga } from 'redux-saga-test-plan' +import { socketActions, WebsocketConnectionPayload } from '../socket/socket.slice' +import { prepareStore } from '../../testUtils/prepareStore' +import { startConnectionSaga } from './socket.saga' + +describe('Start Connection Saga', () => { + const dataPort = 1234 + let store: Store + let factory: FactoryGirl + + beforeEach(async () => { + store = (await prepareStore()).store + factory = await getFactory(store) + }) + + it('socketIOSecret is null - take setSocketIOSecret', async () => { + const payload: WebsocketConnectionPayload = { + dataPort, + } + + await expectSaga(startConnectionSaga, socketActions.startConnection(payload)) + .withState(store.getState()) + .take(connection.actions.setSocketIOSecret) + .run() + }) + + it('socketIOSecret already exist', async () => { + const payload: WebsocketConnectionPayload = { + dataPort, + } + + store.dispatch(connection.actions.setSocketIOSecret('secret')) + + await expectSaga(startConnectionSaga, socketActions.startConnection(payload)) + .withState(store.getState()) + .not.take(connection.actions.setSocketIOSecret) + .run() + }) +}) diff --git a/packages/desktop/src/renderer/sagas/socket/socket.saga.ts b/packages/desktop/src/renderer/sagas/socket/socket.saga.ts index 03646371b5..99037aed24 100644 --- a/packages/desktop/src/renderer/sagas/socket/socket.saga.ts +++ b/packages/desktop/src/renderer/sagas/socket/socket.saga.ts @@ -1,22 +1,39 @@ import { io, Socket } from 'socket.io-client' -import { all, fork, takeEvery, call, put, cancel, FixedTask } from 'typed-redux-saga' +import { all, fork, takeEvery, call, put, cancel, FixedTask, select, take } from 'typed-redux-saga' import { PayloadAction } from '@reduxjs/toolkit' -import { socket as stateManager, messages } from '@quiet/state-manager' +import { socket as stateManager, messages, connection } from '@quiet/state-manager' import { socketActions } from './socket.slice' import { eventChannel } from 'redux-saga' import { displayMessageNotificationSaga } from '../notifications/notifications.saga' - import logger from '../../logger' +import { encodeSecret } from '@quiet/common' + const log = logger('socket') export function* startConnectionSaga( action: PayloadAction['payload']> ): Generator { - const dataPort = action.payload.dataPort + const { dataPort } = action.payload if (!dataPort) { log.error('About to start connection but no dataPort found') } - const socket = yield* call(io, `http://127.0.0.1:${dataPort}`) + + let socketIOSecret = yield* select(connection.selectors.socketIOSecret) + + if (!socketIOSecret) { + yield* take(connection.actions.setSocketIOSecret) + socketIOSecret = yield* select(connection.selectors.socketIOSecret) + } + + if (!socketIOSecret) return + + const token = encodeSecret(socketIOSecret) + const socket = yield* call(io, `http://127.0.0.1:${dataPort}`, { + withCredentials: true, + extraHeaders: { + authorization: `Basic ${token}`, + }, + }) yield* fork(handleSocketLifecycleActions, socket) // Handle opening/restoring connection diff --git a/packages/desktop/src/renderer/testUtils/prepareStore.ts b/packages/desktop/src/renderer/testUtils/prepareStore.ts index 6a8f15d65c..aff3b9f127 100644 --- a/packages/desktop/src/renderer/testUtils/prepareStore.ts +++ b/packages/desktop/src/renderer/testUtils/prepareStore.ts @@ -119,5 +119,6 @@ function* mockSocketConnectionSaga(socket: MockedSocket): Generator { socket.socketClient.emit('connect') }) }) + yield* put(connection.actions.setSocketIOSecret('socketIOSecret')) yield* put(socketActions.startConnection({ dataPort: 4677 })) } diff --git a/packages/e2e-tests/CHANGELOG.md b/packages/e2e-tests/CHANGELOG.md index 7866fc693b..a40de9a683 100644 --- a/packages/e2e-tests/CHANGELOG.md +++ b/packages/e2e-tests/CHANGELOG.md @@ -127,6 +127,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline # [1.9.0-alpha.0](/compare/e2e-tests@1.8.0...e2e-tests@1.9.0-alpha.0) (2023-08-29) +## [1.8.3](https://github.com/TryQuiet/quiet/compare/e2e-tests@1.8.2...e2e-tests@1.8.3) (2023-11-09) **Note:** Version bump only for package e2e-tests diff --git a/packages/e2e-tests/src/tests/backwardsCompatibility.test.ts b/packages/e2e-tests/src/tests/backwardsCompatibility.test.ts index eb463ef500..72cbd8eab9 100644 --- a/packages/e2e-tests/src/tests/backwardsCompatibility.test.ts +++ b/packages/e2e-tests/src/tests/backwardsCompatibility.test.ts @@ -19,8 +19,8 @@ describe.skip('Backwards Compatibility', () => { let generalChannel: Channel let secondChannel: Channel let messagesToCompare: WebElement[] - let sidebar: Sidebar + const dataDir = `e2e_${(Math.random() * 10 ** 18).toString(36)}` const communityName = 'testcommunity' const ownerUsername = 'bob' @@ -28,6 +28,8 @@ describe.skip('Backwards Compatibility', () => { const loopMessages = 'abc'.split('') const newChannelName = 'mid-night-club' + const isAlpha = process.env.FILE_NAME?.toString().includes('alpha') + beforeAll(async () => { ownerAppOldVersion = new App({ dataDir, fileName: 'Quiet-1.2.0-copy.AppImage' }) }) @@ -141,7 +143,7 @@ describe.skip('Backwards Compatibility', () => { await ownerAppNewVersion.open() }) - if (process.env.TEST_MODE) { + if (process.env.TEST_MODE && isAlpha) { it('Close debug modal', async () => { console.log('New version', 2) const debugModal = new DebugModeModal(ownerAppNewVersion.driver) diff --git a/packages/integration-tests/CHANGELOG.md b/packages/integration-tests/CHANGELOG.md index 756e9508a6..d874888668 100644 --- a/packages/integration-tests/CHANGELOG.md +++ b/packages/integration-tests/CHANGELOG.md @@ -140,6 +140,12 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline # [1.10.0-alpha.0](/compare/integration-tests@1.9.0...integration-tests@1.10.0-alpha.0) (2023-08-29) + + +## [1.9.2](https://github.com/TryQuiet/quiet/compare/integration-tests@1.9.1...integration-tests@1.9.2) (2023-11-09) + +**Note:** Version bump only for package integration-tests + ## [1.9.1](https://github.com/TryQuiet/quiet/compare/integration-tests@1.9.0...integration-tests@1.9.1) (2023-09-15) **Note:** Version bump only for package integration-tests diff --git a/packages/mobile/.storybook/index.js b/packages/mobile/.storybook/index.js index 703004a968..2042c91936 100644 --- a/packages/mobile/.storybook/index.js +++ b/packages/mobile/.storybook/index.js @@ -27,6 +27,7 @@ configure(() => { require('../src/components/DeleteChannel/DeleteChannel.stories') require('../src/components/QRCode/QRCode.stories') require('../src/components/Message/Message.stories') + require('../src/components/Notifier/Notifier.stories') require('../src/components/Chat/Chat.stories') require('../src/components/TextWithLink/TextWithLink.stories') require('../src/components/Typography/Typography.stories') diff --git a/packages/mobile/CHANGELOG.md b/packages/mobile/CHANGELOG.md index 425473677f..5940a80639 100644 --- a/packages/mobile/CHANGELOG.md +++ b/packages/mobile/CHANGELOG.md @@ -50,11 +50,13 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline ## [2.0.3-alpha.4](https://github.com/TryQuiet/quiet/compare/@quiet/mobile@2.0.3-alpha.3...@quiet/mobile@2.0.3-alpha.4) (2023-11-13) +## [1.10.10](https://github.com/TryQuiet/quiet/compare/@quiet/mobile@1.10.9...@quiet/mobile@1.10.10) (2023-11-09) ### Features * bump versionCode ([104c656](https://github.com/TryQuiet/quiet/commit/104c6569805efecffcc23a801a8ba91a352966fe)) +* bump versionCode ([08af810](https://github.com/TryQuiet/quiet/commit/08af81032b533beaea800cf1fd53035616c9d5d8)) @@ -346,6 +348,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline # [1.11.0-alpha.0](/compare/@quiet/mobile@1.10.0...@quiet/mobile@1.11.0-alpha.0) (2023-08-29) +## [1.10.9](https://github.com/TryQuiet/quiet/compare/@quiet/mobile@1.10.8...@quiet/mobile@1.10.9) (2023-11-09) **Note:** Version bump only for package @quiet/mobile diff --git a/packages/mobile/android/app/src/main/java/com/quietmobile/Backend/BackendWorker.kt b/packages/mobile/android/app/src/main/java/com/quietmobile/Backend/BackendWorker.kt index 111310380a..e1a6d5792d 100644 --- a/packages/mobile/android/app/src/main/java/com/quietmobile/Backend/BackendWorker.kt +++ b/packages/mobile/android/app/src/main/java/com/quietmobile/Backend/BackendWorker.kt @@ -1,6 +1,7 @@ package com.quietmobile.Backend; import android.content.Context +import android.util.Base64 import android.util.Log import androidx.core.app.NotificationCompat import androidx.work.CoroutineWorker @@ -26,6 +27,7 @@ import org.json.JSONException import org.json.JSONObject import org.torproject.android.binary.TorResourceInstaller import java.util.concurrent.ThreadLocalRandom +import kotlin.collections.ArrayList class BackendWorker(private val context: Context, workerParams: WorkerParameters) : CoroutineWorker(context, workerParams) { @@ -88,8 +90,10 @@ class BackendWorker(private val context: Context, workerParams: WorkerParameters setForeground(createForegroundInfo()) withContext(Dispatchers.IO) { + // Get and store data port for usage in methods across the app val dataPort = Utils.getOpenPort(11000) + val socketIOSecret = Utils.generateRandomString(20) // Init nodejs project launch { @@ -98,7 +102,7 @@ class BackendWorker(private val context: Context, workerParams: WorkerParameters launch { notificationHandler = NotificationHandler(context) - subscribePushNotifications(dataPort) + subscribePushNotifications(dataPort, socketIOSecret) } launch { @@ -112,7 +116,7 @@ class BackendWorker(private val context: Context, workerParams: WorkerParameters * In any case, websocket won't connect until data server starts listening */ delay(WEBSOCKET_CONNECTION_DELAY) - startWebsocketConnection(dataPort) + startWebsocketConnection(dataPort, socketIOSecret) } val dataPath = Utils.createDirectory(context) @@ -122,7 +126,7 @@ class BackendWorker(private val context: Context, workerParams: WorkerParameters val platform = "mobile" - startNodeProjectWithArguments("bundle.cjs --torBinary $torBinary --dataPath $dataPath --dataPort $dataPort --platform $platform") + startNodeProjectWithArguments("bundle.cjs --torBinary $torBinary --dataPath $dataPath --dataPort $dataPort --platform $platform --socketIOSecret $socketIOSecret") } println("FINISHING BACKEND WORKER") @@ -167,8 +171,14 @@ class BackendWorker(private val context: Context, workerParams: WorkerParameters ) } - private fun subscribePushNotifications(port: Int) { - val webSocketClient = IO.socket("http://localhost:$port") + private fun subscribePushNotifications(port: Int, secret: String) { + val encodedSecret = Base64.encodeToString(secret.toByteArray(Charsets.UTF_8), Base64.NO_WRAP) + val options = IO.Options() + val headers = mutableMapOf>() + headers["Authorization"] = listOf("Basic $encodedSecret") + options.extraHeaders = headers + + val webSocketClient = IO.socket("http://127.0.0.1:$port", options) // Listen for events sent from nodejs webSocketClient.on("pushNotification", onPushNotification) // Client won't connect by itself (`connect()` method has to be called manually) @@ -190,10 +200,10 @@ class BackendWorker(private val context: Context, workerParams: WorkerParameters notificationHandler.notify(message, username) } - private fun startWebsocketConnection(port: Int) { + private fun startWebsocketConnection(port: Int, socketIOSecret: String) { Log.d("WEBSOCKET CONNECTION", "Starting on $port") // Proceed only if data port is defined - val websocketConnectionPayload = WebsocketConnectionPayload(port) + val websocketConnectionPayload = WebsocketConnectionPayload(port, socketIOSecret) CommunicationModule.handleIncomingEvents( CommunicationModule.WEBSOCKET_CONNECTION_CHANNEL, Gson().toJson(websocketConnectionPayload), diff --git a/packages/mobile/android/app/src/main/java/com/quietmobile/Scheme/WebsocketConnectionPayload.kt b/packages/mobile/android/app/src/main/java/com/quietmobile/Scheme/WebsocketConnectionPayload.kt index a5a490284a..ac8d89962c 100644 --- a/packages/mobile/android/app/src/main/java/com/quietmobile/Scheme/WebsocketConnectionPayload.kt +++ b/packages/mobile/android/app/src/main/java/com/quietmobile/Scheme/WebsocketConnectionPayload.kt @@ -1,5 +1,6 @@ package com.quietmobile.Scheme data class WebsocketConnectionPayload ( - val dataPort: Int + val dataPort: Int, + val socketIOSecret: String ) diff --git a/packages/mobile/android/app/src/main/java/com/quietmobile/Utils/Utils.kt b/packages/mobile/android/app/src/main/java/com/quietmobile/Utils/Utils.kt index 58da0628ba..123288a0d6 100644 --- a/packages/mobile/android/app/src/main/java/com/quietmobile/Utils/Utils.kt +++ b/packages/mobile/android/app/src/main/java/com/quietmobile/Utils/Utils.kt @@ -7,6 +7,7 @@ import java.io.* import java.net.ConnectException import java.net.InetSocketAddress import java.net.Socket +import java.security.SecureRandom import kotlin.coroutines.resume import kotlin.coroutines.suspendCoroutine @@ -29,6 +30,19 @@ object Utils { return dataDirectory.absolutePath } + fun generateRandomString(length: Int): String { + val CHARACTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" + val secureRandom = SecureRandom() + val randomString = StringBuilder(length) + + repeat(length) { + val randomIndex = secureRandom.nextInt(CHARACTERS.length) + randomString.append(CHARACTERS[randomIndex]) + } + + return randomString.toString() + } + suspend fun getOpenPort(starting: Int) = suspendCoroutine { continuation -> val port = checkPort(starting) continuation.resume(port) diff --git a/packages/mobile/assets/icons/update_graphics.png b/packages/mobile/assets/icons/update_graphics.png new file mode 100644 index 0000000000..7f3c8e281b Binary files /dev/null and b/packages/mobile/assets/icons/update_graphics.png differ diff --git a/packages/mobile/ios/CommunicationModule.swift b/packages/mobile/ios/CommunicationModule.swift index 6b5fb1821a..75c132e14b 100644 --- a/packages/mobile/ios/CommunicationModule.swift +++ b/packages/mobile/ios/CommunicationModule.swift @@ -10,8 +10,8 @@ class CommunicationModule: RCTEventEmitter { static let WEBSOCKET_CONNECTION_CHANNEL = "_WEBSOCKET_CONNECTION_" @objc - func sendDataPort(port: UInt16) { - self.sendEvent(withName: CommunicationModule.BACKEND_EVENT_IDENTIFIER, body: ["channelName": CommunicationModule.WEBSOCKET_CONNECTION_CHANNEL, "payload": ["dataPort": port]]) + func sendDataPort(port: UInt16, socketIOSecret: String) { + self.sendEvent(withName: CommunicationModule.BACKEND_EVENT_IDENTIFIER, body: ["channelName": CommunicationModule.WEBSOCKET_CONNECTION_CHANNEL, "payload": ["dataPort": port, "socketIOSecret": socketIOSecret]]) } @objc diff --git a/packages/mobile/ios/Quiet.xcodeproj/project.pbxproj b/packages/mobile/ios/Quiet.xcodeproj/project.pbxproj index b7a34ce203..be437d02b7 100644 --- a/packages/mobile/ios/Quiet.xcodeproj/project.pbxproj +++ b/packages/mobile/ios/Quiet.xcodeproj/project.pbxproj @@ -12,6 +12,7 @@ 069DD1D7AEEE422CAA8DDBAD /* Rubik-Black.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 087527EFE5B6436EAB420BE2 /* Rubik-Black.ttf */; }; 0FBBD0C07F80428D9985FB09 /* Rubik-LightItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = F07611BD79814082A0C19928 /* Rubik-LightItalic.ttf */; }; 1389B75C9E334BCFB4A3E58B /* Rubik-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = F2CAE82B74814677AFF9779E /* Rubik-Regular.ttf */; }; + 180E120B2AEFB7F900804659 /* Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 180E120A2AEFB7F900804659 /* Utils.swift */; }; 1827A9E229783D6E00245FD3 /* classic-level.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1827A9E129783D6E00245FD3 /* classic-level.framework */; platformFilter = ios; }; 1827A9E329783D7600245FD3 /* classic-level.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1827A9E129783D6E00245FD3 /* classic-level.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; 1827A9E429783D7600245FD3 /* classic-level.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 1827A9E129783D6E00245FD3 /* classic-level.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; @@ -35,15 +36,15 @@ 18FD2A3E296F009E00A2B8C0 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 18FD2A37296F009E00A2B8C0 /* AppDelegate.m */; }; 18FD2A3F296F009E00A2B8C0 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 18FD2A38296F009E00A2B8C0 /* Images.xcassets */; }; 18FD2A40296F009E00A2B8C0 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 18FD2A39296F009E00A2B8C0 /* main.m */; }; - 3C78B58573E0D2A7A1622803 /* libPods-Quiet-QuietTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 865CA6E5185C29ABCD1914D9 /* libPods-Quiet-QuietTests.a */; }; 43B99DB1C98D429295E8CB91 /* Rubik-SemiBoldItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 2BB5F4B8D4F3462CB8AF6312 /* Rubik-SemiBoldItalic.ttf */; }; 5CBDF3C0D937401A886684F8 /* Rubik-Light.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 706E1601C7B649A8A40A7877 /* Rubik-Light.ttf */; }; 62541AEC8BEC401EBAAF6198 /* Rubik-ExtraBold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 18ED84D0299E4E55AB68AA3E /* Rubik-ExtraBold.ttf */; }; - 6CF8B0FBA0941F3F55F1AEC5 /* libPods-Quiet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4A741A825CDCBDA1C56FA789 /* libPods-Quiet.a */; }; 7F80A59D9EC1440186E5D5CF /* Rubik-SemiBold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = BF73B04135634980BC656D6C /* Rubik-SemiBold.ttf */; }; 80CCB457674F4979A3C5DB06 /* Rubik-MediumItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = EBC2A7699E0A49059904DD8C /* Rubik-MediumItalic.ttf */; }; 8A009A60D84E4B08AB0E8152 /* Rubik-Bold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = E820B3E5514B49EE8C72DECB /* Rubik-Bold.ttf */; }; 9EC9E7C54868433990A479EC /* Rubik-ExtraBoldItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 182B8961416E4D4C8A29793D /* Rubik-ExtraBoldItalic.ttf */; }; + 9EFEA2C3C6AE0D6FA6A4C079 /* libPods-Quiet-QuietTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = ED4D442ACC913B8E48E2C569 /* libPods-Quiet-QuietTests.a */; }; + A43CD77BAC37717C692E6333 /* libPods-Quiet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 47D3FEF6693D5638AD9D0AFF /* libPods-Quiet.a */; }; B4D947D797C747ABAC4EDB69 /* Rubik-Medium.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 378A1FDC610441568B568410 /* Rubik-Medium.ttf */; }; DB3A77793E604ACE8E52C67D /* Rubik-BlackItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 23AE2E9AAC824E76A9C4B3D5 /* Rubik-BlackItalic.ttf */; }; DCA6BD5DCC514EEF87705ACB /* Rubik-BoldItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 84720E58493F44BE8C4784E3 /* Rubik-BoldItalic.ttf */; }; @@ -81,7 +82,7 @@ 00E356F21AD99517003FC87E /* QuietTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = QuietTests.m; sourceTree = ""; }; 087527EFE5B6436EAB420BE2 /* Rubik-Black.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Rubik-Black.ttf"; path = "../assets/fonts/Rubik-Black.ttf"; sourceTree = ""; }; 13B07F961A680F5B00A75B9A /* Quiet.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Quiet.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 149DA46FB928274C5FB2FB01 /* Pods-Quiet.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Quiet.debug.xcconfig"; path = "Target Support Files/Pods-Quiet/Pods-Quiet.debug.xcconfig"; sourceTree = ""; }; + 180E120A2AEFB7F900804659 /* Utils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Utils.swift; sourceTree = ""; }; 1827A9E129783D6E00245FD3 /* classic-level.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = "classic-level.framework"; sourceTree = ""; }; 182B8961416E4D4C8A29793D /* Rubik-ExtraBoldItalic.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Rubik-ExtraBoldItalic.ttf"; path = "../assets/fonts/Rubik-ExtraBoldItalic.ttf"; sourceTree = ""; }; 183C484F296C7B6700BA2D8B /* v8-platform.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "v8-platform.h"; sourceTree = ""; }; @@ -596,21 +597,22 @@ 18FD2A39296F009E00A2B8C0 /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = Quiet/main.m; sourceTree = ""; }; 18FD2A3A296F009E00A2B8C0 /* Quiet.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; name = Quiet.entitlements; path = Quiet/Quiet.entitlements; sourceTree = ""; }; 18FD2A3B296F009E00A2B8C0 /* QuietDebug.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; name = QuietDebug.entitlements; path = Quiet/QuietDebug.entitlements; sourceTree = ""; }; + 20C39A27A55F5BFF2B63BC14 /* Pods-Quiet-QuietTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Quiet-QuietTests.debug.xcconfig"; path = "Target Support Files/Pods-Quiet-QuietTests/Pods-Quiet-QuietTests.debug.xcconfig"; sourceTree = ""; }; 2386DA39D8B240D9A80ECBD9 /* Rubik-Italic.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Rubik-Italic.ttf"; path = "../assets/fonts/Rubik-Italic.ttf"; sourceTree = ""; }; 23AE2E9AAC824E76A9C4B3D5 /* Rubik-BlackItalic.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Rubik-BlackItalic.ttf"; path = "../assets/fonts/Rubik-BlackItalic.ttf"; sourceTree = ""; }; - 262E5B8A9D4174F60C5940B1 /* Pods-Quiet-QuietTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Quiet-QuietTests.debug.xcconfig"; path = "Target Support Files/Pods-Quiet-QuietTests/Pods-Quiet-QuietTests.debug.xcconfig"; sourceTree = ""; }; 2BB5F4B8D4F3462CB8AF6312 /* Rubik-SemiBoldItalic.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Rubik-SemiBoldItalic.ttf"; path = "../assets/fonts/Rubik-SemiBoldItalic.ttf"; sourceTree = ""; }; 378A1FDC610441568B568410 /* Rubik-Medium.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Rubik-Medium.ttf"; path = "../assets/fonts/Rubik-Medium.ttf"; sourceTree = ""; }; - 4A741A825CDCBDA1C56FA789 /* libPods-Quiet.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Quiet.a"; sourceTree = BUILT_PRODUCTS_DIR; }; - 56957883430CB6A0A09E6740 /* Pods-Quiet.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Quiet.release.xcconfig"; path = "Target Support Files/Pods-Quiet/Pods-Quiet.release.xcconfig"; sourceTree = ""; }; + 47D3FEF6693D5638AD9D0AFF /* libPods-Quiet.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Quiet.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 599527D61D41D1899FC6F87B /* Pods-Quiet-QuietTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Quiet-QuietTests.release.xcconfig"; path = "Target Support Files/Pods-Quiet-QuietTests/Pods-Quiet-QuietTests.release.xcconfig"; sourceTree = ""; }; 706E1601C7B649A8A40A7877 /* Rubik-Light.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Rubik-Light.ttf"; path = "../assets/fonts/Rubik-Light.ttf"; sourceTree = ""; }; 84720E58493F44BE8C4784E3 /* Rubik-BoldItalic.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Rubik-BoldItalic.ttf"; path = "../assets/fonts/Rubik-BoldItalic.ttf"; sourceTree = ""; }; - 865CA6E5185C29ABCD1914D9 /* libPods-Quiet-QuietTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Quiet-QuietTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; - 9CC7C58C143E70B860940210 /* Pods-Quiet-QuietTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Quiet-QuietTests.release.xcconfig"; path = "Target Support Files/Pods-Quiet-QuietTests/Pods-Quiet-QuietTests.release.xcconfig"; sourceTree = ""; }; BF73B04135634980BC656D6C /* Rubik-SemiBold.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Rubik-SemiBold.ttf"; path = "../assets/fonts/Rubik-SemiBold.ttf"; sourceTree = ""; }; + CD39A872C9F80A6BA04B8A4A /* Pods-Quiet.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Quiet.release.xcconfig"; path = "Target Support Files/Pods-Quiet/Pods-Quiet.release.xcconfig"; sourceTree = ""; }; E820B3E5514B49EE8C72DECB /* Rubik-Bold.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Rubik-Bold.ttf"; path = "../assets/fonts/Rubik-Bold.ttf"; sourceTree = ""; }; EBC2A7699E0A49059904DD8C /* Rubik-MediumItalic.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Rubik-MediumItalic.ttf"; path = "../assets/fonts/Rubik-MediumItalic.ttf"; sourceTree = ""; }; + ED4D442ACC913B8E48E2C569 /* libPods-Quiet-QuietTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Quiet-QuietTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; F07611BD79814082A0C19928 /* Rubik-LightItalic.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Rubik-LightItalic.ttf"; path = "../assets/fonts/Rubik-LightItalic.ttf"; sourceTree = ""; }; + F2C59D3CAFDB714D809D1115 /* Pods-Quiet.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Quiet.debug.xcconfig"; path = "Target Support Files/Pods-Quiet/Pods-Quiet.debug.xcconfig"; sourceTree = ""; }; F2CAE82B74814677AFF9779E /* Rubik-Regular.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Rubik-Regular.ttf"; path = "../assets/fonts/Rubik-Regular.ttf"; sourceTree = ""; }; /* End PBXFileReference section */ @@ -622,7 +624,7 @@ 18C5DE1329782326008036F4 /* leveldown.framework in Frameworks */, 1842897C295466AA00CA5039 /* NodeMobile.framework in Frameworks */, 1827A9E229783D6E00245FD3 /* classic-level.framework in Frameworks */, - 3C78B58573E0D2A7A1622803 /* libPods-Quiet-QuietTests.a in Frameworks */, + 9EFEA2C3C6AE0D6FA6A4C079 /* libPods-Quiet-QuietTests.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -633,7 +635,7 @@ 18C5DE1829782334008036F4 /* leveldown.framework in Frameworks */, 1827A9E329783D7600245FD3 /* classic-level.framework in Frameworks */, 1842897D295466B500CA5039 /* NodeMobile.framework in Frameworks */, - 6CF8B0FBA0941F3F55F1AEC5 /* libPods-Quiet.a in Frameworks */, + A43CD77BAC37717C692E6333 /* libPods-Quiet.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -660,6 +662,7 @@ 13B07FAE1A68108700A75B9A /* Quiet */ = { isa = PBXGroup; children = ( + 180E120A2AEFB7F900804659 /* Utils.swift */, 18FD2A36296F009E00A2B8C0 /* AppDelegate.h */, 18FD2A37296F009E00A2B8C0 /* AppDelegate.m */, 18FD2A38296F009E00A2B8C0 /* Images.xcassets */, @@ -4688,10 +4691,10 @@ 1CEEDB4F07B9978C125775C5 /* Pods */ = { isa = PBXGroup; children = ( - 149DA46FB928274C5FB2FB01 /* Pods-Quiet.debug.xcconfig */, - 56957883430CB6A0A09E6740 /* Pods-Quiet.release.xcconfig */, - 262E5B8A9D4174F60C5940B1 /* Pods-Quiet-QuietTests.debug.xcconfig */, - 9CC7C58C143E70B860940210 /* Pods-Quiet-QuietTests.release.xcconfig */, + F2C59D3CAFDB714D809D1115 /* Pods-Quiet.debug.xcconfig */, + CD39A872C9F80A6BA04B8A4A /* Pods-Quiet.release.xcconfig */, + 20C39A27A55F5BFF2B63BC14 /* Pods-Quiet-QuietTests.debug.xcconfig */, + 599527D61D41D1899FC6F87B /* Pods-Quiet-QuietTests.release.xcconfig */, ); path = Pods; sourceTree = ""; @@ -4702,8 +4705,8 @@ 1827A9E129783D6E00245FD3 /* classic-level.framework */, 18C5DE1029782326008036F4 /* leveldown.framework */, 1842897B295466AA00CA5039 /* NodeMobile.framework */, - 4A741A825CDCBDA1C56FA789 /* libPods-Quiet.a */, - 865CA6E5185C29ABCD1914D9 /* libPods-Quiet-QuietTests.a */, + 47D3FEF6693D5638AD9D0AFF /* libPods-Quiet.a */, + ED4D442ACC913B8E48E2C569 /* libPods-Quiet-QuietTests.a */, ); name = Frameworks; sourceTree = ""; @@ -4769,12 +4772,12 @@ isa = PBXNativeTarget; buildConfigurationList = 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "QuietTests" */; buildPhases = ( - 05887DFAA4618A6767326B2C /* [CP] Check Pods Manifest.lock */, + 3A0DBD8E832195F1C96149E4 /* [CP] Check Pods Manifest.lock */, 00E356EA1AD99517003FC87E /* Sources */, 00E356EB1AD99517003FC87E /* Frameworks */, 00E356EC1AD99517003FC87E /* Resources */, - BCB5CD734D9D7837D92F6F6A /* [CP] Embed Pods Frameworks */, - 96FE87B8CF60D56415A6D872 /* [CP] Copy Pods Resources */, + 906843AE2BDF77EFA0025899 /* [CP] Embed Pods Frameworks */, + 75398FB9BE83762F7A4A343B /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -4790,7 +4793,7 @@ isa = PBXNativeTarget; buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "Quiet" */; buildPhases = ( - 1A881985853DC9243FFCAD06 /* [CP] Check Pods Manifest.lock */, + A66CC1900B2FEB9695F131B5 /* [CP] Check Pods Manifest.lock */, FD10A7F022414F080027D42C /* Start Packager */, 13B07F871A680F5B00A75B9A /* Sources */, 13B07F8C1A680F5B00A75B9A /* Frameworks */, @@ -4802,8 +4805,8 @@ 1827A9E0297837FE00245FD3 /* [CUSTOM NODEJS MOBILE] Remove prebuilds */, 1868C095292F8FE2001D6D5E /* Embed Frameworks */, 18D742A42A41DAD8007D4C4E /* Remove Simulator Strips */, - 33EE387FBE8BC810E57CE73A /* [CP] Embed Pods Frameworks */, - C3B19D7F8A37CA1DEC5A8545 /* [CP] Copy Pods Resources */, + 897206F7B8FF9B5DD7F5561D /* [CP] Embed Pods Frameworks */, + F76D7A280A8CDA06072C296A /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -4899,28 +4902,6 @@ shellPath = /bin/sh; shellScript = "set -e\n\nexport NODE_BINARY=node\n../node_modules/react-native/scripts/react-native-xcode.sh\n"; }; - 05887DFAA4618A6767326B2C /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-Quiet-QuietTests-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; 1827A9DE297828AB00245FD3 /* [CUSTOM NODEJS MOBILE] Mock .node files */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -5011,7 +4992,7 @@ shellPath = /bin/sh; shellScript = "find \"$CODESIGNING_FOLDER_PATH/nodejs-project/node_modules/\" -name \"python3\" | xargs rm\n"; }; - 1A881985853DC9243FFCAD06 /* [CP] Check Pods Manifest.lock */ = { + 3A0DBD8E832195F1C96149E4 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -5026,48 +5007,48 @@ outputFileListPaths = ( ); outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-Quiet-checkManifestLockResult.txt", + "$(DERIVED_FILE_DIR)/Pods-Quiet-QuietTests-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - 33EE387FBE8BC810E57CE73A /* [CP] Embed Pods Frameworks */ = { + 75398FB9BE83762F7A4A343B /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Quiet/Pods-Quiet-frameworks-${CONFIGURATION}-input-files.xcfilelist", + "${PODS_ROOT}/Target Support Files/Pods-Quiet-QuietTests/Pods-Quiet-QuietTests-resources-${CONFIGURATION}-input-files.xcfilelist", ); - name = "[CP] Embed Pods Frameworks"; + name = "[CP] Copy Pods Resources"; outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Quiet/Pods-Quiet-frameworks-${CONFIGURATION}-output-files.xcfilelist", + "${PODS_ROOT}/Target Support Files/Pods-Quiet-QuietTests/Pods-Quiet-QuietTests-resources-${CONFIGURATION}-output-files.xcfilelist", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Quiet/Pods-Quiet-frameworks.sh\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Quiet-QuietTests/Pods-Quiet-QuietTests-resources.sh\"\n"; showEnvVarsInLog = 0; }; - 96FE87B8CF60D56415A6D872 /* [CP] Copy Pods Resources */ = { + 897206F7B8FF9B5DD7F5561D /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Quiet-QuietTests/Pods-Quiet-QuietTests-resources-${CONFIGURATION}-input-files.xcfilelist", + "${PODS_ROOT}/Target Support Files/Pods-Quiet/Pods-Quiet-frameworks-${CONFIGURATION}-input-files.xcfilelist", ); - name = "[CP] Copy Pods Resources"; + name = "[CP] Embed Pods Frameworks"; outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Quiet-QuietTests/Pods-Quiet-QuietTests-resources-${CONFIGURATION}-output-files.xcfilelist", + "${PODS_ROOT}/Target Support Files/Pods-Quiet/Pods-Quiet-frameworks-${CONFIGURATION}-output-files.xcfilelist", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Quiet-QuietTests/Pods-Quiet-QuietTests-resources.sh\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Quiet/Pods-Quiet-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; - BCB5CD734D9D7837D92F6F6A /* [CP] Embed Pods Frameworks */ = { + 906843AE2BDF77EFA0025899 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -5084,7 +5065,29 @@ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Quiet-QuietTests/Pods-Quiet-QuietTests-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; - C3B19D7F8A37CA1DEC5A8545 /* [CP] Copy Pods Resources */ = { + A66CC1900B2FEB9695F131B5 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Quiet-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + F76D7A280A8CDA06072C296A /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -5146,6 +5149,7 @@ 18FD2A3E296F009E00A2B8C0 /* AppDelegate.m in Sources */, 1868BCED292E9212001D6D5E /* NodeRunner.mm in Sources */, 1868C43C2930E255001D6D5E /* CommunicationModule.swift in Sources */, + 180E120B2AEFB7F900804659 /* Utils.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -5162,7 +5166,7 @@ /* Begin XCBuildConfiguration section */ 00E356F61AD99517003FC87E /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 262E5B8A9D4174F60C5940B1 /* Pods-Quiet-QuietTests.debug.xcconfig */; + baseConfigurationReference = 20C39A27A55F5BFF2B63BC14 /* Pods-Quiet-QuietTests.debug.xcconfig */; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; BUNDLE_LOADER = "$(TEST_HOST)"; @@ -5195,7 +5199,7 @@ }; 00E356F71AD99517003FC87E /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 9CC7C58C143E70B860940210 /* Pods-Quiet-QuietTests.release.xcconfig */; + baseConfigurationReference = 599527D61D41D1899FC6F87B /* Pods-Quiet-QuietTests.release.xcconfig */; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; BUNDLE_LOADER = "$(TEST_HOST)"; @@ -5225,7 +5229,7 @@ }; 13B07F941A680F5B00A75B9A /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 149DA46FB928274C5FB2FB01 /* Pods-Quiet.debug.xcconfig */; + baseConfigurationReference = F2C59D3CAFDB714D809D1115 /* Pods-Quiet.debug.xcconfig */; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ARCHS = "$(ARCHS_STANDARD)"; @@ -5319,7 +5323,7 @@ }; 13B07F951A680F5B00A75B9A /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 56957883430CB6A0A09E6740 /* Pods-Quiet.release.xcconfig */; + baseConfigurationReference = CD39A872C9F80A6BA04B8A4A /* Pods-Quiet.release.xcconfig */; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ARCHS = "$(ARCHS_STANDARD)"; diff --git a/packages/mobile/ios/Quiet/AppDelegate.h b/packages/mobile/ios/Quiet/AppDelegate.h index 0f44aa8b5e..51fed2c14d 100644 --- a/packages/mobile/ios/Quiet/AppDelegate.h +++ b/packages/mobile/ios/Quiet/AppDelegate.h @@ -12,6 +12,8 @@ @property uint16_t dataPort; +@property NSString *socketIOSecret; + @property NSString *dataPath; @property RCTBridge *bridge; diff --git a/packages/mobile/ios/Quiet/AppDelegate.m b/packages/mobile/ios/Quiet/AppDelegate.m index 9f3984ca96..7706f61020 100644 --- a/packages/mobile/ios/Quiet/AppDelegate.m +++ b/packages/mobile/ios/Quiet/AppDelegate.m @@ -104,7 +104,7 @@ - (void) initWebsocketConnection { NSTimeInterval delayInSeconds = 5; dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); dispatch_after(popTime, dispatch_get_main_queue(), ^(void) { - [[self.bridge moduleForName:@"CommunicationModule"] sendDataPortWithPort:self.dataPort]; + [[self.bridge moduleForName:@"CommunicationModule"] sendDataPortWithPort:self.dataPort socketIOSecret:self.socketIOSecret]; }); }); } @@ -114,9 +114,13 @@ - (void) spinupBackend:(BOOL)init { // (1/6) Find ports to use in tor and backend configuration FindFreePort *findFreePort = [FindFreePort new]; + Utils *utils = [Utils new]; - self.dataPort = [findFreePort getFirstStartingFromPort:11000]; + if (self.socketIOSecret == nil) { + self.socketIOSecret = [utils generateSecretWithLength:(20)]; + } + self.dataPort = [findFreePort getFirstStartingFromPort:11000]; uint16_t socksPort = [findFreePort getFirstStartingFromPort:12000]; uint16_t controlPort = [findFreePort getFirstStartingFromPort:14000]; uint16_t httpTunnelPort = [findFreePort getFirstStartingFromPort:16000]; @@ -196,16 +200,17 @@ - (NSData *) getAuthCookieData { - (void) launchBackend:(uint16_t)controlPort:(uint16_t)httpTunnelPort:(NSString *)authCookie { self.nodeJsMobile = [RNNodeJsMobile new]; - [self.nodeJsMobile callStartNodeProject:[NSString stringWithFormat:@"bundle.cjs --dataPort %hu --dataPath %@ --controlPort %hu --httpTunnelPort %hu --authCookie %@ --platform %@", self.dataPort, self.dataPath, controlPort, httpTunnelPort, authCookie, platform]]; + [self.nodeJsMobile callStartNodeProject:[NSString stringWithFormat:@"bundle.cjs --dataPort %hu --dataPath %@ --controlPort %hu --httpTunnelPort %hu --authCookie %@ --platform %@ --socketIOSecret %@", self.dataPort, self.dataPath, controlPort, httpTunnelPort, authCookie, platform, self.socketIOSecret]]; } - (void) reviweServices:(uint16_t)controlPort:(uint16_t)httpTunnelPort:(NSString *)authCookie { NSString * dataPortPayload = [NSString stringWithFormat:@"%@:%hu", @"socketIOPort", self.dataPort]; + NSString * socketIOSecretPayload = [NSString stringWithFormat:@"%@:%@", @"socketIOSecret", self.socketIOSecret]; NSString * controlPortPayload = [NSString stringWithFormat:@"%@:%hu", @"torControlPort", controlPort]; NSString * httpTunnelPortPayload = [NSString stringWithFormat:@"%@:%hu", @"httpTunnelPort", httpTunnelPort]; NSString * authCookiePayload = [NSString stringWithFormat:@"%@:%@", @"authCookie", authCookie]; - NSString * payload = [NSString stringWithFormat:@"%@|%@|%@|%@", dataPortPayload, controlPortPayload, httpTunnelPortPayload, authCookiePayload]; + NSString * payload = [NSString stringWithFormat:@"%@|%@|%@|%@|%@", dataPortPayload, socketIOSecretPayload, controlPortPayload, httpTunnelPortPayload, authCookiePayload]; [self.nodeJsMobile sendMessageToNode:@"open":payload]; } diff --git a/packages/mobile/ios/Utils.swift b/packages/mobile/ios/Utils.swift new file mode 100644 index 0000000000..2c36113f99 --- /dev/null +++ b/packages/mobile/ios/Utils.swift @@ -0,0 +1,23 @@ +@objc(Utils) +class Utils: NSObject { + + @objc + func generateSecret(length: Int) -> String { + let characters = Array("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789") + var randomString = "" + + for _ in 0.. - - - - - - + + - - @@ -112,6 +109,13 @@ function App(): JSX.Element { component={PossibleImpersonationAttackScreen} name={ScreenNames.PossibleImpersonationAttackScreen} /> + + + + + + + diff --git a/packages/mobile/src/assets.ts b/packages/mobile/src/assets.ts index b2d5be2dac..0970f3374f 100644 --- a/packages/mobile/src/assets.ts +++ b/packages/mobile/src/assets.ts @@ -1,38 +1,40 @@ -import quiet_icon from '../assets/icons/quiet_icon.png' -import quiet_icon_round from '../assets/icons/quiet_icon_round.png' -import icon_send from '../assets/icons/icon_send.png' -import icon_send_disabled from '../assets/icons/icon_send_disabled.png' -import icon_check_white from '../assets/icons/icon_check_white.png' -import check_circle_green from '../assets/icons/check_circle_green.png' -import check_circle_blank from '../assets/icons/check_circle_blank.png' -import username_registered from '../assets/icons/username_registered.png' import arrow_left from '../assets/icons/arrow_left.png' import arrow_right_short from '../assets/icons/arrow_right_short.png' -import icon_warning from '../assets/icons/icon_warning.png' -import icon_close from '../assets/icons/icon_close.png' -import file_document from '../assets/icons/file_document.png' +import check_circle_blank from '../assets/icons/check_circle_blank.png' +import check_circle_green from '../assets/icons/check_circle_green.png' import dots from '../assets/icons/dots.png' import paperclip_gray from '../assets/icons/paperclip_gray.png' +import file_document from '../assets/icons/file_document.png' +import icon_check_white from '../assets/icons/icon_check_white.png' +import icon_close from '../assets/icons/icon_close.png' +import icon_send from '../assets/icons/icon_send.png' +import icon_send_disabled from '../assets/icons/icon_send_disabled.png' +import icon_warning from '../assets/icons/icon_warning.png' +import quiet_icon from '../assets/icons/quiet_icon.png' +import quiet_icon_round from '../assets/icons/quiet_icon_round.png' +import update_graphics from '../assets/icons/update_graphics.png' +import username_registered from '../assets/icons/username_registered.png' /** * @description This assets are for the app. */ export const appImages = { - quiet_icon, - quiet_icon_round, - icon_send, - icon_send_disabled, - icon_check_white, - check_circle_green, - check_circle_blank, - username_registered, arrow_left, arrow_right_short, - icon_warning, - icon_close, - file_document, + check_circle_blank, + check_circle_green, dots, paperclip_gray, + file_document, + icon_check_white, + icon_close, + icon_send, + icon_send_disabled, + icon_warning, + quiet_icon, + quiet_icon_round, + update_graphics, + username_registered, } /** diff --git a/packages/mobile/src/components/Button/Button.component.tsx b/packages/mobile/src/components/Button/Button.component.tsx index 9c9fc97999..0393c7469d 100644 --- a/packages/mobile/src/components/Button/Button.component.tsx +++ b/packages/mobile/src/components/Button/Button.component.tsx @@ -2,10 +2,11 @@ import React, { FC } from 'react' import { TouchableWithoutFeedback, View } from 'react-native' import { ButtonProps } from './Button.types' import * as Progress from 'react-native-progress' + import { Typography } from '../Typography/Typography.component' import { defaultTheme } from '../../styles/themes/default.theme' -export const Button: FC = ({ onPress, title, width, loading, negative, disabled }) => { +export const Button: FC = ({ onPress, title, width, loading, negative, disabled, newDesign }) => { return ( { @@ -22,12 +23,12 @@ export const Button: FC = ({ onPress, title, width, loading, negati borderRadius: 8, justifyContent: 'center', alignItems: 'center', - minHeight: 45, + minHeight: newDesign ? 50 : 45, width, }} > {!loading ? ( - + {title} ) : ( diff --git a/packages/mobile/src/components/Button/Button.stories.tsx b/packages/mobile/src/components/Button/Button.stories.tsx index 2c4cb72cb3..d5ce0a479d 100644 --- a/packages/mobile/src/components/Button/Button.stories.tsx +++ b/packages/mobile/src/components/Button/Button.stories.tsx @@ -1,9 +1,10 @@ -import { storiesOf } from '@storybook/react-native' import React from 'react' +import { storiesOf } from '@storybook/react-native' import { storybookLog } from '../../utils/functions/storybookLog/storybookLog.function' import { Button } from './Button.component' storiesOf('Button', module) .add('Default', () =>