diff --git a/README.md b/README.md index 27dee56..ca533a1 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,7 @@ frontail is node.js application for serving `tail -F` output to browser using [s ### Features +* basic auth * filtering logs (press Tab to focus input) * terminal-like autoscrolling * marking logs @@ -20,14 +21,16 @@ frontail is node.js application for serving `tail -F` output to browser using [s Options: - -h, --help output usage information - -V, --version output the version number - -p, --port server port, default 9001 - -n, --number starting lines number, default 10 - -l, --lines number on lines stored in frontend, default 2000 - -d, --daemonize run as daemon - --pid-path if run as deamon file that will store the process ID, default /var/run/frontail.pid - --log-path if run as deamon file that will be used as a log, default /dev/null + -h, --help output usage information + -V, --version output the version number + -p, --port server port, default 9001 + -n, --number starting lines number, default 10 + -l, --lines number on lines stored in frontend, default 2000 + -d, --daemonize run as daemon + -U, --user Basic Auth user + -P, --password Basic Auth password + --pid-path if run as daemon file that will store the process ID, default /var/run/frontail.pid + --log-path if run as daemon file that will be used as a log, default /dev/null Web interface is on http://localhost:[port] @@ -39,7 +42,7 @@ Web interface is on http://localhost:[port] (The MIT License) -Copyright 2012 Maciej Winnicki http://maciejwinnicki.pl +Copyright 2013 Maciej Winnicki http://maciejwinnicki.pl This project is free software released under the MIT/X11 license: diff --git a/lib/frontail.js b/lib/frontail.js index d0248f0..785ed8a 100644 --- a/lib/frontail.js +++ b/lib/frontail.js @@ -1,11 +1,12 @@ -var program = require('commander') - , http = require('http') - , fs = require('fs') - , socketio = require('socket.io') - , spawn = require('child_process').spawn - , connect = require('connect') - , sanitizer = require('validator').sanitize - , daemon = require('daemon'); +var program = require('commander') + , http = require('http') + , fs = require('fs') + , socketio = require('socket.io') + , spawn = require('child_process').spawn + , connect = require('connect') + , cookieParser = require('cookie') + , sanitizer = require('validator').sanitize + , daemon = require('daemon'); /** * Parse arg @@ -15,11 +16,13 @@ program .usage('[options] [file ...]') .option('-p, --port ', 'server port, default 9001', Number, 9001) .option('-n, --number ', 'starting lines number, default 10', Number, 10) - .option('-l, --lines ', 'number on lines stored in browser, default 2000', Number, 2000) + .option('-l, --lines ', 'number on lines stored in browser, default 2000', Number, 2000) .option('-d, --daemonize', 'run as daemon') - .option('--pid-path ', 'if run as deamon file that will store the process ID, default /var/run/frontail.pid', + .option('-U, --user ', 'Basic Auth user', String, false) + .option('-P, --password ', 'Basic Auth password', String, false) + .option('--pid-path ', 'if run as daemon file that will store the process id, default /var/run/frontail.pid', String, '/var/run/frontail.pid') - .option('--log-path ', 'if run as deamon file that will be used as a log, default /dev/null', + .option('--log-path ', 'if run as daemon file that will be used as a log, default /dev/null', String, '/dev/null') .parse(process.argv); @@ -31,13 +34,31 @@ if (program.args.length === 0) { process.exit(); } else { var files = program.args; + + var doAuthorization = false; + if (program.user && program.password) { + doAuthorization = true; + var sessionSecret = String(+new Date()) + Math.random(); + var sessionKey = 'sid'; + } } if (program.daemonize) { + /** + * Daemonize process + */ var logFile = fs.openSync(program.logPath, 'a'); + var args = ['-p', program.port, '-n', program.number, '-l', program.lines]; + + if (doAuthorization) { + args = args.concat(['-U', program.user, '-P', program.password]); + } + + args = args.concat(files); + var proc = daemon.daemon( __filename, - ['-p', program.port, '-n', program.number, files], + args, { stdout: logFile, stderr: logFile @@ -46,23 +67,35 @@ if (program.daemonize) { fs.writeFileSync(program.pidPath, proc.pid); } else { /** - * Server setup + * HTTP server setup */ - var app = connect() - .use(connect.static(__dirname + '/assets')) - .use(function (req, res) { - fs.readFile(__dirname + '/index.html', function (err, data) { - if (err) { - res.writeHead(500, {'Content-Type': 'text/plain'}); - res.end('Interal error'); - } else { - res.writeHead(200, {'Content-Type': 'text/html'}); - res.end(data.toString('utf-8').replace( - /__TITLE__/g, 'tail -F ' + files.join(' ')), 'utf-8' - ); - } - }); - }); + var app = connect(); + + if (doAuthorization) { + app + .use(connect.cookieParser()) + .use(connect.session({secret: sessionSecret, key: sessionKey})) + .use(connect.basicAuth(function (user, password) { + return program.user == user & program.password == password; + })); + } + + app + .use(connect.static(__dirname + '/web/assets')) + .use(function (req, res) { + fs.readFile(__dirname + '/web/index.html', function (err, data) { + if (err) { + res.writeHead(500, {'Content-Type': 'text/plain'}); + res.end('Internal error'); + } else { + res.writeHead(200, {'Content-Type': 'text/html'}); + res.end(data.toString('utf-8').replace( + /__TITLE__/g, 'tail -F ' + files.join(' ')), 'utf-8' + ); + } + }); + }); + var server = http.createServer(app).listen(program.port); /** @@ -70,6 +103,21 @@ if (program.daemonize) { */ var io = socketio.listen(server, {log: false}); + if (doAuthorization) { + io.set('authorization', function(handshakeData, accept) { + if (handshakeData.headers.cookie) { + var cookie = cookieParser.parse(handshakeData.headers.cookie); + var sessionId = connect.utils.parseSignedCookie(cookie[sessionKey], sessionSecret); + if (sessionId) { + return accept(null, true); + } + return accept('Invalid cookie', false); + } else { + return accept('No cookie in header', false); + } + }); + } + /** * When connected send starting data */ diff --git a/lib/assets/app.js b/lib/web/assets/app.js similarity index 100% rename from lib/assets/app.js rename to lib/web/assets/app.js diff --git a/lib/assets/styles.css b/lib/web/assets/styles.css similarity index 100% rename from lib/assets/styles.css rename to lib/web/assets/styles.css diff --git a/lib/index.html b/lib/web/index.html similarity index 100% rename from lib/index.html rename to lib/web/index.html diff --git a/package.json b/package.json index 4d902e0..3512dfd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "frontail", - "version": "0.3.1", + "version": "0.3.2", "description": "tail -F output in browser", "homepage": "https://github.com/mthenw/frontail", "author": { @@ -16,7 +16,8 @@ "socket.io": "0.9.13", "connect": "2.7.3", "validator": "0.4.24", - "daemon": "1.0.0" + "daemon": "1.0.0", + "cookie": "0.1.0" }, "repository": { "type": "git",