From 89bb4e7f540ae8e990c572ece0152f670aa47fe1 Mon Sep 17 00:00:00 2001 From: Martin Lehoux <martin.lehoux@supelec.fr> Date: Mon, 11 Feb 2019 19:16:30 +0100 Subject: [PATCH] Release 0.1: Authentication & Session --- .gitignore | 4 +++ README.md | 26 +++++++++++++- config.json.template | 5 +++ index.js | 82 ++++++++++++++++++++++++++++++++++++++++++++ models/character.js | 8 +++++ models/user.js | 25 ++++++++++++++ models/weapon.js | 8 +++++ package.json | 16 +++++++++ views/base.pug | 36 +++++++++++++++++++ views/home.pug | 4 +++ views/signup.pug | 22 ++++++++++++ 11 files changed, 235 insertions(+), 1 deletion(-) create mode 100644 .gitignore create mode 100644 config.json.template create mode 100644 index.js create mode 100644 models/character.js create mode 100644 models/user.js create mode 100644 models/weapon.js create mode 100644 package.json create mode 100644 views/base.pug create mode 100644 views/home.pug create mode 100644 views/signup.pug diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f7c4ce5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +node_modules +yarn.lock +.vscode +config.json \ No newline at end of file diff --git a/README.md b/README.md index dad5fca..b5fe368 100644 --- a/README.md +++ b/README.md @@ -1 +1,25 @@ -# RoleGame \ No newline at end of file +# RoleGame + +## Dependencies + +### Used +* `express`: Fast, unopinionated, minimalist web framework +* `bcrypt`: A bcrypt library for NodeJS. +* `express-session`: Simple session middleware for Express +* `pug`: A clean, whitespace-sensitive template language for writing HTML +* `mongoose`: Mongoose MongoDB ODM +* `body-parser`: Node.js body parsing middleware +* `morgan`: HTTP request logger middleware for node.js + +### To use +* `joi`: Object schema description language and validator for JavaScript objects. + +## Release + +### Next: v0.1.0 +* [ ] Authentication and session +* [ ] Validate mongoose models +* [ ] Make mongoose unique index work + +### Next: v0.2.0 +* [ ] Message handler diff --git a/config.json.template b/config.json.template new file mode 100644 index 0000000..9b502de --- /dev/null +++ b/config.json.template @@ -0,0 +1,5 @@ +{ + "port": Number, + "secret": String, + "cryptRounds": Number +} \ No newline at end of file diff --git a/index.js b/index.js new file mode 100644 index 0000000..d307bc5 --- /dev/null +++ b/index.js @@ -0,0 +1,82 @@ +const express = require('express'); +const session = require('express-session'); +const bodyParser = require('body-parser'); +const User = require('./models/user'); +const mongoose = require('mongoose'); +const bcrypt = require('bcrypt'); +const morgan = require('morgan'); + +const config = require('./config.json'); + +// Utils +const render = (req, res, view, options) => res.render(view, { + ...options, + user: req.session.user, + nextUrl: req.url, +}); + +// Configuration +const app = express(); +app.set('view engine', 'pug'); +app.use(morgan('tiny')); +app.use(session({ + secret: config.secret, + resave: false, + saveUninitialized: false +})); + +// Middlewares +app.use(bodyParser.urlencoded({ + extended: false, +})); +app.use((req, res, next) => { + if (req.session.user || ['/', '/signup', '/login'].includes(req.url)) { + next(); + } else { + return res.redirect('/signup'); + } +}); + +app.get('/', (req, res) => { + return render(req, res, 'home'); +}); +app.get('/signup', (req, res) => { + return render(req, res, 'signup'); +}); +app.post('/signup', (req, res) => { + const passwordHash = bcrypt.hashSync(req.body.password, config.cryptRounds); + User.create(req.body, (err, user) => { + err ? console.error(err) : null; + user.passwordHash = passwordHash; + user.save(); + req.session.user = user; + return res.redirect('/'); + }); +}); +app.post('/login', (req, res) => { + User.findOne({ username: req.body.username }, (err, user) => { + err ? console.error(err) : null; + if (bcrypt.compareSync(req.body.password, user.passwordHash)) { + req.session.user = user; + return res.redirect(req.query.nextUrl || '/'); + } else { + console.error("Bad authentication"); + return res.redirect('/signup'); + } + }); +}); +app.post('/logout', (req, res) => { + req.session.destroy(); + return res.redirect('/'); +}) + +mongoose.connect('mongodb://localhost/rolegame', err => { + if (err) { + console.error('ERROR Unable to connect to Mongo database') + } else { + console.log('Server connected to Mongo database'); + } + app.listen(config.port, () => { + console.log(`Server listening on http://localhost:${config.port}`); + }); +}); diff --git a/models/character.js b/models/character.js new file mode 100644 index 0000000..95853d2 --- /dev/null +++ b/models/character.js @@ -0,0 +1,8 @@ +const mongoose = require('mongoose'); + +const Character = new mongoose.Schema({ + name: String, + level: Number, +}); + +module.exports = mongoose.model('Character', Character); \ No newline at end of file diff --git a/models/user.js b/models/user.js new file mode 100644 index 0000000..fdb9dc2 --- /dev/null +++ b/models/user.js @@ -0,0 +1,25 @@ +const mongoose = require('mongoose'); + +const User = new mongoose.Schema({ + firstName: { + type: String, + required: true, + }, + lastName: { + type: String, + }, + username: { + unique: true, + type: String, + lowercase: true + }, + email: { + type: String, + required: true, + }, + passwordHash: { + type: String, + }, +}); + +module.exports = mongoose.model('User', User); \ No newline at end of file diff --git a/models/weapon.js b/models/weapon.js new file mode 100644 index 0000000..1c74f2e --- /dev/null +++ b/models/weapon.js @@ -0,0 +1,8 @@ +const mongoose = require('mongoose'); + +const Weapon = new mongoose.Schema({ + name: String, + required_level: Number +}); + +module.exports = mongoose.model('Weapon', Weapon); \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..e3b0f52 --- /dev/null +++ b/package.json @@ -0,0 +1,16 @@ +{ + "scripts": { + "dev": "nodemon index.js" + }, + "dependencies": { + "bcrypt": "^3.0.4", + "body-parser": "^1.18.3", + "eslint": "^5.13.0", + "express": "^4.16.4", + "express-session": "^1.15.6", + "mongoose": "^5.4.11", + "morgan": "^1.9.1", + "nodemon": "^1.18.10", + "pug": "^2.0.3" + } +} diff --git a/views/base.pug b/views/base.pug new file mode 100644 index 0000000..38855d3 --- /dev/null +++ b/views/base.pug @@ -0,0 +1,36 @@ +<!DOCTYPE html> +html(lang="en") + head + meta(charset="UTF-8") + meta(name="viewport", content="width=device-width, initial-scale=1.0") + meta(http-equiv="X-UA-Compatible", content="ie=edge") + link(rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.4.1/semantic.min.css" integrity="sha256-9mbkOfVho3ZPXfM7W8sV2SndrGDuh7wuyLjtsWeTI1Q=" crossorigin="anonymous") + title RoleGame + body + block navbar + if !user + form.ui.form(action="/login", method="post") + .ui.pointing.menu + .right.menu + .item + .ui.input.transparent + input#username(type="text" name="username" placeholder="username") + .item + .ui.transparent.input + input#password(type="password" name="password" placeholder="password") + .item + .ui.transparent.input + input.ui.button(type="submit" value="Se connecter") + else + form.ui.form(action="/logout", method="post") + .ui.pointing.menu + .right.menu + .item #{user.firstName} #{user.lastName} + .item + button.ui.icon.button.basic(type="submit") + i.power.off.icon + .ui.container + block main + block script + script(src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha256-3edrmyuQ0w65f8gfBsqowzjJe2iM6n0nKciPUp8y+7E=" crossorigin="anonymous") + script(src="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.4.1/semantic.min.js" integrity="sha256-t8GepnyPmw9t+foMh3mKNvcorqNHamSKtKRxxpUEgFI=" crossorigin="anonymous") \ No newline at end of file diff --git a/views/home.pug b/views/home.pug new file mode 100644 index 0000000..c227ddd --- /dev/null +++ b/views/home.pug @@ -0,0 +1,4 @@ +extends base.pug + +block main + h1.ui.header Bienvenue sur RoleGame diff --git a/views/signup.pug b/views/signup.pug new file mode 100644 index 0000000..afcd907 --- /dev/null +++ b/views/signup.pug @@ -0,0 +1,22 @@ +extends base.pug + +block main + form.ui.form(action="/signup" method="post") + .two.fields + .field + label(for="firstName") Prénom + input#firstName(type="text", name="firstName") + .field + label(for="lastName") Nom de famille + input#lastName(type="text", name="lastName") + .field + label(for="email") Email + input#email(type="email", name="email") + .field + label(for="username") Nom d'utilisateur + input#username(type="text", name="username") + .field + label(for="password") Mot de passe + input#password(type="password", name="password") + .field + input.ui.button(type="submit", value="S'inscrire") \ No newline at end of file -- GitLab