I am writing some tests for some routes that are authenticated using passport and express-session. Under normal operation the routes authenticate fine and everything works as it should.
However when testing using chai, it does not authenticate properly, causing the middleware to redirect to /login.
By using console.log(req.isAuthenticated) shows that it authenticates and adds passport:{user: [userId]} to the session. But on the next request it does not retain the this and shows false for isAuthenticated.
app.js:
//------------------------------------------------------
// STORAGE SITE SERVER SIDE
//------------------------------------------------------
// Matthew Haywood
var path = require("path")
var express = require("express");
var app = express();
var bodyParser = require("body-parser");
var fs = require("fs-extra");
var methodOverride = require("method-override");
var passport = require("passport");
var session = require('express-session')
var flash = require('express-flash');
var bcrypt = require('bcryptjs');
var csv = require('csv-parser');
var schedule = require('node-schedule');
const { exec } = require('child_process');
var sanitizer = require('sanitize')();
var os = require('os-utils');
var addScheduledWorkflowFromFile = require('./routes/workflow/workflowRoutes').addScheduledWorkflowFromFile;
var checkUserMiddleware = require("./middleware/index").checkUser;
global.testFolder;
//Configure the app based on whether its development or production
if (process.env.NODE_ENV === 'production') {
testFolder = '/media/pi/ELEMENTS\ B/';
string_len = 22;
production_flag = true
}
if (process.env.NODE_ENV === 'development') {
testFolder = '/Users/matthaywood/Desktop/StorageSite/StorageSite/public/'
string_len = 59;
}
//PASSPORT SETUP
const initializePassport = require('./passport-config');
const { stdout, allowedNodeEnvironmentFlags } = require("process");
const { timeStamp } = require("console");
const { ResultWithContext } = require("express-validator/src/chain");
initializePassport(
passport,
email => users.find(user => user.email === email),
id => users.find(user => user.id === id)
)
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({ extended: false }))
app.use(flash())
app.use(session({
secret: "Once again rusty is the cutest dog",
// resave: false,
// saveUninitialized: false
}))
app.use(passport.initialize());
app.use(passport.session());
app.use(methodOverride("_method"));
app.use(function (req, res, next) {
res.locals.currentUser = req.user;
next();
});
global.users = [];
global.scheduledWorkflows = []
//LOAD USERS IN FROM JSON FILE
var loadedData = JSON.parse(fs.readFileSync("users.json"))
console.log(loadedData.Users)
users = loadedData.Users
addScheduledWorkflowFromFile()
app.use('/modules', express.static("modules"));
app.use(checkUserMiddleware, express.static(testFolder));
app.use("/", codeRoutes);
//----------------------------------------------------------
// ROUTES
//----------------------------------------------------------
app.get("/", function (req, res) {
res.render("clusterHome.ejs");
})
//Settings Route for displaying current connected HDD
app.get("/settings", function (req, res) {
res.render("settings.ejs");
})
//--------------------------------------------------------------------
// ROUTES FOR USERS AND LOGIN
//--------------------------------------------------------------------
//Register
app.get("/register", function (req, res) {
res.render("register.ejs");
})
app.post("/register", function (req, res) {
var password = req.body.password;
// var name = sanitizer.value(req.body.name,String);
var name = req.body.name;
// var email = sanitizer.value(req.body.email,String);
var email = req.body.email;
var the_hash;
console.log('CREATING NEW ACCOUNT')
bcrypt.hash(password, 10, function (err, hash) {
console.log(err)
the_hash = hash;
the_id = Date.now().toString();
users.push({
id: the_id,
name: name,
email: email,
password: hash
})
fs.mkdirSync(testFolder + name);
fs.mkdirSync(testFolder + name + "/" + "Workflows");
fs.mkdirSync(testFolder + name + "/Workflows/Logs" );
fs.mkdirSync(testFolder + name + "/" + "Code");
var data = JSON.parse(fs.readFileSync("users.json"))
data.Users.push({
id: the_id,
name: name,
email: email,
password: hash
})
data = JSON.stringify(data, null, 2)
fs.writeFileSync("users.json", data)
passport.authenticate('local')(req, res, function () {
res.redirect("/");
})
if (err) {
console.log(err)
res.redirect('/register');
}
})
})
app.post("/delete-account", function(req,res){
var name = req.body.userName
console.log("DELETING ACCOUNT")
console.log(testFolder+name)
var data = JSON.parse(fs.readFileSync("users.json"))
var recordToBeDeleted;
var index;
let obj2 = data.Users.filter((o, i) => {
if(o['name'] == name){
recordToBeDeleted = o
index = i
}
})
data.Users.splice(index, 1)
data = JSON.stringify(data, null, 2)
fs.writeFileSync("users.json", data)
fs.rmdirSync(testFolder + name,{recursive:true})
res.redirect("/")
})
//----------------------------------------------------------
// LOGIN/LOGOUT ROUTES
//----------------------------------------------------------
app.get("/login", function (req, res) {
res.render("login.ejs");
})
app.post("/login", passport.authenticate('local'), function (req, res) {
// console.log(req.session)
// console.log(req.isAuthenticated())
// console.log(req.user)
// console.log(res.user)
res.redirect("/");
})
//Logout
app.delete("/logout", function (req, res) {
req.logOut();
res.redirect('/login')
})
//Unauthorised Route
app.get("/unauthorized", function(req, res){
res.render("unauthorised.ejs");
})
//----------------------------------------------------------
// LOG ROUTES
//----------------------------------------------------------
app.get("/logs", function (req, res) {
res.render("logs.ejs")
})
app.get("/logs/get-data", function (req, res) {
os.cpuUsage(function (v) {
res.status(200).send({ cpuUsage: v })
});
})
//----------------------------------------------------------
// AUTO GIT PULL SCHEDULE
//----------------------------------------------------------
//Only run this job if in production to pull from github
if (process.env.NODE_ENV === 'production') {
const job = schedule.scheduleJob('*/5 * * * *', function () {
exec('git pull', (err, stdout, stderr) => {
if (err) {
console.log(err);
} else {
console.log("Pulling from git repository")
console.log(stdout)
//Restart server if git has pulled changes
if(!stdout.includes("Already up to date.")){
process.exit(1)
}
}
})
})
}
function serverLog(string){
//function used to log to a file
}
app.listen("8080");
module.exports = app; //Export app for testing
Tests:
var server = require('../app.js')
var chai = require('chai')
var chaihttp = require('chai-http')
let should = chai.should();
var expect = chai.expect();
var chaiFiles = require('chai-files');
var request = require('supertest');
const { session } = require('passport');
var Cookie;
chai.use(chaihttp)
// chai.use(passport.session())
//TEST GET ROUTES
const userCredentials = {
"id": "1662139278022",
"name": "JohnDoe",
"email": "JohnDoe@email.com",
"password": "abcd"
}
var authenticatedUser = request.agent(server)
beforeEach(function(done){
authenticatedUser
.post('/login')
.set('Connection', 'keep-alive')
.send(userCredentials)
.end(function(err, res){
user = userCredentials.user
res.should.have.status(302)
console.log(res.headers)
done()
})
})
describe("MAIN 1 - Test GET / route", () => {
it('Should return index page', (done) => {
chai.request(server)
.get('/')
.end((err, res)=> {
res.should.have.status(200);
done();
})
})
})
describe("MAIN 2 - Test GET /register route", () => {
it('Should return index page', (done) => {
chai.request(server)
.get('/register')
.end((err, res)=> {
res.should.have.status(200);
done();
})
})
})
describe("MAIN 3 - Test GET /login route", () => {
it('Should return index page', (done) => {
chai.request(server)
.get('/login')
.end((err, res)=> {
res.should.have.status(200);
done();
})
})
})
describe("MAIN 4 - Test GET /unauthorized route", () => {
it('Should return index page', (done) => {
chai.request(server)
.get('/unauthorized')
.end((err, res)=> {
res.should.have.status(200);
done();
})
})
})
//TEST POST ROUTES
describe("MAIN 5 - Test POST /register route", () => {
it('Should register a new account and create all of the associated files with an account', (done) => {
var password = 'abcd'
var name = 'JohnDoe'
var email = 'JohnDoe@email.com'
chai.request(server)
.post('/register')
.send({
password:password,
name: name,
email: email
})
.end((err, res)=> {
Cookie = res.header["set-cookie"]
res.should.have.status(200);
done();
})
})
})
describe("MAIN 6 - Test POST /login route", () => {
it('Should register a new account and create all of the associated files with an account', (done) => {
var password = 'abcd'
var email = 'JohnDoe@email.com'
chai.request(server)
.post('/login')
.send({
password:password,
email: email,
// provider: 'local'
})
.set("Cookie", Cookie)
.end((err, res)=> {
res.should.have.status(200);
done();
})
})
})
describe("MAIN 7 - Test POST /delete-account route", () => {
it('Should delete an account and remove all of the associated files with', (done) => {
chai.request(server)
.post('/delete-account')
.end((err, res)=> {
res.should.have.status(200)
done();
})
})
})
Output from tests:
> storagesite@1.0.0 test
> mocha --exit
express-session deprecated undefined resave option; provide resave option app.js:63:9
express-session deprecated undefined saveUninitialized option; provide saveUninitialized option app.js:63:9
[
{
id: '1652363745447',
name: 'newaccounta',
email: 'newaccounta@gmail.com',
password: '$2a$10$.aRWTBze1sLZMd9q7m0EK.BNdM0cc/yuSm2/aUqLy06NRdIDq436.'
}
{
id: '1662139278022',
name: 'JohnDoe',
email: 'JohnDoe@email.com',
password: '$2a$10$.aOjplRKpnAEj/gWxwaBD.onWqAMRGO9QkdVuw04Np0Hp6QT8xa8u'
}
]
MAIN 1 - Test GET / route
Middleware
Session {
cookie: { path: '/', _expires: null, originalMaxAge: null, httpOnly: true }
}
false
/login
{
'x-powered-by': 'Express',
location: '/',
vary: 'Accept',
'content-type': 'text/plain; charset=utf-8',
'content-length': '23',
'set-cookie': [
'connect.sid=s%3ATwbTHtqs9Lsdph-YbmKadeedPz7Qw0mG.1OW4Ypu6nd%2FhXvN6mn%2FJCCntN3jQ0%2FFHVRmlnEbUqXw; Path=/; HttpOnly'
],
date: 'Sat, 03 Sep 2022 12:30:46 GMT',
connection: 'keep-alive',
'keep-alive': 'timeout=5'
}
Middleware
Session {
cookie: { path: '/', _expires: null, originalMaxAge: null, httpOnly: true }
}
false
/
(node:43749) [DEP0066] DeprecationWarning: OutgoingMessage.prototype._headers is deprecated
(Use `node --trace-deprecation ...` to show where the warning was created)
Middleware
Session {
cookie: { path: '/', _expires: null, originalMaxAge: null, httpOnly: true }
}
false
/login
✔ Should return index page
MAIN 2 - Test GET /register route
Middleware
Session {
cookie: { path: '/', _expires: null, originalMaxAge: null, httpOnly: true },
passport: { user: '1662139278022' }
}
true
/login
{
'x-powered-by': 'Express',
location: '/',
vary: 'Accept',
'content-type': 'text/plain; charset=utf-8',
'content-length': '23',
date: 'Sat, 03 Sep 2022 12:30:46 GMT',
connection: 'keep-alive',
'keep-alive': 'timeout=5'
}
Middleware
Session {
cookie: { path: '/', _expires: null, originalMaxAge: null, httpOnly: true }
}
false
/register
✔ Should return index page
MAIN 3 - Test GET /login route
Middleware
Session {
cookie: { path: '/', _expires: null, originalMaxAge: null, httpOnly: true },
passport: { user: '1662139278022' }
}
true
/login
{
'x-powered-by': 'Express',
location: '/',
vary: 'Accept',
'content-type': 'text/plain; charset=utf-8',
'content-length': '23',
date: 'Sat, 03 Sep 2022 12:30:46 GMT',
connection: 'keep-alive',
'keep-alive': 'timeout=5'
}
Middleware
Session {
cookie: { path: '/', _expires: null, originalMaxAge: null, httpOnly: true }
}
false
/login
✔ Should return index page
MAIN 4 - Test GET /unauthorized route
Middleware
Session {
cookie: { path: '/', _expires: null, originalMaxAge: null, httpOnly: true },
passport: { user: '1662139278022' }
}
true
/login
{
'x-powered-by': 'Express',
location: '/',
vary: 'Accept',
'content-type': 'text/plain; charset=utf-8',
'content-length': '23',
date: 'Sat, 03 Sep 2022 12:30:46 GMT',
connection: 'keep-alive',
'keep-alive': 'timeout=5'
}
Middleware
Session {
cookie: { path: '/', _expires: null, originalMaxAge: null, httpOnly: true }
}
false
/unauthorized
Middleware
Session {
cookie: { path: '/', _expires: null, originalMaxAge: null, httpOnly: true }
}
false
/login
✔ Should return index page
MAIN 5 - Test POST /register route
Middleware
Session {
cookie: { path: '/', _expires: null, originalMaxAge: null, httpOnly: true },
passport: { user: '1662139278022' }
}
true
/login
{
'x-powered-by': 'Express',
location: '/',
vary: 'Accept',
'content-type': 'text/plain; charset=utf-8',
'content-length': '23',
date: 'Sat, 03 Sep 2022 12:30:46 GMT',
connection: 'keep-alive',
'keep-alive': 'timeout=5'
}
Middleware
Session {
cookie: { path: '/', _expires: null, originalMaxAge: null, httpOnly: true }
}
false
/register
CREATING NEW ACCOUNT
null
Middleware
Session {
cookie: { path: '/', _expires: null, originalMaxAge: null, httpOnly: true }
}
false
/
Middleware
Session {
cookie: { path: '/', _expires: null, originalMaxAge: null, httpOnly: true }
}
false
/login
✔ Should register a new account and create all of the associated files with an account (143ms)
MAIN 6 - Test POST /login route
Middleware
Session {
cookie: { path: '/', _expires: null, originalMaxAge: null, httpOnly: true },
passport: { user: '1662139278022' }
}
true
/login
{
'x-powered-by': 'Express',
location: '/',
vary: 'Accept',
'content-type': 'text/plain; charset=utf-8',
'content-length': '23',
date: 'Sat, 03 Sep 2022 12:30:47 GMT',
connection: 'keep-alive',
'keep-alive': 'timeout=5'
}
Middleware
Session {
cookie: { path: '/', _expires: null, originalMaxAge: null, httpOnly: true },
flash: {}
}
false
/login
Middleware
Session {
cookie: { path: '/', _expires: null, originalMaxAge: null, httpOnly: true },
flash: {},
passport: { user: '1662139278022' }
}
true
/
✔ Should register a new account and create all of the associated files with an account (69ms)
MAIN 7 - Test POST /delete-account route
Middleware
Session {
cookie: { path: '/', _expires: null, originalMaxAge: null, httpOnly: true },
passport: { user: '1662139278022' }
}
true
/login
{
'x-powered-by': 'Express',
location: '/',
vary: 'Accept',
'content-type': 'text/plain; charset=utf-8',
'content-length': '23',
date: 'Sat, 03 Sep 2022 12:30:47 GMT',
connection: 'keep-alive',
'keep-alive': 'timeout=5'
}
Middleware
Session {
cookie: { path: '/', _expires: null, originalMaxAge: null, httpOnly: true }
}
false
/delete-account
Middleware
Session {
cookie: { path: '/', _expires: null, originalMaxAge: null, httpOnly: true }
}
false
/login
✔ Should delete an account and remove all of the associated files with
STORAGE ROUTES - Test GET /storage-home route
Middleware
Session {
cookie: { path: '/', _expires: null, originalMaxAge: null, httpOnly: true },
passport: { user: '1662139278022' }
}
true
/login
{
'x-powered-by': 'Express',
location: '/',
vary: 'Accept',
'content-type': 'text/plain; charset=utf-8',
'content-length': '23',
date: 'Sat, 03 Sep 2022 12:30:47 GMT',
connection: 'keep-alive',
'keep-alive': 'timeout=5'
}
✔ Should return the page for storage home
8 passing (873ms)
Middleware:
function checkUser(req,res,next){
//May need to refactor this as this is working but a bit hacky using req.originalURl etc
//Also does not work for videos
console.log("Middleware")
console.log(req.session)
console.log(req.isAuthenticated())
console.log(req.url)
var user = req.originalUrl.split("/")[1];
var user = user.replace("%20", " ");
if (req.isAuthenticated()) {
if(req.originalUrl.includes('.') && req.originalUrl != "/style.css" && req.originalUrl != "/prism.css" && req.originalUrl != "/prism.js"){
if(user == req.user.name){
console.log("USER " + user + " AUTHORISED FOR " + req.body.currentPath + " DIRECTORY")
return next()
} else {
res.sendStatus(403)
}
} else {
return next();
}
} else if(req.url != "/login" && req.url != "/register" && req.url != "/register?" && req.originalUrl != "/register?" ){
res.redirect('/login');
} else if(req.url === "/login" || req.url === "/register" || req.url === "/register?" || req.originalUrl === "register"){
return next()
}
}