From ae9529706269a6feea202341aca9eb50a341c562 Mon Sep 17 00:00:00 2001 From: jrtechs Date: Sat, 2 Feb 2019 15:04:18 -0500 Subject: [PATCH] Got a basic web page working with a login screen. --- README.MD | 10 +++ fileIO.js | 8 ++- html/footer.html | 39 +++++++++++ html/header.html | 42 ++++++++++++ html/home.html | 1 + html/login.html | 26 +++++++ html/mainTemplate.html | 12 ++++ server.js | 61 +++++++++++++++-- user.js | 150 +++++++++++++++++++++++++++++++++++++++++ 9 files changed, 344 insertions(+), 5 deletions(-) create mode 100644 html/footer.html create mode 100644 html/header.html create mode 100644 html/home.html create mode 100644 html/login.html create mode 100644 html/mainTemplate.html create mode 100644 user.js diff --git a/README.MD b/README.MD index 9372e13..2daba11 100644 --- a/README.MD +++ b/README.MD @@ -13,3 +13,13 @@ This web system enables users to log into the system and view videos using the b - Easy to install - Easily expandable and hackable to fit your needs + + +npm dependencies +```bash +npm install express --save +npm install fs --save +npm install express-session --save +npm install whiskers --save + +``` \ No newline at end of file diff --git a/fileIO.js b/fileIO.js index 02fae92..85a9668 100644 --- a/fileIO.js +++ b/fileIO.js @@ -21,6 +21,12 @@ module.exports = */ getFileAsJSON: function(fileName) { - return JSON.parse(fs.readFileSync(fileName, 'utf8')); + return JSON.parse(module.exports.getFile(fileName)); + }, + + + getFile: function(filename) + { + return fs.readFileSync(filename, 'utf8'); } }; diff --git a/html/footer.html b/html/footer.html new file mode 100644 index 0000000..1a29ceb --- /dev/null +++ b/html/footer.html @@ -0,0 +1,39 @@ +

+ + + + + + + \ No newline at end of file diff --git a/html/header.html b/html/header.html new file mode 100644 index 0000000..95935e4 --- /dev/null +++ b/html/header.html @@ -0,0 +1,42 @@ + + + + + Home Brew Plex Media Server + + + + + + + + + + + + + + +


\ No newline at end of file diff --git a/html/home.html b/html/home.html new file mode 100644 index 0000000..b6cbc2d --- /dev/null +++ b/html/home.html @@ -0,0 +1 @@ +welcome \ No newline at end of file diff --git a/html/login.html b/html/login.html new file mode 100644 index 0000000..bc1dca4 --- /dev/null +++ b/html/login.html @@ -0,0 +1,26 @@ +
+
+
+

Login

+
+
+
+
+ + +
+
+ + +
+
+ +
+
+
+ {if invalidAttempt} +

Invalid Login attempt

+ {/if} +
+
+
\ No newline at end of file diff --git a/html/mainTemplate.html b/html/mainTemplate.html new file mode 100644 index 0000000..c26cb1f --- /dev/null +++ b/html/mainTemplate.html @@ -0,0 +1,12 @@ +{>header} + +
+ {if loggedIn} + {>main} + {else} + {>login} + {/if} + +
+ +{>footer} \ No newline at end of file diff --git a/server.js b/server.js index 2bb53a9..6c36891 100644 --- a/server.js +++ b/server.js @@ -6,19 +6,72 @@ const session = require('express-session'); const fileIO = require('./fileIO'); +const userUtils = require('./user.js'); -const app = express(); +const app = express(); app.use(express.urlencoded()); app.use(express.json()); // if needed + +const CONFIG_FILE_NAME = "conf.json"; +const config = fileIO.getFileAsJSON(CONFIG_FILE_NAME); + /**Initializes sessions for login */ -app.use(session({ secret: "sessionSecret", cookie: { maxAge: 6000000 }})); +app.use(session({ secret: config.sessionSecret, cookie: { maxAge: 6000000 }})); /** Template engine */ const whiskers = require('whiskers'); -const CONFIG_FILE_NAME = "conf.json"; -const config = fileIO.getFileAsJSON(CONFIG_FILE_NAME); \ No newline at end of file + + +function fetchInTemplate(templateContext, templateKey, filename) +{ + templateContext[templateKey] = fileIO.getFile(filename); +} + +function renderHTML(request, result, templateFile, templateDependencyFunction) +{ + var templateContext = Object(); + var prom = []; + + + prom.push(fileIO.getFile("./html/mainTemplate.html")); + prom.push(fetchInTemplate(templateContext, "header", "./html/header.html")); + prom.push(fetchInTemplate(templateContext, "footer", "./html/footer.html")); + if(request.session.login === true) + { + templateContext.loggedIn = true; + prom.push(fetchInTemplate(templateContext, "main","./html/" + templateFile)); + } + else + { + prom.push(fetchInTemplate(templateContext, "login","./html/login.html")); + } + + Promise.all(prom).then(function(content) + { + result.write(whiskers.render(content[0], templateContext)); + result.end(); + }); +} + +app.get('/', (req, res) => renderHTML(req, res, "home.html", null)); + +app.use(express.static('css')); +app.use(express.static('js')); + + +app.post('/login', function(request, result) +{ + if(userUtils.checkLogin(request.body.username, request.body.password, config)) + { + request.session.login = true; + request.session.username = request.body.username; + } + result.redirect('/'); +}); + +app.listen(config.port, () => console.log(`App listening on port ${config.port}!`)); \ No newline at end of file diff --git a/user.js b/user.js new file mode 100644 index 0000000..72f6310 --- /dev/null +++ b/user.js @@ -0,0 +1,150 @@ +/** Crypto package used for hashing */ +const crypto = require('crypto'); + +/** + * Helper function to generate a hashed password + * from a given plain text password. + * + * This uses 64 bits of entropy as the random salt + * and uses sha256 hashing method to hash the password + * combined with the salt. + * + * @param password + * @returns {Object pass: hashedPassword, salt: salt used to hash} + */ +const createHashedPasswordObject = function(password) +{ + const randBuff = crypto.randomBytes(64); + + const salt = crypto.createHash('sha256').update(randBuff).digest('hex'); + + const hashPass = hashPassword(password, salt); + + var hashPassObject = new Object(); + hashPassObject.pass = hashPass; + hashPassObject.salt = salt; + return hashPassObject; +}; + + +/** + * Hashes a pasword with a aprticular salt + * using the crypto library + * + * @param password + * @param salt + */ +const hashPassword = function(password, salt) +{ + return crypto.createHash('sha256') + .update(password + salt) + .digest('hex'); +}; + +/** + * Fetches the index of the user in the configuration. If the + * user does not exists a -1 is returned. + */ +const getIndexOfUser = function(username, configuration) +{ + for(var i = 0; i < configuration.users.length; i++) + { + if (username === configuration.users[i].username) + { + if(username === configuration.users[i].username) + { + return i; + } + } + } + return -1; +}; + + +module.exports = + { + /** + * Checks to see if there was a valid login attempt + * + * @param username + * @param password + * @param configuration + * @returns {boolean} + */ + checkLogin: function(username, password, configuration) + { + const userIndex = getIndexOfUser(username, configuration); + if(userIndex === -1) + return false; + + const hashedPassword = hashPassword(password, configuration.users[userIndex].salt); + return configuration.users[userIndex].password == hashedPassword; + }, + + /** + * Adds a user to the configuration + * + * @param username + * @param password + * @param configuration + * @returns {boolean} + */ + addUser: function(username, password, configuration) + { + const userIndex = getIndexOfUser(username, configuration); + if(userIndex !== -1) + return false; // user already exists + + var newUser = new Object(); + newUser.username = username; + + if(configuration.users.length === 0) + newUser.id = 1; + else + newUser.id = configuration.users[configuration.users.length -1].id + 1; + + const passObject = createHashedPasswordObject(password); + newUser.salt = passObject.salt; + newUser.password = passObject.pass; + + configuration.push(newUser); + + return true; + }, + + /** + * Edits a user based on their id + * + * @param id + * @param userName + * @param password + * @param configuration + */ + editUser: function(id, userName, password, configuration) + { + for(var i = 0; i < configuration.users.length; i++) + { + if (configuration.users[i].id + "" === id) + { + configuration.users[i].username = userName; + + var passObj = createHashedPasswordObject(password); + configuration.users[i].salt = passObj.salt; + configuration.users[i].password = passObj.pass; + } + } + }, + + /** + * Removes a user account from the configuration + * @param id + * @param configuration + */ + removeUser: function(id, configuration) + { + configuration.users = configuration.users.filter(function(value, index, arr) + { + return value.id + "" !== id + }); + } + };