From aa01dbab0fb8aca677b3906e04b90893c3f468bd Mon Sep 17 00:00:00 2001
From: Ferenc Schulcz <schulcz.ferenc@gmail.com>
Date: Fri, 15 Apr 2022 21:10:34 +0200
Subject: [PATCH] Implement user handling with plaintext password storage

---
 db/db.js                 | 43 ++++++++++++++++++++++++++++++++++++++++
 index.js                 |  7 +++++++
 middleware/authMW.js     |  3 +++
 middleware/loginMW.js    | 22 ++++++++++++++++++--
 middleware/logoutMW.js   |  1 +
 middleware/registerMW.js | 37 ++++++++++++++++++++++++++++++++--
 package.json             |  7 +++++--
 static/style.css         |  6 ++++++
 views/login.ejs          | 27 ++++++++++++++++---------
 views/register.ejs       | 20 ++++++++++++++++++-
 10 files changed, 157 insertions(+), 16 deletions(-)
 create mode 100644 db/db.js

diff --git a/db/db.js b/db/db.js
new file mode 100644
index 0000000..f70865e
--- /dev/null
+++ b/db/db.js
@@ -0,0 +1,43 @@
+var mongoose = require('mongoose');
+
+mongoose.connect('mongodb://localhost/szobatinder');
+
+// Models
+//-------
+
+const User = mongoose.model('user', {
+    username: String,
+    password: String
+})
+
+function createUser(username, password, callback) {
+    const user = new User({ username: username, password: password }); // using double ROT-13 for password encryption
+    user.save(err => {
+        console.error(err);
+        callback(err);
+    });
+}
+
+function doesUserExist(username) {
+    return new Promise((resolve, reject) => {
+        User.findOne({ username: username }, (err, user) => {
+            if (user == null) resolve(false);
+            else resolve(true);
+        });
+    });
+}
+
+function checkUserCredentials(username, password) {
+    return new Promise((resolve, reject) => {
+        User.findOne({ username: username, password: password }, (err, user) => {
+            if (user == null) reject('Wrong username or password');
+            else resolve('ok');
+        });
+    });
+}
+
+module.exports = {
+    createUser: createUser,
+    doesUserExist: doesUserExist,
+    checkUserCredentials: checkUserCredentials,
+};
\ No newline at end of file
diff --git a/index.js b/index.js
index b6c7846..e3257f0 100644
--- a/index.js
+++ b/index.js
@@ -2,6 +2,10 @@ const express = require('express');
 //const ejs = require('ejs');
 const app = express();
 
+const bodyParser = require('body-parser');
+const cookieParser = require('cookie-parser');
+const session = require('express-session');
+
 const authMW = require('./middleware/authMW');
 const loginMW = require('./middleware/loginMW');
 const registerMW = require('./middleware/registerMW');
@@ -15,6 +19,9 @@ const matchlistMW = require('./middleware/matchlistMW');
 const profileeditMW = require('./middleware/profileeditMW');
 const renderMW = require('./middleware/renderMW');
 
+app.use(bodyParser.urlencoded());
+app.use(cookieParser());
+app.use(session({ secret: 'McGalagony egy cirmos cica' })); // should be read from some non-version-tracked file
 
 const objectrepository = undefined; // for now
 
diff --git a/middleware/authMW.js b/middleware/authMW.js
index 29465bb..d90dd20 100644
--- a/middleware/authMW.js
+++ b/middleware/authMW.js
@@ -2,6 +2,9 @@
 module.exports = function () {
     return function (req, res, next) {
         // should be placed on all non-public endpoints. If the user has a valid session, sets the username on res.locals.username. Else, redirects to /login.
+        if (typeof req.session.username == 'undefined') return res.redirect('/login');
+
+        res.locals.username = req.session.username;
         return next();
     }
 }
\ No newline at end of file
diff --git a/middleware/loginMW.js b/middleware/loginMW.js
index 06a51f4..90fab65 100644
--- a/middleware/loginMW.js
+++ b/middleware/loginMW.js
@@ -1,7 +1,25 @@
+const db = require('../db/db.js');
 
 module.exports = function (objectrepository) {
     return function (req, res, next) {
-        // does the login based on username and password. If the credentials are corrects, sets a session and redirects to `/`. If not, redirects to /login.
-        return res.redirect('/');
+        // does the login based on username and password. If the credentials are correct, sets a session and redirects to `/`. If not, redirects to /login.
+
+        username = req.body.user;
+        password = req.body.pass;
+
+        if (typeof username == 'undefined' || typeof password == 'undefined') {
+            return res.status(400).render('login');
+        }
+        if (username == "" || password == "") {
+            res.locals.error = 'Fill in all inputs!';
+            return res.status(400).render('login');
+        }
+        return db.checkUserCredentials(username, password).then(value => {
+            req.session.username = username;
+            return res.redirect('/');
+        }).catch(err => {
+            res.locals.error = err;
+            return res.status(400).render('login');
+        })
     }
 }
\ No newline at end of file
diff --git a/middleware/logoutMW.js b/middleware/logoutMW.js
index 48a83b8..854c1f9 100644
--- a/middleware/logoutMW.js
+++ b/middleware/logoutMW.js
@@ -2,6 +2,7 @@
 module.exports = function (objectrepository) {
     return function (req, res, next) {
         // destroys the session and redirects to /.
+        req.session.username = undefined;
         return res.redirect('/');
     }
 }
\ No newline at end of file
diff --git a/middleware/registerMW.js b/middleware/registerMW.js
index 1ed81da..1bce171 100644
--- a/middleware/registerMW.js
+++ b/middleware/registerMW.js
@@ -1,7 +1,40 @@
+const db = require('../db/db.js');
 
 module.exports = function (objectrepository) {
     return function (req, res, next) {
-        // takes a username, a password and a password-again from the registration form. If the username is not taken, and the passwords match, creates the user and creates a session and redirects to `/`. Otherwise it redirets to /register.
-        return next();
+        // takes a username, a password and a password-again from the registration form. If the username is not taken, and the passwords match, creates the user and creates a session and redirects to `/`. Otherwise it displays an error.
+
+        username = req.body.user;
+        password = req.body.pass;
+        password2 = req.body.pass2;
+
+        if (typeof username == 'undefined' || typeof password == 'undefined' || typeof password2 == 'undefined') {
+            return res.status(400).render('register');
+        }
+        if (username == "" || password == "" || password2 == "") {
+            res.locals.error = 'Fill in all inputs!';
+            return res.status(400).render('register');
+        }
+        if (password !== password2) {
+            res.locals.error = 'Passwords do not match!';
+            return res.status(400).render('register');
+        }
+        return db.doesUserExist(username).then(exists => {
+            if (exists) {
+                res.locals.error = 'Username already taken.'; // should also say 'User that uses this name: $username.' :D
+                return res.status(400).render('register');
+            }
+
+            db.createUser(username, password, err => {
+                if (err == null) {
+                    req.session.username = username;
+                    return res.redirect('/');
+                } else {
+                    res.locals.error = 'Could not create user due to a database error.'
+                    return res.status(500).render('register');
+                }
+            });
+
+        });
     }
 }
\ No newline at end of file
diff --git a/package.json b/package.json
index 0c5e400..14caa9e 100644
--- a/package.json
+++ b/package.json
@@ -9,7 +9,10 @@
   "author": "Schulcz Ferenc",
   "license": "ISC",
   "dependencies": {
-    "ejs": "^3.1.6",
-    "express": "^4.17.3"
+    "cookie-parser": "^1.4.6",
+    "ejs": "^3.0.2",
+    "express": "^4.17.3",
+    "express-session": "^1.17.2",
+    "mongoose": "^6.3.0"
   }
 }
diff --git a/static/style.css b/static/style.css
index b156204..f0754ca 100644
--- a/static/style.css
+++ b/static/style.css
@@ -94,4 +94,10 @@ textarea {
 .form-helper {
     font-size: 11px;
     margin-bottom: 20px;
+}
+
+.warning {
+    background-color: #fed243;
+    padding: 20px 20px;
+    border-radius: 3px;
 }
\ No newline at end of file
diff --git a/views/login.ejs b/views/login.ejs
index a973fa2..0151f39 100644
--- a/views/login.ejs
+++ b/views/login.ejs
@@ -1,12 +1,21 @@
 <%- include('_head', {}) %>
 
     <h1>Login</h1>
-    <form action="/login">
-        <label for="user">User: </label>
-        <input type="text" id="user" name="user"><br>
-        <label for="pass">Password: </label>
-        <input type="password" id="pass" name="pass"><br>
-        <input type="submit" class="button" value="Login">
-    </form>
-
-    <%- include('_tail', {}) %>
\ No newline at end of file
+
+    <% if(typeof error!='undefined' ) { %>
+        <p class="warning">
+            <%= error %>
+        </p>
+        <% } %>
+
+            <form action="/login" method="post">
+                <label for="user">User: </label>
+                <input type="text" id="user" name="user"><br>
+                <label for="pass">Password: </label>
+                <input type="password" id="pass" name="pass"><br>
+                <input type="submit" class="button" value="Login">
+            </form>
+
+            <p><a href="/register">Need to register?</a></p>
+
+            <%- include('_tail', {}) %>
\ No newline at end of file
diff --git a/views/register.ejs b/views/register.ejs
index e96760e..70feb9f 100644
--- a/views/register.ejs
+++ b/views/register.ejs
@@ -2,4 +2,22 @@
 
     <h1>Register</h1>
 
-    <%- include('_tail', {}) %>
\ No newline at end of file
+    <% if(typeof error!='undefined' ) { %>
+        <p class="warning">
+            <%= error %>
+        </p>
+        <% } %>
+
+            <form action="/register" method="post">
+                <label for="user">User: </label>
+                <input type="text" id="user" name="user"><br>
+                <label for="pass">Password: </label>
+                <input type="password" id="pass" name="pass"><br>
+                <label for="pass2">Password again: </label>
+                <input type="password" id="pass2" name="pass2"><br>
+                <input type="submit" class="button" value="Register">
+            </form>
+
+            <p><a href="/login">Already have an account?</a></p>
+
+            <%- include('_tail', {}) %>
\ No newline at end of file
-- 
GitLab