From 43851413ea5c28601874fc8c71828588a9f7299c Mon Sep 17 00:00:00 2001 From: flifloo Date: Mon, 17 Aug 2020 11:33:54 +0200 Subject: [PATCH] Add user, login & logout, secure sandwiches and orders pages with login and permissions check --- app.js | 16 ++++++++++++++ config/config_exemple.json | 3 ++- middlewares/sessionCheck.js | 13 +++++++++++ models/user.js | 44 +++++++++++++++++++++++++++++++++++++ package-lock.json | 40 +++++++++++++++++++++++++++++++++ package.json | 1 + routes/login.js | 28 +++++++++++++++++++++++ routes/logout.js | 11 ++++++++++ routes/orders.js | 3 ++- routes/sandwiches.js | 5 +++-- views/login.pug | 13 +++++++++++ 11 files changed, 173 insertions(+), 4 deletions(-) create mode 100644 middlewares/sessionCheck.js create mode 100644 models/user.js create mode 100644 routes/login.js create mode 100644 routes/logout.js create mode 100644 views/login.pug diff --git a/app.js b/app.js index ad78c94..8b78032 100644 --- a/app.js +++ b/app.js @@ -1,14 +1,27 @@ let express = require("express"); let path = require("path"); let cookieParser = require("cookie-parser"); +let session = require("express-session"); let logger = require("morgan"); +let config = require("./config/config.json"); let indexRouter = require("./routes/index"); +let loginRouter = require("./routes/login"); +let logoutRouter = require("./routes/logout"); let commandRouter = require("./routes/command"); let ordersRouter = require("./routes/orders"); let sandwichesRouter = require("./routes/sandwiches"); let app = express(); +let sess = { + secret: config.secret, + cookie: {} +} + +if (app.get("env") === "production") { + app.set("trust proxy", 1); + sess.cookie.secure = true; +} // view engine setup app.set("views", path.join(__dirname, "views")); @@ -18,9 +31,12 @@ app.use(logger("dev")); app.use(express.json()); app.use(express.urlencoded({ extended: false })); app.use(cookieParser()); +app.use(session(sess)); app.use(express.static(path.join(__dirname, "public"))); app.use("/", indexRouter); +app.use("/login", loginRouter); +app.use("/logout", logoutRouter); app.use("/command", commandRouter); app.use("/orders", ordersRouter); app.use("/sandwiches", sandwichesRouter); diff --git a/config/config_exemple.json b/config/config_exemple.json index c48bf9a..77326b7 100644 --- a/config/config_exemple.json +++ b/config/config_exemple.json @@ -3,5 +3,6 @@ "password": null, "database": "database_development", "host": "127.0.0.1", - "dialect": "mysql" + "dialect": "mysql", + "secret": "keyboard cat" } diff --git a/middlewares/sessionCheck.js b/middlewares/sessionCheck.js new file mode 100644 index 0000000..146f9f6 --- /dev/null +++ b/middlewares/sessionCheck.js @@ -0,0 +1,13 @@ +function sessionCheck(permission) { + return (req, res, next) => { + if (!req.session.user) { + req.session.lastUrl = req.originalUrl; + req.session.save(() => res.redirect("/login")); + } else if (req.session.user.permissions < permission) + res.render("error", {message: "Permission denied !", "error": {}}); + else + next(); + } +} + +module.exports = sessionCheck; diff --git a/models/user.js b/models/user.js new file mode 100644 index 0000000..f318601 --- /dev/null +++ b/models/user.js @@ -0,0 +1,44 @@ +"use strict"; +const { + Model +} = require("sequelize"); +module.exports = (sequelize, DataTypes) => { + class User extends Model { + static associate(models) { + } + + checkPassword(password) { + return require("crypto") + .createHash("sha256") + .update(this.username + password) + .digest("base64") === this.passwordHash + } + } + User.init({ + username: { + type: DataTypes.STRING, + primaryKey: true + }, + passwordHash: { + type: DataTypes.STRING, + allowNull: false, + set(value) { + if (value) + this.setDataValue("passwordHash", + require("crypto"). + createHash("sha256"). + update(this.username + value). + digest("base64")); + } + }, + permissions: { // 1 = sandwich page, 2 = order page, 3 = admin + type: DataTypes.INTEGER, + defaultValue: 0, + allowNull: false + } + }, { + sequelize, + modelName: "User", + }); + return User; +}; diff --git a/package-lock.json b/package-lock.json index 8f15c4e..62b6bf2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -495,6 +495,33 @@ } } }, + "express-session": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.17.1.tgz", + "integrity": "sha512-UbHwgqjxQZJiWRTMyhvWGvjBQduGCSBDhhZXYenziMFjxst5rMV+aJZ6hKPHZnPyHGsrqRICxtX8jtEbm/z36Q==", + "requires": { + "cookie": "0.4.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~2.0.0", + "on-headers": "~1.0.2", + "parseurl": "~1.3.3", + "safe-buffer": "5.2.0", + "uid-safe": "~2.1.5" + }, + "dependencies": { + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" + }, + "safe-buffer": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", + "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==" + } + } + }, "ext": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz", @@ -1205,6 +1232,11 @@ "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" }, + "random-bytes": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", + "integrity": "sha1-T2ih3Arli9P7lYSMMDJNt11kNgs=" + }, "range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", @@ -1526,6 +1558,14 @@ "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", "optional": true }, + "uid-safe": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", + "integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==", + "requires": { + "random-bytes": "~1.0.0" + } + }, "umzug": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/umzug/-/umzug-2.3.0.tgz", diff --git a/package.json b/package.json index 43813fc..088991d 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "cookie-parser": "~1.4.4", "debug": "~2.6.9", "express": "~4.16.1", + "express-session": "^1.17.1", "http-errors": "~1.6.3", "morgan": "~1.9.1", "pg": "^8.3.0", diff --git a/routes/login.js b/routes/login.js new file mode 100644 index 0000000..f90a3cc --- /dev/null +++ b/routes/login.js @@ -0,0 +1,28 @@ +const express = require("express"); +const router = express.Router(); +const models = require("../models"); + +router.get("/", async (req, res) => { + if (req.session.user) + res.redirect("/") + else + res.render("login", { title: "Kfet - Login" }); +}) + .post("/", async (req, res) => { + if (!req.body.username || !req.body.password) + res.redirect("/login"); + else { + let u = await models.User.findByPk(req.body.username); + if (!u || !u.checkPassword(req.body.password)) + res.redirect("/login?err=true"); + else { + req.session.user = u; + if (req.session.lastUrl && !req.session.lastUrl.startsWith("/login")) + res.redirect(req.session.lastUrl); + else + res.redirect("/"); + } + } + }); + +module.exports = router; diff --git a/routes/logout.js b/routes/logout.js new file mode 100644 index 0000000..21d61e0 --- /dev/null +++ b/routes/logout.js @@ -0,0 +1,11 @@ +const express = require("express"); +const router = express.Router(); + +router.get("/", (req, res) => { + if (req.session.user) { + req.session.user = null; + } + res.redirect("/"); +}); + +module.exports = router; diff --git a/routes/orders.js b/routes/orders.js index 58c21ef..8c23fb8 100644 --- a/routes/orders.js +++ b/routes/orders.js @@ -1,9 +1,10 @@ let express = require("express"); let router = express.Router(); +let sessionCheck = require("../middlewares/sessionCheck"); let models = require("../models"); -router.get("/", async (req, res) => { +router.get("/", sessionCheck(2), async (req, res) => { let date = req.query.date ? req.query.date : (new Date()).toISOString().substring(0,10); let commands = {}; diff --git a/routes/sandwiches.js b/routes/sandwiches.js index a567464..2b50a43 100644 --- a/routes/sandwiches.js +++ b/routes/sandwiches.js @@ -1,10 +1,11 @@ let express = require("express"); let router = express.Router(); +let sessionCheck = require("../middlewares/sessionCheck"); let models = require("../models"); -let sequelize = require("sequelize") +let sequelize = require("sequelize"); -router.get("/", async (req, res) => { +router.get("/", sessionCheck(1), async (req, res) => { let date = req.query.date ? req.query.date : (new Date()).toISOString().substring(0,10); res.render("sandwiches", { diff --git a/views/login.pug b/views/login.pug new file mode 100644 index 0000000..412b511 --- /dev/null +++ b/views/login.pug @@ -0,0 +1,13 @@ +extends layout + +block content + form.card(action="/login" method="POST") + h1 Login + div.field + label(for="username") Username: + input#username(type="text" name="username" required) + dov.field + label(for="password") Username: + input#password(type="password" name="password" required) + div.field + input(type="submit" value="Login")