diff --git a/assets/styles/gargoyle.scss b/assets/styles/gargoyle.scss index 763186f..79d402e 100644 --- a/assets/styles/gargoyle.scss +++ b/assets/styles/gargoyle.scss @@ -19,6 +19,12 @@ a{ } } +header{ + h1{ + text-align: center; + } +} + .button.button-primary, button.button-primary, input[type="button"].button-primary, @@ -56,6 +62,7 @@ input[type="submit"].button-primary{ } .nav-bar-right{ float: right; + margin-right: 35px; } ul{ diff --git a/index.js b/index.js index 08b71fd..22a63c4 100644 --- a/index.js +++ b/index.js @@ -42,6 +42,10 @@ app.use(express.urlencoded({ extended: true, })); +// load middleware +const userSessionMiddleware = require('./src/middleware/user-session'); +app.use(userSessionMiddleware.userSession); + // load the template engine app.set('view engine', 'twig'); @@ -50,12 +54,18 @@ app.use(express.static('public')); // load route handlers const homeRoutes = require('./src/routes/home'); +const authRoutes = require('./src/routes/auth'); const itemRoutes = require('./src/routes/item'); const licenseRoutes = require('./src/routes/license'); const searchRoutes = require('./src/routes/search'); // register route handlers app.get('/', homeRoutes.getIndex); +app.get('/auth/register', authRoutes.getRegister); +app.post('/auth/register', authRoutes.postRegister); +app.get('/auth/login', authRoutes.getLogin); +app.post('/auth/login', authRoutes.postLogin); +app.get('/auth/logout', authRoutes.getLogout); app.get('/item/add', itemRoutes.getAdd); app.post('/item/add', itemRoutes.postAdd); app.get('/item/:id', itemRoutes.getItem); diff --git a/migrations/0003_add_users_table.js b/migrations/0003_add_users_table.js new file mode 100644 index 0000000..8e378c0 --- /dev/null +++ b/migrations/0003_add_users_table.js @@ -0,0 +1,44 @@ +module.exports = { + up: (queryInterface, Sequelize) => { + return queryInterface.createTable('users', { + id: { + allowNull: false, + autoIncrement: true, + primaryKey: true, + type: Sequelize.INTEGER + }, + username: { + type: Sequelize.DataTypes.STRING, + allowNull: false, + unique: true, + }, + email: { + type: Sequelize.DataTypes.STRING, + allowNull: false, + unique: true, + }, + password: { + type: Sequelize.DataTypes.STRING, + allowNull: false, + }, + salt: { + type: Sequelize.DataTypes.STRING, + allowNull: false, + }, + firstName: { + type: Sequelize.DataTypes.STRING, + allowNull: true, + }, + lastName: { + type: Sequelize.DataTypes.STRING, + allowNull: true, + }, + createdAt: Sequelize.DataTypes.DATE, + updatedAt: Sequelize.DataTypes.DATE, + }); + }, + + down: (queryInterface, Sequelize) => { + return queryInterface.dropTable('users'); + } +}; diff --git a/src/middleware/user-session.js b/src/middleware/user-session.js new file mode 100644 index 0000000..386c17a --- /dev/null +++ b/src/middleware/user-session.js @@ -0,0 +1,18 @@ +const db = require('../models'); +const User = db.users; + +// checks if a session user ID is set, and if so grab the user's info +exports.userSession = async function(req, res, next) { + if ('user' in req.session) { + const user = await User.findAll({ + where: { + id: req.session.user, + }, + }); + + // pass user info to views + res.locals.user = user[0]; + } + + next(); +}; diff --git a/src/models/index.js b/src/models/index.js index c5736ea..937fcfb 100644 --- a/src/models/index.js +++ b/src/models/index.js @@ -10,6 +10,7 @@ db.sequelize = sequelize; db.items = require('./item.js')(sequelize, Sequelize); db.licenses = require('./license.js')(sequelize, Sequelize); +db.users = require('./user.js')(sequelize, Sequelize); module.exports = db; diff --git a/src/models/user.js b/src/models/user.js new file mode 100644 index 0000000..a69a2e3 --- /dev/null +++ b/src/models/user.js @@ -0,0 +1,39 @@ +module.exports = (sequelize, Sequelize) => { + const User = sequelize.define('user', { + + username: { + type: Sequelize.STRING, + allowNull: false, + unique: true, + }, + + email: { + type: Sequelize.STRING, + allowNull: false, + unique: true, + }, + + password: { + type: Sequelize.STRING, + allowNull: false, + }, + + salt: { + type: Sequelize.STRING, + allowNull: false, + }, + + firstName: { + type: Sequelize.STRING, + allowNull: true, + }, + + lastName: { + type: Sequelize.STRING, + allowNull: true, + }, + + }); + + return User; +}; diff --git a/src/routes/auth.js b/src/routes/auth.js new file mode 100644 index 0000000..f192972 --- /dev/null +++ b/src/routes/auth.js @@ -0,0 +1,58 @@ +const db = require('../models'); +const User = db.users; +const crypto = require('crypto'); + +// GET - /auth/login +exports.getLogin = async function(req, res) { + res.render('auth/login.twig'); +}; + +// POST - /auth/login +exports.postLogin = async function(req, res) { + const user = await User.findAll({ + where: { + username: req.body.login_username, + }, + }); + + const attemptedKey = crypto.pbkdf2Sync(req.body.login_password, user[0].salt, 10000, 64, 'sha512'); + const attemptedHash = attemptedKey.toString('hex'); + + if (attemptedHash == user[0].password) { + req.session.user = user[0].id; + res.redirect('/'); + } else { + res.redirect('/auth/login'); + } +} + +// GET - /auth/register +exports.getRegister = async function(req, res) { + res.render('auth/register.twig'); +}; + +// POST - /auth/register +exports.postRegister = async function(req, res) { + const passwordSalt = crypto.randomBytes(32).toString('base64'); + const passwordKey = crypto.pbkdf2Sync(req.body.register_password, passwordSalt, 10000, 64, 'sha512'); + const passwordHash = passwordKey.toString('hex'); + + const user = await User.create({ + username: req.body.register_username, + password: passwordHash, + salt: passwordSalt, + email: req.body.register_email, + firstName: req.body.register_first_name, + lastName: req.body.register_last_name, + }); + + res.redirect('/'); +}; + +// GET - /auth/logout +exports.getLogout = async function(req, res) { + // destroy the user's session + req.session.destroy(); + + res.redirect('/'); +} diff --git a/views/auth/login.twig b/views/auth/login.twig new file mode 100644 index 0000000..aab7874 --- /dev/null +++ b/views/auth/login.twig @@ -0,0 +1,40 @@ +{% extends 'layout.twig' %} + +{% block title %}Home{% endblock %} + +{% block content %} + + +
+
+

Login to your account.

+
+
+ +
+
+

.

+
+ +
+
+
+ +
+ +
+ +
+ + +
+
+
+ +{% endblock %} diff --git a/views/auth/register.twig b/views/auth/register.twig new file mode 100644 index 0000000..b112353 --- /dev/null +++ b/views/auth/register.twig @@ -0,0 +1,67 @@ +{% extends 'layout.twig' %} + +{% block title %}Home{% endblock %} + +{% block content %} + + +
+
+

Register for a new account.

+
+
+ +
+
+

.

+
+ +
+
+
+
+ +
+ +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+ +
+ +
+
+ + +
+
+
+ +{% endblock %} diff --git a/views/layout.twig b/views/layout.twig index e6862f2..c235302 100644 --- a/views/layout.twig +++ b/views/layout.twig @@ -33,6 +33,14 @@ + + {% if user %} + + + {% else %} + + + {% endif %}