Created
December 14, 2015 21:04
-
-
Save lukaaash/e1af88c166acecf94a9a to your computer and use it in GitHub Desktop.
SFTP over WebSockets - simple server with JWT authentication and dummy custom filesystem
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var SFTP = require("sftp-ws"); | |
var jwt = require("jsonwebtoken"); | |
// prepare host and port | |
var host = "localhost"; | |
var port = process.env.port || 4001; | |
// start SFTP over WebSockets server | |
var server = new SFTP.Server({ | |
host: host, | |
port: port, | |
virtualRoot: ".", | |
readOnly: true, | |
verifyClient: verifyClient, | |
log: console // log to console | |
}); | |
console.log("SFTP server listening at ws://%s:%s", host, port); | |
// secret for JWT signatures | |
var secret = "this_is_secret"; | |
// create a dummy API key | |
var key = jwt.sign({ foo: "bar" }, secret); | |
console.log("Authenticate with this API key: %s", key); | |
// to connect, use the following code | |
/* | |
var client = new SFTP.Client(); | |
client.connect("ws://localhost:" + port, { | |
headers: { "Authorization" : "JWT " + key } | |
}); | |
*/ | |
function verifyClient(info, accept) { | |
// get credentials | |
var authorization = info.req.headers["authorization"]; | |
// handle JWT authentication | |
var match = authorization && authorization.match(/^(JWT|Bearer) (.*)$/i); | |
if (!match) { | |
reject("Pass your API key to authenticate."); | |
return; | |
} | |
// get API key | |
var token = match[2]; | |
// verify the token | |
jwt.verify(token, secret, prepare); | |
return; | |
function prepare(err, decoded) { | |
// handle errors | |
if (err) { | |
// log error | |
console.error(err); | |
reject("Token rejected."); | |
return; | |
} | |
// create custom filesystem object | |
var fs = new CustomFilesystem(token) | |
// accept credentials and pass the custom filesystem object | |
return accept({ | |
filesystem: fs, | |
virtualRoot: "/", | |
readOnly: false, | |
}); | |
} | |
function reject(message) { | |
// reject credentials | |
var headers = [ | |
"WWW-Authenticate: Basic realm=\"SFTP\"", | |
"SFTP-Authenticate-Info: " + message, | |
]; | |
accept(false, 401, "Not Authorized", headers); | |
} | |
} | |
var CustomFilesystem = (function () { | |
function CustomFilesystem(token) { | |
this.token = token; | |
this.inner = new SFTP.LocalFilesystem(); | |
} | |
CustomFilesystem.prototype.open = function (path, flags, attrs, callback) { | |
this.inner.open(path, flags, attrs, callback); | |
}; | |
CustomFilesystem.prototype.close = function (handle, callback) { | |
this.inner.close(handle, callback); | |
}; | |
CustomFilesystem.prototype.read = function (handle, buffer, offset, length, position, callback) { | |
this.inner.read(handle, buffer, offset, length, position, callback); | |
}; | |
CustomFilesystem.prototype.write = function (handle, buffer, offset, length, position, callback) { | |
this.inner.write(handle, buffer, offset, length, position, callback); | |
}; | |
CustomFilesystem.prototype.lstat = function (path, callback) { | |
this.inner.lstat(path, callback); | |
}; | |
CustomFilesystem.prototype.fstat = function (handle, callback) { | |
this.inner.fsetstat(handle, callback); | |
}; | |
CustomFilesystem.prototype.setstat = function (path, attrs, callback) { | |
this.inner.setstat(path, attrs, callback); | |
}; | |
CustomFilesystem.prototype.fsetstat = function (handle, attrs, callback) { | |
this.inner.fsetstat(handle, attrs, callback); | |
}; | |
CustomFilesystem.prototype.opendir = function (path, callback) { | |
this.inner.opendir(path, callback); | |
}; | |
CustomFilesystem.prototype.readdir = function (handle, callback) { | |
this.inner.readdir(handle, callback); | |
}; | |
CustomFilesystem.prototype.unlink = function (path, callback) { | |
this.inner.unlink(path, callback); | |
}; | |
CustomFilesystem.prototype.mkdir = function (path, attrs, callback) { | |
this.inner.mkdir(path, attrs, callback); | |
}; | |
CustomFilesystem.prototype.rmdir = function (path, callback) { | |
this.inner.rmdir(path, callback); | |
}; | |
CustomFilesystem.prototype.realpath = function (path, callback) { | |
this.inner.realpath(path, callback); | |
}; | |
CustomFilesystem.prototype.stat = function (path, callback) { | |
this.inner.stat(path, callback); | |
}; | |
CustomFilesystem.prototype.rename = function (oldPath, newPath, flags, callback) { | |
this.inner.rename(oldPath, newPath, flags, callback); | |
}; | |
CustomFilesystem.prototype.readlink = function (path, callback) { | |
this.inner.readlink(path, callback); | |
}; | |
CustomFilesystem.prototype.symlink = function (targetpath, linkpath, callback) { | |
this.inner.symlink(targetpath, linkpath, callback); | |
}; | |
CustomFilesystem.prototype.link = function (oldPath, newPath, callback) { | |
this.inner.link(oldPath, newPath, callback); | |
}; | |
return CustomFilesystem; | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment