From c21eba4246be5c9831705a86592b45c70648c167 Mon Sep 17 00:00:00 2001 From: Mark Powers Date: Sat, 2 Feb 2019 10:51:10 -0500 Subject: Add admin panel --- package-lock.json | 99 +++++++++++++++++++++++++++++++++++++++++++++++++++++ package.json | 2 ++ src/html/admin.html | 2 +- src/html/bread.html | 2 +- src/html/index.html | 2 +- src/html/login.html | 25 ++++++++++++++ src/index.js | 28 +++++++++++++-- src/server.js | 58 ++++++++++++++++++++++++------- 8 files changed, 199 insertions(+), 19 deletions(-) create mode 100644 src/html/login.html diff --git a/package-lock.json b/package-lock.json index 2fe6214..17a85b3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -107,6 +107,11 @@ "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.1.3.tgz", "integrity": "sha512-rDFIzgXcof0jDyjNosjv4Sno77X4KuPeFxG2XZZv1/Kc8DRVGVADdoQyyOVDwPqL36DDmtCQbrpMCqvpPLJQ0w==" }, + "buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" + }, "buffer-from": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", @@ -203,6 +208,15 @@ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" }, + "cookie-parser": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.3.tgz", + "integrity": "sha1-D+MfoZ0AC5X0qt8fU/3CuKIDuqU=", + "requires": { + "cookie": "0.3.1", + "cookie-signature": "1.0.6" + } + }, "cookie-signature": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", @@ -272,6 +286,14 @@ "safer-buffer": "^2.1.0" } }, + "ecdsa-sig-formatter": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.10.tgz", + "integrity": "sha1-HFlQAPBKiJffuFAAiSoPTDOvhsM=", + "requires": { + "safe-buffer": "^5.0.1" + } + }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -512,6 +534,29 @@ "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" }, + "jsonwebtoken": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.4.0.tgz", + "integrity": "sha512-coyXjRTCy0pw5WYBpMvWOMN+Kjaik2MwTUIq9cna/W7NpO9E+iYbumZONAz3hcr+tXFJECoQVrtmIoC3Oz0gvg==", + "requires": { + "jws": "^3.1.5", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1" + }, + "dependencies": { + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + } + } + }, "jsprim": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", @@ -523,11 +568,65 @@ "verror": "1.10.0" } }, + "jwa": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.2.0.tgz", + "integrity": "sha512-Grku9ZST5NNQ3hqNUodSkDfEBqAmGA1R8yiyPHOnLzEKI0GaCQC/XhFmsheXYuXzFQJdILbh+lYBiliqG5R/Vg==", + "requires": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.10", + "safe-buffer": "^5.0.1" + } + }, + "jws": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.1.tgz", + "integrity": "sha512-bGA2omSrFUkd72dhh05bIAN832znP4wOU3lfuXtRBuGTbsmNmDXMQg28f0Vsxaxgk4myF5YkKQpz6qeRpMgX9g==", + "requires": { + "jwa": "^1.2.0", + "safe-buffer": "^5.0.1" + } + }, "lodash": { "version": "4.17.11", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" }, + "lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8=" + }, + "lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY=" + }, + "lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha1-YZwK89A/iwTDH1iChAt3sRzWg0M=" + }, + "lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w=" + }, + "lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=" + }, + "lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=" + }, + "lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=" + }, "long": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", diff --git a/package.json b/package.json index 716b581..ae9a975 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,9 @@ "dependencies": { "body-parser": "^1.18.3", "bootstrap": "^4.1.3", + "cookie-parser": "^1.4.3", "express": "^4.16.4", + "jsonwebtoken": "^8.4.0", "multer": "^1.4.1", "mysql2": "^1.6.4", "request": "^2.88.0", diff --git a/src/html/admin.html b/src/html/admin.html index 7c87ba6..d0d2e81 100644 --- a/src/html/admin.html +++ b/src/html/admin.html @@ -27,7 +27,7 @@ - + diff --git a/src/html/bread.html b/src/html/bread.html index e6eeb47..1ce1478 100644 --- a/src/html/bread.html +++ b/src/html/bread.html @@ -18,7 +18,7 @@ }, created() { fetch(new Request('/posts/bread')).then(response => response.json()) - .then(response => this.posts = response.data); + .then(response => this.posts = response); } }); } diff --git a/src/html/index.html b/src/html/index.html index c7e398b..01f6e4e 100644 --- a/src/html/index.html +++ b/src/html/index.html @@ -19,7 +19,7 @@ }, created() { fetch(new Request('/posts/index')).then(response => response.json()) - .then(response => this.posts = response.data); + .then(response => this.posts = response); } }); } diff --git a/src/html/login.html b/src/html/login.html new file mode 100644 index 0000000..53a2d06 --- /dev/null +++ b/src/html/login.html @@ -0,0 +1,25 @@ + + + + + Mark's Kitchen - Login + + + + + + + +
+

Login

+
+
+ + + +
+
+
+ + + \ No newline at end of file diff --git a/src/index.js b/src/index.js index b47824a..925a166 100644 --- a/src/index.js +++ b/src/index.js @@ -2,8 +2,21 @@ const server = require('./server'); const Sequelize = require('sequelize'); const fs = require('fs'); const path = require('path'); +const jwt = require('jsonwebtoken'); -const dbCreds = JSON.parse(fs.readFileSync(path.join(__dirname, 'config.json'))).database; +const config = JSON.parse(fs.readFileSync(path.join(__dirname, 'config.json'))); + +const dbCreds = config.database; +const secret = config.jwt_secret; + +const jwtFunctions = { + sign: function(message) { + return jwt.sign({ value: message }, secret); + }, + verify: function(token) { + return jwt.verify(token, secret).value; + } +} const database = new Sequelize(dbCreds.database, dbCreds.user, dbCreds.password, { logging(str) { @@ -43,7 +56,16 @@ function setUpModels(){ },}), "pictures": database.define('pictures', { source: { type: Sequelize.TEXT, allowNull: false}, - }) + }), + "users": database.define('user', { + username: { + type: Sequelize.STRING, + allowNull: false, + }, + password: { + type: Sequelize.STRING, + allowNull: false, + },}) } models.pictures.belongsTo(models.posts); return models; @@ -52,6 +74,6 @@ function setUpModels(){ const models = setUpModels(); sync(); -server.setUpRoutes(models); +server.setUpRoutes(models, jwtFunctions); server.listen(); diff --git a/src/server.js b/src/server.js index 4cf76d1..c6630e8 100644 --- a/src/server.js +++ b/src/server.js @@ -1,6 +1,8 @@ const express = require('express'); const bodyParser = require('body-parser'); +const cookieParser = require('cookie-parser'); const request = require('request'); +const crypto = require('crypto'); const multer = require('multer'); var storage = multer.diskStorage({ @@ -8,13 +10,10 @@ var storage = multer.diskStorage({ cb(null, 'src/uploads/') }, filename: function (req, file, cb) { - console.log(file); var ext = ""; if(file.originalname.includes(".")){ ext = "." + file.originalname.split(".")[1]; - console.log(ext); } - console.log(ext); return cb(null, 'img-' + Date.now()+ext) } }) @@ -24,21 +23,46 @@ const port = 80; const server = express(); // server.use(bodyParser.json()); +server.use(cookieParser()) server.use(bodyParser.urlencoded({ extended: true })); +// Route logging server.use(function (req, res, next) { console.debug("express:", req.method, req.originalUrl); next() }) - function listen(){ server.listen(port, () => console.info(`Listening on port ${port}!`)); } -function setUpRoutes(models){ +function setUpRoutes(models, jwtFunctions){ + // Authentication routine + server.use(function(req, res, next) { + if(req.path.startsWith("/admin")){ + let cookie = req.cookies.authorization + if (!cookie) { + res.redirect('/login'); + } + try { + const decryptedUserId = jwtFunctions.verify(cookie); + models.users.findOne({where: {username: decryptedUserId}}).then((user, error) => { + if (user) { + res.locals.user = user.get({ plain: true }); + } else { + res.redirect('/login'); + } + }); + } catch (e){ + res.status(400).send(e.message); + } + } + next(); + }) + server.get('/', (req, res) => res.sendFile(__dirname + "/html/index.html")) server.get('/index', (req, res) => res.sendFile(__dirname + "/html/index.html")) server.get('/admin', (req, res) => res.sendFile(__dirname + "/html/admin.html")); + server.get('/login', (req, res) => res.sendFile(__dirname + "/html/login.html")) server.get('/bread', (req, res) => res.sendFile(__dirname + "/html/bread.html")); server.get('/essay', (req, res) => res.sendFile(__dirname + "/html/essay.html")); server.get('/snake', (req, res) => res.sendFile(__dirname + "/html/snake.html")); @@ -55,16 +79,15 @@ function setUpRoutes(models){ const images = await models.pictures.findAll({ attributes: ["source"], where: { postId: post.id }}).map(x => x.source); post.images = images; } - res.status(200).send({ success: true, data: posts }); + res.status(200).send(posts); next(); } catch (e) { - res.status(400).send({ success: false, error: e.message }); + res.status(400).send(e.message); } }) server.post('/posts', upload.array('images'), async (req, res, next) => { try { console.log(req.body); - const type = req.body.type const newPost = await models.posts.create(req.body); req.files.forEach(async (file) => { @@ -75,8 +98,21 @@ function setUpRoutes(models){ res.redirect(`/${type}`); next(); } catch (e) { - res.status(400).send({ success: false, error: e.message }); + res.status(400).send(e.message); + } + }) + server.post('/login', async (req, res, next) => { + console.log(req.body); + const hash = crypto.createHash("sha512").update(req.body.password, "binary").digest("base64"); + console.log(hash); + const user = await models.users.findOne({where: { username: req.body.username, password: hash }}) + if(user){ + const token = jwtFunctions.sign(user.username); + res.redirect('/admin'); + } else { + res.redirect('/login'); } + next(); }) @@ -84,10 +120,6 @@ function setUpRoutes(models){ server.get('/css/:id', (req, res) => { res.sendFile(__dirname + "/css/"+req.params.id); }); - server.get('/photo/:id', (req, res) => { - // res.setHeater("Content-Type", "image") - res.sendFile(__dirname + "/photo/"+req.params.id); - }); server.get('/uploads/:id', (req, res) => { res.sendFile(__dirname + "/uploads/"+req.params.id); }); -- cgit v1.2.3