WIP
This commit is contained in:
parent
becd62a1fb
commit
938eb9ac97
9 changed files with 153 additions and 68 deletions
76
bin/cert-gen.sh
Executable file
76
bin/cert-gen.sh
Executable file
|
|
@ -0,0 +1,76 @@
|
|||
#!/usr/bin/env bash
|
||||
# This is for setting up cryptographic certificates for a development environment
|
||||
set -e
|
||||
|
||||
DOMAIN=localhost
|
||||
|
||||
LOG_FILE=/tmp/cert-gen.log
|
||||
CERT_DIR=$PWD/certs
|
||||
KEY_DIR=$PWD/certs
|
||||
|
||||
CONFIG_DIR=$HOME/.lamassu
|
||||
|
||||
mkdir -p $CERT_DIR
|
||||
mkdir -p $CONFIG_DIR >> $LOG_FILE 2>&1
|
||||
|
||||
echo "Generating seed..."
|
||||
SEEDS_DIR=seeds
|
||||
SEED_FILE=$SEEDS_DIR/seed.txt
|
||||
mkdir -p $SEEDS_DIR >> $LOG_FILE 2>&1
|
||||
SEED=$(openssl rand -hex 32)
|
||||
echo $SEED > $SEED_FILE
|
||||
|
||||
echo "Generating SSL certificates (takes a few seconds)..."
|
||||
|
||||
CA_KEY_PATH=$KEY_DIR/Lamassu_OP_Root_CA.key
|
||||
CA_PATH=$CERT_DIR/Lamassu_OP_Root_CA.pem
|
||||
SERVER_KEY_PATH=$KEY_DIR/Lamassu_OP.key
|
||||
SERVER_CERT_PATH=$CERT_DIR/Lamassu_OP.pem
|
||||
|
||||
openssl genrsa \
|
||||
-out $CA_KEY_PATH \
|
||||
4096 >> $LOG_FILE 2>&1
|
||||
|
||||
openssl req \
|
||||
-x509 \
|
||||
-new \
|
||||
-nodes \
|
||||
-key $CA_KEY_PATH \
|
||||
-days 3560 \
|
||||
-out $CA_PATH \
|
||||
-subj "/C=IS/ST=/L=Reykjavik/O=Lamassu Operator CA/CN=lamassu-operator.is" \
|
||||
>> $LOG_FILE 2>&1
|
||||
|
||||
openssl genrsa \
|
||||
-out $SERVER_KEY_PATH \
|
||||
4096 >> $LOG_FILE 2>&1
|
||||
|
||||
openssl req -new \
|
||||
-key $SERVER_KEY_PATH \
|
||||
-out /tmp/Lamassu_OP.csr.pem \
|
||||
-subj "/C=IS/ST=/L=Reykjavik/O=Lamassu Operator/CN=$DOMAIN" \
|
||||
>> $LOG_FILE 2>&1
|
||||
|
||||
openssl x509 \
|
||||
-req -in /tmp/Lamassu_OP.csr.pem \
|
||||
-CA $CA_PATH \
|
||||
-CAkey $CA_KEY_PATH \
|
||||
-CAcreateserial \
|
||||
-out $SERVER_CERT_PATH \
|
||||
-days 3650 >> $LOG_FILE 2>&1
|
||||
|
||||
rm /tmp/Lamassu_OP.csr.pem
|
||||
|
||||
cat <<EOF > $CONFIG_DIR/lamassu.json
|
||||
{
|
||||
"postgresql": "psql://lamassu:lamassu@localhost/lamassu",
|
||||
"seedPath": "$SEED_FILE",
|
||||
"caPath": "$CA_PATH",
|
||||
"certPath": "$SERVER_CERT_PATH",
|
||||
"keyPath": "$SERVER_KEY_PATH",
|
||||
"hostname": "$DOMAIN",
|
||||
"logLevel": "debug"
|
||||
}
|
||||
EOF
|
||||
|
||||
echo "Done."
|
||||
|
|
@ -4,6 +4,7 @@ const app = require('../lib/app')
|
|||
|
||||
process.on('unhandledRejection', err => {
|
||||
console.log('Unhandled rejection')
|
||||
console.dir(err)
|
||||
console.log(err.stack)
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
const path = require('path')
|
||||
const fs = require('fs')
|
||||
const pify = require('pify')
|
||||
const readFile = pify(fs.readFile)
|
||||
const db = require('./db')
|
||||
|
||||
const CA_PATH = path.resolve(__dirname, '..', 'certs', 'root-ca.crt.pem')
|
||||
const options = require('./options')
|
||||
const logger = require('./logger')
|
||||
|
||||
function pullToken (token) {
|
||||
const sql = `delete from pairing_tokens
|
||||
|
|
@ -14,26 +13,31 @@ function pullToken (token) {
|
|||
}
|
||||
|
||||
function pair (token, deviceId) {
|
||||
pullToken(token)
|
||||
return pullToken(token)
|
||||
.then(r => {
|
||||
if (r.expired) return false
|
||||
|
||||
const insertSql = `insert into devices (device_id, name) values ($1, $2)
|
||||
on conflict (device_id)
|
||||
do update set name=$1, paired=TRUE, display=TRUE`
|
||||
do update set name=$2, paired=TRUE, display=TRUE`
|
||||
|
||||
return db.none(insertSql, [deviceId, r.name])
|
||||
.then(() => true)
|
||||
})
|
||||
.catch(() => false)
|
||||
.catch(err => {
|
||||
logger.debug(err)
|
||||
return false
|
||||
})
|
||||
}
|
||||
|
||||
function authorizeCaDownload (caToken) {
|
||||
return pullToken(caToken)
|
||||
}
|
||||
.then(r => {
|
||||
if (r.expired) throw new Error('Expired')
|
||||
|
||||
function ca () {
|
||||
return readFile(CA_PATH)
|
||||
const caPath = options.caPath
|
||||
return readFile(caPath, {encoding: 'utf8'})
|
||||
})
|
||||
}
|
||||
|
||||
function isPaired (deviceId) {
|
||||
|
|
@ -43,4 +47,4 @@ function isPaired (deviceId) {
|
|||
.then(() => true)
|
||||
}
|
||||
|
||||
module.exports = {pair, authorizeCaDownload, ca, isPaired}
|
||||
module.exports = {pair, authorizeCaDownload, isPaired}
|
||||
|
|
|
|||
|
|
@ -171,16 +171,11 @@ exports.configure = function configure (config) {
|
|||
|
||||
const cryptoCodes = getCryptoCodes()
|
||||
|
||||
console.log('DEBUG30')
|
||||
|
||||
return configManager.loadAccounts()
|
||||
.then(accounts => {
|
||||
cryptoCodes.forEach(cryptoCode => {
|
||||
console.log('DEBUG31')
|
||||
const cryptoScopedConfig = configManager.cryptoScoped(cryptoCode, cachedConfig)
|
||||
|
||||
console.log('DEBUG31.1')
|
||||
|
||||
// TICKER [required] configure (or load)
|
||||
loadOrConfigPlugin(
|
||||
tickerPlugins[cryptoCode],
|
||||
|
|
@ -195,8 +190,6 @@ exports.configure = function configure (config) {
|
|||
}
|
||||
)
|
||||
|
||||
console.log('DEBUG31.2')
|
||||
|
||||
// Give each crypto a different derived seed so as not to allow any
|
||||
// plugin to spend another plugin's funds
|
||||
const cryptoSeed = hkdf.derive(cryptoCode, 32)
|
||||
|
|
@ -298,7 +291,6 @@ exports.pollQueries = function pollQueries (deviceId) {
|
|||
function _sendCoins (toAddress, cryptoAtoms, cryptoCode) {
|
||||
return new Promise((resolve, reject) => {
|
||||
_sendCoinsCb(toAddress, cryptoAtoms, cryptoCode, (err, txHash) => {
|
||||
console.log('DEBUG12: %j, %j', err, txHash)
|
||||
if (err) return reject(err)
|
||||
return resolve(txHash)
|
||||
})
|
||||
|
|
@ -308,7 +300,6 @@ function _sendCoins (toAddress, cryptoAtoms, cryptoCode) {
|
|||
function _sendCoinsCb (toAddress, cryptoAtoms, cryptoCode, cb) {
|
||||
const walletPlugin = walletPlugins[cryptoCode]
|
||||
const transactionFee = null
|
||||
logger.debug('Sending coins [%s] to: %s', cryptoCode, toAddress)
|
||||
|
||||
if (cryptoCode === 'BTC') {
|
||||
walletPlugin.sendBitcoins(toAddress, cryptoAtoms.truncated().toNumber(), transactionFee, cb)
|
||||
|
|
@ -320,12 +311,9 @@ function _sendCoinsCb (toAddress, cryptoAtoms, cryptoCode, cb) {
|
|||
// NOTE: This will fail if we have already sent coins because there will be
|
||||
// a db unique db record in the table already.
|
||||
function executeTx (deviceId, tx) {
|
||||
console.log('DEBUG16: %j', tx)
|
||||
return db.addOutgoingTx(deviceId, tx)
|
||||
.then(() => _sendCoins(tx.toAddress, tx.cryptoAtoms, tx.cryptoCode))
|
||||
.then(txHash => {
|
||||
console.log('DEBUG13: %j', txHash)
|
||||
|
||||
const fee = null // Need to fill this out in plugins
|
||||
const toSend = {cryptoAtoms: tx.cryptoAtoms, fiat: tx.fiat}
|
||||
|
||||
|
|
@ -412,7 +400,6 @@ exports.cashOut = function cashOut (deviceId, tx) {
|
|||
}
|
||||
|
||||
exports.dispenseAck = function (deviceId, tx) {
|
||||
console.log('DEBUG23: %j', tx)
|
||||
const config = getConfig(deviceId)
|
||||
const cartridges = [ config.currencies.topCashOutDenomination,
|
||||
config.currencies.bottomCashOutDenomination ]
|
||||
|
|
@ -767,7 +754,6 @@ function checkNotification () {
|
|||
return sendMessage(rec)
|
||||
})
|
||||
.then(results => {
|
||||
console.log('DEBUG25')
|
||||
if (results && results.length > 0) logger.debug('Successfully sent alerts')
|
||||
})
|
||||
.catch(err => {
|
||||
|
|
|
|||
|
|
@ -29,7 +29,6 @@ function getInsertQuery (tableName, fields) {
|
|||
|
||||
// logs inputted bill and overall tx status (if available)
|
||||
exports.recordBill = function recordBill (deviceId, rec) {
|
||||
console.log('DEBUG10: %j', rec)
|
||||
const fields = [
|
||||
'id',
|
||||
'device_id',
|
||||
|
|
@ -54,8 +53,6 @@ exports.recordBill = function recordBill (deviceId, rec) {
|
|||
rec.fiat
|
||||
]
|
||||
|
||||
console.log('DEBUG11: %j', values)
|
||||
|
||||
return db.none(getInsertQuery('bills', fields), values)
|
||||
.catch(err => {
|
||||
if (isUniqueViolation(err)) return logger.warn('Attempt to report bill twice')
|
||||
|
|
@ -127,24 +124,19 @@ exports.addInitialIncoming = function addInitialIncoming (deviceId, tx) {
|
|||
function insertDispense (deviceId, tx, cartridges) {
|
||||
const fields = [
|
||||
'device_id', 'cash_out_txs_id',
|
||||
'dispense1', 'reject1', 'count1',
|
||||
'dispense2', 'reject2', 'count2',
|
||||
'refill', 'error'
|
||||
'dispense1', 'reject1',
|
||||
'dispense2', 'reject2', 'error'
|
||||
]
|
||||
|
||||
const sql = getInsertQuery('dispenses', fields)
|
||||
|
||||
console.log('DEBUG24: %j', tx)
|
||||
|
||||
const dispense1 = tx.bills[0].actualDispense
|
||||
const dispense2 = tx.bills[1].actualDispense
|
||||
const reject1 = tx.bills[0].rejected
|
||||
const reject2 = tx.bills[1].rejected
|
||||
const count1 = cartridges[0].count
|
||||
const count2 = cartridges[1].count
|
||||
const values = [
|
||||
deviceId, tx.id,
|
||||
dispense1, reject1, count1, dispense2, reject2, count2,
|
||||
dispense1, reject1, dispense2, reject2,
|
||||
false, tx.error
|
||||
]
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
'use strict'
|
||||
|
||||
const morgan = require('morgan')
|
||||
const helmet = require('helmet')
|
||||
const bodyParser = require('body-parser')
|
||||
const BigNumber = require('bignumber.js')
|
||||
|
|
@ -76,20 +77,13 @@ function poll (req, res) {
|
|||
|
||||
const config = plugins.getConfig(deviceId)
|
||||
|
||||
console.log('DEBUG30')
|
||||
|
||||
plugins.pollQueries(deviceId)
|
||||
.then(results => {
|
||||
console.log('DEBUG31')
|
||||
|
||||
const cartridges = results.cartridges
|
||||
|
||||
const reboot = pid && reboots[deviceId] && reboots[deviceId] === pid
|
||||
console.log('DEBUG2: %j, %s, %s', reboots, reboot, pid)
|
||||
const langs = config.languages.machineLanguages
|
||||
|
||||
console.log('DEBUG33.1')
|
||||
|
||||
const locale = {
|
||||
currency: config.currencies.fiatCurrency,
|
||||
localeInfo: {
|
||||
|
|
@ -126,7 +120,6 @@ function poll (req, res) {
|
|||
}
|
||||
|
||||
function trade (req, res, next) {
|
||||
console.log('DEBUG24')
|
||||
const tx = req.body
|
||||
tx.cryptoAtoms = new BigNumber(tx.cryptoAtoms)
|
||||
|
||||
|
|
@ -183,22 +176,20 @@ function ca (req, res) {
|
|||
const token = req.query.token
|
||||
|
||||
return pairing.authorizeCaDownload(token)
|
||||
.then(valid => {
|
||||
if (valid) return res.json({ca: pairing.ca()})
|
||||
|
||||
return res.status(408).end()
|
||||
})
|
||||
.then(ca => res.json({ca}))
|
||||
.catch(() => res.status(408).end())
|
||||
}
|
||||
|
||||
function pair (req, res) {
|
||||
function pair (req, res, next) {
|
||||
const token = req.query.token
|
||||
const deviceId = getDeviceId(req)
|
||||
|
||||
return pairing.pair(token, deviceId)
|
||||
.then(valid => {
|
||||
if (valid) return cacheAndRespond(req, res)
|
||||
if (valid) return res.end()
|
||||
throw httpError('Pairing failed')
|
||||
})
|
||||
.catch(next)
|
||||
}
|
||||
|
||||
function phoneCode (req, res) {
|
||||
|
|
@ -233,20 +224,17 @@ function registerRedeem (req, res) {
|
|||
.then(() => cacheAndRespond(req, res))
|
||||
}
|
||||
|
||||
function waitForDispense (req, res) {
|
||||
function waitForDispense (req, res, next) {
|
||||
logger.debug('waitForDispense')
|
||||
return plugins.fetchTx(req.params.txId)
|
||||
.then(tx => {
|
||||
logger.debug('tx fetched')
|
||||
logger.debug(tx)
|
||||
if (!tx) return res.sendStatus(404)
|
||||
if (tx.status === req.query.status) return res.sendStatus(304)
|
||||
res.json({tx})
|
||||
})
|
||||
.catch(err => {
|
||||
logger.error(err)
|
||||
res.sendStatus(500)
|
||||
})
|
||||
.then(tx => {
|
||||
logger.debug('tx fetched')
|
||||
logger.debug(tx)
|
||||
if (!tx) return res.sendStatus(404)
|
||||
if (tx.status === req.query.status) return res.sendStatus(304)
|
||||
res.json({tx})
|
||||
})
|
||||
.catch(next)
|
||||
}
|
||||
|
||||
function dispense (req, res) {
|
||||
|
|
@ -261,12 +249,12 @@ function isUniqueViolation (err) {
|
|||
}
|
||||
|
||||
function cacheAction (req, res, next) {
|
||||
console.log('DEBUG22: %s', req.path)
|
||||
const requestId = req.headers['request-id']
|
||||
if (!requestId) return next()
|
||||
|
||||
const sql = `insert into idempotents (request_id, device_id, body, status, pending)
|
||||
values ($1, $2, $3, $4, $5)`
|
||||
|
||||
const requestId = req.headers['request-id']
|
||||
const deviceId = getDeviceId(req)
|
||||
|
||||
db.none(sql, [requestId, deviceId, {}, 204, true])
|
||||
|
|
@ -284,21 +272,25 @@ function cacheAction (req, res, next) {
|
|||
}
|
||||
|
||||
function updateCachedAction (req, body, status) {
|
||||
const requestId = req.headers['request-id']
|
||||
if (!requestId) return Promise.resolve()
|
||||
|
||||
const sql = `update idempotents set body=$1, status=$2, pending=$3
|
||||
where request_id=$4 and device_id=$5 and pending=$6`
|
||||
|
||||
const requestId = req.headers['request-id']
|
||||
const deviceId = getDeviceId(req)
|
||||
|
||||
return db.none(sql, [body, status, false, requestId, deviceId, true])
|
||||
}
|
||||
|
||||
function postErrorHandler (err, req, res, next) {
|
||||
function errorHandler (err, req, res, next) {
|
||||
const statusCode = err.code || 500
|
||||
const json = {error: err.message}
|
||||
|
||||
logger.debug(err)
|
||||
|
||||
return updateCachedAction(req, json, statusCode)
|
||||
.then(() => res.status(statusCode).json({}))
|
||||
.then(() => res.status(statusCode).json(json))
|
||||
}
|
||||
|
||||
function cacheAndRespond (req, res, _body, _status) {
|
||||
|
|
@ -361,6 +353,7 @@ function init (opts) {
|
|||
? (req, res, next) => next()
|
||||
: authorize
|
||||
|
||||
app.use(morgan('dev'))
|
||||
app.use(helmet())
|
||||
app.use(bodyParser.json())
|
||||
app.use(filterOldRequests)
|
||||
|
|
@ -388,7 +381,7 @@ function init (opts) {
|
|||
app.get('/await_dispense/:txId', authMiddleware, waitForDispense)
|
||||
app.post('/dispense', authMiddleware, dispense)
|
||||
|
||||
app.post('*', postErrorHandler)
|
||||
app.use('*', errorHandler)
|
||||
|
||||
localApp.get('/pid', (req, res) => {
|
||||
const deviceId = req.query.device_id
|
||||
|
|
|
|||
14
migrations/019-remove-dispense-counts.js
Normal file
14
migrations/019-remove-dispense-counts.js
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
var db = require('./db')
|
||||
|
||||
exports.up = function (next) {
|
||||
var sql = [
|
||||
'alter table dispenses drop column count1',
|
||||
'alter table dispenses drop column count2',
|
||||
'alter table dispenses drop column refill'
|
||||
]
|
||||
db.multi(sql, next)
|
||||
}
|
||||
|
||||
exports.down = function (next) {
|
||||
next()
|
||||
}
|
||||
|
|
@ -27,6 +27,7 @@
|
|||
"lamassu-twilio": "^1.1.1",
|
||||
"migrate": "^0.2.2",
|
||||
"minimist": "^1.2.0",
|
||||
"morgan": "^1.7.0",
|
||||
"node-hkdf-sync": "^1.0.0",
|
||||
"numeral": "^1.5.3",
|
||||
"pg": "^6.1.0",
|
||||
|
|
|
|||
18
yarn.lock
18
yarn.lock
|
|
@ -86,6 +86,10 @@ base64url@^2.0.0, base64url@2.0.0:
|
|||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/base64url/-/base64url-2.0.0.tgz#eac16e03ea1438eff9423d69baa36262ed1f70bb"
|
||||
|
||||
basic-auth@~1.0.3:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/basic-auth/-/basic-auth-1.0.4.tgz#030935b01de7c9b94a824b29f3fccb750d3a5290"
|
||||
|
||||
bcrypt-pbkdf@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.0.tgz#3ca76b85241c7170bf7d9703e7b9aa74630040d4"
|
||||
|
|
@ -969,6 +973,16 @@ mkdirp@^0.5.0:
|
|||
dependencies:
|
||||
minimist "0.0.8"
|
||||
|
||||
morgan:
|
||||
version "1.7.0"
|
||||
resolved "https://registry.yarnpkg.com/morgan/-/morgan-1.7.0.tgz#eb10ca8e50d1abe0f8d3dad5c0201d052d981c62"
|
||||
dependencies:
|
||||
basic-auth "~1.0.3"
|
||||
debug "~2.2.0"
|
||||
depd "~1.1.0"
|
||||
on-finished "~2.3.0"
|
||||
on-headers "~1.0.1"
|
||||
|
||||
ms@^0.7.1:
|
||||
version "0.7.2"
|
||||
resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.2.tgz#ae25cf2512b3885a1d95d7f037868d8431124765"
|
||||
|
|
@ -1040,6 +1054,10 @@ on-finished@~2.3.0:
|
|||
dependencies:
|
||||
ee-first "1.1.1"
|
||||
|
||||
on-headers@~1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.1.tgz#928f5d0f470d49342651ea6794b0857c100693f7"
|
||||
|
||||
optimist@^0.6.1:
|
||||
version "0.6.1"
|
||||
resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue