From cb2e1b3907b568b388d61b8a71710940d26db289 Mon Sep 17 00:00:00 2001 From: csrapr <26280794+csrapr@users.noreply.github.com> Date: Mon, 24 May 2021 21:57:39 +0100 Subject: [PATCH 01/18] Feat: adds async local storage to admin server --- lib/constants.js | 8 ++++++- lib/new-admin/admin-server.js | 6 +++++ .../graphql/modules/authentication.js | 5 ++-- .../middlewares/cleanUserSessions.js | 23 +++++++++++++++++++ lib/new-admin/middlewares/session.js | 13 +++++++---- 5 files changed, 47 insertions(+), 8 deletions(-) create mode 100644 lib/new-admin/middlewares/cleanUserSessions.js diff --git a/lib/constants.js b/lib/constants.js index aa7319fa..9e526ce6 100644 --- a/lib/constants.js +++ b/lib/constants.js @@ -1,3 +1,5 @@ +const T = require('./time') + const anonymousCustomer = { uuid: '47ac1184-8102-11e7-9079-8f13a7117867', name: 'anonymous' @@ -8,6 +10,8 @@ const cassetteMaxCapacity = 500 const AUTHENTICATOR_ISSUER_ENTITY = 'Lamassu' const AUTH_TOKEN_EXPIRATION_TIME = '30 minutes' const REGISTRATION_TOKEN_EXPIRATION_TIME = '30 minutes' +const USER_SESSIONS_TABLE_NAME = 'user_sessions' +const USER_SESSIONS_CLEAR_INTERVAL = 1 * T.hour const AUTOMATIC = 'automatic' const MANUAL = 'manual' @@ -19,5 +23,7 @@ module.exports = { AUTH_TOKEN_EXPIRATION_TIME, REGISTRATION_TOKEN_EXPIRATION_TIME, AUTOMATIC, - MANUAL + MANUAL, + USER_SESSIONS_TABLE_NAME, + USER_SESSIONS_CLEAR_INTERVAL } diff --git a/lib/new-admin/admin-server.js b/lib/new-admin/admin-server.js index b666333f..05d1e377 100644 --- a/lib/new-admin/admin-server.js +++ b/lib/new-admin/admin-server.js @@ -19,6 +19,9 @@ const logger = require('../logger') const session = require('./middlewares/session') const { AuthDirective } = require('./graphql/directives') const { typeDefs, resolvers } = require('./graphql/schema') +const computeSchema = require('../compute-schema') +const cleanUserSessions = require('./middlewares/cleanUserSessions') +const { USER_SESSIONS_CLEAR_INTERVAL } = require('../constants') const devMode = require('minimist')(process.argv.slice(2)).dev const idPhotoCardBasedir = _.get('idPhotoCardDir', options) @@ -32,6 +35,7 @@ if (!hostname) { } const app = express() + app.use(helmet()) app.use(compression()) app.use(nocache()) @@ -39,6 +43,8 @@ app.use(cookieParser()) app.use(express.json()) app.use(express.urlencoded({ extended: true })) // support encoded bodies app.use(express.static(path.resolve(__dirname, '..', '..', 'public'))) +app.use(computeSchema) +app.use(cleanUserSessions(USER_SESSIONS_CLEAR_INTERVAL)) app.use(session) const apolloServer = new ApolloServer({ diff --git a/lib/new-admin/graphql/modules/authentication.js b/lib/new-admin/graphql/modules/authentication.js index 798e6aa1..690bc907 100644 --- a/lib/new-admin/graphql/modules/authentication.js +++ b/lib/new-admin/graphql/modules/authentication.js @@ -30,8 +30,7 @@ const authenticateUser = (username, password) => { const destroySessionIfSameUser = (context, user) => { const sessionUser = getUserFromCookie(context) - if (sessionUser && user.id === sessionUser.id) - context.req.session.destroy() + if (sessionUser && user.id === sessionUser.id) { context.req.session.destroy() } } const destroySessionIfBeingUsed = (sessID, context) => { @@ -60,7 +59,7 @@ const executeProtectedAction = (code, id, context, action) => { if (user.role !== 'superuser') { return action() } - + return confirm2FA(code, context) .then(() => action()) }) diff --git a/lib/new-admin/middlewares/cleanUserSessions.js b/lib/new-admin/middlewares/cleanUserSessions.js new file mode 100644 index 00000000..c156c740 --- /dev/null +++ b/lib/new-admin/middlewares/cleanUserSessions.js @@ -0,0 +1,23 @@ +const { asyncLocalStorage } = require('../../async-storage') +const db = require('../../db') +const { USER_SESSIONS_TABLE_NAME } = require('../../constants') + +const schemaCache = {} + +const cleanUserSessions = (cleanInterval) => (req, res, next) => { + const schema = asyncLocalStorage.getStore() ? asyncLocalStorage.getStore().get('schema') : null + const now = Date.now() + + if (!schema) return next() + if (schema && schemaCache.schema + cleanInterval > now) return next() + + console.log('Clearing old sessions for schema', schema) + return db.$none('DELETE FROM $1^ WHERE expire < to_timestamp($2)', [USER_SESSIONS_TABLE_NAME, now]) + .then(() => { + schemaCache.schema = now + return next() + }) + .catch(next) +} + +module.exports = cleanUserSessions diff --git a/lib/new-admin/middlewares/session.js b/lib/new-admin/middlewares/session.js index edb323d8..dc0ba1fd 100644 --- a/lib/new-admin/middlewares/session.js +++ b/lib/new-admin/middlewares/session.js @@ -3,10 +3,11 @@ const express = require('express') const router = express.Router() const hkdf = require('futoin-hkdf') const session = require('express-session') -const pgSession = require('connect-pg-simple')(session) +const PgSession = require('connect-pg-simple')(session) const mnemonicHelpers = require('../../mnemonic-helpers') const db = require('../../db') const options = require('../../options') +const { USER_SESSIONS_TABLE_NAME } = require('../../constants') const getSecret = () => { const mnemonic = fs.readFileSync(options.mnemonicPath, 'utf8') @@ -19,10 +20,14 @@ const getSecret = () => { const hostname = options.hostname +const lamaDb = { + query: (query, values, qrm) => db.$query(query, values, qrm, false) +} + router.use('*', session({ - store: new pgSession({ - pgPromise: db, - tableName: 'user_sessions' + store: new PgSession({ + pgPromise: lamaDb, + tableName: USER_SESSIONS_TABLE_NAME }), name: 'lid', secret: getSecret(), From 2e45098033aef77d9495b0e5be5986fac5bba581 Mon Sep 17 00:00:00 2001 From: csrapr <26280794+csrapr@users.noreply.github.com> Date: Tue, 25 May 2021 18:44:05 +0100 Subject: [PATCH 02/18] Fix: lamassu-register with async local store --- bin/lamassu-register | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/bin/lamassu-register b/bin/lamassu-register index 1e73f714..1fbb98a4 100755 --- a/bin/lamassu-register +++ b/bin/lamassu-register @@ -1,5 +1,6 @@ #!/usr/bin/env node +const { asyncLocalStorage, defaultStore } = require('../lib/async-storage') const authentication = require('../lib/new-admin/graphql/modules/authentication') const options = require('../lib/options') @@ -29,20 +30,22 @@ if (role !== 'user' && role !== 'superuser') { process.exit(2) } -authentication.createRegisterToken(name, role).then(token => { - if (!token) { - console.log(`A user named ${name} already exists!`) - process.exit(2) - } +asyncLocalStorage.run(defaultStore(), () => { + authentication.createRegisterToken(name, role).then(token => { + if (!token) { + console.log(`A user named ${name} already exists!`) + process.exit(2) + } - if (domain === 'localhost') { - console.log(`https://${domain}:3001/register?t=${token.token}`) - } else { - console.log(`https://${domain}/register?t=${token.token}`) - } + if (domain === 'localhost') { + console.log(`https://${domain}:3001/register?t=${token.token}`) + } else { + console.log(`https://${domain}/register?t=${token.token}`) + } - process.exit(0) -}).catch(err => { - console.log('Error: %s', err) - process.exit(3) + process.exit(0) + }).catch(err => { + console.log('Error: %s', err) + process.exit(3) + }) }) From 0272dc2bd17cc7f56ebc42b422cc792e81b5956b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Salgado?= Date: Wed, 15 Sep 2021 15:43:39 +0100 Subject: [PATCH 03/18] chore: remove db $ instances fix: add compute-schema file fix: to_timestamp() psql usage chore: rephrase expired session deletion --- lib/compute-schema.js | 10 ++++++++++ lib/new-admin/middlewares/cleanUserSessions.js | 4 ++-- lib/new-admin/middlewares/session.js | 2 +- 3 files changed, 13 insertions(+), 3 deletions(-) create mode 100644 lib/compute-schema.js diff --git a/lib/compute-schema.js b/lib/compute-schema.js new file mode 100644 index 00000000..00b6d332 --- /dev/null +++ b/lib/compute-schema.js @@ -0,0 +1,10 @@ +const { asyncLocalStorage, defaultStore } = require('./async-storage') + +const computeSchema = (req, res, next) => { + const store = defaultStore() + asyncLocalStorage.run(store, () => { + next() + }) +} + +module.exports = computeSchema diff --git a/lib/new-admin/middlewares/cleanUserSessions.js b/lib/new-admin/middlewares/cleanUserSessions.js index c156c740..33cb5b04 100644 --- a/lib/new-admin/middlewares/cleanUserSessions.js +++ b/lib/new-admin/middlewares/cleanUserSessions.js @@ -11,8 +11,8 @@ const cleanUserSessions = (cleanInterval) => (req, res, next) => { if (!schema) return next() if (schema && schemaCache.schema + cleanInterval > now) return next() - console.log('Clearing old sessions for schema', schema) - return db.$none('DELETE FROM $1^ WHERE expire < to_timestamp($2)', [USER_SESSIONS_TABLE_NAME, now]) + console.log('Clearing expired sessions for schema', schema) + return db.none('DELETE FROM $1^ WHERE expire < to_timestamp($2 / 1000.0)', [USER_SESSIONS_TABLE_NAME, now]) .then(() => { schemaCache.schema = now return next() diff --git a/lib/new-admin/middlewares/session.js b/lib/new-admin/middlewares/session.js index dc0ba1fd..24d07dad 100644 --- a/lib/new-admin/middlewares/session.js +++ b/lib/new-admin/middlewares/session.js @@ -21,7 +21,7 @@ const getSecret = () => { const hostname = options.hostname const lamaDb = { - query: (query, values, qrm) => db.$query(query, values, qrm, false) + query: (query, values, qrm) => db.query(query, values, qrm, false) } router.use('*', session({ From c0d1db834b864526e6a773a2cf0b776ec4598b42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Salgado?= Date: Wed, 15 Sep 2021 18:34:09 +0100 Subject: [PATCH 04/18] refactor: move apollo server context creator to middleware function --- lib/new-admin/admin-server.js | 25 ++----------------------- lib/new-admin/middlewares/context.js | 25 +++++++++++++++++++++++++ lib/new-admin/middlewares/index.js | 9 +++++++++ 3 files changed, 36 insertions(+), 23 deletions(-) create mode 100644 lib/new-admin/middlewares/context.js create mode 100644 lib/new-admin/middlewares/index.js diff --git a/lib/new-admin/admin-server.js b/lib/new-admin/admin-server.js index 05d1e377..b4c88651 100644 --- a/lib/new-admin/admin-server.js +++ b/lib/new-admin/admin-server.js @@ -16,12 +16,11 @@ const options = require('../options') const users = require('../users') const logger = require('../logger') -const session = require('./middlewares/session') const { AuthDirective } = require('./graphql/directives') const { typeDefs, resolvers } = require('./graphql/schema') const computeSchema = require('../compute-schema') -const cleanUserSessions = require('./middlewares/cleanUserSessions') const { USER_SESSIONS_CLEAR_INTERVAL } = require('../constants') +const { session, cleanUserSessions, buildApolloContext } = require('./middlewares') const devMode = require('minimist')(process.argv.slice(2)).dev const idPhotoCardBasedir = _.get('idPhotoCardDir', options) @@ -59,27 +58,7 @@ const apolloServer = new ApolloServer({ logger.error(error) return error }, - context: async ({ req, res }) => { - if (!req.session.user) return { req } - - const user = await users.verifyAndUpdateUser( - req.session.user.id, - req.headers['user-agent'] || 'Unknown', - req.ip - ) - if (!user || !user.enabled) throw new AuthenticationError('Authentication failed') - - req.session.ua = req.headers['user-agent'] || 'Unknown' - req.session.ipAddress = req.ip - req.session.lastUsed = new Date(Date.now()).toISOString() - req.session.user.id = user.id - req.session.user.role = user.role - - res.set('role', user.role) - res.set('Access-Control-Expose-Headers', 'role') - - return { req } - } + context: async (obj) => buildApolloContext(obj) }) apolloServer.applyMiddleware({ diff --git a/lib/new-admin/middlewares/context.js b/lib/new-admin/middlewares/context.js new file mode 100644 index 00000000..db4687d2 --- /dev/null +++ b/lib/new-admin/middlewares/context.js @@ -0,0 +1,25 @@ +const users = require('../../users') + +const buildApolloContext = async ({ req, res }) => { + if (!req.session.user) return { req } + + const user = await users.verifyAndUpdateUser( + req.session.user.id, + req.headers['user-agent'] || 'Unknown', + req.ip + ) + if (!user || !user.enabled) throw new AuthenticationError('Authentication failed') + + req.session.ua = req.headers['user-agent'] || 'Unknown' + req.session.ipAddress = req.ip + req.session.lastUsed = new Date(Date.now()).toISOString() + req.session.user.id = user.id + req.session.user.role = user.role + + res.set('role', user.role) + res.set('Access-Control-Expose-Headers', 'role') + + return { req } +} + +module.exports = buildApolloContext diff --git a/lib/new-admin/middlewares/index.js b/lib/new-admin/middlewares/index.js new file mode 100644 index 00000000..cedba31c --- /dev/null +++ b/lib/new-admin/middlewares/index.js @@ -0,0 +1,9 @@ +const cleanUserSessions = require('./cleanUserSessions') +const buildApolloContext = require('./context') +const session = require('./session') + +module.exports = { + cleanUserSessions, + buildApolloContext, + session +} From 5a442165471c715ceb73460e3705e774124cd1b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Salgado?= Date: Thu, 16 Sep 2021 15:18:10 +0100 Subject: [PATCH 05/18] fix: session middleware order --- lib/new-admin/admin-server.js | 2 +- lib/users.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/new-admin/admin-server.js b/lib/new-admin/admin-server.js index b4c88651..93cf01ad 100644 --- a/lib/new-admin/admin-server.js +++ b/lib/new-admin/admin-server.js @@ -42,9 +42,9 @@ app.use(cookieParser()) app.use(express.json()) app.use(express.urlencoded({ extended: true })) // support encoded bodies app.use(express.static(path.resolve(__dirname, '..', '..', 'public'))) -app.use(computeSchema) app.use(cleanUserSessions(USER_SESSIONS_CLEAR_INTERVAL)) app.use(session) +app.use(computeSchema) const apolloServer = new ApolloServer({ typeDefs, diff --git a/lib/users.js b/lib/users.js index b11a0bf3..3eaa1b74 100644 --- a/lib/users.js +++ b/lib/users.js @@ -59,7 +59,7 @@ function verifyAndUpdateUser (id, ua, ip) { .then(user => { if (!user) return null - const sql2 = `UPDATE users SET last_accessed=now(), last_accessed_from=$1, last_accessed_address=$2 WHERE id=$3 RETURNING id, role, enabled` + const sql2 = `UPDATE users SET last_accessed=now(), last_accessed_from=$1, last_accessed_address=$2 WHERE id=$3 RETURNING id, username, role, enabled` return db.one(sql2, [ua, ip, id]) }) .then(user => user) From 4ce4a325029ce1e6a1455047185ac3ecdfab0d66 Mon Sep 17 00:00:00 2001 From: csrapr <26280794+csrapr@users.noreply.github.com> Date: Fri, 9 Jul 2021 11:22:42 +0100 Subject: [PATCH 06/18] Feat: allow polling of multiple db schemas --- lib/app.js | 3 +- lib/poller.js | 152 +++++++++++++++++++++++++++++++++++++--------- package-lock.json | 5 ++ package.json | 1 + 4 files changed, 129 insertions(+), 32 deletions(-) diff --git a/lib/app.js b/lib/app.js index 14ace5aa..72394605 100644 --- a/lib/app.js +++ b/lib/app.js @@ -64,8 +64,7 @@ function loadSanctions (settings) { function startServer (settings) { return Promise.resolve() .then(() => { - poller.start(settings) - + poller.setup(['public']) const httpsServerOptions = { key: fs.readFileSync(options.keyPath), cert: fs.readFileSync(options.certPath), diff --git a/lib/poller.js b/lib/poller.js index 52d43f76..8a44df96 100644 --- a/lib/poller.js +++ b/lib/poller.js @@ -1,5 +1,5 @@ const _ = require('lodash/fp') - +const Queue = require('queue-promise') const plugins = require('./plugins') const notifier = require('./notifier') const T = require('./time') @@ -11,6 +11,10 @@ const sanctions = require('./ofac/index') const coinAtmRadar = require('./coinatmradar/coinatmradar') const configManager = require('./new-config-manager') const complianceTriggers = require('./compliance-triggers') +const { asyncLocalStorage, defaultStore } = require('./async-storage') +const settingsLoader = require('./new-settings-loader') +const NodeCache = require('node-cache') +const util = require('util') const INCOMING_TX_INTERVAL = 30 * T.seconds const LIVE_INCOMING_TX_INTERVAL = 5 * T.seconds @@ -24,25 +28,68 @@ const LOGS_CLEAR_INTERVAL = 1 * T.day const SANCTIONS_INITIAL_DOWNLOAD_INTERVAL = 5 * T.minutes const SANCTIONS_UPDATE_INTERVAL = 1 * T.week const RADAR_UPDATE_INTERVAL = 5 * T.minutes -const PRUNE_MACHINES_HEARBEAT = 1 * T.day +const PRUNE_MACHINES_HEARTBEAT = 1 * T.day const CHECK_NOTIFICATION_INTERVAL = 20 * T.seconds - const PENDING_INTERVAL = 10 * T.seconds +const CACHE_ENTRY_TTL = 3600 // seconds -const coinFilter = ['ETH'] +const FAST_QUEUE_WAIT = 1 * T.seconds +const SLOW_QUEUE_WAIT = 10 * T.seconds -let _pi, _settings +const FAST_QUEUE = new Queue({ + concurrent: 600, + interval: FAST_QUEUE_WAIT +}) -function reload (__settings) { - _settings = __settings - _pi = plugins(_settings) - logger.debug('settings reloaded in poller') - updateAndLoadSanctions() +const SLOW_QUEUE = new Queue({ + concurrent: 10, + interval: SLOW_QUEUE_WAIT +}) + +// Fix for asyncLocalStorage store being lost due to callback-based queue +FAST_QUEUE.enqueue = util.promisify(FAST_QUEUE.enqueue) +SLOW_QUEUE.enqueue = util.promisify(SLOW_QUEUE.enqueue) + +const QUEUE = { + FAST: FAST_QUEUE, + SLOW: SLOW_QUEUE } -function pi () { return _pi } -function settings () { return _settings } +const coinFilter = ['ETH'] +const schemaCallbacks = new Map() + +const cachedVariables = new NodeCache({ + stdTTL: CACHE_ENTRY_TTL, + checkperiod: CACHE_ENTRY_TTL, + deleteOnExpire: false, + useClones: false // pass values by reference instead of cloning +}) + +cachedVariables.on('expired', (key, val) => { + if (!val.isReloading) { + // since val is passed by reference we don't need to do cachedVariables.set() + val.isReloading = true + return reload(key) + } +}) + +function reload (schema) { + const store = defaultStore() + store.set('schema', schema) + // set asyncLocalStorage so settingsLoader loads settings for the right schema + asyncLocalStorage.run(store, () => { + return settingsLoader.loadLatest().then(settings => { + const pi = plugins(settings) + cachedVariables.set(schema, { settings, pi, isReloading: false }) + logger.debug(`settings for schema "${schema}" reloaded in poller`) + return updateAndLoadSanctions() + }) + }) +} + +function pi () { return cachedVariables.get(asyncLocalStorage.getStore().get('schema')).pi } +function settings () { return cachedVariables.get(asyncLocalStorage.getStore().get('schema')).settings } function initialSanctionsDownload () { const structs = sanctions.getStructs() @@ -70,9 +117,40 @@ function updateCoinAtmRadar () { .then(rates => coinAtmRadar.update(rates, settings())) } -function start (__settings) { - reload(__settings) +function initializeEachSchema (schemas = ['public']) { + // for each schema set "thread variables" and do polling + return _.forEach(schema => { + const store = defaultStore() + store.set('schema', schema) + return asyncLocalStorage.run(store, () => { + return settingsLoader.loadLatest().then(settings => { + // prevent inadvertedly clearing the array without clearing timeouts + if (schemaCallbacks.has(schema)) throw new Error(`The schema "${schema}" cannot be initialized twice on poller`) + const pi = plugins(settings) + cachedVariables.set(schema, { settings, pi, isReloading: false }) + schemaCallbacks.set(schema, []) + return doPolling(schema) + }) + }).catch(console.error) + }, schemas) +} +function addToQueue (func, interval, schema, queue, ...vars) { + return schemaCallbacks.get(schema).push(setInterval(() => { + return queue.enqueue().then(() => { + // get plugins or settings from the cache every time func is run + const loadVariables = vars.length > 0 && typeof vars[0] === 'function' + if (loadVariables) { + const funcVars = [...vars] + funcVars[0] = vars[0]() + return func(...funcVars) + } + return func(...vars) + }).catch(console.error) + }, interval)) +} + +function doPolling (schema) { pi().executeTrades() pi().pong() pi().clearOldLogs() @@ -87,23 +165,37 @@ function start (__settings) { notifier.checkNotification(pi()) updateCoinAtmRadar() - setInterval(() => pi().executeTrades(), TRADE_INTERVAL) - setInterval(() => cashOutTx.monitorLiveIncoming(settings(), false, coinFilter), LIVE_INCOMING_TX_INTERVAL) - setInterval(() => cashOutTx.monitorStaleIncoming(settings(), false, coinFilter), INCOMING_TX_INTERVAL) + addToQueue(pi().executeTrades, TRADE_INTERVAL, schema, QUEUE.FAST) + addToQueue(cashOutTx.monitorLiveIncoming, LIVE_INCOMING_TX_INTERVAL, schema, QUEUE.FAST, settings, false, coinFilter) + addToQueue(cashOutTx.monitorStaleIncoming, INCOMING_TX_INTERVAL, schema, QUEUE.FAST, settings, false, coinFilter) if (!_.isEmpty(coinFilter)) { - setInterval(() => cashOutTx.monitorLiveIncoming(settings(), true, coinFilter), LIVE_INCOMING_TX_INTERVAL_FILTER) - setInterval(() => cashOutTx.monitorStaleIncoming(settings(), true, coinFilter), INCOMING_TX_INTERVAL_FILTER) + addToQueue(cashOutTx.monitorLiveIncoming, LIVE_INCOMING_TX_INTERVAL_FILTER, schema, QUEUE.FAST, settings, true, coinFilter) + addToQueue(cashOutTx.monitorStaleIncoming, INCOMING_TX_INTERVAL_FILTER, schema, QUEUE.FAST, settings, true, coinFilter) } - setInterval(() => cashOutTx.monitorUnnotified(settings()), UNNOTIFIED_INTERVAL) - setInterval(() => cashInTx.monitorPending(settings()), PENDING_INTERVAL) - setInterval(() => pi().sweepHd(), SWEEP_HD_INTERVAL) - setInterval(() => pi().pong(), PONG_INTERVAL) - setInterval(() => pi().clearOldLogs(), LOGS_CLEAR_INTERVAL) - setInterval(() => notifier.checkNotification(pi()), CHECK_NOTIFICATION_INTERVAL) - setInterval(initialSanctionsDownload, SANCTIONS_INITIAL_DOWNLOAD_INTERVAL) - setInterval(updateAndLoadSanctions, SANCTIONS_UPDATE_INTERVAL) - setInterval(updateCoinAtmRadar, RADAR_UPDATE_INTERVAL) - setInterval(() => pi().pruneMachinesHeartbeat(), PRUNE_MACHINES_HEARBEAT) + addToQueue(cashOutTx.monitorUnnotified, UNNOTIFIED_INTERVAL, schema, QUEUE.FAST, settings) + addToQueue(cashInTx.monitorPending, PENDING_INTERVAL, schema, QUEUE.FAST, settings) + addToQueue(pi().sweepHd, SWEEP_HD_INTERVAL, schema, QUEUE.FAST, settings) + addToQueue(pi().pong, PONG_INTERVAL, schema, QUEUE.FAST) + addToQueue(pi().clearOldLogs, LOGS_CLEAR_INTERVAL, schema, QUEUE.SLOW) + addToQueue(notifier.checkNotification, CHECK_NOTIFICATION_INTERVAL, schema, QUEUE.FAST, pi) + addToQueue(initialSanctionsDownload, SANCTIONS_INITIAL_DOWNLOAD_INTERVAL, schema, QUEUE.SLOW) + addToQueue(updateAndLoadSanctions, SANCTIONS_UPDATE_INTERVAL, schema, QUEUE.SLOW) + addToQueue(updateCoinAtmRadar, RADAR_UPDATE_INTERVAL, schema, QUEUE.SLOW) + addToQueue(pi().pruneMachinesHeartbeat(), PRUNE_MACHINES_HEARTBEAT, schema, QUEUE.SLOW, settings) } -module.exports = { start, reload } +function setup (schemasToAdd = [], schemasToRemove = []) { + // clear callback array for each schema in schemasToRemove and clear cached variables + _.forEach(schema => { + const callbacks = schemaCallbacks.get(schema) + _.forEach(clearInterval, callbacks) + schemaCallbacks.delete(schema) + cachedVariables.del(schema) + }, schemasToRemove) + + return initializeEachSchema(schemasToAdd) +} + +const getActiveSchemas = () => Array.from(schemaCallbacks.keys()) + +module.exports = { setup, reload, getActiveSchemas } diff --git a/package-lock.json b/package-lock.json index 983fa99c..1a48e424 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17669,6 +17669,11 @@ "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" }, + "queue-promise": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/queue-promise/-/queue-promise-2.2.1.tgz", + "integrity": "sha512-C3eyRwLF9m6dPV4MtqMVFX+Xmc7keZ9Ievm3jJ/wWM5t3uVbFnGsJXwpYzZ4LaIEcX9bss/mdaKzyrO6xheRuA==" + }, "random-bytes": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", diff --git a/package.json b/package.json index dafdfd7a..ae4647ff 100644 --- a/package.json +++ b/package.json @@ -65,6 +65,7 @@ "pretty-ms": "^2.1.0", "promise-sequential": "^1.1.1", "request-promise": "^4.2.6", + "queue-promise": "^2.2.1", "semver": "^7.1.3", "serve-static": "^1.12.4", "socket.io": "^2.0.3", From 121ced632ef3bf4cfeb4bc5d56f607a49d973d11 Mon Sep 17 00:00:00 2001 From: csrapr <26280794+csrapr@users.noreply.github.com> Date: Tue, 25 May 2021 17:40:22 +0100 Subject: [PATCH 07/18] Feat: use pg-promise notify for poller update --- lib/new-settings-loader.js | 6 +++++- lib/poller.js | 10 +++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/lib/new-settings-loader.js b/lib/new-settings-loader.js index fd0cc435..d71e171c 100644 --- a/lib/new-settings-loader.js +++ b/lib/new-settings-loader.js @@ -1,6 +1,7 @@ const _ = require('lodash/fp') const db = require('./db') const migration = require('./config-migration') +const { asyncLocalStorage } = require('./async-storage') const OLD_SETTINGS_LOADER_SCHEMA_VERSION = 1 const NEW_SETTINGS_LOADER_SCHEMA_VERSION = 2 @@ -73,7 +74,10 @@ function saveConfig (config) { return loadLatestConfigOrNone() .then(currentConfig => { const newConfig = _.assign(currentConfig, config) - return db.none(configSql, ['config', { config: newConfig }, true, NEW_SETTINGS_LOADER_SCHEMA_VERSION]) + return db.tx(t => { + return t.none(configSql, ['config', { config: newConfig }, true, NEW_SETTINGS_LOADER_SCHEMA_VERSION]) + .then(() => t.none('NOTIFY $1:name, $2', ['poller', asyncLocalStorage.getStore().get('schema')])) + }).catch(console.error) }) } diff --git a/lib/poller.js b/lib/poller.js index 8a44df96..943d0054 100644 --- a/lib/poller.js +++ b/lib/poller.js @@ -15,6 +15,7 @@ const { asyncLocalStorage, defaultStore } = require('./async-storage') const settingsLoader = require('./new-settings-loader') const NodeCache = require('node-cache') const util = require('util') +const db = require('./db') const INCOMING_TX_INTERVAL = 30 * T.seconds const LIVE_INCOMING_TX_INTERVAL = 5 * T.seconds @@ -74,11 +75,18 @@ cachedVariables.on('expired', (key, val) => { } }) +db.connect({ direct: true }).then(sco => { + sco.client.on('notification', data => { + return reload(data.payload) + }) + return sco.none('LISTEN $1:name', 'poller') +}).catch(console.error) + function reload (schema) { const store = defaultStore() store.set('schema', schema) // set asyncLocalStorage so settingsLoader loads settings for the right schema - asyncLocalStorage.run(store, () => { + return asyncLocalStorage.run(store, () => { return settingsLoader.loadLatest().then(settings => { const pi = plugins(settings) cachedVariables.set(schema, { settings, pi, isReloading: false }) From 32e5b1ba8717b88d56924bbf7997296362311d16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Salgado?= Date: Thu, 16 Sep 2021 18:49:09 +0100 Subject: [PATCH 08/18] fix: add connect() function to list of db wrapper functions --- lib/db.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/db.js b/lib/db.js index 55c65983..3e58ac2b 100644 --- a/lib/db.js +++ b/lib/db.js @@ -21,7 +21,8 @@ const stripDefaultDbFuncs = dbCtx => { tx: dbCtx.$tx, task: dbCtx.$task, batch: dbCtx.batch, - multi: dbCtx.$multi + multi: dbCtx.$multi, + connect: dbCtx.connect } } From 7135a0365422ae6461f5a6dcec65b92519dbdcdc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Salgado?= Date: Fri, 17 Sep 2021 16:57:28 +0100 Subject: [PATCH 09/18] feat: add operatorId to l-a-s middlewares fix: make changes to db event handler to receive more complex payloads feat: machine actions previously on REST now work based on notifications feat: state middleware now operates based on operatorId as well chore: remove old localAppRoutes related code --- lib/machine-loader.js | 27 +++++++++-- lib/new-admin/admin-server.js | 1 + .../graphql/resolvers/machine.resolver.js | 2 +- .../graphql/resolvers/settings.resolver.js | 11 +---- lib/new-admin/services/machines.js | 5 +- lib/new-settings-loader.js | 2 +- lib/poller.js | 36 +++++++++++++- lib/routes.js | 5 -- lib/routes/cashboxRoutes.js | 3 +- lib/routes/localAppRoutes.js | 48 ------------------- lib/routes/pollingRoutes.js | 9 ++-- 11 files changed, 71 insertions(+), 78 deletions(-) delete mode 100644 lib/routes/localAppRoutes.js diff --git a/lib/machine-loader.js b/lib/machine-loader.js index 5ce26a5a..f7e19dfa 100644 --- a/lib/machine-loader.js +++ b/lib/machine-loader.js @@ -157,18 +157,37 @@ function unpair (rec) { } function reboot (rec) { - return axios.post(`http://localhost:3030/reboot?device_id=${rec.deviceId}`) + return db.none('NOTIFY $1:name, $2', ['poller', JSON.stringify( + { + type: 'machineAction', + action: 'reboot', + value: _.pick(['deviceId', 'operatorId', 'action'], rec) + } + )]) } function shutdown (rec) { - return axios.post(`http://localhost:3030/shutdown?device_id=${rec.deviceId}`) + return db.none('NOTIFY $1:name, $2', ['poller', JSON.stringify( + { + type: 'machineAction', + action: 'shutdown', + value: _.pick(['deviceId', 'operatorId', 'action'], rec) + } + )]) } function restartServices (rec) { - return axios.post(`http://localhost:3030/restartServices?device_id=${rec.deviceId}`) + return db.none('NOTIFY $1:name, $2', ['poller', JSON.stringify( + { + type: 'machineAction', + action: 'restartServices', + value: _.pick(['deviceId', 'operatorId', 'action'], rec) + } + )]) } -function setMachine (rec) { +function setMachine (rec, operatorId) { + rec.operatorId = operatorId switch (rec.action) { case 'rename': return renameMachine(rec) case 'emptyCashInBills': return emptyCashInBills(rec) diff --git a/lib/new-admin/admin-server.js b/lib/new-admin/admin-server.js index 93cf01ad..8b7938cf 100644 --- a/lib/new-admin/admin-server.js +++ b/lib/new-admin/admin-server.js @@ -18,6 +18,7 @@ const logger = require('../logger') const { AuthDirective } = require('./graphql/directives') const { typeDefs, resolvers } = require('./graphql/schema') +const findOperatorId = require('../middlewares/operatorId') const computeSchema = require('../compute-schema') const { USER_SESSIONS_CLEAR_INTERVAL } = require('../constants') const { session, cleanUserSessions, buildApolloContext } = require('./middlewares') diff --git a/lib/new-admin/graphql/resolvers/machine.resolver.js b/lib/new-admin/graphql/resolvers/machine.resolver.js index c2e5bc2c..771bb479 100644 --- a/lib/new-admin/graphql/resolvers/machine.resolver.js +++ b/lib/new-admin/graphql/resolvers/machine.resolver.js @@ -18,7 +18,7 @@ const resolvers = { machine: (...[, { deviceId }]) => machineLoader.getMachine(deviceId) }, Mutation: { - machineAction: (...[, { deviceId, action, cashbox, cassette1, cassette2, newName }]) => machineAction({ deviceId, action, cashbox, cassette1, cassette2, newName }) + machineAction: (...[, { deviceId, action, cashbox, cassette1, cassette2, newName }, context]) => machineAction({ deviceId, action, cashbox, cassette1, cassette2, newName }, context) } } diff --git a/lib/new-admin/graphql/resolvers/settings.resolver.js b/lib/new-admin/graphql/resolvers/settings.resolver.js index 329be89e..383f9309 100644 --- a/lib/new-admin/graphql/resolvers/settings.resolver.js +++ b/lib/new-admin/graphql/resolvers/settings.resolver.js @@ -1,11 +1,5 @@ -const got = require('got') - -const logger = require('../../../logger') const settingsLoader = require('../../../new-settings-loader') -const notify = () => got.post('http://localhost:3030/dbChange') - .catch(e => logger.error('lamassu-server not responding')) - const resolvers = { Query: { accounts: () => settingsLoader.showAccounts(), @@ -14,10 +8,7 @@ const resolvers = { Mutation: { saveAccounts: (...[, { accounts }]) => settingsLoader.saveAccounts(accounts), // resetAccounts: (...[, { schemaVersion }]) => settingsLoader.resetAccounts(schemaVersion), - saveConfig: (...[, { config }]) => settingsLoader.saveConfig(config).then(it => { - notify() - return it - }), + saveConfig: (...[, { config }]) => settingsLoader.saveConfig(config), // resetConfig: (...[, { schemaVersion }]) => settingsLoader.resetConfig(schemaVersion), // migrateConfigAndAccounts: () => settingsLoader.migrate() } diff --git a/lib/new-admin/services/machines.js b/lib/new-admin/services/machines.js index 9a612935..85796601 100644 --- a/lib/new-admin/services/machines.js +++ b/lib/new-admin/services/machines.js @@ -6,13 +6,14 @@ function getMachine (machineId) { .then(machines => machines.find(({ deviceId }) => deviceId === machineId)) } -function machineAction ({ deviceId, action, cashbox, cassette1, cassette2, newName }) { +function machineAction ({ deviceId, action, cashbox, cassette1, cassette2, newName }, context) { + const operatorId = context.res.locals.operatorId return getMachine(deviceId) .then(machine => { if (!machine) throw new UserInputError(`machine:${deviceId} not found`, { deviceId }) return machine }) - .then(machineLoader.setMachine({ deviceId, action, cashbox, cassettes: [cassette1, cassette2], newName })) + .then(machineLoader.setMachine({ deviceId, action, cashbox, cassettes: [cassette1, cassette2], newName }, operatorId)) .then(getMachine(deviceId)) } diff --git a/lib/new-settings-loader.js b/lib/new-settings-loader.js index d71e171c..18da5c61 100644 --- a/lib/new-settings-loader.js +++ b/lib/new-settings-loader.js @@ -76,7 +76,7 @@ function saveConfig (config) { const newConfig = _.assign(currentConfig, config) return db.tx(t => { return t.none(configSql, ['config', { config: newConfig }, true, NEW_SETTINGS_LOADER_SCHEMA_VERSION]) - .then(() => t.none('NOTIFY $1:name, $2', ['poller', asyncLocalStorage.getStore().get('schema')])) + .then(() => t.none('NOTIFY $1:name, $2', ['poller', JSON.stringify({ type: 'reload', schema: asyncLocalStorage.getStore().get('schema') })])) }).catch(console.error) }) } diff --git a/lib/poller.js b/lib/poller.js index 943d0054..c3ba3334 100644 --- a/lib/poller.js +++ b/lib/poller.js @@ -16,6 +16,7 @@ const settingsLoader = require('./new-settings-loader') const NodeCache = require('node-cache') const util = require('util') const db = require('./db') +const state = require('./middlewares/state') const INCOMING_TX_INTERVAL = 30 * T.seconds const LIVE_INCOMING_TX_INTERVAL = 5 * T.seconds @@ -77,7 +78,15 @@ cachedVariables.on('expired', (key, val) => { db.connect({ direct: true }).then(sco => { sco.client.on('notification', data => { - return reload(data.payload) + const parsedData = JSON.parse(data.payload) + switch (parsedData.type) { + case 'reload': + return reload(parsedData.schema) + case 'machineAction': + return machineAction(parsedData.action, parsedData.value) + default: + break + } }) return sco.none('LISTEN $1:name', 'poller') }).catch(console.error) @@ -90,12 +99,35 @@ function reload (schema) { return settingsLoader.loadLatest().then(settings => { const pi = plugins(settings) cachedVariables.set(schema, { settings, pi, isReloading: false }) - logger.debug(`settings for schema "${schema}" reloaded in poller`) + logger.debug(`Settings for schema '${schema}' reloaded in poller`) return updateAndLoadSanctions() }) }) } +function machineAction (type, value) { + const deviceId = value.deviceId + const operatorId = value.operatorId + const pid = state.pids?.[operatorId]?.[deviceId]?.pid + + switch (type) { + case 'reboot': + logger.debug(`Rebooting machine '${deviceId}' from operator ${operatorId}`) + state.reboots[operatorId] = { [deviceId]: pid } + break + case 'shutdown': + logger.debug(`Shutting down machine '${deviceId}' from operator ${operatorId}`) + state.shutdowns[operatorId] = { [deviceId]: pid } + break + case 'restartServices': + logger.debug(`Restarting services of machine '${deviceId}' from operator ${operatorId}`) + state.restartServicesMap[operatorId] = { [deviceId]: pid } + break + default: + break + } +} + function pi () { return cachedVariables.get(asyncLocalStorage.getStore().get('schema')).pi } function settings () { return cachedVariables.get(asyncLocalStorage.getStore().get('schema')).settings } diff --git a/lib/routes.js b/lib/routes.js index d2c25a40..4c694fd5 100644 --- a/lib/routes.js +++ b/lib/routes.js @@ -30,8 +30,6 @@ const verifyUserRoutes = require('./routes/verifyUserRoutes') const verifyTxRoutes = require('./routes/verifyTxRoutes') const verifyPromoCodeRoutes = require('./routes/verifyPromoCodeRoutes') -const localAppRoutes = require('./routes/localAppRoutes') - const app = express() const localApp = express() @@ -87,7 +85,4 @@ app.use((req, res) => { res.status(404).json({ error: 'No such route' }) }) -// localapp routes -localApp.use('/', localAppRoutes) - module.exports = { app, localApp } diff --git a/lib/routes/cashboxRoutes.js b/lib/routes/cashboxRoutes.js index 6c740c08..1747187a 100644 --- a/lib/routes/cashboxRoutes.js +++ b/lib/routes/cashboxRoutes.js @@ -8,6 +8,7 @@ const { getCashInSettings } = require('../new-config-manager') const { AUTOMATIC } = require('../constants.js') function notifyCashboxRemoval (req, res, next) { + const operatorId = res.locals.operatorId return Promise.all([getMachine(req.deviceId), loadLatestConfig()]) .then(([machine, config]) => { const cashInSettings = getCashInSettings(config) @@ -15,7 +16,7 @@ function notifyCashboxRemoval (req, res, next) { return res.status(200).send({ status: 'OK' }) } return cashbox.createCashboxBatch(req.deviceId, machine.cashbox) - .then(() => setMachine({ deviceId: req.deviceId, action: 'emptyCashInBills' })) + .then(() => setMachine({ deviceId: req.deviceId, action: 'emptyCashInBills' }, operatorId)) .then(() => res.status(200).send({ status: 'OK' })) }) .catch(next) diff --git a/lib/routes/localAppRoutes.js b/lib/routes/localAppRoutes.js deleted file mode 100644 index d235faa8..00000000 --- a/lib/routes/localAppRoutes.js +++ /dev/null @@ -1,48 +0,0 @@ -const express = require('express') -const router = express.Router() - -const state = require('../middlewares/state') - -router.get('/pid', (req, res) => { - const deviceId = req.query.device_id - const pidRec = state.pids[deviceId] - res.json(pidRec) -}) - -router.post('/reboot', (req, res) => { - const deviceId = req.query.device_id - const pid = state.pids[deviceId] && state.pids[deviceId].pid - - if (!deviceId || !pid) { - return res.sendStatus(400) - } - - state.reboots[deviceId] = pid - res.sendStatus(200) -}) - -router.post('/shutdown', (req, res) => { - const deviceId = req.query.device_id - const pid = state.pids[deviceId] && state.pids[deviceId].pid - - if (!deviceId || !pid) { - return res.sendStatus(400) - } - - state.shutdowns[deviceId] = pid - res.sendStatus(200) -}) - -router.post('/restartServices', (req, res) => { - const deviceId = req.query.device_id - const pid = state.pids[deviceId] && state.pids[deviceId].pid - - if (!deviceId || !pid) { - return res.sendStatus(400) - } - - state.restartServicesMap[deviceId] = pid - res.sendStatus(200) -}) - -module.exports = router diff --git a/lib/routes/pollingRoutes.js b/lib/routes/pollingRoutes.js index cd64f6b5..593b0e9e 100644 --- a/lib/routes/pollingRoutes.js +++ b/lib/routes/pollingRoutes.js @@ -31,6 +31,7 @@ function poll (req, res, next) { const serialNumber = req.query.sn const pid = req.query.pid const settings = req.settings + const operatorId = res.locals.operatorId const localeConfig = configManager.getLocale(deviceId, settings.config) const zeroConfLimits = _.reduce((acc, cryptoCode) => { acc[cryptoCode] = configManager.getWalletSettings(cryptoCode, settings.config).zeroConfLimit @@ -48,15 +49,15 @@ function poll (req, res, next) { const receipt = configManager.getReceipt(settings.config) const terms = configManager.getTermsConditions(settings.config) - state.pids[deviceId] = { pid, ts: Date.now() } + state.pids[operatorId] = { [deviceId]: { pid, ts: Date.now() } } return pi.pollQueries(serialNumber, deviceTime, req.query, machineVersion, machineModel) .then(results => { const cassettes = results.cassettes - const reboot = pid && state.reboots[deviceId] && state.reboots[deviceId] === pid - const shutdown = pid && state.shutdowns[deviceId] && state.shutdowns[deviceId] === pid - const restartServices = pid && state.restartServicesMap[deviceId] && state.restartServicesMap[deviceId] === pid + const reboot = pid && state.reboots?.[operatorId]?.[deviceId] === pid + const shutdown = pid && state.shutdowns?.[operatorId]?.[deviceId] === pid + const restartServices = pid && state.restartServicesMap?.[operatorId]?.[deviceId] === pid const langs = localeConfig.languages const locale = { From 990ab3258392df6729a65373f2ac0115f4750e0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Salgado?= Date: Wed, 22 Sep 2021 19:21:00 +0100 Subject: [PATCH 10/18] feat: db uses asynclocalstorage set schema feat: set user input related header in public route requests to identify schema fix: small fixes --- lib/compute-schema.js | 4 +--- lib/db.js | 5 ++++- lib/new-admin/admin-server.js | 3 ++- lib/new-admin/middlewares/context.js | 7 +++++-- lib/routes.js | 3 +-- .../src/pages/Authentication/Input2FAState.js | 17 ++++++++++++++++- .../src/pages/Authentication/LoginState.js | 5 +++++ .../src/pages/Authentication/Setup2FAState.js | 15 +++++++++++++-- new-lamassu-admin/src/routing/PrivateRoute.js | 2 ++ new-lamassu-admin/src/utils/apollo.js | 12 +++++++++--- 10 files changed, 58 insertions(+), 15 deletions(-) diff --git a/lib/compute-schema.js b/lib/compute-schema.js index 00b6d332..e065281c 100644 --- a/lib/compute-schema.js +++ b/lib/compute-schema.js @@ -2,9 +2,7 @@ const { asyncLocalStorage, defaultStore } = require('./async-storage') const computeSchema = (req, res, next) => { const store = defaultStore() - asyncLocalStorage.run(store, () => { - next() - }) + return asyncLocalStorage.run(store, () => next()) } module.exports = computeSchema diff --git a/lib/db.js b/lib/db.js index 3e58ac2b..43c25537 100644 --- a/lib/db.js +++ b/lib/db.js @@ -38,7 +38,10 @@ const _task = (obj, opts, cb) => { }) } -const getSchema = () => 'public' +const getSchema = () => { + const store = asyncLocalStorage.getStore() ?? defaultStore() + return asyncLocalStorage.run(store, () => store.get('schema')) +} const getDefaultSchema = () => 'ERROR_SCHEMA' const searchPathWrapper = (t, cb) => { diff --git a/lib/new-admin/admin-server.js b/lib/new-admin/admin-server.js index 8b7938cf..b1ccda5d 100644 --- a/lib/new-admin/admin-server.js +++ b/lib/new-admin/admin-server.js @@ -44,8 +44,9 @@ app.use(express.json()) app.use(express.urlencoded({ extended: true })) // support encoded bodies app.use(express.static(path.resolve(__dirname, '..', '..', 'public'))) app.use(cleanUserSessions(USER_SESSIONS_CLEAR_INTERVAL)) -app.use(session) app.use(computeSchema) +app.use(findOperatorId) +app.use(session) const apolloServer = new ApolloServer({ typeDefs, diff --git a/lib/new-admin/middlewares/context.js b/lib/new-admin/middlewares/context.js index db4687d2..aeba75eb 100644 --- a/lib/new-admin/middlewares/context.js +++ b/lib/new-admin/middlewares/context.js @@ -1,7 +1,7 @@ const users = require('../../users') const buildApolloContext = async ({ req, res }) => { - if (!req.session.user) return { req } + if (!req.session.user) return { req, res } const user = await users.verifyAndUpdateUser( req.session.user.id, @@ -14,12 +14,15 @@ const buildApolloContext = async ({ req, res }) => { req.session.ipAddress = req.ip req.session.lastUsed = new Date(Date.now()).toISOString() req.session.user.id = user.id + req.session.user.username = user.username req.session.user.role = user.role + res.set('role', user.role) + res.cookie('email', user.username) res.set('Access-Control-Expose-Headers', 'role') - return { req } + return { req, res } } module.exports = buildApolloContext diff --git a/lib/routes.js b/lib/routes.js index 4c694fd5..0ed9c557 100644 --- a/lib/routes.js +++ b/lib/routes.js @@ -31,7 +31,6 @@ const verifyTxRoutes = require('./routes/verifyTxRoutes') const verifyPromoCodeRoutes = require('./routes/verifyPromoCodeRoutes') const app = express() -const localApp = express() const configRequiredRoutes = [ '/poll', @@ -85,4 +84,4 @@ app.use((req, res) => { res.status(404).json({ error: 'No such route' }) }) -module.exports = { app, localApp } +module.exports = { app } diff --git a/new-lamassu-admin/src/pages/Authentication/Input2FAState.js b/new-lamassu-admin/src/pages/Authentication/Input2FAState.js index 2cb6cce1..dad864f3 100644 --- a/new-lamassu-admin/src/pages/Authentication/Input2FAState.js +++ b/new-lamassu-admin/src/pages/Authentication/Input2FAState.js @@ -56,7 +56,17 @@ const Input2FAState = ({ state, dispatch }) => { const [input2FA, { error: mutationError }] = useMutation(INPUT_2FA, { onCompleted: ({ input2FA: success }) => { - success ? getUserData() : setInvalidToken(true) + if (success) { + return getUserData({ + context: { + headers: { + email: state.clientField, + 'Access-Control-Expose-Headers': 'email' + } + } + }) + } + return setInvalidToken(true) } }) @@ -82,6 +92,11 @@ const Input2FAState = ({ state, dispatch }) => { password: state.passwordField, code: state.twoFAField, rememberMe: state.rememberMeField + }, + context: { + headers: { + email: state.clientField + } } }) } diff --git a/new-lamassu-admin/src/pages/Authentication/LoginState.js b/new-lamassu-admin/src/pages/Authentication/LoginState.js index ef6f8d1b..936c7cd5 100644 --- a/new-lamassu-admin/src/pages/Authentication/LoginState.js +++ b/new-lamassu-admin/src/pages/Authentication/LoginState.js @@ -53,6 +53,11 @@ const LoginState = ({ state, dispatch }) => { variables: { username, password + }, + context: { + headers: { + email: username + } } }) diff --git a/new-lamassu-admin/src/pages/Authentication/Setup2FAState.js b/new-lamassu-admin/src/pages/Authentication/Setup2FAState.js index 346291b8..abf2c2e5 100644 --- a/new-lamassu-admin/src/pages/Authentication/Setup2FAState.js +++ b/new-lamassu-admin/src/pages/Authentication/Setup2FAState.js @@ -69,6 +69,7 @@ const Setup2FAState = ({ state, dispatch }) => { const { error: queryError } = useQuery(GET_2FA_SECRET, { variables: { username: state.clientField, password: state.passwordField }, + context: { headers: { email: state.clientField } }, onCompleted: ({ get2FASecret }) => { setSecret(get2FASecret.secret) setOtpauth(get2FASecret.otpauth) @@ -84,7 +85,16 @@ const Setup2FAState = ({ state, dispatch }) => { const [setup2FA, { error: mutationError }] = useMutation(SETUP_2FA, { onCompleted: ({ setup2FA: success }) => { - success ? getUserData() : setInvalidToken(true) + success + ? getUserData({ + context: { + headers: { + email: state.clientField, + 'Access-Control-Expose-Headers': 'email' + } + } + }) + : setInvalidToken(true) } }) @@ -155,7 +165,8 @@ const Setup2FAState = ({ state, dispatch }) => { password: state.passwordField, rememberMe: state.rememberMeField, codeConfirmation: twoFAConfirmation - } + }, + context: { headers: { email: state.clientField } } }) }} buttonClassName={classes.loginButton}> diff --git a/new-lamassu-admin/src/routing/PrivateRoute.js b/new-lamassu-admin/src/routing/PrivateRoute.js index 2861612d..c17ba94f 100644 --- a/new-lamassu-admin/src/routing/PrivateRoute.js +++ b/new-lamassu-admin/src/routing/PrivateRoute.js @@ -8,6 +8,8 @@ import { isLoggedIn } from './utils' const PrivateRoute = ({ ...rest }) => { const { userData } = useContext(AppContext) + console.log('isLoggedIn', isLoggedIn(userData)) + return isLoggedIn(userData) ? : } diff --git a/new-lamassu-admin/src/utils/apollo.js b/new-lamassu-admin/src/utils/apollo.js index 5512815d..594490fb 100644 --- a/new-lamassu-admin/src/utils/apollo.js +++ b/new-lamassu-admin/src/utils/apollo.js @@ -12,7 +12,7 @@ import AppContext from 'src/AppContext' const URI = process.env.NODE_ENV === 'development' ? 'https://localhost:8070' : '' -const getClient = (history, location, setUserData, setRole) => +const getClient = (history, location, getUserData, setUserData, setRole) => new ApolloClient({ link: ApolloLink.from([ onError(({ graphQLErrors, networkError }) => { @@ -67,8 +67,14 @@ const getClient = (history, location, setUserData, setRole) => const Provider = ({ children }) => { const history = useHistory() const location = useLocation() - const { setUserData, setRole } = useContext(AppContext) - const client = getClient(history, location, setUserData, setRole) + const { userData, setUserData, setRole } = useContext(AppContext) + const client = getClient( + history, + location, + () => userData, + setUserData, + setRole + ) return {children} } From 5b13ffe3d9e766c99916b8406bb832169d601f99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Salgado?= Date: Wed, 22 Sep 2021 21:23:22 +0100 Subject: [PATCH 11/18] feat: encode pazuz_operatoridentifier header chore: rename cookies to fit a standard fix: small fixes --- lib/new-admin/graphql/modules/authentication.js | 2 +- .../graphql/resolvers/customer.resolver.js | 2 +- lib/new-admin/middlewares/context.js | 9 +++++---- lib/new-admin/middlewares/session.js | 2 +- new-lamassu-admin/package-lock.json | 5 +++++ new-lamassu-admin/package.json | 1 + new-lamassu-admin/src/App.js | 4 +++- .../src/pages/Authentication/Input2FAState.js | 6 +++--- .../src/pages/Authentication/LoginState.js | 3 ++- .../src/pages/Authentication/Setup2FAState.js | 16 ++++++++++++---- new-lamassu-admin/src/routing/PrivateRoute.js | 2 -- new-lamassu-admin/src/utils/apollo.js | 2 +- package-lock.json | 5 +++++ package.json | 1 + 14 files changed, 41 insertions(+), 19 deletions(-) diff --git a/lib/new-admin/graphql/modules/authentication.js b/lib/new-admin/graphql/modules/authentication.js index 690bc907..c5be75b6 100644 --- a/lib/new-admin/graphql/modules/authentication.js +++ b/lib/new-admin/graphql/modules/authentication.js @@ -44,7 +44,7 @@ const getUserFromCookie = context => { } const getLamassuCookie = context => { - return context.req.cookies && context.req.cookies.lid + return context.req.cookies && context.req.cookies.lamassu_sid } const initializeSession = (context, user, rememberMe) => { diff --git a/lib/new-admin/graphql/resolvers/customer.resolver.js b/lib/new-admin/graphql/resolvers/customer.resolver.js index f33c3276..6863e8b3 100644 --- a/lib/new-admin/graphql/resolvers/customer.resolver.js +++ b/lib/new-admin/graphql/resolvers/customer.resolver.js @@ -13,7 +13,7 @@ const resolvers = { }, Mutation: { setCustomer: (root, { customerId, customerInput }, context, info) => { - const token = !!context.req.cookies.lid && context.req.session.user.id + const token = !!context.req.cookies.lamassu_sid && context.req.session.user.id if (customerId === anonymous.uuid) return customers.getCustomerById(customerId) return customers.updateCustomer(customerId, customerInput, token) } diff --git a/lib/new-admin/middlewares/context.js b/lib/new-admin/middlewares/context.js index aeba75eb..70e7d62f 100644 --- a/lib/new-admin/middlewares/context.js +++ b/lib/new-admin/middlewares/context.js @@ -1,3 +1,5 @@ +const { AuthenticationError } = require('apollo-server-express') +const base64 = require('base-64') const users = require('../../users') const buildApolloContext = async ({ req, res }) => { @@ -17,10 +19,9 @@ const buildApolloContext = async ({ req, res }) => { req.session.user.username = user.username req.session.user.role = user.role - - res.set('role', user.role) - res.cookie('email', user.username) - res.set('Access-Control-Expose-Headers', 'role') + res.set('lamassu_role', user.role) + res.cookie('pazuz_operatoridentifier', base64.encode(user.username)) + res.set('Access-Control-Expose-Headers', 'lamassu_role') return { req, res } } diff --git a/lib/new-admin/middlewares/session.js b/lib/new-admin/middlewares/session.js index 24d07dad..6c7ea98c 100644 --- a/lib/new-admin/middlewares/session.js +++ b/lib/new-admin/middlewares/session.js @@ -29,7 +29,7 @@ router.use('*', session({ pgPromise: lamaDb, tableName: USER_SESSIONS_TABLE_NAME }), - name: 'lid', + name: 'lamassu_sid', secret: getSecret(), resave: false, saveUninitialized: false, diff --git a/new-lamassu-admin/package-lock.json b/new-lamassu-admin/package-lock.json index f6429e07..cad5fe31 100644 --- a/new-lamassu-admin/package-lock.json +++ b/new-lamassu-admin/package-lock.json @@ -8192,6 +8192,11 @@ } } }, + "base-64": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/base-64/-/base-64-1.0.0.tgz", + "integrity": "sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg==" + }, "base-x": { "version": "3.0.8", "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.8.tgz", diff --git a/new-lamassu-admin/package.json b/new-lamassu-admin/package.json index 24c17fa3..5b66bd32 100644 --- a/new-lamassu-admin/package.json +++ b/new-lamassu-admin/package.json @@ -14,6 +14,7 @@ "apollo-link-error": "^1.1.13", "apollo-link-http": "^1.5.17", "axios": "0.21.1", + "base-64": "^1.0.0", "bignumber.js": "9.0.0", "classnames": "2.2.6", "countries-and-timezones": "^2.4.0", diff --git a/new-lamassu-admin/src/App.js b/new-lamassu-admin/src/App.js index 48373584..1c0eedae 100644 --- a/new-lamassu-admin/src/App.js +++ b/new-lamassu-admin/src/App.js @@ -154,7 +154,9 @@ const App = () => { const [userData, setUserData] = useState(null) const setRole = role => { - if (userData && userData.role !== role) { + console.log('role', role) + console.log('userData', userData) + if (userData && role && userData.role !== role) { setUserData({ ...userData, role }) } } diff --git a/new-lamassu-admin/src/pages/Authentication/Input2FAState.js b/new-lamassu-admin/src/pages/Authentication/Input2FAState.js index dad864f3..b899db26 100644 --- a/new-lamassu-admin/src/pages/Authentication/Input2FAState.js +++ b/new-lamassu-admin/src/pages/Authentication/Input2FAState.js @@ -1,5 +1,6 @@ import { useMutation, useLazyQuery } from '@apollo/react-hooks' import { makeStyles } from '@material-ui/core/styles' +import base64 from 'base-64' import gql from 'graphql-tag' import React, { useContext, useState } from 'react' import { useHistory } from 'react-router-dom' @@ -60,8 +61,7 @@ const Input2FAState = ({ state, dispatch }) => { return getUserData({ context: { headers: { - email: state.clientField, - 'Access-Control-Expose-Headers': 'email' + pazuz_operatoridentifier: base64.encode(state.clientField) } } }) @@ -95,7 +95,7 @@ const Input2FAState = ({ state, dispatch }) => { }, context: { headers: { - email: state.clientField + pazuz_operatoridentifier: base64.encode(state.clientField) } } }) diff --git a/new-lamassu-admin/src/pages/Authentication/LoginState.js b/new-lamassu-admin/src/pages/Authentication/LoginState.js index 936c7cd5..8ef4e848 100644 --- a/new-lamassu-admin/src/pages/Authentication/LoginState.js +++ b/new-lamassu-admin/src/pages/Authentication/LoginState.js @@ -1,5 +1,6 @@ import { useMutation } from '@apollo/react-hooks' import { makeStyles } from '@material-ui/core/styles' +import base64 from 'base-64' import { Field, Form, Formik } from 'formik' import gql from 'graphql-tag' import React from 'react' @@ -56,7 +57,7 @@ const LoginState = ({ state, dispatch }) => { }, context: { headers: { - email: username + pazuz_operatoridentifier: base64.encode(username) } } }) diff --git a/new-lamassu-admin/src/pages/Authentication/Setup2FAState.js b/new-lamassu-admin/src/pages/Authentication/Setup2FAState.js index abf2c2e5..3cbafebc 100644 --- a/new-lamassu-admin/src/pages/Authentication/Setup2FAState.js +++ b/new-lamassu-admin/src/pages/Authentication/Setup2FAState.js @@ -1,5 +1,6 @@ import { useMutation, useQuery, useLazyQuery } from '@apollo/react-hooks' import { makeStyles } from '@material-ui/core/styles' +import base64 from 'base-64' import gql from 'graphql-tag' import QRCode from 'qrcode.react' import React, { useContext, useState } from 'react' @@ -69,7 +70,11 @@ const Setup2FAState = ({ state, dispatch }) => { const { error: queryError } = useQuery(GET_2FA_SECRET, { variables: { username: state.clientField, password: state.passwordField }, - context: { headers: { email: state.clientField } }, + context: { + headers: { + pazuz_operatoridentifier: base64.encode(state.clientField) + } + }, onCompleted: ({ get2FASecret }) => { setSecret(get2FASecret.secret) setOtpauth(get2FASecret.otpauth) @@ -89,8 +94,7 @@ const Setup2FAState = ({ state, dispatch }) => { ? getUserData({ context: { headers: { - email: state.clientField, - 'Access-Control-Expose-Headers': 'email' + pazuz_operatoridentifier: base64.encode(state.clientField) } } }) @@ -166,7 +170,11 @@ const Setup2FAState = ({ state, dispatch }) => { rememberMe: state.rememberMeField, codeConfirmation: twoFAConfirmation }, - context: { headers: { email: state.clientField } } + context: { + headers: { + pazuz_operatoridentifier: base64.encode(state.clientField) + } + } }) }} buttonClassName={classes.loginButton}> diff --git a/new-lamassu-admin/src/routing/PrivateRoute.js b/new-lamassu-admin/src/routing/PrivateRoute.js index c17ba94f..2861612d 100644 --- a/new-lamassu-admin/src/routing/PrivateRoute.js +++ b/new-lamassu-admin/src/routing/PrivateRoute.js @@ -8,8 +8,6 @@ import { isLoggedIn } from './utils' const PrivateRoute = ({ ...rest }) => { const { userData } = useContext(AppContext) - console.log('isLoggedIn', isLoggedIn(userData)) - return isLoggedIn(userData) ? : } diff --git a/new-lamassu-admin/src/utils/apollo.js b/new-lamassu-admin/src/utils/apollo.js index 594490fb..f4fb1b88 100644 --- a/new-lamassu-admin/src/utils/apollo.js +++ b/new-lamassu-admin/src/utils/apollo.js @@ -36,7 +36,7 @@ const getClient = (history, location, getUserData, setUserData, setRole) => } = context if (headers) { - const role = headers.get('role') + const role = headers.get('lamassu_role') setRole(role) } diff --git a/package-lock.json b/package-lock.json index 1a48e424..faf61a02 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5979,6 +5979,11 @@ } } }, + "base-64": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/base-64/-/base-64-1.0.0.tgz", + "integrity": "sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg==" + }, "base-x": { "version": "3.0.9", "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.9.tgz", diff --git a/package.json b/package.json index ae4647ff..b75034b6 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "apollo-server-express": "2.25.1", "argon2": "0.28.2", "axios": "0.21.1", + "base-64": "^1.0.0", "base-x": "3.0.9", "bchaddrjs": "^0.3.0", "bignumber.js": "9.0.1", From 6f285d30c57dfd6c29c48d8d5976515d0125e16f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Salgado?= Date: Wed, 22 Sep 2021 21:40:12 +0100 Subject: [PATCH 12/18] chore: remove logs --- new-lamassu-admin/src/App.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/new-lamassu-admin/src/App.js b/new-lamassu-admin/src/App.js index 1c0eedae..565d8b47 100644 --- a/new-lamassu-admin/src/App.js +++ b/new-lamassu-admin/src/App.js @@ -154,8 +154,6 @@ const App = () => { const [userData, setUserData] = useState(null) const setRole = role => { - console.log('role', role) - console.log('userData', userData) if (userData && role && userData.role !== role) { setUserData({ ...userData, role }) } From eb184403201b00fecfccdd7964af8d5bfc8725ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Salgado?= Date: Thu, 23 Sep 2021 15:41:33 +0100 Subject: [PATCH 13/18] feat: omit pazuz-related headers when build target is lamassu fix: rename headers to get up to standard feat: pazuz user registration links now contain schema identification param --- .../src/pages/Authentication/Input2FAState.js | 24 +++++-- .../src/pages/Authentication/LoginState.js | 12 +++- .../src/pages/Authentication/Register.js | 19 +++++- .../src/pages/Authentication/Setup2FAState.js | 65 ++++++++++++------- .../UserManagement/modals/CreateUserModal.js | 7 +- 5 files changed, 92 insertions(+), 35 deletions(-) diff --git a/new-lamassu-admin/src/pages/Authentication/Input2FAState.js b/new-lamassu-admin/src/pages/Authentication/Input2FAState.js index b899db26..c872c9a6 100644 --- a/new-lamassu-admin/src/pages/Authentication/Input2FAState.js +++ b/new-lamassu-admin/src/pages/Authentication/Input2FAState.js @@ -2,6 +2,7 @@ import { useMutation, useLazyQuery } from '@apollo/react-hooks' import { makeStyles } from '@material-ui/core/styles' import base64 from 'base-64' import gql from 'graphql-tag' +import * as R from 'ramda' import React, { useContext, useState } from 'react' import { useHistory } from 'react-router-dom' @@ -58,13 +59,18 @@ const Input2FAState = ({ state, dispatch }) => { const [input2FA, { error: mutationError }] = useMutation(INPUT_2FA, { onCompleted: ({ input2FA: success }) => { if (success) { - return getUserData({ + const options = { context: { headers: { - pazuz_operatoridentifier: base64.encode(state.clientField) + 'Pazuz-Operator-Identifier': base64.encode(state.clientField) } } - }) + } + return getUserData( + process.env.REACT_APP_BUILD_TARGET === 'LAMASSU' + ? R.omit(['context'], options) + : options + ) } return setInvalidToken(true) } @@ -86,7 +92,7 @@ const Input2FAState = ({ state, dispatch }) => { return } - input2FA({ + const options = { variables: { username: state.clientField, password: state.passwordField, @@ -95,10 +101,16 @@ const Input2FAState = ({ state, dispatch }) => { }, context: { headers: { - pazuz_operatoridentifier: base64.encode(state.clientField) + 'Pazuz-Operator-Identifier': base64.encode(state.clientField) } } - }) + } + + input2FA( + process.env.REACT_APP_BUILD_TARGET === 'LAMASSU' + ? R.omit(['context'], options) + : options + ) } const getErrorMsg = () => { diff --git a/new-lamassu-admin/src/pages/Authentication/LoginState.js b/new-lamassu-admin/src/pages/Authentication/LoginState.js index 8ef4e848..86470ec3 100644 --- a/new-lamassu-admin/src/pages/Authentication/LoginState.js +++ b/new-lamassu-admin/src/pages/Authentication/LoginState.js @@ -3,6 +3,7 @@ import { makeStyles } from '@material-ui/core/styles' import base64 from 'base-64' import { Field, Form, Formik } from 'formik' import gql from 'graphql-tag' +import * as R from 'ramda' import React from 'react' import * as Yup from 'yup' @@ -50,17 +51,22 @@ const LoginState = ({ state, dispatch }) => { const [login, { error: mutationError }] = useMutation(LOGIN) const submitLogin = async (username, password, rememberMe) => { - const { data: loginResponse } = await login({ + const options = { variables: { username, password }, context: { headers: { - pazuz_operatoridentifier: base64.encode(username) + 'Pazuz-Operator-Identifier': base64.encode(username) } } - }) + } + const { data: loginResponse } = await login( + process.env.REACT_APP_BUILD_TARGET === 'LAMASSU' + ? R.omit(['context'], options) + : options + ) if (!loginResponse.login) return diff --git a/new-lamassu-admin/src/pages/Authentication/Register.js b/new-lamassu-admin/src/pages/Authentication/Register.js index 10d785b1..2ad5b53c 100644 --- a/new-lamassu-admin/src/pages/Authentication/Register.js +++ b/new-lamassu-admin/src/pages/Authentication/Register.js @@ -1,8 +1,10 @@ import { useQuery, useMutation } from '@apollo/react-hooks' import { makeStyles, Grid } from '@material-ui/core' import Paper from '@material-ui/core/Paper' +// import base64 from 'base-64' import { Field, Form, Formik } from 'formik' import gql from 'graphql-tag' +import * as R from 'ramda' import React, { useReducer } from 'react' import { useLocation, useHistory } from 'react-router-dom' import * as Yup from 'yup' @@ -91,10 +93,16 @@ const Register = () => { const classes = useStyles() const history = useHistory() const token = QueryParams().get('t') + const identifier = QueryParams().get('id') ?? null const [state, dispatch] = useReducer(reducer, initialState) - const { error: queryError, loading } = useQuery(VALIDATE_REGISTER_LINK, { + const queryOptions = { + context: { + headers: { + 'Pazuz-Operator-Identifier': identifier + } + }, variables: { token: token }, onCompleted: ({ validateRegisterLink: info }) => { if (!info) { @@ -114,7 +122,14 @@ const Register = () => { dispatch({ type: 'failure' }) - }) + } + + const { error: queryError, loading } = useQuery( + VALIDATE_REGISTER_LINK, + process.env.REACT_APP_BUILD_TARGET === 'LAMASSU' + ? R.omit(['context'], queryOptions) + : queryOptions + ) const [register, { error: mutationError }] = useMutation(REGISTER, { onCompleted: ({ register: success }) => { diff --git a/new-lamassu-admin/src/pages/Authentication/Setup2FAState.js b/new-lamassu-admin/src/pages/Authentication/Setup2FAState.js index 3cbafebc..18df7d6f 100644 --- a/new-lamassu-admin/src/pages/Authentication/Setup2FAState.js +++ b/new-lamassu-admin/src/pages/Authentication/Setup2FAState.js @@ -3,6 +3,7 @@ import { makeStyles } from '@material-ui/core/styles' import base64 from 'base-64' import gql from 'graphql-tag' import QRCode from 'qrcode.react' +import * as R from 'ramda' import React, { useContext, useState } from 'react' import { useHistory } from 'react-router-dom' @@ -68,18 +69,39 @@ const Setup2FAState = ({ state, dispatch }) => { setInvalidToken(false) } - const { error: queryError } = useQuery(GET_2FA_SECRET, { + const queryOptions = { variables: { username: state.clientField, password: state.passwordField }, context: { headers: { - pazuz_operatoridentifier: base64.encode(state.clientField) + 'Pazuz-Operator-Identifier': base64.encode(state.clientField) } }, onCompleted: ({ get2FASecret }) => { setSecret(get2FASecret.secret) setOtpauth(get2FASecret.otpauth) } - }) + } + + const mutationOptions = { + variables: { + username: state.clientField, + password: state.passwordField, + rememberMe: state.rememberMeField, + codeConfirmation: twoFAConfirmation + }, + context: { + headers: { + 'Pazuz-Operator-Identifier': base64.encode(state.clientField) + } + } + } + + const { error: queryError } = useQuery( + GET_2FA_SECRET, + process.env.REACT_APP_BUILD_TARGET === 'LAMASSU' + ? R.omit(['context'], queryOptions) + : queryOptions + ) const [getUserData] = useLazyQuery(GET_USER_DATA, { onCompleted: ({ userData }) => { @@ -90,14 +112,19 @@ const Setup2FAState = ({ state, dispatch }) => { const [setup2FA, { error: mutationError }] = useMutation(SETUP_2FA, { onCompleted: ({ setup2FA: success }) => { + const options = { + context: { + headers: { + 'Pazuz-Operator-Identifier': base64.encode(state.clientField) + } + } + } success - ? getUserData({ - context: { - headers: { - pazuz_operatoridentifier: base64.encode(state.clientField) - } - } - }) + ? getUserData( + process.env.REACT_APP_BUILD_TARGET === 'LAMASSU' + ? R.omit(['context'], options) + : options + ) : setInvalidToken(true) } }) @@ -163,19 +190,11 @@ const Setup2FAState = ({ state, dispatch }) => { setInvalidToken(true) return } - setup2FA({ - variables: { - username: state.clientField, - password: state.passwordField, - rememberMe: state.rememberMeField, - codeConfirmation: twoFAConfirmation - }, - context: { - headers: { - pazuz_operatoridentifier: base64.encode(state.clientField) - } - } - }) + setup2FA( + process.env.REACT_APP_BUILD_TARGET === 'LAMASSU' + ? R.omit(['context'], mutationOptions) + : mutationOptions + ) }} buttonClassName={classes.loginButton}> Done diff --git a/new-lamassu-admin/src/pages/UserManagement/modals/CreateUserModal.js b/new-lamassu-admin/src/pages/UserManagement/modals/CreateUserModal.js index 486d811a..aabeac38 100644 --- a/new-lamassu-admin/src/pages/UserManagement/modals/CreateUserModal.js +++ b/new-lamassu-admin/src/pages/UserManagement/modals/CreateUserModal.js @@ -1,5 +1,6 @@ import { useMutation } from '@apollo/react-hooks' import { makeStyles } from '@material-ui/core/styles' +import base64 from 'base-64' import classnames from 'classnames' import { Field, Form, Formik } from 'formik' import gql from 'graphql-tag' @@ -74,7 +75,11 @@ const CreateUserModal = ({ state, dispatch }) => { const [createUser, { error }] = useMutation(CREATE_USER, { onCompleted: ({ createRegisterToken: token }) => { - setCreateUserURL(urlResolver(`/register?t=${token.token}`)) + const queryParams = + process.env.REACT_APP_BUILD_TARGET === 'LAMASSU' + ? `t=${token.token}` + : `t=${token.token}&id=${base64.encode(usernameField)}` + setCreateUserURL(urlResolver(`/register?${queryParams}`)) } }) From 16513a823848f3b36d44af721eed67d20c807e3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Salgado?= Date: Tue, 5 Oct 2021 14:46:21 +0100 Subject: [PATCH 14/18] feat: add nil UUID to match new machine pairing logic --- lib/new-admin/services/pairing.js | 3 ++- lib/routes.js | 2 +- lib/routes/txRoutes.js | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/new-admin/services/pairing.js b/lib/new-admin/services/pairing.js index ffe2ceed..14effa24 100644 --- a/lib/new-admin/services/pairing.js +++ b/lib/new-admin/services/pairing.js @@ -3,6 +3,7 @@ const pify = require('pify') const readFile = pify(fs.readFile) const crypto = require('crypto') const baseX = require('base-x') +const { NIL } = require('uuid') const options = require('../../options') const db = require('../../db') @@ -19,7 +20,7 @@ function totem (name) { return readFile(caPath) .then(data => { const caHash = crypto.createHash('sha256').update(data).digest() - const token = crypto.randomBytes(32) + const token = Buffer.concat([crypto.randomBytes(32), NIL]) const hexToken = token.toString('hex') const caHexToken = crypto.createHash('sha256').update(hexToken).digest('hex') const buf = Buffer.concat([caHash, token, Buffer.from(options.hostname)]) diff --git a/lib/routes.js b/lib/routes.js index 0ed9c557..22b72bf2 100644 --- a/lib/routes.js +++ b/lib/routes.js @@ -25,7 +25,7 @@ const phoneCodeRoutes = require('./routes/phoneCodeRoutes') const pollingRoutes = require('./routes/pollingRoutes') const stateRoutes = require('./routes/stateRoutes') const termsAndConditionsRoutes = require('./routes/termsAndConditionsRoutes') -const txRoutes = require('./routes/txRoutes') +const { router: txRoutes } = require('./routes/txRoutes') const verifyUserRoutes = require('./routes/verifyUserRoutes') const verifyTxRoutes = require('./routes/verifyTxRoutes') const verifyPromoCodeRoutes = require('./routes/verifyPromoCodeRoutes') diff --git a/lib/routes/txRoutes.js b/lib/routes/txRoutes.js index 218d223f..0bfbfd07 100644 --- a/lib/routes/txRoutes.js +++ b/lib/routes/txRoutes.js @@ -66,4 +66,4 @@ router.post('/', postTx) router.get('/:id', getTx) router.get('/', getPhoneTx) -module.exports = router +module.exports = { postTx, getTx, getPhoneTx, router } From 9260e4a698eeb40012632327186488c179d7936c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Salgado?= Date: Tue, 12 Oct 2021 19:03:41 +0100 Subject: [PATCH 15/18] feat: add apollolink split to query both lamassu and pazuz apollo servers feat: integrate accounting with pazuz related screens --- new-lamassu-admin/package.json | 4 +- .../src/pages/ATMWallet/ATMWallet.js | 108 ++++++-- .../src/pages/Accounting/Accounting.js | 130 ++++----- new-lamassu-admin/src/pages/Assets/Assets.js | 247 +++++++++++------- new-lamassu-admin/src/utils/apollo.js | 18 +- 5 files changed, 314 insertions(+), 193 deletions(-) diff --git a/new-lamassu-admin/package.json b/new-lamassu-admin/package.json index 5b66bd32..6d4a0e43 100644 --- a/new-lamassu-admin/package.json +++ b/new-lamassu-admin/package.json @@ -97,8 +97,8 @@ "storybook": "start-storybook -p 9009 -s public", "postinstall": "patch-package", "build-storybook": "build-storybook -s public", - "start-lamassu": "REACT_APP_BUILD_TARGET=LAMASSU react-scripts start", - "start-pazuz": "REACT_APP_BUILD_TARGET=PAZUZ react-scripts start" + "lamassu": "REACT_APP_BUILD_TARGET=LAMASSU react-scripts start", + "pazuz": "REACT_APP_BUILD_TARGET=PAZUZ react-scripts start" }, "browserslist": { "production": [ diff --git a/new-lamassu-admin/src/pages/ATMWallet/ATMWallet.js b/new-lamassu-admin/src/pages/ATMWallet/ATMWallet.js index cd35530e..79b03c10 100644 --- a/new-lamassu-admin/src/pages/ATMWallet/ATMWallet.js +++ b/new-lamassu-admin/src/pages/ATMWallet/ATMWallet.js @@ -1,9 +1,12 @@ +import { useQuery } from '@apollo/react-hooks' import { Paper } from '@material-ui/core' import { makeStyles } from '@material-ui/core/styles' import classnames from 'classnames' +import gql from 'graphql-tag' import * as R from 'ramda' -import React from 'react' +import React, { useContext } from 'react' +import AppContext from 'src/AppContext' import TitleSection from 'src/components/layout/TitleSection' import { H3, Info2, Label2, Label3, P } from 'src/components/typography' import { ReactComponent as BitcoinLogo } from 'src/styling/logos/icon-bitcoin-colour.svg' @@ -17,6 +20,37 @@ import styles from './ATMWallet.styles' const useStyles = makeStyles(styles) +const GET_OPERATOR_BY_USERNAME = gql` + query operatorByUsername($username: String) { + operatorByUsername(username: $username) { + id + entityId + name + fiatBalances + cryptoBalances + machines + joined + assetValue + preferredFiatCurrency + contactInfo { + name + email + } + fundings { + id + origin + destination + fiatAmount + fiatBalanceAfter + fiatCurrency + created + status + description + } + } + } +` + const CHIPS_PER_ROW = 6 const Assets = ({ balance, wallets, currency }) => { @@ -35,7 +69,7 @@ const Assets = ({ balance, wallets, currency }) => { {balance.toLocaleString('en-US', { maximumFractionDigits: 2 })} - {currency} + {R.toUpper(currency)} @@ -49,7 +83,7 @@ const Assets = ({ balance, wallets, currency }) => { })} - {currency} + {R.toUpper(currency)} @@ -61,7 +95,7 @@ const Assets = ({ balance, wallets, currency }) => { {balance.toLocaleString('en-US', { maximumFractionDigits: 2 })} - {currency} + {R.toUpper(currency)} @@ -129,66 +163,82 @@ const WalletInfoChip = ({ wallet, currency }) => { const ATMWallet = () => { const classes = useStyles({ numberOfChips: CHIPS_PER_ROW }) + const { userData } = useContext(AppContext) + + const { data, loading } = useQuery(GET_OPERATOR_BY_USERNAME, { + context: { clientName: 'pazuz' }, + variables: { username: userData?.username } + }) + + const operatorData = R.path(['operatorByUsername'], data) const wallets = [ { cryptoCode: 'BTC', name: 'Bitcoin', - amount: 2.7, - fiatValue: 81452, + amount: operatorData?.cryptoBalances.xbt ?? 0, + fiatValue: 0, isHedged: true }, { cryptoCode: 'ETH', name: 'Ethereum', - amount: 4.1, - fiatValue: 4924, + amount: operatorData?.cryptoBalances.eth ?? 0, + fiatValue: 0, isHedged: true }, { cryptoCode: 'LTC', name: 'Litecoin', - amount: 15, - fiatValue: 3016, + amount: operatorData?.cryptoBalances.ltc ?? 0, + fiatValue: 0, isHedged: true }, { cryptoCode: 'ZEC', name: 'Z-Cash', - amount: 20, - fiatValue: 2887, + amount: operatorData?.cryptoBalances.zec ?? 0, + fiatValue: 0, isHedged: false }, { cryptoCode: 'BCH', name: 'Bitcoin Cash', - amount: 10.7, - fiatValue: 7074, + amount: operatorData?.cryptoBalances.bch ?? 0, + fiatValue: 0, isHedged: true }, { cryptoCode: 'DASH', name: 'Dash', - amount: 10.7, - fiatValue: 1091, + amount: operatorData?.cryptoBalances.dash ?? 0, + fiatValue: 0, isHedged: false } ] return ( - <> - - -

ATM Wallets

-
- {R.map( - it => ( - - ), - wallets - )} -
- + !loading && ( + <> + + +

ATM Wallets

+
+ {R.map( + it => ( + + ), + wallets + )} +
+ + ) ) } diff --git a/new-lamassu-admin/src/pages/Accounting/Accounting.js b/new-lamassu-admin/src/pages/Accounting/Accounting.js index 1af4dbc7..7834b50a 100644 --- a/new-lamassu-admin/src/pages/Accounting/Accounting.js +++ b/new-lamassu-admin/src/pages/Accounting/Accounting.js @@ -1,7 +1,11 @@ +import { useQuery } from '@apollo/react-hooks' import { makeStyles } from '@material-ui/core/styles' +import gql from 'graphql-tag' import moment from 'moment' -import React from 'react' +import * as R from 'ramda' +import React, { useContext } from 'react' +import AppContext from 'src/AppContext' import { Tooltip } from 'src/components/Tooltip' import TitleSection from 'src/components/layout/TitleSection' import DataTable from 'src/components/tables/DataTable' @@ -9,47 +13,42 @@ import { H4, Info2, P } from 'src/components/typography' import styles from './Accounting.styles' -const mockData = [ - { - operation: 'Hedging summary', - direction: 'in', - extraInfo: 'This is mocked information', - amount: 486, - currency: 'USD', - balanceAfterTx: 10438, - date: '2021-02-22T20:16:12.020Z' - }, - { - operation: 'Funding transaction', - direction: 'in', - amount: 2000, - currency: 'USD', - balanceAfterTx: 9952, - date: '2021-02-22T12:40:32.020Z' - }, - { - operation: 'ZEC hot wallet top up', - direction: 'out', - amount: 1000, - currency: 'USD', - balanceAfterTx: 7952, - date: '2021-02-21T16:30:44.020Z' - }, - { - operation: 'Funding transaction', - direction: 'in', - amount: 8000, - currency: 'USD', - balanceAfterTx: 8952, - date: '2021-02-21T08:16:20.020Z' - } -] - const formatCurrency = amount => amount.toLocaleString('en-US', { maximumFractionDigits: 2 }) const useStyles = makeStyles(styles) +const GET_OPERATOR_BY_USERNAME = gql` + query operatorByUsername($username: String) { + operatorByUsername(username: $username) { + id + entityId + name + fiatBalances + cryptoBalances + machines + joined + assetValue + preferredFiatCurrency + contactInfo { + name + email + } + fundings { + id + origin + destination + fiatAmount + fiatBalanceAfter + fiatCurrency + created + status + description + } + } + } +` + const Assets = ({ balance, hedgingReserve, currency }) => { const classes = useStyles() @@ -62,7 +61,7 @@ const Assets = ({ balance, hedgingReserve, currency }) => { {formatCurrency(balance)} - {currency} + {R.toUpper(currency)} @@ -74,7 +73,7 @@ const Assets = ({ balance, hedgingReserve, currency }) => { {formatCurrency(hedgingReserve)} - {currency} + {R.toUpper(currency)} @@ -86,7 +85,7 @@ const Assets = ({ balance, hedgingReserve, currency }) => { {formatCurrency(balance - hedgingReserve)} - {currency} + {R.toUpper(currency)} @@ -96,6 +95,14 @@ const Assets = ({ balance, hedgingReserve, currency }) => { const Accounting = () => { const classes = useStyles() + const { userData } = useContext(AppContext) + + const { data, loading } = useQuery(GET_OPERATOR_BY_USERNAME, { + context: { clientName: 'pazuz' }, + variables: { username: userData?.username } + }) + + const operatorData = R.path(['operatorByUsername'], data) const elements = [ { @@ -106,7 +113,7 @@ const Accounting = () => { view: it => { return ( - {it.operation} + {it.description} {!!it.extraInfo && (

{it.extraInfo}

@@ -122,18 +129,15 @@ const Accounting = () => { size: 'sm', textAlign: 'right', view: it => - `${ - it.direction === 'in' - ? formatCurrency(it.amount) - : formatCurrency(-it.amount) - } ${it.currency}` + `${formatCurrency(it.fiatAmount)} ${R.toUpper(it.fiatCurrency)}` }, { header: 'Balance after operation', width: 250, size: 'sm', textAlign: 'right', - view: it => `${formatCurrency(it.balanceAfterTx)} ${it.currency}` + view: it => + `${formatCurrency(it.fiatBalanceAfter)} ${R.toUpper(it.fiatCurrency)}` }, { header: 'Date', @@ -152,18 +156,26 @@ const Accounting = () => { ] return ( - <> - - -

Fiat balance history

- - + !loading && ( + <> + + +

Fiat balance history

+ + + ) ) } diff --git a/new-lamassu-admin/src/pages/Assets/Assets.js b/new-lamassu-admin/src/pages/Assets/Assets.js index 2948529e..45f23670 100644 --- a/new-lamassu-admin/src/pages/Assets/Assets.js +++ b/new-lamassu-admin/src/pages/Assets/Assets.js @@ -1,3 +1,4 @@ +import { useQuery } from '@apollo/react-hooks' import Grid from '@material-ui/core/Grid' import Table from '@material-ui/core/Table' import TableBody from '@material-ui/core/TableBody' @@ -6,78 +7,47 @@ import TableContainer from '@material-ui/core/TableContainer' import TableHead from '@material-ui/core/TableHead' import TableRow from '@material-ui/core/TableRow' import { makeStyles, withStyles } from '@material-ui/core/styles' +import gql from 'graphql-tag' import * as R from 'ramda' -import React from 'react' +import React, { useContext } from 'react' +import AppContext from 'src/AppContext' import TitleSection from 'src/components/layout/TitleSection' import { H4, Label2, P, Info2 } from 'src/components/typography' import styles from './Assets.styles' const useStyles = makeStyles(styles) -const mockData = [ - { - id: 'fiatBalance', - display: 'Fiat balance', - amount: 10438, - currency: 'USD', - class: 'Available balance' - }, - { - id: 'hedgingReserve', - display: 'Hedging reserve', - amount: -1486, - currency: 'USD', - class: 'Available balance', - direction: 'out' - }, - { - id: 'hedgedWalletAssets', - display: 'Hedged wallet assets', - amount: 96446, - currency: 'USD', - class: 'Wallet assets', - direction: 'in' - }, - { - id: 'unhedgedWalletAssets', - display: 'Unhedged wallet assets', - amount: 3978, - currency: 'USD', - class: 'Wallet assets', - direction: 'in' +const GET_OPERATOR_BY_USERNAME = gql` + query operatorByUsername($username: String) { + operatorByUsername(username: $username) { + id + entityId + name + fiatBalances + cryptoBalances + machines + joined + assetValue + preferredFiatCurrency + contactInfo { + name + email + } + fundings { + id + origin + destination + fiatAmount + fiatBalanceAfter + fiatCurrency + created + status + description + } + } } -] - -const mockDataTotal = [ - { - id: 'fiatBalance', - display: 'Fiat balance', - amount: 10438, - currency: 'USD' - }, - { - id: 'hedgingReserve', - display: 'Hedging reserve', - amount: -1486, - currency: 'USD', - direction: 'out' - }, - { - id: 'hedgedWalletAssets', - display: 'Market value of hedged wallet assets', - amount: 94980, - currency: 'USD', - direction: 'in' - }, - { - id: 'unhedgedWalletAssets', - display: 'Unhedged wallet assets', - amount: 3978, - currency: 'USD', - direction: 'in' - } -] +` const cellStyling = { borderBottom: '4px solid white', @@ -163,47 +133,126 @@ const formatCurrency = amount => const Assets = () => { const classes = useStyles() + const { userData } = useContext(AppContext) - const filterByClass = x => - R.filter(it => R.path(['class'])(it) === x)(mockData) + const { data, loading } = useQuery(GET_OPERATOR_BY_USERNAME, { + context: { clientName: 'pazuz' }, + variables: { username: userData?.username } + }) + + const operatorData = R.path(['operatorByUsername'], data) + + console.log('operatorData', operatorData) + + const balanceData = [ + { + id: 'fiatBalance', + display: 'Fiat balance', + amount: + operatorData?.fiatBalances[operatorData?.preferredFiatCurrency] ?? 0, + currency: R.toUpper(operatorData?.preferredFiatCurrency ?? ''), + class: 'Available balance' + }, + { + id: 'hedgingReserve', + display: 'Hedging reserve', + amount: + operatorData?.fiatBalances[operatorData?.preferredFiatCurrency] ?? 0, + currency: R.toUpper(operatorData?.preferredFiatCurrency ?? ''), + class: 'Available balance', + direction: 'out' + } + ] + + const walletData = [ + { + id: 'hedgedWalletAssets', + display: 'Hedged wallet assets', + amount: 0, + currency: R.toUpper(operatorData?.preferredFiatCurrency ?? ''), + class: 'Wallet assets', + direction: 'in' + }, + { + id: 'unhedgedWalletAssets', + display: 'Unhedged wallet assets', + amount: 0, + currency: R.toUpper(operatorData?.preferredFiatCurrency ?? ''), + class: 'Wallet assets', + direction: 'in' + } + ] + + const totalData = [ + { + id: 'fiatBalance', + display: 'Fiat balance', + amount: + operatorData?.fiatBalances[operatorData?.preferredFiatCurrency] ?? 0, + currency: R.toUpper(operatorData?.preferredFiatCurrency ?? '') + }, + { + id: 'hedgingReserve', + display: 'Hedging reserve', + amount: 0, + currency: R.toUpper(operatorData?.preferredFiatCurrency ?? ''), + direction: 'out' + }, + { + id: 'hedgedWalletAssets', + display: 'Market value of hedged wallet assets', + amount: 0, + currency: R.toUpper(operatorData?.preferredFiatCurrency ?? ''), + direction: 'in' + }, + { + id: 'unhedgedWalletAssets', + display: 'Unhedged wallet assets', + amount: 0, + currency: R.toUpper(operatorData?.preferredFiatCurrency ?? ''), + direction: 'in' + } + ] return ( - <> - -
- - - -
- -
-
- -
+ !loading && ( + <> + +
+ + + +
+ +
+
+ +
+
+
+ + +
+ +
+
- - -
- -
-
-
- -
- +
+ + ) ) } diff --git a/new-lamassu-admin/src/utils/apollo.js b/new-lamassu-admin/src/utils/apollo.js index f4fb1b88..b04b9b69 100644 --- a/new-lamassu-admin/src/utils/apollo.js +++ b/new-lamassu-admin/src/utils/apollo.js @@ -12,6 +12,9 @@ import AppContext from 'src/AppContext' const URI = process.env.NODE_ENV === 'development' ? 'https://localhost:8070' : '' +const ALT_URI = + process.env.NODE_ENV === 'development' ? 'http://localhost:4001' : '' + const getClient = (history, location, getUserData, setUserData, setRole) => new ApolloClient({ link: ApolloLink.from([ @@ -43,10 +46,17 @@ const getClient = (history, location, getUserData, setUserData, setRole) => return response }) }), - new HttpLink({ - credentials: 'include', - uri: `${URI}/graphql` - }) + ApolloLink.split( + operation => operation.getContext().clientName === 'pazuz', + new HttpLink({ + credentials: 'include', + uri: `${ALT_URI}/graphql` + }), + new HttpLink({ + credentials: 'include', + uri: `${URI}/graphql` + }) + ) ]), cache: new InMemoryCache(), defaultOptions: { From 62ce359a805ca79b782a32d40a2c93e5b7aff10a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Salgado?= Date: Wed, 10 Nov 2021 16:11:07 +0000 Subject: [PATCH 16/18] fix: remove logs --- new-lamassu-admin/src/pages/Assets/Assets.js | 2 -- new-lamassu-admin/src/pages/Authentication/Register.js | 1 - 2 files changed, 3 deletions(-) diff --git a/new-lamassu-admin/src/pages/Assets/Assets.js b/new-lamassu-admin/src/pages/Assets/Assets.js index 45f23670..b549c322 100644 --- a/new-lamassu-admin/src/pages/Assets/Assets.js +++ b/new-lamassu-admin/src/pages/Assets/Assets.js @@ -142,8 +142,6 @@ const Assets = () => { const operatorData = R.path(['operatorByUsername'], data) - console.log('operatorData', operatorData) - const balanceData = [ { id: 'fiatBalance', diff --git a/new-lamassu-admin/src/pages/Authentication/Register.js b/new-lamassu-admin/src/pages/Authentication/Register.js index 2ad5b53c..b07fa4c0 100644 --- a/new-lamassu-admin/src/pages/Authentication/Register.js +++ b/new-lamassu-admin/src/pages/Authentication/Register.js @@ -1,7 +1,6 @@ import { useQuery, useMutation } from '@apollo/react-hooks' import { makeStyles, Grid } from '@material-ui/core' import Paper from '@material-ui/core/Paper' -// import base64 from 'base-64' import { Field, Form, Formik } from 'formik' import gql from 'graphql-tag' import * as R from 'ramda' From b9114b6a333cfa6620eb6bdb57f16e5443c254c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Salgado?= Date: Thu, 11 Nov 2021 00:01:03 +0000 Subject: [PATCH 17/18] fix: use dev database as default for pazuz --- lib/options-loader.js | 6 ++++-- new-lamassu-admin/src/pages/ATMWallet/ATMWallet.js | 11 ++++++----- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/lib/options-loader.js b/lib/options-loader.js index 81061d7c..29ec60ec 100644 --- a/lib/options-loader.js +++ b/lib/options-loader.js @@ -6,6 +6,8 @@ const _ = require('lodash/fp') require('dotenv').config() +const DATABASE = process.env.LAMASSU_DB ?? 'DEV' + const dbMapping = psqlConf => ({ STRESS_TEST: _.replace('lamassu', 'lamassu_stress', psqlConf), RELEASE: _.replace('lamassu', 'lamassu_release', psqlConf), @@ -39,7 +41,7 @@ function load () { opts: JSON.parse(fs.readFileSync(globalConfigPath)) } - config.opts.postgresql = dbMapping(config.opts.postgresql)[process.env.LAMASSU_DB] + config.opts.postgresql = dbMapping(config.opts.postgresql)[DATABASE] return config } catch (_) { @@ -50,7 +52,7 @@ function load () { opts: JSON.parse(fs.readFileSync(homeConfigPath)) } - config.opts.postgresql = dbMapping(config.opts.postgresql)[process.env.LAMASSU_DB] + config.opts.postgresql = dbMapping(config.opts.postgresql)[DATABASE] return config } catch (_) { diff --git a/new-lamassu-admin/src/pages/ATMWallet/ATMWallet.js b/new-lamassu-admin/src/pages/ATMWallet/ATMWallet.js index 79b03c10..9c501948 100644 --- a/new-lamassu-admin/src/pages/ATMWallet/ATMWallet.js +++ b/new-lamassu-admin/src/pages/ATMWallet/ATMWallet.js @@ -51,6 +51,9 @@ const GET_OPERATOR_BY_USERNAME = gql` } ` +const formatCurrency = amount => + amount.toLocaleString('en-US', { maximumFractionDigits: 2 }) + const CHIPS_PER_ROW = 6 const Assets = ({ balance, wallets, currency }) => { @@ -66,7 +69,7 @@ const Assets = ({ balance, wallets, currency }) => {

Available balance

- {balance.toLocaleString('en-US', { maximumFractionDigits: 2 })} + {formatCurrency(balance)} {R.toUpper(currency)} @@ -78,9 +81,7 @@ const Assets = ({ balance, wallets, currency }) => {

Total balance in wallets

- {walletFiatSum().toLocaleString('en-US', { - maximumFractionDigits: 2 - })} + {formatCurrency(walletFiatSum())} {R.toUpper(currency)} @@ -92,7 +93,7 @@ const Assets = ({ balance, wallets, currency }) => {

Total assets

- {balance.toLocaleString('en-US', { maximumFractionDigits: 2 })} + {formatCurrency(balance)} {R.toUpper(currency)} From 2ff9ac5bcd81ffc5551c50cb7703fd2ca112cef4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Salgado?= Date: Tue, 16 Nov 2021 00:12:19 +0000 Subject: [PATCH 18/18] fix: remove lamassu login-related filtering fix: use logger instead of console --- .../middlewares/cleanUserSessions.js | 3 ++- lib/new-admin/middlewares/session.js | 6 +---- .../src/pages/Authentication/Input2FAState.js | 13 ++--------- .../src/pages/Authentication/LoginState.js | 7 +----- .../src/pages/Authentication/Register.js | 5 +---- .../src/pages/Authentication/Setup2FAState.js | 22 +++---------------- .../UserManagement/modals/CreateUserModal.js | 1 + 7 files changed, 11 insertions(+), 46 deletions(-) diff --git a/lib/new-admin/middlewares/cleanUserSessions.js b/lib/new-admin/middlewares/cleanUserSessions.js index 33cb5b04..2f4e10fe 100644 --- a/lib/new-admin/middlewares/cleanUserSessions.js +++ b/lib/new-admin/middlewares/cleanUserSessions.js @@ -1,6 +1,7 @@ const { asyncLocalStorage } = require('../../async-storage') const db = require('../../db') const { USER_SESSIONS_TABLE_NAME } = require('../../constants') +const logger = require('../../logger') const schemaCache = {} @@ -11,7 +12,7 @@ const cleanUserSessions = (cleanInterval) => (req, res, next) => { if (!schema) return next() if (schema && schemaCache.schema + cleanInterval > now) return next() - console.log('Clearing expired sessions for schema', schema) + logger.debug(`Clearing expired sessions for schema ${schema}`) return db.none('DELETE FROM $1^ WHERE expire < to_timestamp($2 / 1000.0)', [USER_SESSIONS_TABLE_NAME, now]) .then(() => { schemaCache.schema = now diff --git a/lib/new-admin/middlewares/session.js b/lib/new-admin/middlewares/session.js index 6c7ea98c..fbdf4465 100644 --- a/lib/new-admin/middlewares/session.js +++ b/lib/new-admin/middlewares/session.js @@ -20,13 +20,9 @@ const getSecret = () => { const hostname = options.hostname -const lamaDb = { - query: (query, values, qrm) => db.query(query, values, qrm, false) -} - router.use('*', session({ store: new PgSession({ - pgPromise: lamaDb, + pgPromise: db, tableName: USER_SESSIONS_TABLE_NAME }), name: 'lamassu_sid', diff --git a/new-lamassu-admin/src/pages/Authentication/Input2FAState.js b/new-lamassu-admin/src/pages/Authentication/Input2FAState.js index c872c9a6..34063f3d 100644 --- a/new-lamassu-admin/src/pages/Authentication/Input2FAState.js +++ b/new-lamassu-admin/src/pages/Authentication/Input2FAState.js @@ -2,7 +2,6 @@ import { useMutation, useLazyQuery } from '@apollo/react-hooks' import { makeStyles } from '@material-ui/core/styles' import base64 from 'base-64' import gql from 'graphql-tag' -import * as R from 'ramda' import React, { useContext, useState } from 'react' import { useHistory } from 'react-router-dom' @@ -66,11 +65,7 @@ const Input2FAState = ({ state, dispatch }) => { } } } - return getUserData( - process.env.REACT_APP_BUILD_TARGET === 'LAMASSU' - ? R.omit(['context'], options) - : options - ) + return getUserData(options) } return setInvalidToken(true) } @@ -106,11 +101,7 @@ const Input2FAState = ({ state, dispatch }) => { } } - input2FA( - process.env.REACT_APP_BUILD_TARGET === 'LAMASSU' - ? R.omit(['context'], options) - : options - ) + input2FA(options) } const getErrorMsg = () => { diff --git a/new-lamassu-admin/src/pages/Authentication/LoginState.js b/new-lamassu-admin/src/pages/Authentication/LoginState.js index 86470ec3..6f83fc26 100644 --- a/new-lamassu-admin/src/pages/Authentication/LoginState.js +++ b/new-lamassu-admin/src/pages/Authentication/LoginState.js @@ -3,7 +3,6 @@ import { makeStyles } from '@material-ui/core/styles' import base64 from 'base-64' import { Field, Form, Formik } from 'formik' import gql from 'graphql-tag' -import * as R from 'ramda' import React from 'react' import * as Yup from 'yup' @@ -62,11 +61,7 @@ const LoginState = ({ state, dispatch }) => { } } } - const { data: loginResponse } = await login( - process.env.REACT_APP_BUILD_TARGET === 'LAMASSU' - ? R.omit(['context'], options) - : options - ) + const { data: loginResponse } = await login(options) if (!loginResponse.login) return diff --git a/new-lamassu-admin/src/pages/Authentication/Register.js b/new-lamassu-admin/src/pages/Authentication/Register.js index b07fa4c0..92dab513 100644 --- a/new-lamassu-admin/src/pages/Authentication/Register.js +++ b/new-lamassu-admin/src/pages/Authentication/Register.js @@ -3,7 +3,6 @@ import { makeStyles, Grid } from '@material-ui/core' import Paper from '@material-ui/core/Paper' import { Field, Form, Formik } from 'formik' import gql from 'graphql-tag' -import * as R from 'ramda' import React, { useReducer } from 'react' import { useLocation, useHistory } from 'react-router-dom' import * as Yup from 'yup' @@ -125,9 +124,7 @@ const Register = () => { const { error: queryError, loading } = useQuery( VALIDATE_REGISTER_LINK, - process.env.REACT_APP_BUILD_TARGET === 'LAMASSU' - ? R.omit(['context'], queryOptions) - : queryOptions + queryOptions ) const [register, { error: mutationError }] = useMutation(REGISTER, { diff --git a/new-lamassu-admin/src/pages/Authentication/Setup2FAState.js b/new-lamassu-admin/src/pages/Authentication/Setup2FAState.js index 18df7d6f..231aae48 100644 --- a/new-lamassu-admin/src/pages/Authentication/Setup2FAState.js +++ b/new-lamassu-admin/src/pages/Authentication/Setup2FAState.js @@ -3,7 +3,6 @@ import { makeStyles } from '@material-ui/core/styles' import base64 from 'base-64' import gql from 'graphql-tag' import QRCode from 'qrcode.react' -import * as R from 'ramda' import React, { useContext, useState } from 'react' import { useHistory } from 'react-router-dom' @@ -96,12 +95,7 @@ const Setup2FAState = ({ state, dispatch }) => { } } - const { error: queryError } = useQuery( - GET_2FA_SECRET, - process.env.REACT_APP_BUILD_TARGET === 'LAMASSU' - ? R.omit(['context'], queryOptions) - : queryOptions - ) + const { error: queryError } = useQuery(GET_2FA_SECRET, queryOptions) const [getUserData] = useLazyQuery(GET_USER_DATA, { onCompleted: ({ userData }) => { @@ -119,13 +113,7 @@ const Setup2FAState = ({ state, dispatch }) => { } } } - success - ? getUserData( - process.env.REACT_APP_BUILD_TARGET === 'LAMASSU' - ? R.omit(['context'], options) - : options - ) - : setInvalidToken(true) + success ? getUserData(options) : setInvalidToken(true) } }) @@ -190,11 +178,7 @@ const Setup2FAState = ({ state, dispatch }) => { setInvalidToken(true) return } - setup2FA( - process.env.REACT_APP_BUILD_TARGET === 'LAMASSU' - ? R.omit(['context'], mutationOptions) - : mutationOptions - ) + setup2FA(mutationOptions) }} buttonClassName={classes.loginButton}> Done diff --git a/new-lamassu-admin/src/pages/UserManagement/modals/CreateUserModal.js b/new-lamassu-admin/src/pages/UserManagement/modals/CreateUserModal.js index aabeac38..b6c21dc3 100644 --- a/new-lamassu-admin/src/pages/UserManagement/modals/CreateUserModal.js +++ b/new-lamassu-admin/src/pages/UserManagement/modals/CreateUserModal.js @@ -76,6 +76,7 @@ const CreateUserModal = ({ state, dispatch }) => { const [createUser, { error }] = useMutation(CREATE_USER, { onCompleted: ({ createRegisterToken: token }) => { const queryParams = + // Pazuz-created register tokens add a field to identify the creator process.env.REACT_APP_BUILD_TARGET === 'LAMASSU' ? `t=${token.token}` : `t=${token.token}&id=${base64.encode(usernameField)}`