From 124e35d153ee8081e9c1695bdab851db29eb0941 Mon Sep 17 00:00:00 2001 From: Josh Harvey Date: Tue, 20 Dec 2016 14:08:07 +0200 Subject: [PATCH] validate min/max values on save --- bin/lamassu-admin-server | 5 +-- lamassu-schema.json | 2 +- lib/admin/accounts.js | 6 ++-- lib/admin/config.js | 68 ++++++++++++++++++++++++++++++---------- public/elm.js | 8 ++--- 5 files changed, 61 insertions(+), 28 deletions(-) diff --git a/bin/lamassu-admin-server b/bin/lamassu-admin-server index 513a5dfa..fd8ae01f 100755 --- a/bin/lamassu-admin-server +++ b/bin/lamassu-admin-server @@ -87,10 +87,11 @@ app.post('/api/account', (req, res) => { app.get('/api/config/:config', (req, res) => config.fetchConfigGroup(req.params.config).then(c => res.json(c))) -app.post('/api/config', (req, res) => { +app.post('/api/config', (req, res, next) => { config.saveConfigGroup(req.body) .then(c => res.json(c)) .then(() => dbNotify()) + .catch(next) }) app.get('/api/accounts/account/:account', (req, res) => { @@ -111,7 +112,7 @@ app.post('/api/machines', (req, res) => { }) app.get('/api/status', (req, res, next) => { - return Promise.all([server.status(), config.validateConfig()]) + return Promise.all([server.status(), config.validateCurrentConfig()]) .then(([serverStatus, invalidConfigGroups]) => res.send({ server: serverStatus, invalidConfigGroups diff --git a/lamassu-schema.json b/lamassu-schema.json index 308fff3c..77eef681 100644 --- a/lamassu-schema.json +++ b/lamassu-schema.json @@ -101,7 +101,7 @@ }, { "code": "min", - "min": 0 + "min": 10 } ] }, diff --git a/lib/admin/accounts.js b/lib/admin/accounts.js index 948776c5..374478e4 100644 --- a/lib/admin/accounts.js +++ b/lib/admin/accounts.js @@ -39,10 +39,8 @@ function selectedAccounts () { const mapSchema = code => schemas[code] return config.fetchConfig() - .then(data => { - if (!data) return [] - - const accountCodes = _.uniq(data.config.map(mapAccount) + .then(conf => { + const accountCodes = _.uniq(conf.map(mapAccount) .filter(_.identity)) return _.sortBy(_.get('display'), accountCodes.map(mapSchema) diff --git a/lib/admin/config.js b/lib/admin/config.js index 5376d4de..9e174c33 100644 --- a/lib/admin/config.js +++ b/lib/admin/config.js @@ -24,7 +24,7 @@ function fetchConfig () { order by id desc limit 1` return db.oneOrNone(sql, ['config']) - .then(row => row && row.data) + .then(row => row ? row.data.config : []) } function allScopes (cryptoScopes, machineScopes) { @@ -110,10 +110,43 @@ function getField (schema, group, fieldCode) { const fetchMachines = () => machines.getMachines() .then(machineList => machineList.map(r => r.deviceId)) -function validateConfig () { - return Promise.all([fetchSchema(), fetchConfig(), fetchMachines()]) - .then(([schema, configRec, machineList]) => { - const config = configRec ? configRec.config : [] +function validateFieldParameter (value, validator) { + switch (validator.code) { + case 'required': + return true // We don't validate this here + case 'min': + return value >= validator.min + case 'max': + return value <= validator.max + default: + throw new Error('Unknown validation type: ' + validator.code) + } +} + +// Validates specific field properties other than required property +function enforceValidConfigParameters (fieldInstances) { + return fetchSchema() + .then(schema => { + const pickField = fieldCode => schema.fields.find(r => r.code === fieldCode) + + return fieldInstances.every(fieldInstance => { + const fieldCode = fieldInstance.fieldLocator.code + const field = pickField(fieldCode) + const fieldValue = fieldInstance.fieldValue + + const isValid = field.fieldValidation + .every(validator => validateFieldParameter(fieldValue.value, validator)) + + if (isValid) return true + + throw new Error('Invalid config value') + }) + }) +} + +function validateConfig (config) { + return Promise.all([fetchSchema(), fetchMachines()]) + .then(([schema, machineList]) => { const cryptos = getCryptos(config, machineList) return schema.groups.filter(group => { return group.fields.some(fieldCode => { @@ -128,11 +161,15 @@ function validateConfig () { .then(arr => arr.map(r => r.code)) } +function validateCurrentConfig () { + return fetchConfig() + .then(validateConfig) +} + function fetchConfigGroup (code) { const fieldLocatorCodeEq = R.pathEq(['fieldLocator', 'code']) return Promise.all([fetchSchema(), fetchData(), fetchConfig(), fetchMachines()]) .then(([schema, data, config, machineList]) => { - const configValues = config ? config.config : [] const groupSchema = schema.groups.find(r => r.code === code) if (!groupSchema) throw new Error('No such group schema: ' + code) @@ -149,7 +186,7 @@ function fetchConfigGroup (code) { const configFields = R.uniq(R.flatten(candidateFields)).filter(R.identity) const values = configFields - .reduce((acc, configField) => acc.concat(configValues.filter(fieldLocatorCodeEq(configField))), []) + .reduce((acc, configField) => acc.concat(config.filter(fieldLocatorCodeEq(configField))), []) groupSchema.fields = undefined groupSchema.entries = schemaFields @@ -157,7 +194,7 @@ function fetchConfigGroup (code) { return { schema: groupSchema, values: values, - selectedCryptos: getCryptos(configValues, machineList), + selectedCryptos: getCryptos(config, machineList), data: data } }) @@ -216,17 +253,15 @@ function fetchData () { function dbSaveConfig (config) { const sql = 'insert into user_config (type, data) values ($1, $2)' - return db.none(sql, ['config', config]) + return db.none(sql, ['config', {config}]) } function saveConfigGroup (results) { if (results.values.length === 0) return fetchConfigGroup(results.groupCode) - return fetchConfig() - .then(config => { - if (!config) config = {config: []} - const oldValues = config.config - + return enforceValidConfigParameters(results.values) + .then(fetchConfig) + .then(oldValues => { results.values.forEach(newValue => { const oldValueIndex = oldValues .findIndex(old => old.fieldLocator.code === newValue.fieldLocator.code && @@ -251,15 +286,14 @@ function saveConfigGroup (results) { if (!R.isNil(newValue.fieldValue)) oldValues.push(newValue) }) - return dbSaveConfig(config) + return dbSaveConfig(oldValues) .then(() => fetchConfigGroup(results.groupCode)) }) - .catch(e => console.error(e.stack)) } module.exports = { fetchConfigGroup, saveConfigGroup, - validateConfig, + validateCurrentConfig, fetchConfig } diff --git a/public/elm.js b/public/elm.js index 0c653e5c..c7e22369 100644 --- a/public/elm.js +++ b/public/elm.js @@ -25386,9 +25386,9 @@ var _user$project$Config$validateMax = F2( case 'FieldPercentageValue': return _elm_lang$core$Native_Utils.cmp( _elm_lang$core$Basics$floor(_p6._0), - max) < 0; + max) < 1; case 'FieldIntegerValue': - return _elm_lang$core$Native_Utils.cmp(_p6._0, max) < 0; + return _elm_lang$core$Native_Utils.cmp(_p6._0, max) < 1; default: return true; } @@ -25400,9 +25400,9 @@ var _user$project$Config$validateMin = F2( case 'FieldPercentageValue': return _elm_lang$core$Native_Utils.cmp( _elm_lang$core$Basics$ceiling(_p7._0), - min) > 0; + min) > -1; case 'FieldIntegerValue': - return _elm_lang$core$Native_Utils.cmp(_p7._0, min) > 0; + return _elm_lang$core$Native_Utils.cmp(_p7._0, min) > -1; default: return true; }