NodeJS is a flexible asynchronous server. However, some of the many features that were available in Apache (like htaccess) are no longer an option. Instead, one must implement their own file protection.
Note: The following has been tested in Node v4.2.6
The advantage of a custom implementation is flexibility. Permissions cna be driven by a user database or within the logic of the express server.
The following code shows a simple implementation that uses a file-by-file approach to protection.
// server.js
#!/bin/env node
var express = require('express'),;
var fs = require('fs');
var path = require('path');
var App = function() {
var self = this;
self.port = 8080;
self.ipAddress = "127.0.0.1";
self.getFileExtension = function(file){
if (typeof(file)=='string'){
var splits = file.split('.');
return splits[splits.length-1];
}
return '';
};
self.getMimeType = function(ext){
if (typeof(ext)!='string'){
ext = '';
}
ext = ext.toLowerCase();
switch(ext)
{
case 'js' :
return 'application/x-javascript';
case 'json' :
return 'application/json';
case 'jpg' :
case 'jpeg' :
case 'jpe' :
return 'image/jpg';
case 'png' :
case 'gif' :
case 'bmp' :
case 'tiff' :
return 'image/'+ ext.toLowerCase();
case 'css' :
case 'map' :
return 'text/css';
case 'xml' :
return 'application/xml';
case 'doc' :
case 'docx' :
return 'application/msword';
case 'xls' :
case 'xlt' :
case 'xlm' :
case 'xld' :
case 'xla' :
case 'xlc' :
case 'xlw' :
case 'xll' :
return 'application/vnd.ms-excel';
case 'ppt' :
case 'pps' :
return 'application/vnd.ms-powerpoint';
case 'rtf' :
return 'application/rtf';
case 'pdf' :
return 'application/pdf';
case 'html' :
case 'htm' :
case 'php' :
return 'text/html';
case 'swift' :
case 'txt' :
return 'text/plain';
case 'mpeg' :
case 'mpg' :
case 'mpe' :
return 'video/mpeg';
case 'mp3' :
return 'audio/mpeg3';
case 'wav' :
return 'audio/wav';
case 'aiff' :
case 'aif' :
return 'audio/aiff';
case 'avi' :
return 'video/msvideo';
case 'wmv' :
return 'video/x-ms-wmv';
case 'mov' :
return 'video/quicktime';
case 'zip' :
return 'application/zip';
case 'tar' :
return 'application/x-tar';
case 'swf' :
return 'application/x-shockwave-flash';
default :
return 'application/octet-stream';
}
};
self.getFileMimeType = function(file){
return self.getMimeType(self.getFileExtension(file));
};
self.getAuthLogin = function(req){
var ret = {login: null, password: null};
var b64auth = '';
if (typeof(req)!="undefined"){
if (typeof(req.headers)!="undefined"){
if (typeof(req.headers.authorization)!="undefined"){
var splits = req.headers.authorization.split(' ');
if (splits.length > 1){
b64auth = splits[1];
}
}
}
}
var bufferSplits = new Buffer(b64auth, 'base64').toString().split(':');
ret.login = bufferSplits[0];
if (bufferSplits.length >1) {
ret.password = bufferSplits[1];
}
return ret;
};
self.protectFile = function(auth, file, req, res){
var a = self.getAuthLogin(req);
if (!a.login || !a.password || a.login !== auth.login || a.password !== auth.password) {
res.set('WWW-Authenticate', 'Basic realm="Protected"');
res.status(401).send('Authentication required.');
return;
}
var filePath = path.resolve(__dirname, file);
res.setHeader('Content-Type', self.getFileMimeType(filePath));
return res.send(fs.readFileSync(filePath));
};
self.initializeServer = function() {
self.app = express();
self.app.get('/protected-files/:file', function(req, res){
if (typeof(req.params.file)=="string"){
var file = req.params.file.toString().toLowerCase();
// define a protected file alias
if (file === 'myprotectedpdf.pdf') {
// the file is named e7d2555a-5b06-4d48-b085-527b39f37759.pdf
// that's a pretty secret name!
// the file is located in the adjacent 'privatefiles' directory
return self.protectFile({login: 'pdfuser', password: 'secret'}, "privatefiles/e7d2555a-5b06-4d48-b085-527b39f37759.pdf", req, res);
}
// maybe don't rename the file this time
if (file === 'test.txt') {
return self.protectFile({login: 'test', password: 'test'}, "privatefiles/test.txt", req, res);
}
}
return res.status(404).send('Not Found');
});
};
/**
* Initializes the sample application.
*/
self.initialize = function(callback) {
self.initializeServer();
if (typeof(callback)=="function"){
callback();
}
};
self.start = function() {
self.app.listen(self.port, self.ipAddress, function() {
console.log('%s: Node server started on %s:%d ...', Date(Date.now() ), self.ipAddress, self.port);
});
};
};
/**
* Main code.
*/
var app = new App();
app.initialize(function(){
app.start();
});
Now when a user tries to access a file, they must first enter a username and password.