diff --git a/.gitignore b/.gitignore index 8a7a5499..77b66660 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ node_modules/* config.json logs/* pids/* +.idea/ diff --git a/app.js b/app.js index 84f431ad..a8f2306b 100755 --- a/app.js +++ b/app.js @@ -24,24 +24,27 @@ // // Module dependencies // -var express = require('express'), - util = require('util'), - fs = require('fs'), - path = require('path'), - OAuth = require('oauth').OAuth, - OAuth2 = require('oauth/lib/oauth2').OAuth2, - query = require('querystring'), - url = require('url'), - http = require('http'), - https = require('https'), - crypto = require('crypto'), - redis = require('redis'), - RedisStore = require('connect-redis')(express); +var express = require('express'), + util = require('util'), + fs = require('fs'), + path = require('path'), + OAuth = require('oauth').OAuth, + OAuth2 = require('oauth/lib/oauth2').OAuth2, + query = require('querystring'), + url = require('url'), + http = require('http'), + https = require('https'), + crypto = require('crypto'), + domain = require('domain'), + redis = require('redis'), + jwt = require('jwt-simple'), + request = require('request'), + RedisStore = require('connect-redis')(express); // Configuration try { var config = require('./config.json'); -} catch(e) { +} catch (e) { console.error("File config.json not found or is invalid. Try: `cp config.json.sample config.json`"); process.exit(1); } @@ -53,7 +56,7 @@ var defaultDB = '0'; config.redis.database = config.redis.database || defaultDB; if (process.env.REDISTOGO_URL) { - var rtg = require("url").parse(process.env.REDISTOGO_URL); + var rtg = require("url").parse(process.env.REDISTOGO_URL); config.redis.host = rtg.hostname; config.redis.port = rtg.port; config.redis.password = rtg.auth.split(":")[1]; @@ -62,9 +65,9 @@ if (process.env.REDISTOGO_URL) { var db = redis.createClient(config.redis.port, config.redis.host); db.auth(config.redis.password); -db.on("error", function(err) { +db.on("error", function (err) { if (config.debug) { - console.log("Error " + err); + console.log("Error " + err); } }); @@ -83,34 +86,47 @@ try { if (config.debug) { console.log(util.inspect(apisConfig)); } -} catch(e) { +} catch (e) { console.error("File apiconfig.json not found or is invalid."); process.exit(1); } var app = module.exports = express(); -app.configure(function() { +app.configure(function () { app.set('views', __dirname + '/views'); app.set('view engine', 'jade'); + app.use(function (req, res, next) { + var d = domain.create(); + d.on("error", function (error) { + console.log(error); + res.end("Error procesing:", error.message); + }); + d.add(req); + d.add(res); + d.add(next); + d.run(function () { + next(); + }); + }); app.use(express.logger()); app.use(express.bodyParser()); app.use(express.methodOverride()); app.use(express.cookieParser()); app.use(express.session({ secret: config.sessionSecret, - store: new RedisStore({ - 'host': config.redis.host, - 'port': config.redis.port, - 'pass': config.redis.password, - 'db' : config.redis.database, + store: new RedisStore({ + 'host': config.redis.host, + 'port': config.redis.port, + 'pass': config.redis.password, + 'db': config.redis.database, 'maxAge': 1209600000 }) })); // Global basic authentication on server (applied if configured) if (config.basicAuth && config.basicAuth.username && config.basicAuth.password) { - app.use(express.basicAuth(function(user, pass, callback) { + app.use(express.basicAuth(function (user, pass, callback) { var result = (user === config.basicAuth.username && pass === config.basicAuth.password); callback(null /* error */, result); })); @@ -122,11 +138,11 @@ app.configure(function() { app.use(express.static(__dirname + '/public')); }); -app.configure('development', function() { +app.configure('development', function () { app.use(express.errorHandler({ dumpExceptions: true, showStack: true })); }); -app.configure('production', function() { +app.configure('production', function () { app.use(express.errorHandler()); }); @@ -144,12 +160,12 @@ function oauth(req, res, next) { refererURL = url.parse(req.headers.referer), callbackURL = refererURL.protocol + '//' + refererURL.host + '/authSuccess/' + apiName, oa = new OAuth(apiConfig.oauth.requestURL, - apiConfig.oauth.accessURL, - apiKey, - apiSecret, - apiConfig.oauth.version, - callbackURL, - apiConfig.oauth.crypt); + apiConfig.oauth.accessURL, + apiKey, + apiSecret, + apiConfig.oauth.version, + callbackURL, + apiConfig.oauth.crypt); if (config.debug) { console.log('OAuth type: ' + apiConfig.oauth.type); @@ -160,7 +176,7 @@ function oauth(req, res, next) { } // Check if the API even uses OAuth, then if the method requires oauth, then if the session is not authed - if (apiConfig.oauth.type == 'three-legged' && req.body.oauth == 'authrequired' && (!req.session[apiName] || !req.session[apiName].authed) ) { + if (apiConfig.oauth.type == 'three-legged' && req.body.oauth == 'authrequired' && (!req.session[apiName] || !req.session[apiName].authed)) { if (config.debug) { console.log('req.session: ' + util.inspect(req.session)); console.log('headers: ' + util.inspect(req.headers)); @@ -168,7 +184,7 @@ function oauth(req, res, next) { console.log('sessionID: ' + util.inspect(req.sessionID)); } - oa.getOAuthRequestToken(function(err, oauthToken, oauthTokenSecret, results) { + oa.getOAuthRequestToken(function (err, oauthToken, oauthTokenSecret, results) { if (err) { res.send("Error getting OAuth request token : " + util.inspect(err), 500); } else { @@ -202,22 +218,76 @@ function oauth(req, res, next) { } -function oauth2(req, res, next){ +function oauth2(req, res, next) { console.log('OAuth2 process started'); var apiName = req.body.apiName, apiConfig = apisConfig[apiName]; - if (apiConfig.oauth2) { - var apiKey = req.body.apiKey || req.body.key, - apiSecret = req.body.apiSecret || req.body.secret, - refererURL = url.parse(req.headers.referer), - callbackURL = refererURL.protocol + '//' + refererURL.host + '/oauth2Success/' + apiName, - key = req.sessionID + ':' + apiName, - oa = new OAuth2(apiKey, - apiSecret, - apiConfig.oauth2.baseSite, - apiConfig.oauth2.authorizeURL, - apiConfig.oauth2.accessTokenURL); + if (!apiConfig.oauth2) { + next(); + return; + } + + var apiKey = req.body.apiKey || req.body.key, + apiSecret = req.body.apiSecret || req.body.secret, + refererURL = url.parse(req.headers.referer), + callbackURL = refererURL.protocol + '//' + refererURL.host + '/oauth2Success/' + apiName, + key = req.sessionID + ':' + apiName; + + if (apiConfig.oauth2.type == 'bearer-jwt') { + console.log("Starting the JWT Bearer OAUTH, v2.0"); + + //API url + var accessTokenAPIUrl = apiConfig.protocol + "://" + apiConfig.baseURL + apiConfig.oauth2.accessTokenURL; + + var claimSet = { + id: apiKey, + scope: "all", //For now only all scope is supported + exp: (1000 * 60 * 60 * 24) //24 hours + }; + + var jwtToken = jwt.encode(claimSet, apiSecret); + + request.post(accessTokenAPIUrl, { + form: { + "grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer", //Only this grant type is supported, i.e. Bearer. Pass this string as it is. + "assertion": jwtToken //pass the token + } + }, function (err, resp, body) { + if (err) { + res.send("Error getting OAuth access token : " + util.inspect(err), 500); + } + var bodyObj = JSON.parse(body); + var token = bodyObj[apiConfig.oauth2.tokenName]; + console.log("Access Token:", token); + if (!token) { + req.session[apiName] = null; + db.del(key + ':access_token', function () { + db.del(key + ':refresh_token', function () { + res.send(body); + }); + }); + } else { + db.mset([key + ':access_token', token, + key + ':refresh_token', token + ], function (err, results2) { + req.session[apiName] = {}; + req.session[apiName].authed = true; + if (config.debug) { + console.log('session[apiName].authed: ' + util.inspect(req.session)); + } + res.end(body); + }); + } + } + ); + + } else { + var oa = new OAuth2(apiKey, + apiSecret, + apiConfig.oauth2.baseSite, + apiConfig.oauth2.authorizeURL, + apiConfig.oauth2.accessTokenURL); if (apiConfig.oauth2.tokenName) { oa.setAccessTokenName(apiConfig.oauth2.tokenName); @@ -233,7 +303,7 @@ function oauth2(req, res, next){ var redirectUrl; if (apiConfig.oauth2.type == 'authorization-code') { - redirectUrl = oa.getAuthorizeUrl({redirect_uri : callbackURL, response_type : 'code'}); + redirectUrl = oa.getAuthorizeUrl({redirect_uri: callbackURL, response_type: 'code'}); db.set(key + ':apiKey', apiKey, redis.print); db.set(key + ':apiSecret', apiSecret, redis.print); @@ -248,7 +318,7 @@ function oauth2(req, res, next){ } else if (apiConfig.oauth2.type == 'implicit') { oa._authorizeUrl = oa._accessTokenUrl; - redirectUrl = oa.getAuthorizeUrl({redirect_uri : callbackURL, response_type : 'token'}); + redirectUrl = oa.getAuthorizeUrl({redirect_uri: callbackURL, response_type: 'token'}); db.set(key + ':apiKey', apiKey, redis.print); db.set(key + ':apiSecret', apiSecret, redis.print); @@ -265,10 +335,10 @@ function oauth2(req, res, next){ var accessURL = apiConfig.oauth2.baseSite + apiConfig.oauth2.accessTokenURL; var basic_cred = apiKey + ':' + apiSecret; var encoded_basic = new Buffer(basic_cred).toString('base64'); - + var http_method = (apiConfig.oauth2.authorizationHeader == 'Y') ? "POST" : "GET"; - var header = (apiConfig.oauth2.authorizationHeader == 'Y') ? {'Authorization' : 'Basic ' + encoded_basic} : ''; - var fillerpost = query.stringify({grant_type : "client_credentials", client_id : apiKey, client_secret : apiSecret}); + var header = (apiConfig.oauth2.authorizationHeader == 'Y') ? {'Authorization': 'Basic ' + encoded_basic} : ''; + var fillerpost = query.stringify({grant_type: "client_credentials", client_id: apiKey, client_secret: apiSecret}); db.set(key + ':apiKey', apiKey, redis.print); db.set(key + ':apiSecret', apiSecret, redis.print); @@ -280,38 +350,38 @@ function oauth2(req, res, next){ db.expire(key + ':baseURL', 1209600000); //client_credentials w/Authorization header - oa._request(http_method, accessURL, header, + oa._request(http_method, accessURL, header, fillerpost, - '', function(error, data, response) { - if (error) { - res.send("Error getting OAuth access token : " + util.inspect(error), 500); - } - else { - var results; - try { - results = JSON.parse(data); - } - catch(e) { - results = query.parse(data) + '', function (error, data, response) { + if (error) { + res.send("Error getting OAuth access token : " + util.inspect(error), 500); } - var oauth2access_token = results["access_token"]; - var oauth2refresh_token = results["refresh_token"]; + else { + var results; + try { + results = JSON.parse(data); + } + catch (e) { + results = query.parse(data) + } + var oauth2access_token = results["access_token"]; + var oauth2refresh_token = results["refresh_token"]; - if (config.debug) { - console.log('results: ' + util.inspect(results)); - } - db.mset([key + ':access_token', oauth2access_token, + if (config.debug) { + console.log('results: ' + util.inspect(results)); + } + db.mset([key + ':access_token', oauth2access_token, key + ':refresh_token', oauth2refresh_token - ], function(err, results2) { - db.set(key + ':accessToken', oauth2access_token, redis.print); - db.set(key + ':refreshToken', oauth2refresh_token, redis.print); - db.expire(key + ':accessToken', 1209600000); - db.expire(key + ':refreshToken', 1209600000); - - res.send({'refresh': callbackURL}); - }); - } - }) + ], function (err, results2) { + db.set(key + ':accessToken', oauth2access_token, redis.print); + db.set(key + ':refreshToken', oauth2refresh_token, redis.print); + db.expire(key + ':accessToken', 1209600000); + db.expire(key + ':refreshToken', 1209600000); + + res.send({'refresh': callbackURL}); + }); + } + }) } } } @@ -319,72 +389,72 @@ function oauth2(req, res, next){ function oauth2Success(req, res, next) { console.log('oauth2Success started'); - var apiKey, - apiSecret, - apiName = req.params.api, - apiConfig = apisConfig[apiName], - key = req.sessionID + ':' + apiName, - baseURL; - - if (config.debug) { - console.log('apiName: ' + apiName); - console.log('key: ' + key); - console.log(util.inspect(req.params)); - } - db.mget([ - key + ':apiKey', - key + ':apiSecret', - key + ':baseURL', - key + ':accessToken', - key + ':refreshToken' - ], function(err, result) { - if (err) { - console.log(util.inspect(err)); - } - apiKey = result[0]; - apiSecret = result[1]; - baseURL = result[2]; + var apiKey, + apiSecret, + apiName = req.params.api, + apiConfig = apisConfig[apiName], + key = req.sessionID + ':' + apiName, + baseURL; - if (result[3] && apiConfig.oauth2.type == 'client_credentials') { - req.session[apiName] = {}; - req.session[apiName].authed = true; - if (config.debug) { - console.log('session[apiName].authed: ' + util.inspect(req.session)); - } - next(); - } + if (config.debug) { + console.log('apiName: ' + apiName); + console.log('key: ' + key); + console.log(util.inspect(req.params)); + } + db.mget([ + key + ':apiKey', + key + ':apiSecret', + key + ':baseURL', + key + ':accessToken', + key + ':refreshToken' + ], function (err, result) { + if (err) { + console.log(util.inspect(err)); + } + apiKey = result[0]; + apiSecret = result[1]; + baseURL = result[2]; + if (result[3] && apiConfig.oauth2.type == 'client_credentials') { + req.session[apiName] = {}; + req.session[apiName].authed = true; if (config.debug) { - console.log(util.inspect(">>"+req.query.oauth_verifier)); + console.log('session[apiName].authed: ' + util.inspect(req.session)); } + next(); + } - var oa = new OAuth2(apiKey, - apiSecret, - apiConfig.oauth2.baseSite, - apiConfig.oauth2.authorizeURL, - apiConfig.oauth2.accessTokenURL); + if (config.debug) { + console.log(util.inspect(">>" + req.query.oauth_verifier)); + } - if (apiConfig.oauth2.tokenName) { - oa.setAccessTokenName(apiConfig.oauth2.tokenName); - } + var oa = new OAuth2(apiKey, + apiSecret, + apiConfig.oauth2.baseSite, + apiConfig.oauth2.authorizeURL, + apiConfig.oauth2.accessTokenURL); - if (config.debug) { - console.log(util.inspect(oa)); - } + if (apiConfig.oauth2.tokenName) { + oa.setAccessTokenName(apiConfig.oauth2.tokenName); + } + + if (config.debug) { + console.log(util.inspect(oa)); + } - if (apiConfig.oauth2.type == 'authorization-code') { - oa.getOAuthAccessToken(req.query.code, - {grant_type : "authorization_code", redirect_uri : baseURL, client_id : apiKey, client_secret : apiSecret}, - function(error, oauth2access_token, oauth2refresh_token, results){ + if (apiConfig.oauth2.type == 'authorization-code') { + oa.getOAuthAccessToken(req.query.code, + {grant_type: "authorization_code", redirect_uri: baseURL, client_id: apiKey, client_secret: apiSecret}, + function (error, oauth2access_token, oauth2refresh_token, results) { if (error) { - res.send("Error getting OAuth access token : " + util.inspect(error) + "["+oauth2access_token+"]"+ "["+oauth2refresh_token+"]", 500); + res.send("Error getting OAuth access token : " + util.inspect(error) + "[" + oauth2access_token + "]" + "[" + oauth2refresh_token + "]", 500); } else { if (config.debug) { console.log('results: ' + util.inspect(results)); } db.mset([key + ':access_token', oauth2access_token, - key + ':refresh_token', oauth2refresh_token - ], function(err, results2) { + key + ':refresh_token', oauth2refresh_token + ], function (err, results2) { req.session[apiName] = {}; req.session[apiName].authed = true; if (config.debug) { @@ -394,11 +464,11 @@ function oauth2Success(req, res, next) { }); } }); - } - else if (apiConfig.oauth2.type == 'implicit') { - next(); - } - }); + } + else if (apiConfig.oauth2.type == 'implicit') { + next(); + } + }); } @@ -426,7 +496,7 @@ function oauthSuccess(req, res, next) { key + ':requestTokenSecret', key + ':apiKey', key + ':apiSecret' - ], function(err, result) { + ], function (err, result) { if (err) { console.log(util.inspect(err)); } @@ -436,34 +506,34 @@ function oauthSuccess(req, res, next) { apiSecret = result[3]; if (config.debug) { - console.log(util.inspect(">>"+oauthRequestToken)); - console.log(util.inspect(">>"+oauthRequestTokenSecret)); - console.log(util.inspect(">>"+req.query.oauth_verifier)); + console.log(util.inspect(">>" + oauthRequestToken)); + console.log(util.inspect(">>" + oauthRequestTokenSecret)); + console.log(util.inspect(">>" + req.query.oauth_verifier)); } var oa = new OAuth(apiConfig.oauth.requestURL, - apiConfig.oauth.accessURL, - apiKey, - apiSecret, - apiConfig.oauth.version, - null, - apiConfig.oauth.crypt); + apiConfig.oauth.accessURL, + apiKey, + apiSecret, + apiConfig.oauth.version, + null, + apiConfig.oauth.crypt); if (config.debug) { console.log(util.inspect(oa)); } - oa.getOAuthAccessToken(oauthRequestToken, oauthRequestTokenSecret, req.query.oauth_verifier, function(error, oauthAccessToken, oauthAccessTokenSecret, results) { + oa.getOAuthAccessToken(oauthRequestToken, oauthRequestTokenSecret, req.query.oauth_verifier, function (error, oauthAccessToken, oauthAccessTokenSecret, results) { if (error) { - res.send("Error getting OAuth access token : " + util.inspect(error) + "["+oauthAccessToken+"]"+ "["+oauthAccessTokenSecret+"]"+ "["+util.inspect(results)+"]", 500); + res.send("Error getting OAuth access token : " + util.inspect(error) + "[" + oauthAccessToken + "]" + "[" + oauthAccessTokenSecret + "]" + "[" + util.inspect(results) + "]", 500); } else { if (config.debug) { console.log('results: ' + util.inspect(results)); } db.mset([key + ':accessToken', oauthAccessToken, key + ':accessTokenSecret', oauthAccessTokenSecret - ], function(err, results2) { + ], function (err, results2) { req.session[apiName] = {}; req.session[apiName].authed = true; if (config.debug) { @@ -489,7 +559,7 @@ function processRequest(req, res, next) { var reqQuery = req.body, customHeaders = {}, params = reqQuery.params || {}, - locations = reqQuery.locations || {}, + locations = reqQuery.locations || {}, methodURL = reqQuery.methodUri, httpMethod = reqQuery.httpMethod, apiKey = reqQuery.apiKey, @@ -500,27 +570,27 @@ function processRequest(req, res, next) { implicitAccessToken = reqQuery.accessToken; for (var param in params) { - if (params.hasOwnProperty(param)) { - if (params[param] !== '') { - if (locations[param] == 'header') { - // Extract custom headers from the params - customHeaders[param] = params[param]; - delete params[param]; - } else { - // Replace placeholders in the methodURL with matching params - // URL params are prepended with ":" - var regx = new RegExp(':' + param); - - // If the param is actually a part of the URL, put it in the URL and remove the param - if (!!regx.test(methodURL)) { - methodURL = methodURL.replace(regx, encodeURIComponent(params[param])); - delete params[param] - } - } - } else { - delete params[param]; // Delete blank params - } - } + if (params.hasOwnProperty(param)) { + if (params[param] !== '') { + if (locations[param] == 'header') { + // Extract custom headers from the params + customHeaders[param] = params[param]; + delete params[param]; + } else { + // Replace placeholders in the methodURL with matching params + // URL params are prepended with ":" + var regx = new RegExp(':' + param); + + // If the param is actually a part of the URL, put it in the URL and remove the param + if (!!regx.test(methodURL)) { + methodURL = methodURL.replace(regx, encodeURIComponent(params[param])); + delete params[param] + } + } + } else { + delete params[param]; // Delete blank params + } + } } var baseHostInfo = apiConfig.baseURL.split(':'); @@ -549,7 +619,7 @@ function processRequest(req, res, next) { path: apiConfig.publicPath + methodURL// + ((paramString.length > 0) ? '?' + paramString : "") }; - if (['POST','DELETE','PUT'].indexOf(httpMethod) !== -1) { + if (['POST', 'DELETE', 'PUT'].indexOf(httpMethod) !== -1) { var requestBody = query.stringify(params); } @@ -563,24 +633,24 @@ function processRequest(req, res, next) { } db.mget([key + ':apiKey', - key + ':apiSecret', - key + ':accessToken', - key + ':accessTokenSecret' - ], - function(err, results) { - - var apiKey = (typeof reqQuery.apiKey == "undefined" || reqQuery.apiKey == "undefined")?results[0]:reqQuery.apiKey, - apiSecret = (typeof reqQuery.apiSecret == "undefined" || reqQuery.apiSecret == "undefined")?results[1]:reqQuery.apiSecret, + key + ':apiSecret', + key + ':accessToken', + key + ':accessTokenSecret' + ], + function (err, results) { + + var apiKey = (typeof reqQuery.apiKey == "undefined" || reqQuery.apiKey == "undefined") ? results[0] : reqQuery.apiKey, + apiSecret = (typeof reqQuery.apiSecret == "undefined" || reqQuery.apiSecret == "undefined") ? results[1] : reqQuery.apiSecret, accessToken = results[2], accessTokenSecret = results[3]; var oa = new OAuth(apiConfig.oauth.requestURL || null, - apiConfig.oauth.accessURL || null, - apiKey || null, - apiSecret || null, - apiConfig.oauth.version || null, - null, - apiConfig.oauth.crypt); + apiConfig.oauth.accessURL || null, + apiKey || null, + apiSecret || null, + apiConfig.oauth.version || null, + null, + apiConfig.oauth.crypt); if (config.debug) { console.log('Access token: ' + accessToken); @@ -588,7 +658,7 @@ function processRequest(req, res, next) { console.log('key: ' + key); } - oa.getProtectedResource(privateReqURL, httpMethod, accessToken, accessTokenSecret, function (error, data, response) { + oa.getProtectedResource(privateReqURL, httpMethod, accessToken, accessTokenSecret, function (error, data, response) { req.call = privateReqURL; if (error) { @@ -619,15 +689,15 @@ function processRequest(req, res, next) { var body, oa = new OAuth(null, - null, - apiKey || null, - apiSecret || null, - apiConfig.oauth.version || null, - null, - apiConfig.oauth.crypt); + null, + apiKey || null, + apiSecret || null, + apiConfig.oauth.version || null, + null, + apiConfig.oauth.crypt); var resource = options.protocol + '://' + options.host + options.path, - cb = function(error, data, response) { + cb = function (error, data, response) { if (error) { if (error.data == 'Server Error' || error.data == '') { req.result = 'Server Error'; @@ -667,14 +737,14 @@ function processRequest(req, res, next) { switch (httpMethod) { case 'GET': console.log(resource); - oa.get(resource, '', '',cb); + oa.get(resource, '', '', cb); break; case 'PUT': case 'POST': oa.post(resource, '', '', JSON.stringify(obj), null, cb); break; case 'DELETE': - oa.delete(resource,'','',cb); + oa.delete(resource, '', '', cb); break; } @@ -687,13 +757,13 @@ function processRequest(req, res, next) { if (implicitAccessToken) { db.mset([key + ':access_token', implicitAccessToken - ], function(err, results2) { - req.session[apiName] = {}; - req.session[apiName].authed = true; - if (config.debug) { - console.log('session[apiName].authed: ' + util.inspect(req.session)); - } - }); + ], function (err, results2) { + req.session[apiName] = {}; + req.session[apiName].authed = true; + if (config.debug) { + console.log('session[apiName].authed: ' + util.inspect(req.session)); + } + }); } if (reqQuery.oauth == 'authrequired' || (req.session[apiName] && req.session[apiName].authed)) { @@ -702,21 +772,21 @@ function processRequest(req, res, next) { } db.mget([key + ':apiKey', - key + ':apiSecret', - key + ':access_token', - key + ':refresh_token' - ], - function(err, results) { - var apiKey = (typeof reqQuery.apiKey == "undefined" || reqQuery.apiKey == "undefined")?results[0]:reqQuery.apiKey, - apiSecret = (typeof reqQuery.apiSecret == "undefined" || reqQuery.apiSecret == "undefined")?results[1]:reqQuery.apiSecret, + key + ':apiSecret', + key + ':access_token', + key + ':refresh_token' + ], + function (err, results) { + var apiKey = (typeof reqQuery.apiKey == "undefined" || reqQuery.apiKey == "undefined") ? results[0] : reqQuery.apiKey, + apiSecret = (typeof reqQuery.apiSecret == "undefined" || reqQuery.apiSecret == "undefined") ? results[1] : reqQuery.apiSecret, access_token = (implicitAccessToken) ? implicitAccessToken : results[2], refresh_token = results[3]; var oa = new OAuth2(apiKey, - apiSecret, - apiConfig.oauth2.baseSite, - apiConfig.oauth2.authorizeURL, - apiConfig.oauth2.accessTokenURL); + apiSecret, + apiConfig.oauth2.baseSite, + apiConfig.oauth2.authorizeURL, + apiConfig.oauth2.accessTokenURL); if (apiConfig.oauth2.tokenName) { oa.setAccessTokenName(apiConfig.oauth2.tokenName); @@ -729,12 +799,18 @@ function processRequest(req, res, next) { } if (apiConfig.oauth2.authorizationHeader && (apiConfig.oauth2.authorizationHeader == 'Y')) { - var headers = {Authorization : "Bearer " + access_token}; + var headers = {Authorization: "Bearer " + access_token}; } oa._request(httpMethod, privateReqURL, headers, requestBody, access_token, function (error, data, response) { req.call = privateReqURL; + if (error && error.statusCode == 201) { + data = error.data; + response = {headers: {statusCode: error.statusCode}, statusCode: error.statusCode}; + error = null; + } + if (error) { console.log('Got error: ' + util.inspect(error)); @@ -749,7 +825,8 @@ function processRequest(req, res, next) { next(); } else { req.resultHeaders = response.headers; - req.result = JSON.parse(data); + req.result = data; + res.statusCode = response.statusCode; next(); } }); @@ -768,7 +845,7 @@ function processRequest(req, res, next) { function unsecuredCall() { console.log('Unsecured Call'); - if (['POST','PUT','DELETE'].indexOf(httpMethod) === -1) { + if (['POST', 'PUT', 'DELETE'].indexOf(httpMethod) === -1) { options.path += ((paramString.length > 0) ? '?' + paramString : ""); } @@ -788,13 +865,13 @@ function processRequest(req, res, next) { var timeStamp, sig; if (apiConfig.signature.type == 'signed_md5') { // Add signature parameter - timeStamp = Math.round(new Date().getTime()/1000); + timeStamp = Math.round(new Date().getTime() / 1000); sig = crypto.createHash('md5').update('' + apiKey + apiSecret + timeStamp + '').digest(apiConfig.signature.digest); options.path += '&' + apiConfig.signature.sigParam + '=' + sig; } else if (apiConfig.signature.type == 'signed_sha256') { // sha256(key+secret+epoch) // Add signature parameter - timeStamp = Math.round(new Date().getTime()/1000); + timeStamp = Math.round(new Date().getTime() / 1000); sig = crypto.createHash('sha256').update('' + apiKey + apiSecret + timeStamp + '').digest(apiConfig.signature.digest); options.path += '&' + apiConfig.signature.sigParam + '=' + sig; } @@ -809,7 +886,7 @@ function processRequest(req, res, next) { for (var x = 0, len = reqQuery.headerNames.length; x < len; x++) { if (config.debug) { - console.log('Setting header: ' + reqQuery.headerNames[x] + ':' + reqQuery.headerValues[x]); + console.log('Setting header: ' + reqQuery.headerNames[x] + ':' + reqQuery.headerValues[x]); } if (reqQuery.headerNames[x] != '') { headers[reqQuery.headerNames[x]] = reqQuery.headerValues[x]; @@ -818,7 +895,7 @@ function processRequest(req, res, next) { options.headers = headers; } - if(options.headers === void 0){ + if (options.headers === void 0) { options.headers = {} } if (!options.headers['Content-Length']) { @@ -849,7 +926,7 @@ function processRequest(req, res, next) { } // API Call. response is the response from the API, res is the response we will send back to the user. - var apiCall = doRequest(options, function(response) { + var apiCall = doRequest(options,function (response) { response.setEncoding('utf-8'); if (config.debug) { @@ -861,11 +938,11 @@ function processRequest(req, res, next) { var body = ''; - response.on('data', function(data) { + response.on('data', function (data) { body += data; }); - response.on('end', function() { + response.on('end', function () { delete options.agent; var responseContentType = response.headers['content-type']; @@ -887,13 +964,13 @@ function processRequest(req, res, next) { next(); }) - }).on('error', function(e) { - if (config.debug) { - console.log('HEADERS: ' + JSON.stringify(res.headers)); - console.log("Got error: " + e.message); - console.log("Error: " + util.inspect(e)); - } - }); + }).on('error', function (e) { + if (config.debug) { + console.log('HEADERS: ' + JSON.stringify(res.headers)); + console.log("Got error: " + e.message); + console.log("Error: " + util.inspect(e)); + } + }); if (requestBody) { apiCall.end(requestBody, 'utf-8'); @@ -909,7 +986,7 @@ function checkPathForAPI(req, res, next) { if (!req.params) req.params = {}; if (!req.params.api) { // If api wasn't passed in as a parameter, check the path to see if it's there - var pathName = req.url.replace('/',''); + var pathName = req.url.replace('/', ''); // Is it a valid API - if there's a config file we can assume so fs.stat(path.join(config.apiConfigDir, pathName + '.json'), function (error, stats) { if (stats) { @@ -945,14 +1022,14 @@ function dynamicHelpers(req, res, next) { // // Routes // -app.get('/', function(req, res) { +app.get('/', function (req, res) { res.render('listAPIs', { title: config.title }); }); // Process the API request -app.post('/processReq', oauth, processRequest, function(req, res) { +app.post('/processReq', oauth, processRequest, function (req, res) { var result = { headers: req.resultHeaders, response: req.result, @@ -968,26 +1045,26 @@ app.all('/auth2', oauth2); // OAuth callback page, closes the window immediately after storing access token/secret -app.get('/authSuccess/:api', oauthSuccess, function(req, res) { +app.get('/authSuccess/:api', oauthSuccess, function (req, res) { res.render('authSuccess', { title: 'OAuth Successful' }); }); // OAuth callback page, closes the window immediately after storing access token/secret -app.get('/oauth2Success/:api', oauth2Success, function(req, res) { +app.get('/oauth2Success/:api', oauth2Success, function (req, res) { res.render('authSuccess', { title: 'OAuth Successful' }); }); -app.post('/upload', function(req, res) { - res.redirect('back'); +app.post('/upload', function (req, res) { + res.redirect('back'); }); // API shortname, all lowercase -app.get('/:api([^\.]+)', function(req, res) { - req.params.api=req.params.api.replace(/\/$/,''); +app.get('/:api([^\.]+)', function (req, res) { + req.params.api = req.params.api.replace(/\/$/, ''); res.render('api'); }); @@ -996,7 +1073,7 @@ app.get('/:api([^\.]+)', function(req, res) { if (!module.parent) { var port = process.env.PORT || config.port; var l = app.listen(port); - l.on('listening', function(err) { + l.on('listening', function (err) { console.log("Express server listening on port %d", port); }); } diff --git a/config.json.sample b/config.json.sample deleted file mode 100644 index 1983fc97..00000000 --- a/config.json.sample +++ /dev/null @@ -1,18 +0,0 @@ -{ - "title" : "I/O Docs - http://github.com/mashery/iodocs", - "address": "0.0.0.0", - "port" : 3000, - "apiConfigDir" : "./public/data/", - "debug" : false, - "sessionSecret" : "12345", - "basicAuth" : { - "username" : "", - "password" : "" - }, - "redis" : { - "host" : "localhost", - "port" : 6379, - "password" : "", - "database" : "0" - } -} diff --git a/package.json b/package.json index d549df0e..03bf0f04 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,9 @@ "oauth": "0.9.10", "redis": "0.8.3", "querystring": "0.1.0", - "supervisor": ">= 0.5.x" + "supervisor": ">= 0.5.x", + "jwt-simple":"*", + "request":"*" }, "devDependencies": {}, "main": "index", diff --git a/public/data/apiconfig.json b/public/data/apiconfig.json index 16cbffba..f0c4b754 100644 --- a/public/data/apiconfig.json +++ b/public/data/apiconfig.json @@ -50,6 +50,22 @@ }, "keyParam":"" }, + "jwttest": { + "name": "JWT BASED (OAuth 2.0 Auth Code)", + "protocol": "http", + "baseURL": "localhost:9091", + "publicPath": "", + "privatePath": "", + "auth": "oauth2", + "oauth2": { + "type": "bearer-jwt", + "baseSite": "localhost:9091", + "accessTokenURL": "/oauth/v2/token", + "tokenName": "access_token", + "authorizationHeader": "Y" + }, + "keyParam": "" + }, "rdio": { "name": "Rdio Beta (OAuth 2.0 Client Credentials)", "protocol": "https", diff --git a/public/data/jwttest.json b/public/data/jwttest.json new file mode 100644 index 00000000..d7741196 --- /dev/null +++ b/public/data/jwttest.json @@ -0,0 +1,59 @@ +{ + "endpoints": [ + { + "name": "List Methods", + "methods": [ + { + "MethodName": "List Cluster Workers", + "Synopsis": "This method is used to list the cluster workers currently functioning. Its also lists its system info.

Will return the following status codes:

  1. 200: Successful
  2. 400: Validation Errors
  3. 401: Authentication Failure
  4. 500: Server Error

", + "HTTPMethod": "GET", + "URI": "/cluster/worker/list", + "RequiresOAuth": "Y", + "parameters": [ + { + "Name": "skip", + "Required": "N", + "Default": "0", + "Type": "number", + "Description": "For Pagination. How many records to skip." + }, + { + "Name": "limit", + "Required": "N", + "Default": "-1", + "Type": "number", + "Description": "For Pagination. How many records to fetch. '-1' means infinite." + } + ], + "responses": [ + { + "Type": "SUCCESS - List Fetched", + "StatusCode": 200, + "Synopsis": "When everything goes good, you get the list.", + "Format": "JSON", + "parameters": [ + + ] + }, + { + "Type": "ERROR - Server Error", + "StatusCode": 500, + "Synopsis": "When some error on server occurs.", + "Format": "JSON", + "parameters": [ + { + "Name": "error", + "Type": "number", + "Description": "The Error Message" + } + ], + "sampleResponse": { + "error": "Unable to process, try again later." + } + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/views/api.jade b/views/api.jade index df3cab11..73aa2ba3 100644 --- a/views/api.jade +++ b/views/api.jade @@ -151,6 +151,7 @@ block content - each description, choice in parameter.EnumeratedDescription dt #{choice} dd #{description} + // Create header fields and button to add/remove headers. - if (method.headers && method.headers.length > 0) div.headers h4.title @@ -175,6 +176,51 @@ block content td a(href='#', class='remove') Remove a(href='#', class='add-headers') Add Header - // Create header fields and button to add/remove headers. + div(style='background:#EFF5F5;padding:10px;border:1px solid #9ac0de') + h2 Response Types + button(onclick="$(this).parent('div').children('.responseBlock').toggle('fast');return false;") Show / Hide + span See the possible responses of the API method. + hr + - if (method.responses && method.responses.length > 0) + - var paramCount =0 + - each response in method.responses + div(style='background:#EEE;padding:10px;border:1px solid #9ac0de;margin-bottom:5px;display:none',class='responseBlock') + h4 + strong #{response.StatusCode} + span(style="display:inline-block;width:10px;") + span #{response.Type} + p #{response.Synopsis} + br + strong Respons Code: + span #{response.StatusCode} + br + strong Format: + span #{response.Format} + br + br + strong Response Data Description + br + - if (response.parameters) + table.parameters + thead + tr + th Parameter + th Type + th Description + tbody + - each parameter in response.parameters + div + tr + td.name!=parameter.Name + td.type!=parameter.Type + td.description + p!=parameter.Description || 'No description' + br + - if(response.sampleResponse) + h4 + strong Example Response + pre #{JSON.stringify(response.sampleResponse,null,4)} + br + hr - if (!method['read-only']) - input(type='submit', id=method.MethodName, value='Try it!') + input(type='submit', id=method.MethodName, value='Try it!') \ No newline at end of file