'How to restrict users to only be able to view a specific folder in a statically served filesystem? Express.js

I have an express application set up to use a filesystem which contains a folder for each user.

app.use(express.static(mainFolder));

The problem is that I can't seem to restrict users from viewing each others files.

In account A, I have a folder for photos

mainFolder > AccountA > Photos > thePhoto.png

However, After I log into account B I can access this photo by typing in

localhost:8080/AccountA/Photos/thePhoto.png

I have already created a middleware to check that the user is logged in and this stops anyone not logged in from viewing the photo. Now I need a to restrict each user from viewing other accounts files/photos etc.

I am aware that I could do

app.use(express.static(mainFolder/accountA));

However I would have to this for each user. Could I put this in the authentication route so that after a user has authenticated the app uses this file?

I have also looked at How to serve user specific static content in Express.js and this does not help.

Here is the code for my middleware:

function checkAuthenticated(req, res, next) {
    if (req.isAuthenticated()) {
        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()
    }
}

Here is my 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 middleware = require("./middleware/index");

var workflowRoutes = require('./routes/workflow/workflowRoutes').router;
var storageRoutes = require('./routes/storage/storageRoutes').router;
var codeRoutes = require('./routes/code/codeRoutes').router;

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

function checkAuthenticated(req, res, next) {
    if (req.isAuthenticated()) {
        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()
    }
}

function checkUser(req,res,next){

    if(req.body.currentPath != undefined){
        console.log('CheckUser function ' + req.body.currentPath)
        var endDir = req.body.currentPath.replace(testFolder, '');
        var user = endDir.split('/')[1]
        if(user == req.user.name){
            console.log("USER " + user + " AUTHORISED FOR " + req.body.currentPath + " DIRECTORY")
            return next()
        } else {
            res.redirect("/Unauthorised");
            return "Failed";
        }
    } else {
        next();
    }
}

app.use(checkAuthenticated);
app.use(checkUser);

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(express.static(__dirname + "/public"));
app.use('/modules', express.static("modules"));


app.use("/", workflowRoutes);
app.use("/", storageRoutes);
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');
        } 
    })
})

//----------------------------------------------------------
//                   LOGIN/LOGOUT ROUTES
//----------------------------------------------------------

app.get("/login", function (req, res) {
    res.render("login.ejs");
})

app.post("/login", passport.authenticate('local'), function (req, res) {
    app.use(express.static(testFolder+"/"+req.user.name));
    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");


Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source