Browse Source

Fix database query built from user-controlled sources

Add celebrate middleware to PUT/POST/DELETE
main
Haris Razis 4 years ago
parent
commit
9068afa466
  1. 3
      server/index.js
  2. 153
      server/package-lock.json
  3. 1
      server/package.json
  4. 40
      server/routes/athletes.js
  5. 13
      server/routes/auth.js
  6. 8
      server/routes/data.js
  7. 9
      server/routes/user.js
  8. 37
      server/schemas/joi.js
  9. 3
      web/src/store/modules/athletes.ts
  10. 16
      web/src/store/modules/user.ts

3
server/index.js

@ -27,8 +27,9 @@ app.use(rateLimit({
max: 100
}));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({
extended: true
extended: false
}));
app.use(

153
server/package-lock.json

@ -11,6 +11,7 @@
"@influxdata/influxdb-client": "^1.9.0",
"bcrypt": "^5.0.0",
"body-parser": "^1.19.0",
"celebrate": "^13.0.4",
"chalk": "^4.1.0",
"cookie-session": "^1.4.0",
"cors": "^2.8.5",
@ -28,11 +29,42 @@
"socket.io-redis": "^6.0.1"
}
},
"node_modules/@hapi/hoek": {
"version": "9.1.1",
"resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.1.1.tgz",
"integrity": "sha512-CAEbWH7OIur6jEOzaai83jq3FmKmv4PmX1JYfs9IrYcGEVI/lyL1EXJGCj7eFVJ0bg5QR8LMxBlEtA+xKiLpFw=="
},
"node_modules/@hapi/topo": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.0.0.tgz",
"integrity": "sha512-tFJlT47db0kMqVm3H4nQYgn6Pwg10GTZHb1pwmSiv1K4ks6drQOtfEF5ZnPjkvC+y4/bUPHK+bc87QvLcL+WMw==",
"dependencies": {
"@hapi/hoek": "^9.0.0"
}
},
"node_modules/@influxdata/influxdb-client": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/@influxdata/influxdb-client/-/influxdb-client-1.9.0.tgz",
"integrity": "sha512-wJ+qfGukxMxpDE5d2XHiOspKheWTb1PdPOd2eD3MVuh8hjr6fqyJ3zuacTp/WVubeyppPhM4vYcDjkKCg1OEhA=="
},
"node_modules/@sideway/address": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.0.tgz",
"integrity": "sha512-wAH/JYRXeIFQRsxerIuLjgUu2Xszam+O5xKeatJ4oudShOOirfmsQ1D6LL54XOU2tizpCYku+s1wmU0SYdpoSA==",
"dependencies": {
"@hapi/hoek": "^9.0.0"
}
},
"node_modules/@sideway/formula": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.0.tgz",
"integrity": "sha512-vHe7wZ4NOXVfkoRb8T5otiENVlT7a3IAiw7H5M2+GO+9CDgcVUUsX1zalAztCmwyOr2RUTGJdgB+ZvSVqmdHmg=="
},
"node_modules/@sideway/pinpoint": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz",
"integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ=="
},
"node_modules/@sindresorhus/is": {
"version": "0.14.0",
"resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz",
@ -444,6 +476,16 @@
"node": ">=6"
}
},
"node_modules/celebrate": {
"version": "13.0.4",
"resolved": "https://registry.npmjs.org/celebrate/-/celebrate-13.0.4.tgz",
"integrity": "sha512-gUtAjEtFyY9PvuuQJq1uyuF46gLetVZzyUKXBDBqqvgzCjTSfwXP8L+WcGt1NrLQvUxXdlzhFolW2Bt9DDEV+g==",
"dependencies": {
"escape-html": "1.0.3",
"joi": "17.x.x",
"lodash": "4.17.x"
}
},
"node_modules/chalk": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
@ -1314,6 +1356,18 @@
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
},
"node_modules/joi": {
"version": "17.3.0",
"resolved": "https://registry.npmjs.org/joi/-/joi-17.3.0.tgz",
"integrity": "sha512-Qh5gdU6niuYbUIUV5ejbsMiiFmBdw8Kcp8Buj2JntszCkCfxJ9Cz76OtHxOZMPXrt5810iDIXs+n1nNVoquHgg==",
"dependencies": {
"@hapi/hoek": "^9.0.0",
"@hapi/topo": "^5.0.0",
"@sideway/address": "^4.1.0",
"@sideway/formula": "^3.0.0",
"@sideway/pinpoint": "^2.0.0"
}
},
"node_modules/json-buffer": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz",
@ -1354,6 +1408,11 @@
"node": ">=8"
}
},
"node_modules/lodash": {
"version": "4.17.20",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
"integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA=="
},
"node_modules/lowercase-keys": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz",
@ -1533,9 +1592,9 @@
}
},
"node_modules/mongoose": {
"version": "5.11.10",
"resolved": "https://registry.npmjs.org/mongoose/-/mongoose-5.11.10.tgz",
"integrity": "sha512-daE2L6VW7WNywv7tL2KUkBViWvODbzr50Of1kJpIbzW3w3N5/TYcgSmhCsEDWfYGQXbun2rdd7+sOdsEC8zQSQ==",
"version": "5.11.11",
"resolved": "https://registry.npmjs.org/mongoose/-/mongoose-5.11.11.tgz",
"integrity": "sha512-JgKKAosJf6medPOZi2LmO7sMz7Sg00mgjyPAKari3alzL+R/n8D+zKK29iGtJpNNtv9IKy14H37CWuiaZ7016w==",
"dependencies": {
"@types/mongodb": "^3.5.27",
"bson": "^1.1.4",
@ -1627,9 +1686,9 @@
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
},
"node_modules/needle": {
"version": "2.5.2",
"resolved": "https://registry.npmjs.org/needle/-/needle-2.5.2.tgz",
"integrity": "sha512-LbRIwS9BfkPvNwNHlsA41Q29kL2L/6VaOJ0qisM5lLWsTV3nP15abO5ITL6L81zqFhzjRKDAYjpcBcwM0AVvLQ==",
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/needle/-/needle-2.6.0.tgz",
"integrity": "sha512-KKYdza4heMsEfSWD7VPUIz3zX2XDwOyX2d+geb4vrERZMT5RMU6ujjaD+I5Yr54uZxQ2w6XRTAhHBbSCyovZBg==",
"dependencies": {
"debug": "^3.2.6",
"iconv-lite": "^0.4.4",
@ -1689,9 +1748,9 @@
}
},
"node_modules/nodemon": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.6.tgz",
"integrity": "sha512-4I3YDSKXg6ltYpcnZeHompqac4E6JeAMpGm8tJnB9Y3T0ehasLa4139dJOcCrB93HHrUMsCrKtoAlXTqT5n4AQ==",
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.7.tgz",
"integrity": "sha512-XHzK69Awgnec9UzHr1kc8EomQh4sjTQ8oRf8TsGrSmHDx9/UmiGG9E/mM3BuTfNeFwdNBvrqQq/RHL0xIeyFOA==",
"hasInstallScript": true,
"dependencies": {
"chokidar": "^3.2.2",
@ -2851,11 +2910,42 @@
}
},
"dependencies": {
"@hapi/hoek": {
"version": "9.1.1",
"resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.1.1.tgz",
"integrity": "sha512-CAEbWH7OIur6jEOzaai83jq3FmKmv4PmX1JYfs9IrYcGEVI/lyL1EXJGCj7eFVJ0bg5QR8LMxBlEtA+xKiLpFw=="
},
"@hapi/topo": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.0.0.tgz",
"integrity": "sha512-tFJlT47db0kMqVm3H4nQYgn6Pwg10GTZHb1pwmSiv1K4ks6drQOtfEF5ZnPjkvC+y4/bUPHK+bc87QvLcL+WMw==",
"requires": {
"@hapi/hoek": "^9.0.0"
}
},
"@influxdata/influxdb-client": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/@influxdata/influxdb-client/-/influxdb-client-1.9.0.tgz",
"integrity": "sha512-wJ+qfGukxMxpDE5d2XHiOspKheWTb1PdPOd2eD3MVuh8hjr6fqyJ3zuacTp/WVubeyppPhM4vYcDjkKCg1OEhA=="
},
"@sideway/address": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.0.tgz",
"integrity": "sha512-wAH/JYRXeIFQRsxerIuLjgUu2Xszam+O5xKeatJ4oudShOOirfmsQ1D6LL54XOU2tizpCYku+s1wmU0SYdpoSA==",
"requires": {
"@hapi/hoek": "^9.0.0"
}
},
"@sideway/formula": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.0.tgz",
"integrity": "sha512-vHe7wZ4NOXVfkoRb8T5otiENVlT7a3IAiw7H5M2+GO+9CDgcVUUsX1zalAztCmwyOr2RUTGJdgB+ZvSVqmdHmg=="
},
"@sideway/pinpoint": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz",
"integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ=="
},
"@sindresorhus/is": {
"version": "0.14.0",
"resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz",
@ -3179,6 +3269,16 @@
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
"integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg=="
},
"celebrate": {
"version": "13.0.4",
"resolved": "https://registry.npmjs.org/celebrate/-/celebrate-13.0.4.tgz",
"integrity": "sha512-gUtAjEtFyY9PvuuQJq1uyuF46gLetVZzyUKXBDBqqvgzCjTSfwXP8L+WcGt1NrLQvUxXdlzhFolW2Bt9DDEV+g==",
"requires": {
"escape-html": "1.0.3",
"joi": "17.x.x",
"lodash": "4.17.x"
}
},
"chalk": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
@ -3859,6 +3959,18 @@
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
},
"joi": {
"version": "17.3.0",
"resolved": "https://registry.npmjs.org/joi/-/joi-17.3.0.tgz",
"integrity": "sha512-Qh5gdU6niuYbUIUV5ejbsMiiFmBdw8Kcp8Buj2JntszCkCfxJ9Cz76OtHxOZMPXrt5810iDIXs+n1nNVoquHgg==",
"requires": {
"@hapi/hoek": "^9.0.0",
"@hapi/topo": "^5.0.0",
"@sideway/address": "^4.1.0",
"@sideway/formula": "^3.0.0",
"@sideway/pinpoint": "^2.0.0"
}
},
"json-buffer": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz",
@ -3893,6 +4005,11 @@
"package-json": "^6.3.0"
}
},
"lodash": {
"version": "4.17.20",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
"integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA=="
},
"lowercase-keys": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz",
@ -4009,9 +4126,9 @@
}
},
"mongoose": {
"version": "5.11.10",
"resolved": "https://registry.npmjs.org/mongoose/-/mongoose-5.11.10.tgz",
"integrity": "sha512-daE2L6VW7WNywv7tL2KUkBViWvODbzr50Of1kJpIbzW3w3N5/TYcgSmhCsEDWfYGQXbun2rdd7+sOdsEC8zQSQ==",
"version": "5.11.11",
"resolved": "https://registry.npmjs.org/mongoose/-/mongoose-5.11.11.tgz",
"integrity": "sha512-JgKKAosJf6medPOZi2LmO7sMz7Sg00mgjyPAKari3alzL+R/n8D+zKK29iGtJpNNtv9IKy14H37CWuiaZ7016w==",
"requires": {
"@types/mongodb": "^3.5.27",
"bson": "^1.1.4",
@ -4078,9 +4195,9 @@
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
},
"needle": {
"version": "2.5.2",
"resolved": "https://registry.npmjs.org/needle/-/needle-2.5.2.tgz",
"integrity": "sha512-LbRIwS9BfkPvNwNHlsA41Q29kL2L/6VaOJ0qisM5lLWsTV3nP15abO5ITL6L81zqFhzjRKDAYjpcBcwM0AVvLQ==",
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/needle/-/needle-2.6.0.tgz",
"integrity": "sha512-KKYdza4heMsEfSWD7VPUIz3zX2XDwOyX2d+geb4vrERZMT5RMU6ujjaD+I5Yr54uZxQ2w6XRTAhHBbSCyovZBg==",
"requires": {
"debug": "^3.2.6",
"iconv-lite": "^0.4.4",
@ -4130,9 +4247,9 @@
}
},
"nodemon": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.6.tgz",
"integrity": "sha512-4I3YDSKXg6ltYpcnZeHompqac4E6JeAMpGm8tJnB9Y3T0ehasLa4139dJOcCrB93HHrUMsCrKtoAlXTqT5n4AQ==",
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.7.tgz",
"integrity": "sha512-XHzK69Awgnec9UzHr1kc8EomQh4sjTQ8oRf8TsGrSmHDx9/UmiGG9E/mM3BuTfNeFwdNBvrqQq/RHL0xIeyFOA==",
"requires": {
"chokidar": "^3.2.2",
"debug": "^3.2.6",

1
server/package.json

@ -13,6 +13,7 @@
"@influxdata/influxdb-client": "^1.9.0",
"bcrypt": "^5.0.0",
"body-parser": "^1.19.0",
"celebrate": "^13.0.4",
"chalk": "^4.1.0",
"cookie-session": "^1.4.0",
"cors": "^2.8.5",

40
server/routes/athletes.js

@ -1,41 +1,61 @@
const express = require('express')
const router = express.Router();
const mongoose = require('mongoose');
const {requireAuth} = require('../middlewares/middleware');
const {celebrate} = require('celebrate');
const {requireAuth} = require('../middlewares/middleware');
const {athleteUpdateSchema, guid} = require('../schemas/joi');
const Athlete = mongoose.model('Athlete');
router.get('/api/athletes', requireAuth, async (req, res) => {
router.get('/api/athletes',
requireAuth,
async (req, res) => {
const athletes = await Athlete.find();
res.send(athletes);
});
router.get('/api/athletes/:id', requireAuth, async (req, res) => {
router.get('/api/athletes/:id',
requireAuth,
celebrate(guid),
async (req, res) => {
const athlete = await Athlete.findById(req.params.id);
res.send(athlete)
});
router.get('/api/athletes/:id/edit', requireAuth, async (req, res) => {
router.get('/api/athletes/:id/edit',
requireAuth,
celebrate(guid),
async (req, res) => {
const athlete = await Athlete.findById(req.params.id)
res.send(athlete)
});
router.put('/api/athletes/:id', requireAuth, async (req, res) => {
}
)
;
router.put('/api/athletes/:id',
requireAuth,
celebrate(athleteUpdateSchema, guid),
async (req, res) => {
const {name, _trainer} = req.body
const updateAthlete = {name, _trainer}
if (name || _trainer)
await Athlete.findByIdAndUpdate(req.params.id, updateAthlete, {}, (err, athlete) => {
if (err)
return res.status(400).json({errors: 'Something went wrong!0'});
return res.status(400).json({errors: 'Something went wrong!'});
res.send(athlete)
})
});
router.delete('/api/athlete/:id', requireAuth, async (req, res) => {
router.delete('/api/athlete/:id',
requireAuth,
celebrate(guid),
async (req, res) => {
await Athlete.findByIdAndDelete(req.params.id)
});
}
)
;
module.exports = router;

13
server/routes/auth.js

@ -1,8 +1,12 @@
const express = require('express');
const passport = require('passport')
const router = express.Router();
const {celebrate} = require('celebrate');
const {userAuthSchema} = require('../schemas/joi')
router.post('/auth/login',
celebrate(userAuthSchema),
(req, res, next) => {
passport.authenticate('local', {}, (err, user, info) => {
if (err)
@ -14,15 +18,16 @@ router.post('/auth/login',
return res.status(200).json({user: req.user});
});
})(req, res, next);
}
);
});
router.post('/auth/logout', (req, res) => {
router.post('/auth/logout',
(req, res) => {
req.logout();
res.redirect('/');
});
router.get('/auth/current_user', (req, res) => {
router.get('/auth/current_user',
(req, res) => {
if (!req.user)
return res.status(404).json({errors: 'No current user!'})

8
server/routes/data.js

@ -7,14 +7,18 @@ const Athlete = mongoose.model('Athlete');
const {influx_bucket} = require('../config/keys')
const {iQuery} = require('../actions/influx_actions')
router.get('/api/data', requireAuth, async (req, res) => {
router.get('/api/data',
requireAuth,
async (req, res) => {
const query = `from(bucket: "${influx_bucket}") |> range(start: -1h)`;
const data = await iQuery(query);
res.send(data);
});
router.get('/api/data/:id', requireAuth, async (req, res) => {
router.get('/api/data/:id',
requireAuth,
async (req, res) => {
const athlete = await Athlete.findById(req.params.id);
const query = `from(bucket: "${influx_bucket}") |> range(start: -1h) |> filter(fn: (r) => r.client == "${athlete.id}")`;

9
server/routes/user.js

@ -2,11 +2,16 @@ const express = require('express')
const router = express.Router();
const mongoose = require('mongoose');
const bcrypt = require('bcrypt');
const {requireAuth} = require('../middlewares/middleware');
const {celebrate} = require('celebrate');
const {requireAuth} = require('../middlewares/middleware');
const User = mongoose.model('User')
const {userUpdateSchema, guid} = require('../schemas/joi');
router.put('/api/user/:id', requireAuth, async (req, res) => {
router.put('/api/user/:id',
requireAuth,
celebrate(userUpdateSchema, guid),
async (req, res) => {
const {username, email, password, newPassword} = req.body
if (password && newPassword) {

37
server/schemas/joi.js

@ -0,0 +1,37 @@
const {Joi} = require('celebrate');
const guid = {
params:{
userId: Joi.string().guid().required()
}
}
const userAuthSchema = {
body: {
username: Joi.string().required(),
password: Joi.string().required(),
}
};
const userUpdateSchema = {
body: {
_id: Joi.string().required(),
username: Joi.string().required(),
email: Joi.any(),
password: Joi.string().allow(''),
newPassword: Joi.string().allow(''),
}
};
const athleteUpdateSchema = {
body: {
_id: Joi.string().required(),
id: Joi.string().required(),
socketID: Joi.string().required(),
name: Joi.string().required(),
__v: Joi.number().integer(),
_trainer: Joi.string().allow(''),
}
}
module.exports = {guid, userAuthSchema, userUpdateSchema, athleteUpdateSchema}

3
web/src/store/modules/athletes.ts

@ -1,6 +1,5 @@
import {Action, Module, Mutation, VuexModule} from 'vuex-module-decorators'
import axios, {AxiosResponse} from "axios";
import qs from 'qs';
import {UserInterface} from "@/store/modules/user";
@ -74,7 +73,7 @@ export default class Athletes extends VuexModule {
axios({
method: 'PUT',
url: `/api/athletes/${athlete._id}`,
data: qs.stringify({...athlete})
data: {...athlete}
})
.then((resp: AxiosResponse) => {
this.context.commit('save_athlete', resp.data)

16
web/src/store/modules/user.ts

@ -97,15 +97,13 @@ export default class User extends VuexModule {
}
@Action
private updateUser(user: UserInterface) {
private specificUser(id: string) {
return new Promise((resolve, reject) => {
axios({
method: 'PUT',
url: `/api/user/${user._id}`,
data: qs.stringify({...user})
method: 'GET',
url: `/api/user/${id}`
})
.then((resp: AxiosResponse) => {
this.context.commit('auth_success', resp.data);
resolve(resp)
})
.catch((err: Error) => {
@ -115,13 +113,15 @@ export default class User extends VuexModule {
}
@Action
private specificUser(id: string) {
private updateUser(user: UserInterface) {
return new Promise((resolve, reject) => {
axios({
method: 'GET',
url: `/api/user/${id}`
method: 'PUT',
url: `/api/user/${user._id}`,
data: {...user}
})
.then((resp: AxiosResponse) => {
this.context.commit('auth_success', resp.data);
resolve(resp)
})
.catch((err: Error) => {

Loading…
Cancel
Save