From 1f6403947362a8a3ae56a00138abf402d2dbb753 Mon Sep 17 00:00:00 2001 From: Andrey Sobolev Date: Tue, 5 Jul 2022 12:43:17 +0700 Subject: [PATCH] Support secret changes and token become unauchorized (#2205) Signed-off-by: Andrey Sobolev --- dev/tool/package.json | 2 +- dev/tool/src/index.ts | 9 +++++- packages/platform/src/status.ts | 6 ++++ packages/presentation/src/connect.ts | 28 ++++++++++++++----- plugins/client-resources/src/connection.ts | 24 ++++++++++++---- plugins/client-resources/src/index.ts | 9 ++++-- plugins/client/src/index.ts | 7 ++++- .../src/components/LoginApp.svelte | 7 +++-- .../src/components/SelectWorkspace.svelte | 17 +++++++++-- plugins/login-resources/src/utils.ts | 16 +++++++++-- server/ws/src/__tests__/server.test.ts | 28 +++++++++++-------- server/ws/src/server.ts | 19 ++++++++++--- tests/install-elastic-plugin-setup.sh | 8 ++++++ tests/prepare.sh | 2 +- tests/setup-elastic.sh | 5 ++-- tests/tool.sh | 1 + 16 files changed, 147 insertions(+), 41 deletions(-) create mode 100755 tests/install-elastic-plugin-setup.sh diff --git a/dev/tool/package.json b/dev/tool/package.json index 7a79e27fd5..8b7e46df07 100644 --- a/dev/tool/package.json +++ b/dev/tool/package.json @@ -13,7 +13,7 @@ "docker:build": "docker build -t hardcoreeng/tool .", "docker:staging": "../../common/scripts/docker_tag.sh hardcoreeng/tool staging", "docker:push": "../../common/scripts/docker_tag.sh hardcoreeng/tool", - "run-local": "cross-env MINIO_ACCESS_KEY=minioadmin MINIO_SECRET_KEY=minioadmin MINIO_ENDPOINT=localhost MONGO_URL=mongodb://localhost:27017 TRANSACTOR_URL=ws:/localhost:3333 TELEGRAM_DATABASE=telegram-service ELASTIC_URL=http://localhost:9200 REKONI_URL=http://localhost:4004 ts-node ./src/index.ts", + "run-local": "cross-env SERVER_SECRET=secret MINIO_ACCESS_KEY=minioadmin MINIO_SECRET_KEY=minioadmin MINIO_ENDPOINT=localhost MONGO_URL=mongodb://localhost:27017 TRANSACTOR_URL=ws:/localhost:3333 TELEGRAM_DATABASE=telegram-service ELASTIC_URL=http://localhost:9200 REKONI_URL=http://localhost:4004 ts-node ./src/index.ts", "upgrade": "rushx run-local upgrade", "lint": "eslint src", "format": "prettier --write src && eslint --fix src" diff --git a/dev/tool/src/index.ts b/dev/tool/src/index.ts index b5d602789d..25e6f10b96 100644 --- a/dev/tool/src/index.ts +++ b/dev/tool/src/index.ts @@ -30,7 +30,7 @@ import { } from '@anticrm/account' import { setMetadata } from '@anticrm/platform' import { backup, backupList, createFileBackupStorage, createMinioBackupStorage, restore } from '@anticrm/server-backup' -import { decodeToken, generateToken } from '@anticrm/server-token' +import serverToken, { decodeToken, generateToken } from '@anticrm/server-token' import toolPlugin, { prepareTools, version } from '@anticrm/server-tool' import { program } from 'commander' import { Db, MongoClient } from 'mongodb' @@ -46,6 +46,12 @@ import { diffWorkspace, dumpWorkspace, restoreWorkspace } from './workspace' const { mongodbUri, minio } = prepareTools() +const serverSecret = process.env.SERVER_SECRET +if (serverSecret === undefined) { + console.error('please provide server secret') + process.exit(1) +} + const transactorUrl = process.env.TRANSACTOR_URL if (transactorUrl === undefined) { console.error('please provide transactor url.') @@ -60,6 +66,7 @@ if (elasticUrl === undefined) { setMetadata(toolPlugin.metadata.Endpoint, transactorUrl) setMetadata(toolPlugin.metadata.Transactor, transactorUrl) +setMetadata(serverToken.metadata.Secret, serverSecret) async function withDatabase (uri: string, f: (db: Db, client: MongoClient) => Promise): Promise { console.log(`connecting to database '${uri}'...`) diff --git a/packages/platform/src/status.ts b/packages/platform/src/status.ts index 9a4864d3e5..47cdf105d0 100644 --- a/packages/platform/src/status.ts +++ b/packages/platform/src/status.ts @@ -73,6 +73,12 @@ export const OK = new Status(Severity.OK, platform.status.OK, {}) */ export const ERROR = new Status(Severity.ERROR, platform.status.BadError, {}) +/** + * Error Status for Unauthorized + * @public + */ +export const UNAUTHORIZED = new Status(Severity.ERROR, platform.status.Unauthorized, {}) + /** * @public * @param message - diff --git a/packages/presentation/src/connect.ts b/packages/presentation/src/connect.ts index b210a86a51..461f3915e1 100644 --- a/packages/presentation/src/connect.ts +++ b/packages/presentation/src/connect.ts @@ -22,9 +22,20 @@ export async function connect (title: string): Promise { } const getClient = await getResource(client.function.GetClient) - const instance = await getClient(token, endpoint, () => { - location.reload() - }) + const instance = await getClient( + token, + endpoint, + () => { + location.reload() + }, + () => { + clearMetadata() + navigate({ + path: [login.component.LoginApp], + query: {} + }) + } + ) console.log('logging in as', email) const me = await instance.findOne(contact.class.EmployeeAccount, { email }) @@ -33,10 +44,7 @@ export async function connect (title: string): Promise { setCurrentAccount(me) } else { console.error('WARNING: no employee account found.') - setMetadataLocalStorage(login.metadata.LoginToken, null) - setMetadataLocalStorage(login.metadata.LoginEndpoint, null) - setMetadataLocalStorage(login.metadata.LoginEmail, null) - setMetadataLocalStorage(login.metadata.CurrentWorkspace, null) + clearMetadata() navigate({ path: [login.component.LoginApp], query: { navigateUrl: encodeURIComponent(JSON.stringify(getCurrentLocation())) } @@ -73,3 +81,9 @@ export async function connect (title: string): Promise { return instance } +function clearMetadata (): void { + setMetadataLocalStorage(login.metadata.LoginToken, null) + setMetadataLocalStorage(login.metadata.LoginEndpoint, null) + setMetadataLocalStorage(login.metadata.LoginEmail, null) + setMetadataLocalStorage(login.metadata.CurrentWorkspace, null) +} diff --git a/plugins/client-resources/src/connection.ts b/plugins/client-resources/src/connection.ts index 75b6e5c65a..ef23c8b4c8 100644 --- a/plugins/client-resources/src/connection.ts +++ b/plugins/client-resources/src/connection.ts @@ -30,7 +30,7 @@ import type { TxResult } from '@anticrm/core' import core from '@anticrm/core' -import { getMetadata, PlatformError, readResponse, ReqId, serialize } from '@anticrm/platform' +import { getMetadata, PlatformError, readResponse, ReqId, serialize, UNAUTHORIZED } from '@anticrm/platform' class DeferredPromise { readonly promise: Promise @@ -53,7 +53,8 @@ class Connection implements ClientConnection { constructor ( private readonly url: string, private readonly handler: TxHander, - private readonly onUpgrade?: () => void + private readonly onUpgrade?: () => void, + private readonly onUnauthorized?: () => void ) { console.log('connection created') this.interval = setInterval(() => { @@ -72,7 +73,11 @@ class Connection implements ClientConnection { try { return await this.openConnection() } catch (err: any) { - console.log('failed to connect') + console.log('failed to connect', err) + if (err.code === UNAUTHORIZED.code) { + this.onUnauthorized?.() + throw err + } await new Promise((resolve) => { setTimeout(() => { @@ -93,6 +98,10 @@ class Connection implements ClientConnection { websocket.onmessage = (event: MessageEvent) => { const resp = readResponse(event.data) if (resp.id === -1 && resp.result === 'hello') { + if (resp.error !== undefined) { + reject(resp.error) + return + } resolve(websocket) return } @@ -191,6 +200,11 @@ class Connection implements ClientConnection { /** * @public */ -export async function connect (url: string, handler: TxHander, onUpgrade?: () => void): Promise { - return new Connection(url, handler, onUpgrade) +export async function connect ( + url: string, + handler: TxHander, + onUpgrade?: () => void, + onUnauthorized?: () => void +): Promise { + return new Connection(url, handler, onUpgrade, onUnauthorized) } diff --git a/plugins/client-resources/src/index.ts b/plugins/client-resources/src/index.ts index c44934080a..48c66d7e5b 100644 --- a/plugins/client-resources/src/index.ts +++ b/plugins/client-resources/src/index.ts @@ -32,7 +32,12 @@ export default async () => { return { function: { - GetClient: async (token: string, endpoint: string, onUpgrade?: () => void): Promise => { + GetClient: async ( + token: string, + endpoint: string, + onUpgrade?: () => void, + onUnauthorized?: () => void + ): Promise => { if (token !== _token && client !== undefined) { await client.close() client = undefined @@ -43,7 +48,7 @@ export default async () => { (handler: TxHander) => { const url = new URL(`/${token}`, endpoint) console.log('connecting to', url.href) - return connect(url.href, handler, onUpgrade) + return connect(url.href, handler, onUpgrade, onUnauthorized) }, filterModel ? getPlugins() : undefined ) diff --git a/plugins/client/src/index.ts b/plugins/client/src/index.ts index 914ddf55f1..79d59b05d4 100644 --- a/plugins/client/src/index.ts +++ b/plugins/client/src/index.ts @@ -52,7 +52,12 @@ export interface ClientSocket { /** * @public */ -export type ClientFactory = (token: string, endpoint: string, onUpgrade?: () => void) => Promise +export type ClientFactory = ( + token: string, + endpoint: string, + onUpgrade?: () => void, + onUnauthorized?: () => void +) => Promise export default plugin(clientId, { metadata: { diff --git a/plugins/login-resources/src/components/LoginApp.svelte b/plugins/login-resources/src/components/LoginApp.svelte index a683ba3c8d..7155e92323 100644 --- a/plugins/login-resources/src/components/LoginApp.svelte +++ b/plugins/login-resources/src/components/LoginApp.svelte @@ -14,7 +14,7 @@ // limitations under the License. -->