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 || {}; if (!opt.verify) { this.to.next(context); return; } /** * 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 (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;