Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Verification options to limit WS connection #369

Merged
merged 1 commit into from
May 8, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lib/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
require('./nts');
require('./s3');
try{require('./ws');}catch(e){require('./wsp/server');}
require('./verify');
require('./file');
module.exports = Gun;
}());
146 changes: 146 additions & 0 deletions lib/verify.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
var url = require('url');
var Gun = require('../gun');

/**
* Verify the origin
*
* @param {RegExp|Array|String|Function} allowed The allowed origins
* @param {String} origin String representation of the request URL
* @return {Boolean} Whether or not the origin is valid
*/
var verifyOrigin = function(allowed, origin) {
var isValid = false;
if (allowed instanceof RegExp) {
isValid = allowed.test(origin);
} else if (allowed instanceof Array) {
isValid = allowed.indexOf(origin) !== -1;
} else if (allowed instanceof Function) {
isValid = allowed(origin);
} else {
isValid = allowed === origin;
}
return isValid;
};

/**
* Verify the authentication header
*
* @todo make this callback based
*
* @param {Function|String} check Check option passed in
* @param {String} authToken The auth token passed in query string
* @param {Object} query Full query string as an object
* @return {Boolean} Whether or not the auth header is valid
*/
var verifyAuth = function(check, authToken, query) {
var isValid = false;
if (check instanceof Function) {
isValid = check(authToken, query);
} else {
isValid = check === authToken;
}
return isValid === true;
};

Gun.on('opt', function(context) {
var opt = context.opt || {};
var ws = opt.ws || {};

/**
* verify when instantiating Gun can contain the following keys:
* allowOrigins: Array|RegExp|String
* auth: String|Function
* authKey: String
* check: Function
*/
var verify = opt.verify || {};
if (!verify) {
this.to.next(context);
return;
}

if (ws.verifyClient && !verify.override) {
throw Error('Cannot override existing verifyClient option in `ws` configuration.');
}

/**
* Attach a verifyClient to the WS configuration.
*
* @param {Object} info Request information
* @param {Function} callback Called when verification is complete
*/
ws.verifyClient = function(info, callback) {

// Callback Definitions
var errorCallback = (errorCode, message) => {
callback(false, errorCode, message);
};
var successCallback = () => {
callback(true);
};

// 0. Verify security
if (verify.requireSecure && !info.secure) {
errorCallback(400, 'Insecure connection');
return;
}

// 1. Verify request origin
if (verify.allowOrigins && !verifyOrigin(verify.allowOrigins, info.origin)) {
errorCallback(403, 'Origin forbidden');
return;
}

// 2. Check authentication
if (verify.auth) {

// Retrieve parameters from the query string
// and convert into an object
var queryUrl = url.parse(info.req.url, true);
queryUrl.query = queryUrl.query || {};

// Get the header defined by the user
// Or use authorization by default.
var token = (verify.authKey)
? queryUrl.query[verify.authKey]
: queryUrl.query.authorization;

// Check the token against the verification function
if (!token || !verifyAuth(verify.auth, token, queryUrl.query)) {
errorCallback(403, 'Forbidden');
return;
}
}

// If no additional verification check is provided,
// simply return true at this point since all
// provided verifications have passed.
if (!verify.check) {
successCallback();
return;
}

// 3. Pass to generic check handler
// This can return a value; alternatively, this can use the
// callback functionality
var isValid = verify.check(info, successCallback, errorCallback);

// Check returned a response, pass this to the callback
// If not, assume the user will call
if (typeof isValid !== 'undefined') {
if (typeof isValid === 'boolean') {
if (isValid === true) {
successCallback();
} else {
errorCallback(400);
}
}
}
};
context.opt.ws = ws;

// Pass to next plugins
this.to.next(context);
});

module.exports = Gun;