From 737672c07ae7c4114fc02c2df502085b2609a1b2 Mon Sep 17 00:00:00 2001 From: Rafael Taranto Date: Fri, 11 Apr 2025 07:39:21 +0100 Subject: [PATCH 1/3] chore: deprecate backwards compatibility code --- lib/cash-in/cash-in-atomic.js | 6 +- lib/compliance.js | 9 +- lib/routes.js | 5 -- lib/routes/customerRoutes.js | 16 +--- lib/routes/phoneCodeRoutes.js | 74 ---------------- lib/routes/pollingRoutes.js | 160 +--------------------------------- 6 files changed, 8 insertions(+), 262 deletions(-) delete mode 100644 lib/routes/phoneCodeRoutes.js diff --git a/lib/cash-in/cash-in-atomic.js b/lib/cash-in/cash-in-atomic.js index 35d2bdbe..4995520e 100644 --- a/lib/cash-in/cash-in-atomic.js +++ b/lib/cash-in/cash-in-atomic.js @@ -40,11 +40,7 @@ function insertNewBills (t, billRows, machineTx) { const bills = pullNewBills(billRows, machineTx) if (_.isEmpty(bills)) return Promise.resolve([]) - const _dbBills = _.map(cashInLow.massage, bills) - // BACKWARDS_COMPATIBILITY 8.1 - // bills before 8.6 don't have destination_unit - const dbBills = _.map(_.defaults({ destination_unit: 'cashbox'}))(_dbBills) - + const dbBills = _.map(cashInLow.massage, bills) const billsByDestination = _.countBy(_.get(['destination_unit']) ,dbBills) const columns = ['id', 'fiat', 'fiat_code', 'crypto_code', 'cash_in_fee', 'cash_in_txs_id', 'device_time', 'destination_unit'] diff --git a/lib/compliance.js b/lib/compliance.js index becf9ee3..ddb27065 100644 --- a/lib/compliance.js +++ b/lib/compliance.js @@ -59,10 +59,7 @@ function matchOfac (deviceId, customer) { }) } -// BACKWARDS_COMPATIBILITY 7.5 -// machines before 7.5 need to test sanctionsActive here -function validateOfac (deviceId, sanctionsActive, customer) { - if (!sanctionsActive) return Promise.resolve(true) +function validateOfac (deviceId, customer) { if (customer.sanctionsOverride === 'blocked') return Promise.resolve(false) if (customer.sanctionsOverride === 'verified') return Promise.resolve(true) @@ -70,8 +67,8 @@ function validateOfac (deviceId, sanctionsActive, customer) { .then(didMatch => !didMatch) } -function validationPatch (deviceId, sanctionsActive, customer) { - return validateOfac(deviceId, sanctionsActive, customer) +function validationPatch (deviceId, customer) { + return validateOfac(deviceId, customer) .then(sanctions => _.isNil(customer.sanctions) || customer.sanctions !== sanctions ? { sanctions } : diff --git a/lib/routes.js b/lib/routes.js index 5631df6d..91302dfd 100644 --- a/lib/routes.js +++ b/lib/routes.js @@ -22,7 +22,6 @@ const logsRoutes = require('./routes/logsRoutes') const pairingRoutes = require('./routes/pairingRoutes') const diagnosticsRoutes = require('./routes/diagnosticsRoutes') const performanceRoutes = require('./routes/performanceRoutes') -const phoneCodeRoutes = require('./routes/phoneCodeRoutes') const pollingRoutes = require('./routes/pollingRoutes') const stateRoutes = require('./routes/stateRoutes') const termsAndConditionsRoutes = require('./routes/termsAndConditionsRoutes') @@ -95,10 +94,6 @@ const loadRoutes = async () => { app.use('/verify_transaction', verifyTxRoutes) app.use('/verify_promo_code', verifyPromoCodeRoutes) - // BACKWARDS_COMPATIBILITY 9.0 - // machines before 9.0 still use the phone_code route - app.use('/phone_code', phoneCodeRoutes) - app.use('/customer', customerRoutes) app.use('/tx', txRoutes) diff --git a/lib/routes/customerRoutes.js b/lib/routes/customerRoutes.js index 1b51b872..8b4ea330 100644 --- a/lib/routes/customerRoutes.js +++ b/lib/routes/customerRoutes.js @@ -1,6 +1,5 @@ const express = require('express') const router = express.Router() -const semver = require('semver') const _ = require('lodash/fp') const { zonedTimeToUtc, utcToZonedTime } = require('date-fns-tz/fp') const { add, intervalToDuration } = require('date-fns/fp') @@ -79,11 +78,7 @@ const createPendingManualComplianceNotifs = (settings, customer, deviceId) => { function updateCustomer (req, res, next) { const id = req.params.id - const machineVersion = req.query.version - const txId = req.query.txId const patch = req.body - const triggers = configManager.getTriggers(req.settings.config) - const compatTriggers = complianceTriggers.getBackwardsCompatibleTriggers(triggers) const deviceId = req.deviceId const settings = req.settings @@ -96,15 +91,8 @@ function updateCustomer (req, res, next) { .catch(next) } - // BACKWARDS_COMPATIBILITY 7.5 - // machines before 7.5 expect customer with sanctions result - const isOlderMachineVersion = !machineVersion || semver.lt(machineVersion, '7.5.0-beta.0') customers.getById(id) - .then(customer => - !customer ? Promise.reject(httpError('Not Found', 404)) : - !isOlderMachineVersion ? {} : - compliance.validationPatch(deviceId, !!compatTriggers.sanctions, _.merge(customer, patch)) - ) + .then(customer => !customer ? Promise.reject(httpError('Not Found', 404)) : {}) .then(_.merge(patch)) .then(newPatch => customers.updatePhotoCard(id, newPatch)) .then(newPatch => customers.updateFrontCamera(id, newPatch)) @@ -135,7 +123,7 @@ function triggerSanctions (req, res, next) { customers.getById(id) .then(customer => { if (!customer) { throw httpError('Not Found', 404) } - return compliance.validationPatch(req.deviceId, true, customer) + return compliance.validationPatch(req.deviceId, customer) .then(patch => customers.update(id, patch)) }) .then(customer => respond(req, res, { customer })) diff --git a/lib/routes/phoneCodeRoutes.js b/lib/routes/phoneCodeRoutes.js deleted file mode 100644 index 25fa3795..00000000 --- a/lib/routes/phoneCodeRoutes.js +++ /dev/null @@ -1,74 +0,0 @@ -const express = require('express') -const router = express.Router() -const semver = require('semver') -const _ = require('lodash/fp') - -const compliance = require('../compliance') -const complianceTriggers = require('../compliance-triggers') -const configManager = require('../new-config-manager') -const { get, add, getById, update } = require('../customers') -const httpError = require('../route-helpers').httpError -const plugins = require('../plugins') -const Tx = require('../tx') -const respond = require('../respond') -const loyalty = require('../loyalty') - -function addOrUpdateCustomer (req) { - const customerData = req.body - const machineVersion = req.query.version - const triggers = configManager.getTriggers(req.settings.config) - const compatTriggers = complianceTriggers.getBackwardsCompatibleTriggers(triggers) - const maxDaysThreshold = complianceTriggers.maxDaysThreshold(triggers) - - return get(customerData.phone) - .then(customer => { - if (customer) return customer - - return add(req.body) - }) - .then(customer => getById(customer.id)) - .then(customer => { - // BACKWARDS_COMPATIBILITY 7.5 - // machines before 7.5 expect customer with sanctions result - const isOlderMachineVersion = !machineVersion || semver.lt(machineVersion, '7.5.0-beta.0') - const shouldRunOfacCompat = !compatTriggers.sanctions && isOlderMachineVersion - if (!shouldRunOfacCompat) return customer - - return compliance.validationPatch(req.deviceId, !!compatTriggers.sanctions, customer) - .then(patch => { - if (_.isEmpty(patch)) return customer - return update(customer.id, patch) - }) - }) - .then(customer => { - return Tx.customerHistory(customer.id, maxDaysThreshold) - .then(result => { - customer.txHistory = result - return customer - }) - }) - .then(customer => { - return loyalty.getCustomerActiveIndividualDiscount(customer.id) - .then(discount => ({ ...customer, discount })) - }) -} - -function getCustomerWithPhoneCode (req, res, next) { - const pi = plugins(req.settings, req.deviceId) - const phone = req.body.phone - - return pi.getPhoneCode(phone) - .then(code => { - return addOrUpdateCustomer(req) - .then(customer => respond(req, res, { code, customer })) - }) - .catch(err => { - if (err.name === 'BadNumberError') throw httpError('Bad number', 401) - throw err - }) - .catch(next) -} - -router.post('/', getCustomerWithPhoneCode) - -module.exports = router diff --git a/lib/routes/pollingRoutes.js b/lib/routes/pollingRoutes.js index 96ce9135..a433f950 100644 --- a/lib/routes/pollingRoutes.js +++ b/lib/routes/pollingRoutes.js @@ -4,166 +4,10 @@ const _ = require('lodash/fp') const router = express.Router() -const complianceTriggers = require('../compliance-triggers') -const configManager = require('../new-config-manager') -const plugins = require('../plugins') -const semver = require('semver') -const state = require('../middlewares/state') const version = require('../../package.json').version -const { batchGetCustomInfoRequest, getCustomInfoRequests } = require('../new-admin/services/customInfoRequests') -const urlsToPing = [ - `us.archive.ubuntu.com`, - `uk.archive.ubuntu.com`, - `za.archive.ubuntu.com`, - `cn.archive.ubuntu.com` -] - -const speedtestFiles = [ - { - url: 'https://github.com/lamassu/speed-test-assets/raw/main/python-defaults_2.7.18-3.tar.gz', - size: 44668 - } -] - -function checkHasLightning (settings) { - return configManager.getWalletSettings('BTC', settings.config).layer2 !== 'no-layer2' -} - -const createTerms = terms => (terms.active && terms.text) ? ({ - active: terms.active, - title: terms.title, - text: nmd(terms.text), - accept: terms.acceptButtonText, - cancel: terms.cancelButtonText -}) : null - -const buildTriggers = (allTriggers) => { - const normalTriggers = [] - const customTriggers = _.filter(o => { - if (_.isEmpty(o.customInfoRequestId) || _.isNil(o.customInfoRequestId)) normalTriggers.push(o) - return !_.isNil(o.customInfoRequestId) && !_.isEmpty(o.customInfoRequestId) - }, allTriggers) - - return _.flow([_.map(_.get('customInfoRequestId')), batchGetCustomInfoRequest])(customTriggers) - .then(res => { - res.forEach((details, index) => { - // make sure we aren't attaching the details to the wrong trigger - if (customTriggers[index].customInfoRequestId !== details.id) return - customTriggers[index] = { ...customTriggers[index], customInfoRequest: details } - }) - return [...normalTriggers, ...customTriggers] - }) -} - -function poll (req, res, next) { - const machineVersion = req.query.version - const machineModel = req.query.model - const deviceId = req.deviceId - const deviceTime = req.deviceTime - 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 - return acc - }, {}, localeConfig.cryptoCurrencies) - const pi = plugins(settings, deviceId) - const hasLightning = checkHasLightning(settings) - - const operatorInfo = configManager.getOperatorInfo(settings.config) - const machineInfo = { deviceId: req.deviceId, deviceName: req.deviceName } - const cashOutConfig = configManager.getCashOut(deviceId, settings.config) - const receipt = configManager.getReceipt(settings.config) - const terms = configManager.getTermsConditions(settings.config) - const enablePaperWalletOnly = configManager.getCompliance(settings.config).enablePaperWalletOnly - - state.pids = _.update(operatorId, _.set(deviceId, { pid, ts: Date.now() }), state.pids) - - // BACKWARDS_COMPATIBILITY 8.1 - // Machines after 8.1 only need the server version from the initial polling request. - if (semver.gte(machineVersion, '8.1.0-beta.0')) - return res.json({ version }) - - return Promise.all([ - pi.recordPing(deviceTime, machineVersion, machineModel), - pi.pollQueries(), - buildTriggers(configManager.getTriggers(settings.config)), - configManager.getTriggersAutomation(getCustomInfoRequests(true), settings.config, true), - ]) - .then(([_pingRes, results, triggers, triggersAutomation]) => { - 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 emptyUnit = pid && state.emptyUnit?.[operatorId]?.[deviceId] === pid - const refillUnit = pid && state.refillUnit?.[operatorId]?.[deviceId] === pid - const langs = localeConfig.languages - - const locale = { - fiatCode: localeConfig.fiatCurrency, - localeInfo: { - primaryLocale: langs[0], - primaryLocales: langs, - country: localeConfig.country - } - } - - const response = { - error: null, - locale, - version, - receiptPrintingActive: receipt.active, - automaticReceiptPrint: receipt.automaticPrint, - smsReceiptActive: receipt.sms, - enablePaperWalletOnly, - twoWayMode: cashOutConfig.active, - zeroConfLimits, - reboot, - shutdown, - restartServices, - emptyUnit, - refillUnit, - hasLightning, - receipt, - operatorInfo, - machineInfo, - triggers, - triggersAutomation, - speedtestFiles, - urlsToPing - } - - // BACKWARDS_COMPATIBILITY 7.6 - // Machines before 7.6 expect a single zeroConfLimit value per machine. - if (!semver.gte(machineVersion, '7.6.0-beta.0')) - response.zeroConfLimit = _.min(_.values(zeroConfLimits)) - - // BACKWARDS_COMPATIBILITY 7.5 - // machines before 7.5 expect old compliance - if (!machineVersion || semver.lt(machineVersion, '7.5.0-beta.0')) { - const compatTriggers = complianceTriggers.getBackwardsCompatibleTriggers(triggers) - response.smsVerificationActive = !!compatTriggers.sms - response.smsVerificationThreshold = compatTriggers.sms - response.idCardDataVerificationActive = !!compatTriggers.idCardData - response.idCardDataVerificationThreshold = compatTriggers.idCardData - response.idCardPhotoVerificationActive = !!compatTriggers.idCardPhoto - response.idCardPhotoVerificationThreshold = compatTriggers.idCardPhoto - response.sanctionsVerificationActive = !!compatTriggers.sanctions - response.sanctionsVerificationThreshold = compatTriggers.sanctions - response.frontCameraVerificationActive = !!compatTriggers.facephoto - response.frontCameraVerificationThreshold = compatTriggers.facephoto - } - - // BACKWARDS_COMPATIBILITY 7.4.9 - // machines before 7.4.9 expect t&c on poll - if (!machineVersion || semver.lt(machineVersion, '7.4.9')) { - response.terms = createTerms(terms) - } - - return res.json(_.assign(response, results)) - }) - .catch(next) +function poll (req, res) { + return res.json({ version }) } router.get('/', poll) From 1830f90c2c0e8f2e8d2b915e99cd48f21506e210 Mon Sep 17 00:00:00 2001 From: Rafael Taranto Date: Fri, 18 Apr 2025 17:20:16 +0100 Subject: [PATCH 2/3] feat: reject requests from incompatible machines --- lib/middlewares/rejectIncompatbleMachines.js | 24 ++++++++++++++++++++ lib/routes.js | 6 +++-- lib/routes/pollingRoutes.js | 15 ------------ package-lock.json | 3 ++- package.json | 2 +- 5 files changed, 31 insertions(+), 19 deletions(-) create mode 100644 lib/middlewares/rejectIncompatbleMachines.js delete mode 100644 lib/routes/pollingRoutes.js diff --git a/lib/middlewares/rejectIncompatbleMachines.js b/lib/middlewares/rejectIncompatbleMachines.js new file mode 100644 index 00000000..f105ea84 --- /dev/null +++ b/lib/middlewares/rejectIncompatbleMachines.js @@ -0,0 +1,24 @@ +const semver = require('semver') +const version = require('../../package.json').version +const logger = require('../logger') + +const rejectIncompatibleMachines = function (req, res, next) { + const machineVersion = req.query.version + const deviceId = req.deviceId + + if (!machineVersion) return next() + + const serverMajor = semver.major(version) + const machineMajor = semver.major(machineVersion) + + if (serverMajor - machineMajor > 1) { + logger.error(`Machine version too old: ${machineVersion} deviceId: ${deviceId}`) + return res.status(400).json({ + error: 'Machine version too old' + }) + } + + next() +} + +module.exports = rejectIncompatibleMachines \ No newline at end of file diff --git a/lib/routes.js b/lib/routes.js index 91302dfd..110f9686 100644 --- a/lib/routes.js +++ b/lib/routes.js @@ -22,7 +22,7 @@ const logsRoutes = require('./routes/logsRoutes') const pairingRoutes = require('./routes/pairingRoutes') const diagnosticsRoutes = require('./routes/diagnosticsRoutes') const performanceRoutes = require('./routes/performanceRoutes') -const pollingRoutes = require('./routes/pollingRoutes') +const rejectIncompatibleMachines = require('./middlewares/rejectIncompatbleMachines') const stateRoutes = require('./routes/stateRoutes') const termsAndConditionsRoutes = require('./routes/termsAndConditionsRoutes') const { router: txRoutes } = require('./routes/txRoutes') @@ -81,7 +81,6 @@ const loadRoutes = async () => { // other app routes app.use('/graphql', recordPing) - app.use('/poll', pollingRoutes) app.use('/terms_conditions', termsAndConditionsRoutes) app.use('/state', stateRoutes) app.use('/cashbox', cashboxRoutes) @@ -103,6 +102,9 @@ const loadRoutes = async () => { app.use('/probe', probeRoutes) + // Not all requests have the machine version on the url + // rejecting poll is enough to render the machine "stuck" + app.use(rejectIncompatibleMachines) await graphQLServer.start() app.use('/graphql', express.json(), diff --git a/lib/routes/pollingRoutes.js b/lib/routes/pollingRoutes.js deleted file mode 100644 index a433f950..00000000 --- a/lib/routes/pollingRoutes.js +++ /dev/null @@ -1,15 +0,0 @@ -const express = require('express') -const nmd = require('nano-markdown') -const _ = require('lodash/fp') - -const router = express.Router() - -const version = require('../../package.json').version - -function poll (req, res) { - return res.json({ version }) -} - -router.get('/', poll) - -module.exports = router diff --git a/package-lock.json b/package-lock.json index c8abac1c..1e0ffa4e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -86,7 +86,7 @@ "promise-sequential": "^1.1.1", "queue-promise": "^2.2.1", "request-promise": "^4.2.6", - "semver": "^7.1.3", + "semver": "^7.7.1", "serve-static": "^1.12.4", "talisman": "^0.20.0", "telnyx": "^1.25.5", @@ -18226,6 +18226,7 @@ "version": "7.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "license": "ISC", "bin": { "semver": "bin/semver.js" }, diff --git a/package.json b/package.json index ca3a7045..8c3c876e 100644 --- a/package.json +++ b/package.json @@ -83,7 +83,7 @@ "promise-sequential": "^1.1.1", "queue-promise": "^2.2.1", "request-promise": "^4.2.6", - "semver": "^7.1.3", + "semver": "^7.7.1", "serve-static": "^1.12.4", "talisman": "^0.20.0", "telnyx": "^1.25.5", From 892f46f64ce347dc7052092e71f9dc3dbdb294f9 Mon Sep 17 00:00:00 2001 From: Rafael Taranto Date: Tue, 22 Apr 2025 13:57:12 +0100 Subject: [PATCH 3/3] fix: reject future machines as well --- lib/middlewares/rejectIncompatbleMachines.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/middlewares/rejectIncompatbleMachines.js b/lib/middlewares/rejectIncompatbleMachines.js index f105ea84..48ecef6f 100644 --- a/lib/middlewares/rejectIncompatbleMachines.js +++ b/lib/middlewares/rejectIncompatbleMachines.js @@ -18,6 +18,13 @@ const rejectIncompatibleMachines = function (req, res, next) { }) } + if (serverMajor < machineMajor) { + logger.error(`Machine version too new: ${machineVersion} deviceId: ${deviceId}`) + return res.status(400).json({ + error: 'Machine version too new' + }) + } + next() }