Browse Source

Got a basic web page working with a login screen.

pull/6/head
jrtechs 5 years ago
parent
commit
ae95297062
9 changed files with 344 additions and 5 deletions
  1. +10
    -0
      README.MD
  2. +7
    -1
      fileIO.js
  3. +39
    -0
      html/footer.html
  4. +42
    -0
      html/header.html
  5. +1
    -0
      html/home.html
  6. +26
    -0
      html/login.html
  7. +12
    -0
      html/mainTemplate.html
  8. +57
    -4
      server.js
  9. +150
    -0
      user.js

+ 10
- 0
README.MD View File

@ -13,3 +13,13 @@ This web system enables users to log into the system and view videos using the b
- Easy to install - Easy to install
- Easily expandable and hackable to fit your needs - 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
```

+ 7
- 1
fileIO.js View File

@ -21,6 +21,12 @@ module.exports =
*/ */
getFileAsJSON: function(fileName) 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');
} }
}; };

+ 39
- 0
html/footer.html View File

@ -0,0 +1,39 @@
<br><br>
<!-- Footer -->
<footer class="text-center">
<div class="footer-below">
<div class="container">
<div class="row">
<div class="col-lg-12">
<p class="m-0 text-center text-white"><p>&copy; 2019 Jrtechs<p></p>
</div>
</div>
</div>
</div>
</footer>
<script>
/**
* This is some simple JS to get the navbar working on a small/mobile screen
* without using the entire boostrap JS library.
*/
const e = document.querySelector(".navbar-toggler");
const t = document.querySelector(".navbar-collapse");
e.onclick = function()
{
if (e.getAttribute("aria-expanded") == "false")
{
t.classList.remove('collapse');
e.setAttribute('aria-expanded', true);
}
else
{
e.setAttribute("aria-expanded", false);
t.classList.add('collapse');
}
}
</script>
</body>
</html>

+ 42
- 0
html/header.html View File

@ -0,0 +1,42 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>Home Brew Plex Media Server</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-GJzZqFGwb1QTTN6wy59ffF1BuGJpLSa9DkKMp0DgiMDm4iYMj70gZWKYbI706tWS" crossorigin="anonymous">
<style>
html, body, h1, h2, h3, h4, h5, h6 {
font-family: 'Open Sans', sans-serif;
}
p{font-size:18px;}
</style>
</head>
<body>
<div class="navbar navbar-expand-lg navbar-light fixed-top bg-light" id="mainNav">
<div class="container">
<a class="navbar-brand" href="/">Home</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent"
aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav mr-auto">
<li class="nav-item">
<a class="nav-link" href="/users">Users</a>
</li>
</ul>
</div>
</div>
</div>
<br><br><br>

+ 1
- 0
html/home.html View File

@ -0,0 +1 @@
welcome

+ 26
- 0
html/login.html View File

@ -0,0 +1,26 @@
<div class="justify-content-center">
<div class="card">
<div class="card-header">
<h2>Login</h2>
</div>
<div class="card-body">
<form action="/login" method ="post" class="p-2">
<div class="form-group">
<label for="username">User Name</label>
<input class="form-control" type="text" id="username" name="username" placeholder="Enter username" required>
</div>
<div class="form-group">
<label for="password">Password</label>
<input class="form-control" type="password" name="password" id="password" placeholder="Password" required>
</div>
<div class="text-center">
<button class="btn btn-lg btn-secondary">Login</button>
</div>
<br>
</form>
{if invalidAttempt}
<p>Invalid Login attempt</p>
{/if}
</div>
</div>
</div>

+ 12
- 0
html/mainTemplate.html View File

@ -0,0 +1,12 @@
{>header}
<div class="container">
{if loggedIn}
{>main}
{else}
{>login}
{/if}
</div>
{>footer}

+ 57
- 4
server.js View File

@ -6,19 +6,72 @@ const session = require('express-session');
const fileIO = require('./fileIO'); const fileIO = require('./fileIO');
const userUtils = require('./user.js');
const app = express();
const app = express();
app.use(express.urlencoded()); app.use(express.urlencoded());
app.use(express.json()); // if needed app.use(express.json()); // if needed
const CONFIG_FILE_NAME = "conf.json";
const config = fileIO.getFileAsJSON(CONFIG_FILE_NAME);
/**Initializes sessions for login */ /**Initializes sessions for login */
app.use(session({ secret: "sessionSecret", cookie: { maxAge: 6000000 }}));
app.use(session({ secret: config.sessionSecret, cookie: { maxAge: 6000000 }}));
/** Template engine */ /** Template engine */
const whiskers = require('whiskers'); const whiskers = require('whiskers');
const CONFIG_FILE_NAME = "conf.json";
const config = fileIO.getFileAsJSON(CONFIG_FILE_NAME);
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}!`));

+ 150
- 0
user.js View File

@ -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
});
}
};

Loading…
Cancel
Save