CartoDB-SQL-API/lib/auth/oauth.js

192 lines
6.5 KiB
JavaScript
Raw Normal View History

2018-10-24 21:42:33 +08:00
'use strict';
// too bound to the request object, but ok for now
2015-05-13 00:00:30 +08:00
var _ = require('underscore');
var OAuthUtil = require('oauth-client');
var step = require('step');
var CdbRequest = require('../models/cartodb-request');
var cdbReq = new CdbRequest();
2019-12-24 01:19:08 +08:00
var oAuth = (function () {
var me = {
oauth_database: 3,
oauth_user_key: 'rails:oauth_access_tokens:<%= oauth_access_key %>',
is_oauth_request: true
};
// oauth token cases:
// * in GET request
// * in header
me.parseTokens = function (req) {
2019-12-27 01:28:01 +08:00
var queryOauth = _.clone(req.method === 'POST' ? req.body : req.query);
var headerOauth = {};
var oauthVariables = ['oauth_body_hash',
2019-12-24 01:19:08 +08:00
'oauth_consumer_key',
'oauth_token',
'oauth_signature_method',
'oauth_signature',
'oauth_timestamp',
'oauth_nonce',
'oauth_version'];
// pull only oauth tokens out of query
2019-12-27 01:28:01 +08:00
var nonOauth = _.difference(_.keys(queryOauth), oauthVariables);
_.each(nonOauth, function (key) { delete queryOauth[key]; });
2019-12-24 01:19:08 +08:00
// pull oauth tokens out of header
2019-12-27 01:28:01 +08:00
var headerString = req.headers.authorization;
if (!_.isUndefined(headerString)) {
_.each(oauthVariables, function (oauthKey) {
var matchedString = headerString.match(new RegExp(oauthKey + '="([^"]+)"'));
if (!_.isNull(matchedString)) {
headerOauth[oauthKey] = decodeURIComponent(matchedString[1]);
2019-12-24 01:19:08 +08:00
}
});
2015-05-13 00:00:30 +08:00
}
2019-12-24 01:19:08 +08:00
// merge header and query oauth tokens. preference given to header oauth
2019-12-27 01:28:01 +08:00
return _.defaults(headerOauth, queryOauth);
2019-12-24 01:19:08 +08:00
};
// remove oauthy tokens from an object
me.splitParams = function (obj) {
var removed = null;
for (var prop in obj) {
if (/^oauth_\w+$/.test(prop)) {
if (!removed) {
removed = {};
}
removed[prop] = obj[prop];
delete obj[prop];
}
}
2019-12-24 01:19:08 +08:00
return removed;
};
me.getAllowedHosts = function () {
var oauthConfig = global.settings.oauth || {};
return oauthConfig.allowedHosts || ['carto.com', 'cartodb.com'];
};
// do new fancy get User ID
me.verifyRequest = function (req, metadataBackend, callback) {
var that = this;
// TODO: review this
var httpProto = req.protocol;
if (!httpProto || (httpProto !== 'http' && httpProto !== 'https')) {
var msg = 'Unknown HTTP protocol ' + httpProto + '.';
var unknownProtocolErr = new Error(msg);
unknownProtocolErr.http_status = 500;
return callback(unknownProtocolErr);
}
2012-06-30 07:54:21 +08:00
2019-12-24 01:19:08 +08:00
var username = cdbReq.userByReq(req);
var requestTokens;
var signature;
step(
function getTokensFromURL () {
return oAuth.parseTokens(req);
},
function getOAuthHash (err, _requestTokens) {
if (err) {
throw err;
}
// this is oauth request only if oauth headers are present
this.is_oauth_request = !_.isEmpty(_requestTokens);
if (this.is_oauth_request) {
requestTokens = _requestTokens;
that.getOAuthHash(metadataBackend, requestTokens.oauth_token, this);
} else {
return null;
}
},
function regenerateSignature (err, oAuthHash) {
if (err) {
throw err;
}
if (!this.is_oauth_request) {
return null;
}
var consumer = OAuthUtil.createConsumer(oAuthHash.consumer_key, oAuthHash.consumer_secret);
2019-12-27 01:28:01 +08:00
var accessToken = OAuthUtil.createToken(oAuthHash.access_token_token, oAuthHash.access_token_secret);
var signer = OAuthUtil.createHmac(consumer, accessToken);
2019-12-24 01:19:08 +08:00
var method = req.method;
var hostsToValidate = {};
var requestHost = req.headers.host;
hostsToValidate[requestHost] = true;
that.getAllowedHosts().forEach(function (allowedHost) {
hostsToValidate[username + '.' + allowedHost] = true;
});
that.splitParams(req.query);
// remove oauth_signature from body
if (req.body) {
delete req.body.oauth_signature;
}
signature = requestTokens.oauth_signature;
// remove signature from requestTokens
delete requestTokens.oauth_signature;
var requestParams = _.extend({}, req.body, requestTokens, req.query);
var hosts = Object.keys(hostsToValidate);
var requestSignatures = hosts.map(function (host) {
var url = httpProto + '://' + host + req.path;
return signer.sign(method, url, requestParams);
});
return requestSignatures.reduce(function (validSignature, requestSignature) {
if (signature === requestSignature && !_.isUndefined(requestSignature)) {
validSignature = true;
}
return validSignature;
}, false);
},
function finishValidation (err, hasValidSignature) {
const authorizationLevel = hasValidSignature ? 'master' : null;
return callback(err, authorizationLevel);
}
);
};
2012-06-30 07:54:21 +08:00
2019-12-24 01:19:08 +08:00
me.getOAuthHash = function (metadataBackend, oAuthAccessKey, callback) {
metadataBackend.getOAuthHash(oAuthAccessKey, callback);
};
2012-06-30 07:54:21 +08:00
2019-12-24 01:19:08 +08:00
return me;
2015-05-13 00:00:30 +08:00
})();
2019-12-24 01:19:08 +08:00
function OAuthAuth (req, metadataBackend) {
this.req = req;
2018-02-27 02:02:05 +08:00
this.metadataBackend = metadataBackend;
this.isOAuthRequest = null;
}
2019-12-24 01:19:08 +08:00
OAuthAuth.prototype.verifyCredentials = function (callback) {
if (this.hasCredentials()) {
2018-02-27 02:02:05 +08:00
oAuth.verifyRequest(this.req, this.metadataBackend, callback);
} else {
callback(null, false);
}
};
2019-12-24 01:19:08 +08:00
OAuthAuth.prototype.getCredentials = function () {
return oAuth.parseTokens(this.req);
};
2019-12-24 01:19:08 +08:00
OAuthAuth.prototype.hasCredentials = function () {
if (this.isOAuthRequest === null) {
2019-12-27 01:28:01 +08:00
var passedTokens = oAuth.parseTokens(this.req);
this.isOAuthRequest = !_.isEmpty(passedTokens);
}
return this.isOAuthRequest;
};
module.exports = OAuthAuth;
module.exports.backend = oAuth;