diff --git a/keyserver/package.json b/keyserver/package.json index 2f5b51059..c7aa30344 100644 --- a/keyserver/package.json +++ b/keyserver/package.json @@ -1,121 +1,121 @@ { "name": "keyserver", "version": "0.0.1", "type": "module", "private": true, "license": "BSD-3-Clause", "main": "dist/keyserver", "scripts": { "clean": "rm -rf dist/ && rm -rf node_modules/ && mkdir dist", "babel-build-comm-config": ". bash/source-nvm.sh && yarn --silent babel src/lib/utils/comm-config.js --out-dir dist/lib/utils/ --config-file ./.babelrc.cjs", "babel-build": ". bash/source-nvm.sh && yarn --silent babel src/ --out-dir dist/ --config-file ./.babelrc.cjs --verbose --ignore 'src/landing/flow-typed','src/landing/node_modules','src/landing/package.json','src/lib/flow-typed','src/lib/node_modules','src/lib/package.json','src/web/flow-typed','src/web/node_modules','src/web/package.json','src/web/dist','src/web/webpack.config.js','src/web/account-bar.react.js','src/web/app.react.js','src/web/calendar','src/web/chat','src/web/flow','src/web/loading-indicator.react.js','src/web/modals','src/web/root.js','src/web/router-history.js','src/web/script.js','src/web/selectors/chat-selectors.js','src/web/selectors/entry-selectors.js','src/web/splash','src/web/vector-utils.js','src/web/vectors.react.js'", "rsync": "rsync -rLpmuv --exclude '*/package.json' --exclude '*/node_modules/*' --include '*.json' --include '*.cjs' --include '*.node' --exclude '*.*' src/ dist/", "prod-build": "yarn babel-build && yarn rsync", "prod": "KEYSERVER=true node --trace-warnings --loader=./loader.mjs dist/keyserver", "profile-prod": "KEYSERVER=true KEYSERVER_CPU_PROFILING_ENABLED=true 0x --output-dir cpu_profiling_logs/{pid}.0x -o -- node --trace-warnings --loader=./loader.mjs dist/keyserver", "clear-profile-logs": "rm -rf cpu_profiling_logs/", "dev-rsync": "yarn --silent chokidar --initial --silent -s 'src/**/*.json' 'src/**/*.cjs' -c 'yarn rsync > /dev/null 2>&1'", - "dev": ". bash/source-nvm.sh && yarn concurrently --names=\"BABEL,RSYNC,NODEM\" -c \"bgBlue.bold,bgMagenta.bold,bgGreen.bold\" \"yarn babel-build --source-maps --watch\" \"yarn dev-rsync\" \". bash/source-nvm.sh && KEYSERVER=true NODE_ENV=development nodemon -e js,json,cjs --watch dist --no-warnings=ExperimentalWarning --loader=./loader.mjs dist/keyserver\"", + "dev": ". bash/source-nvm.sh && yarn concurrently --names=\"BABEL,RSYNC,NODEM\" -c \"bgBlue.bold,bgMagenta.bold,bgGreen.bold\" \"yarn babel-build --source-maps --watch\" \"yarn dev-rsync\" \". bash/source-nvm.sh && KEYSERVER=true AUTHORITATIVE_KEYSERVER=true NODE_ENV=development nodemon -e js,json,cjs --watch dist --no-warnings=ExperimentalWarning --loader=./loader.mjs dist/keyserver\"", "dev-log-metrics": "KEYSERVER_ENDPOINT_METRICS_ENABLED=true yarn dev", "script": ". bash/source-nvm.sh && NODE_ENV=development node --loader=./loader.mjs", "test": "jest" }, "devDependencies": { "0x": "^5.7.0", "@babel/cli": "^7.23.4", "@babel/core": "^7.23.7", "@babel/node": "^7.22.19", "@babel/plugin-syntax-dynamic-import": "^7.8.3", "@babel/plugin-transform-class-properties": "^7.23.3", "@babel/plugin-transform-nullish-coalescing-operator": "^7.23.4", "@babel/plugin-transform-object-rest-spread": "^7.23.4", "@babel/plugin-transform-optional-chaining": "^7.23.4", "@babel/plugin-transform-private-methods": "^7.24.1", "@babel/plugin-transform-runtime": "^7.23.7", "@babel/preset-env": "^7.23.7", "@babel/preset-flow": "^7.23.3", "@babel/preset-react": "^7.23.3", "babel-jest": "^29.7.0", "babel-plugin-transform-import-meta": "2.2.1", "chokidar-cli": "^2.1.0", "concurrently": "^5.3.0", "flow-bin": "^0.202.1", "flow-typed": "^3.2.1", "internal-ip": "4.3.0", "jest": "^29.7.0", "nodemon": "^2.0.4" }, "dependencies": { "@babel/runtime": "^7.23.7", "@commapp/olm": "0.1.1", "@parse/node-apn": "^3.2.0", "@vingle/bmp-js": "^0.2.5", "bad-words": "^3.0.4", "common-tags": "^1.7.2", "compression": "^1.7.4", "cookie-parser": "^1.4.3", "cors": "^2.8.5", "dateformat": "^3.0.3", "detect-browser": "^4.0.4", "ethers": "^6.11.1", "express": "^4.17.3", "express-ws": "^4.0.0", "file-type": "^12.3.0", "firebase-admin": "^10.1.0", "geoip-lite": "^1.4.5", "invariant": "^2.2.4", "landing": "0.0.1", "lib": "0.0.1", "lodash": "^4.17.21", "multer": "^1.4.1", "mysql2": "^2.3.3", "natural": "^6.2.0", "node-schedule": "^2.1.0", "nodemailer": "^6.6.1", "qrcode": "^1.5.3", "react": "18.1.0", "react-dom": "18.1.0", "react-html-email": "^3.0.0", "react-redux": "^7.1.1", "react-router": "^5.2.0", "redis": "^3.1.1", "redux": "^4.0.4", "rereadable-stream": "^1.4.5", "rust-node-addon": "0.0.1", "sharp": "^0.30.5", "siwe": "^2.1.4", "sql-template-strings": "^2.2.2", "tcomb": "^3.2.29", "twin-bcrypt": "^2.1.1", "uuid": "^3.4.0", "web": "0.0.1", "web-push": "^3.5.0", "ws": "^8.13.0" }, "optionalDependencies": { "bufferutil": "^4.0.5", "utf-8-validate": "^5.0.7" }, "nodemonConfig": { "delay": "200" }, "jest": { "roots": [ "/src" ], "transform": { "\\.js$": [ "babel-jest", { "rootMode": "upward" } ] }, "transformIgnorePatterns": [ "/node_modules/(?!@babel/runtime)" ], "setupFiles": [ "/jest-setup.js" ] } } diff --git a/keyserver/src/user/identity.js b/keyserver/src/user/identity.js index f337295bd..31260104e 100644 --- a/keyserver/src/user/identity.js +++ b/keyserver/src/user/identity.js @@ -1,128 +1,142 @@ // @flow import type { QueryResults } from 'mysql'; import ashoat from 'lib/facts/ashoat.js'; import { getCommConfig } from 'lib/utils/comm-config.js'; import { ashoatKeyserverID } from 'lib/utils/validation-utils.js'; import type { UserCredentials } from './checks'; import { SQL, dbQuery } from '../database/database.js'; const metadataKeys = Object.freeze({ USER_ID: 'user_id', ACCESS_TOKEN: 'access_token', }); type MetadataKey = $Values; // This information is minted when registering with identity service // Naming should reflect the rust-bindings: userId -> user_id export type IdentityInfo = { +userId: string, +accessToken: string }; // This function should only be used to fetch string values from the metadata // table async function fetchMetadata( keys: $ReadOnlyArray, ): Promise> { const metadataQuery = SQL` SELECT name, data FROM metadata WHERE name IN (${keys}) `; const [result] = await dbQuery(metadataQuery); const metadataMap = new Map(); for (const row of result) { metadataMap.set(row.name, row.data); } return metadataMap; } async function fetchIdentityInfo(): Promise { const keys = [metadataKeys.USER_ID, metadataKeys.ACCESS_TOKEN]; const metadataMap = await fetchMetadata(keys); const userID = metadataMap.get(metadataKeys.USER_ID); const accessToken = metadataMap.get(metadataKeys.ACCESS_TOKEN); if (!userID || !accessToken) { return null; } return { userId: userID, accessToken }; } let cachedKeyserverID: ?string; async function thisKeyserverID(): Promise { if (cachedKeyserverID) { return cachedKeyserverID; } const config = await getCommConfig({ folder: 'secrets', name: 'user_credentials', }); if (!config?.usingIdentityCredentials) { return ashoatKeyserverID; } const identityInfo = await fetchIdentityInfo(); cachedKeyserverID = identityInfo?.userId ?? ashoatKeyserverID; return cachedKeyserverID; } +async function isAuthoritativeKeyserver(): Promise { + if (process.env.AUTHORITATIVE_KEYSERVER) { + return true; + } + + const id = await thisKeyserverID(); + if (id === ashoat.id) { + return true; + } + + return false; +} + export type AdminData = { +username: string, +id: string, }; async function thisKeyserverAdmin(): Promise { const config = await getCommConfig({ folder: 'secrets', name: 'user_credentials', }); if (!config) { return { id: ashoat.id, username: ashoat.username, }; } const id = await thisKeyserverID(); return { id, username: config.username, }; } function saveMetadata( metadataMap: Map, ): Promise { const entries = [...metadataMap.entries()]; const updateQuery = SQL` REPLACE INTO metadata (name, data) VALUES ${entries} `; return dbQuery(updateQuery); } function saveIdentityInfo(userInfo: IdentityInfo): Promise { const metadataMap = new Map(); metadataMap.set(metadataKeys.USER_ID, userInfo.userId); metadataMap.set(metadataKeys.ACCESS_TOKEN, userInfo.accessToken); return saveMetadata(metadataMap); } export { fetchIdentityInfo, thisKeyserverID, thisKeyserverAdmin, saveIdentityInfo, + isAuthoritativeKeyserver, };