From 8e4ef3da31e75672bc6e72234f2940c578ca6a63 Mon Sep 17 00:00:00 2001 From: Andrei Gavrilescu Date: Tue, 19 Sep 2023 13:19:09 +0300 Subject: [PATCH] Add jwt authorized endpoint for dump visualization --- .env | 3 +- package-lock.json | 209 ++++++++++++++++++++++++- package.json | 3 +- scripts/deploy.sh | 2 +- src/client/app/config/actions-types.js | 5 + src/client/app/config/actions.js | 22 +++ src/client/app/config/reducer.js | 15 ++ src/client/app/files/actions.js | 12 +- src/client/app/index.js | 24 ++- src/client/app/raw/legacy.js | 2 +- src/client/app/render.js | 9 +- src/client/app/requests.js | 2 + src/client/app/store.js | 2 + src/server/auth.mjs | 19 --- src/server/basic-auth.mjs | 12 ++ src/server/index.mjs | 18 ++- src/server/jwt-auth.mjs | 31 ++++ src/server/routes/config.mjs | 13 ++ 18 files changed, 360 insertions(+), 43 deletions(-) create mode 100644 src/client/app/config/actions-types.js create mode 100644 src/client/app/config/actions.js create mode 100644 src/client/app/config/reducer.js delete mode 100644 src/server/auth.mjs create mode 100644 src/server/basic-auth.mjs create mode 100644 src/server/jwt-auth.mjs create mode 100644 src/server/routes/config.mjs diff --git a/.env b/.env index c75a161..df4eb3a 100644 --- a/.env +++ b/.env @@ -3,5 +3,6 @@ APP_PORT=8087 AWS_REGION=us-west-2 RTCSTATS_S3_BUCKET=jitsi-micros-rtcstats-server RTCSTATS_METADATA_TABLE=rtcstats-meta-table-local +RTCSTATS_JWT_PUBLIC_KEY= +RTCSTATS_FILES_ENDPOINT= USERS_FILE=.data/users.json - diff --git a/package-lock.json b/package-lock.json index c3440fb..44267a0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "visualizer-server", - "version": "0.2.10", + "version": "0.3.5", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "visualizer-server", - "version": "0.2.10", + "version": "0.3.5", "license": "ISC", "dependencies": { "@reduxjs/toolkit": "^1.9.0", @@ -17,6 +17,7 @@ "dynamoose": "^2.6.0", "express": "^4.17.1", "express-basic-auth": "^1.2.0", + "jsonwebtoken": "^9.0.2", "morgan": "^1.10.0", "plotly.js-dist": "^2.22.0", "react": "^17.0.2", @@ -2880,6 +2881,11 @@ "isarray": "^1.0.0" } }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" + }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -3512,6 +3518,14 @@ "uuid": "dist/bin/uuid" } }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -5575,6 +5589,41 @@ "node": ">=6" } }, + "node_modules/jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jsonwebtoken/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/jsx-ast-utils": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.3.tgz", @@ -5588,6 +5637,25 @@ "node": ">=4.0" } }, + "node_modules/jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, "node_modules/kind-of": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", @@ -5704,6 +5772,41 @@ "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", "dev": true }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" + }, "node_modules/lodash.truncate": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", @@ -5746,7 +5849,6 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, "dependencies": { "yallist": "^4.0.0" }, @@ -8838,8 +8940,7 @@ "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "node_modules/yaml": { "version": "1.10.2", @@ -10970,6 +11071,11 @@ "isarray": "^1.0.0" } }, + "buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" + }, "buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -11425,6 +11531,14 @@ } } }, + "ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "requires": { + "safe-buffer": "^5.0.1" + } + }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -12949,6 +13063,33 @@ "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true }, + "jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "requires": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "dependencies": { + "semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, "jsx-ast-utils": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.3.tgz", @@ -12959,6 +13100,25 @@ "object.assign": "^4.1.3" } }, + "jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "requires": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "requires": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, "kind-of": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", @@ -13052,6 +13212,41 @@ "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", "dev": true }, + "lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" + }, + "lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" + }, + "lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" + }, + "lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" + }, + "lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" + }, + "lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" + }, + "lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" + }, "lodash.truncate": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", @@ -13091,7 +13286,6 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, "requires": { "yallist": "^4.0.0" } @@ -15377,8 +15571,7 @@ "yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "yaml": { "version": "1.10.2", diff --git a/package.json b/package.json index abb6a64..678e647 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "visualizer-server", - "version": "0.2.10", + "version": "0.3.5", "description": "Visualize RTC stats data", "repository": "https://github.com/8x8Cloud/rtc-visualizer", "author": "8x8", @@ -75,6 +75,7 @@ "dynamoose": "^2.6.0", "express": "^4.17.1", "express-basic-auth": "^1.2.0", + "jsonwebtoken": "^9.0.2", "morgan": "^1.10.0", "plotly.js-dist": "^2.22.0", "react": "^17.0.2", diff --git a/scripts/deploy.sh b/scripts/deploy.sh index 9711a0e..31ad224 100755 --- a/scripts/deploy.sh +++ b/scripts/deploy.sh @@ -28,7 +28,7 @@ REPOSITORY=${REPOSITORY_URL}:${VERSION} echo Building tag image $REPOSITORY -docker build -t $REPOSITORY . +docker build --platform linux/amd64 -t $REPOSITORY . if [[ ${SHOULD_PUSH_IMAGE} == true ]] then diff --git a/src/client/app/config/actions-types.js b/src/client/app/config/actions-types.js new file mode 100644 index 0000000..3247fc7 --- /dev/null +++ b/src/client/app/config/actions-types.js @@ -0,0 +1,5 @@ +import { generateActions } from '../utils' + +export default generateActions([ + 'SetConfig' +]) diff --git a/src/client/app/config/actions.js b/src/client/app/config/actions.js new file mode 100644 index 0000000..7a27075 --- /dev/null +++ b/src/client/app/config/actions.js @@ -0,0 +1,22 @@ +import Actions from './actions-types' + +export const setConfig = (config) => ({ + type: Actions.SetConfig, + payload: config +}) + +export const fetchConfig = () => { + return async (dispatch) => { + try { + const response = await fetch('/meet-external/rtc-visualizer/config') + if (!response.ok) { + throw new Error('Unable to fetch confiug data') + } + + const config = await response.json() + dispatch(setConfig(config)) + } catch (error) { + console.error('Error fetching config:', error) + } + } +} diff --git a/src/client/app/config/reducer.js b/src/client/app/config/reducer.js new file mode 100644 index 0000000..032b7e0 --- /dev/null +++ b/src/client/app/config/reducer.js @@ -0,0 +1,15 @@ +import Actions from './actions-types' + +const initialState = { + isSet: false, + filesEndpoint: '' +} + +export default (state = initialState, action) => { + switch (action.type) { + case Actions.SetConfig: + return { ...state, ...action.payload, isSet: true } + default: + return state + } +} diff --git a/src/client/app/files/actions.js b/src/client/app/files/actions.js index a02758b..5f3f0f9 100644 --- a/src/client/app/files/actions.js +++ b/src/client/app/files/actions.js @@ -13,14 +13,22 @@ const fetchFileError = (name, error) => ({ } }) -export const fetchFile = name => async (dispatch) => { +export const fetchFileBasicAuth = name => fetchFile(name, false) + +export const fetchFileJWTAuth = name => fetchFile(name, true) + +const fetchFile = (name, jwtAuthorization) => async (dispatch, getState) => { + const { config: { filesEndpoint = '' } = {} } = getState() + + const fileUrl = jwtAuthorization ? Urls.JWTFile(name, filesEndpoint) : Urls.File(name) + dispatch({ type: Actions.FetchFileStart, payload: name }) try { - const res = await fetch(Urls.File(name)) + const res = await fetch(fileUrl) dispatch({ type: Actions.FetchFileFinish, diff --git a/src/client/app/index.js b/src/client/app/index.js index 23ab214..1710cec 100644 --- a/src/client/app/index.js +++ b/src/client/app/index.js @@ -1,13 +1,29 @@ -import React from 'react' +import React, { useEffect } from 'react' +import { useSelector, useDispatch } from 'react-redux' + import Search from './search' import Render from './render' +import { fetchConfig } from './config/actions' export default () => { + const dispatch = useDispatch() + + const isConfigSet = useSelector((state) => state.config.isSet) + + useEffect(() => { + // Fetch the config when the component mounts + dispatch(fetchConfig()) + }, [dispatch]) + return (
- {(new URLSearchParams(window.location.search).has('dumpId')) - ? - : } + { + isConfigSet + ? (new URLSearchParams(window.location.search).has('dumpId')) + ? + : + : 'Loading config...' + }
) } diff --git a/src/client/app/raw/legacy.js b/src/client/app/raw/legacy.js index 0cb6839..9d01d2a 100644 --- a/src/client/app/raw/legacy.js +++ b/src/client/app/raw/legacy.js @@ -1,4 +1,4 @@ -import Plotly from 'plotly.js-dist'; +import Plotly from 'plotly.js-dist' const fileFormat = 2 diff --git a/src/client/app/render.js b/src/client/app/render.js index 226faa1..b98cef3 100644 --- a/src/client/app/render.js +++ b/src/client/app/render.js @@ -5,7 +5,7 @@ import 'react-tabs/style/react-tabs.css' import { showStats } from './raw/legacy' import { getFileStatus } from './files/selectors' import { Console } from 'console-feed' -import { fetchFile } from './files/actions' +import { fetchFileBasicAuth, fetchFileJWTAuth } from './files/actions' export default () => { const dispatch = useDispatch() @@ -13,8 +13,13 @@ export default () => { useEffect(() => { const params = new URLSearchParams(window.location.search) const dumpId = params.get('dumpId') + const useJWTAuthorization = params.get('useJWTAuthorization') if (dumpId) { - dispatch(fetchFile(dumpId)) + if (useJWTAuthorization) { + dispatch(fetchFileJWTAuth(dumpId)) + } else { + dispatch(fetchFileBasicAuth(dumpId)) + } } }, [dispatch]) diff --git a/src/client/app/requests.js b/src/client/app/requests.js index dcc4de2..6f7838d 100644 --- a/src/client/app/requests.js +++ b/src/client/app/requests.js @@ -13,6 +13,8 @@ export const Urls = { File: name => `/files/${name}`, + JWTFile: (name, filesEndpoint) => `${filesEndpoint}/rtc-visualizer/files/${name}`, + Render: name => `/?dumpId=${name}`, Download: name => `/download/${name}` diff --git a/src/client/app/store.js b/src/client/app/store.js index 2409d15..37b15f1 100644 --- a/src/client/app/store.js +++ b/src/client/app/store.js @@ -1,9 +1,11 @@ import { configureStore } from '@reduxjs/toolkit' +import config from './config/reducer' import files from './files/reducer' import search from './search/reducer' import searchMiddleware from './search/middleware' const reducer = { + config, files, search } diff --git a/src/server/auth.mjs b/src/server/auth.mjs deleted file mode 100644 index d334eb5..0000000 --- a/src/server/auth.mjs +++ /dev/null @@ -1,19 +0,0 @@ -import fs from 'fs' -import basicAuth from 'express-basic-auth' - -const { USERS_FILE } = process.env -const users = JSON.parse(fs.readFileSync(USERS_FILE)) -const AUTHLIST = ['/search'] - -export default (req, res, next) => { - for (const authenticated of AUTHLIST) { - if (req.path === authenticated) { - return basicAuth({ - users, - challenge: true - })(req, res, next) - } - } - - next() -} diff --git a/src/server/basic-auth.mjs b/src/server/basic-auth.mjs new file mode 100644 index 0000000..437eeed --- /dev/null +++ b/src/server/basic-auth.mjs @@ -0,0 +1,12 @@ +import fs from 'fs' +import basicAuth from 'express-basic-auth' + +const { USERS_FILE } = process.env +const users = JSON.parse(fs.readFileSync(USERS_FILE)) + +export default (req, res, next) => { + return basicAuth({ + users, + challenge: true + })(req, res, next) +} diff --git a/src/server/index.mjs b/src/server/index.mjs index 668734f..6b3aff0 100644 --- a/src/server/index.mjs +++ b/src/server/index.mjs @@ -3,12 +3,14 @@ import express from 'express' import './config.mjs' import log, { expressLog } from './logger.mjs' +import config from './routes/config.mjs' import filesRoutes from './routes/files.mjs' import searchRoutes from './routes/search.mjs' import downloadRoutes from './routes/download.mjs' import healthRoute from './routes/health.mjs' import versionRoute from './routes/version.mjs' -import auth from './auth.mjs' +import basicAuth from './basic-auth.mjs' +import jwtAuth from './jwt-auth.mjs' const { APP_PORT } = process.env @@ -16,18 +18,26 @@ const app = express() // use custom logger app.use(expressLog) +app.use('/healthcheck', healthRoute) -// use basic auth -app.use(auth) +// Config object needs to be available on all environments (JaaS, standalone) +app.use('/rtc-visualizer/config', config) +app.use('/meet-external/rtc-visualizer/config', config) + +// use just jwt authentication for this path +app.use('/rtc-visualizer/files', jwtAuth, filesRoutes) +app.use('/rtc-visualizer', express.static(path.join(path.resolve(), 'public'))) // serve static files from /public app.use(express.static(path.join(path.resolve(), 'public'))) +// use basic auth +app.use(basicAuth) + app.use('/files', filesRoutes) app.use('/search', searchRoutes) app.use('/download', downloadRoutes) app.use('/version', versionRoute) -app.use('/healthcheck', healthRoute) app.listen(APP_PORT, () => { log.info('App started on port: %s', APP_PORT) diff --git a/src/server/jwt-auth.mjs b/src/server/jwt-auth.mjs new file mode 100644 index 0000000..7c6d30b --- /dev/null +++ b/src/server/jwt-auth.mjs @@ -0,0 +1,31 @@ +import jwt from 'jsonwebtoken' +import log from './logger.mjs' + +function addPEMHeaders (headerlessPEMKey) { + return `-----BEGIN CERTIFICATE-----\n${headerlessPEMKey}\n-----END CERTIFICATE-----` +} + +const { RTCSTATS_JWT_PUBLIC_KEY } = process.env +const formattedKey = addPEMHeaders(RTCSTATS_JWT_PUBLIC_KEY) + +export default (req, res, next) => { + const { headers: { authorization = '' } = {} } = req + + if (authorization.startsWith('Bearer ')) { + try { + const bearerToken = authorization.substring(7) + const decodedToken = jwt.verify(bearerToken, formattedKey) + + req.user = decodedToken + + next() + } catch (error) { + log.error(`Bearer authorization failed: ${JSON.stringify(error)}`) + res.status(401).json({ error: 'Unauthorized' }) + } + } else { + log.warn(`Bearer authorization token not present, found: ${authorization}`) + + res.status(401).json({ error: 'Unauthorized' }) + } +} diff --git a/src/server/routes/config.mjs b/src/server/routes/config.mjs new file mode 100644 index 0000000..bc64881 --- /dev/null +++ b/src/server/routes/config.mjs @@ -0,0 +1,13 @@ +import express from 'express' + +const router = express.Router() + +router.get('/', (req, res) => { + const config = { + filesEndpoint: process.env.RTCSTATS_FILES_ENDPOINT + } + + res.json(config) +}) + +export default router