@sap/approuter
Advanced tools
Comparing version 5.10.1 to 5.11.0
@@ -8,2 +8,12 @@ # Change Log | ||
## 5.11.0 - 2019-01-22 | ||
### Added | ||
- Client credentials token support. | ||
## 5.10.2 - 2019-01-08 | ||
### Fixed | ||
- Fix proxy issue in Connectivity flow. | ||
## 5.10.1 - 2019-01-03 | ||
@@ -10,0 +20,0 @@ |
@@ -43,17 +43,21 @@ 'use strict'; | ||
} | ||
if (destination && destination.proxyType === 'OnPremise' && req.app.services && req.app.services['connectivity']) { | ||
tokens['connectivity'] = req.app.services['connectivity'].token; | ||
} | ||
addCorrleationIdHeader(headers, req); | ||
addCorrelationIdHeader(headers, req); | ||
addXForwardingHeaders(headers, req); | ||
removeSecurityHeaders(headers, req); | ||
headerUtil.updateSapPassport(headers); | ||
tokens['connProxyToken'] = req.app && req.app.connProxyToken && req.app.connProxyToken.accessToken; | ||
tokens['html5AppsRepoToken'] = req.app && req.app.html5AppsRepoToken && req.app.html5AppsRepoToken.accessToken; | ||
if (req.session && req.session.user && req.session.user.destinations | ||
&& req.session.user.destinations[destination.name] | ||
&& req.session.user.destinations[destination.name].authToken){ | ||
&& req.session.user.destinations[destination.name] | ||
&& req.session.user.destinations[destination.name].authToken){ | ||
tokens['authToken'] = req.session.user.destinations[destination.name].authToken; | ||
} | ||
route = req.internalUrl ? req.internalUrl.route : null; | ||
if (route && route.service && req.app && req.app.services && req.app.services[route.service] && req.app.services[route.service].token){ | ||
tokens[route.service] = req.app.services[route.service].token; | ||
} | ||
if (route && route.service === 'html5-apps-repo-rt') { | ||
var appKey = dynamicRoutingUtils.getApplicationKey(req); | ||
let appKey = dynamicRoutingUtils.getApplicationKey(req); | ||
if (appKey && appKey.appPrefix) { | ||
@@ -80,3 +84,3 @@ addAppHostIdHeader(appKey.appPrefix, headers); | ||
headers['SAP-Connectivity-Authentication'] = 'Bearer ' + tokens.accessToken; | ||
headers['Proxy-Authorization'] = 'Bearer ' + tokens.connProxyToken; | ||
headers['Proxy-Authorization'] = 'Bearer ' + tokens['connectivity'].accessToken; | ||
if (destination.cloudConnectorLocationId) { | ||
@@ -90,10 +94,10 @@ headers['SAP-Connectivity-SCC-Location_ID'] = destination.cloudConnectorLocationId; | ||
} | ||
// Common for on premise and cloud destination flows | ||
// Use tokens provided from destination service | ||
if (tokens['authToken']){ | ||
// Common for on premise and cloud destination flows | ||
// Use tokens provided from destination service | ||
if (tokens['authToken']) { | ||
headers.authorization = tokens['authToken'].type + ' ' + tokens['authToken'].value; | ||
} | ||
} else if (route && route.service) {// Service flow | ||
if (businessServiceUtils.getGrantType(route.credentials) !== 'user_token') {// html5 apps repo flow | ||
headers['authorization'] = 'Bearer ' + tokens.html5AppsRepoToken; | ||
if (businessServiceUtils.getGrantType(route.credentials) === 'client_credentials') {// html5 apps repo flow | ||
headers.authorization = tokens[route.service].tokenType + ' ' + tokens[route.service].accessToken; | ||
} else { | ||
@@ -104,3 +108,3 @@ headers.authorization = 'Bearer ' + tokens.accessToken; | ||
if (tokens && tokens.accessToken) { | ||
// In case of Logout: | ||
// In case of Logout: | ||
// env destination => we send JWT token in tokens.accessToken as a String | ||
@@ -116,15 +120,10 @@ // destination service destination => we send destination auth token in tokens.accessToken as an Object | ||
function addCorrleationIdHeader(headers, req) { | ||
function addCorrelationIdHeader(headers, req) { | ||
if (req.headers['x-correlationid']) { | ||
return; | ||
} | ||
else if (req.headers['x-request-id']) { | ||
} else if (req.headers['x-request-id']) { | ||
headers['x-correlationid'] = req.headers['x-request-id']; | ||
} | ||
else if (req.headers['x-vcap-request-id']){ | ||
} else if (req.headers['x-vcap-request-id']){ | ||
headers['x-correlationid'] = req.headers['x-vcap-request-id']; | ||
} | ||
else { | ||
} else { | ||
headers['x-correlationid'] = uuid(); | ||
@@ -154,2 +153,2 @@ } | ||
} | ||
} | ||
} |
@@ -35,3 +35,3 @@ { | ||
"enum": [ | ||
"user_token" | ||
"user_token", "client_credentials" | ||
] | ||
@@ -38,0 +38,0 @@ } |
@@ -27,3 +27,3 @@ 'use strict'; | ||
var destinationUtils = require('../utils/destination-utils'); | ||
var destinationConfigSchema = require('./schemas/destinations-srv-config-schema.json'); | ||
var urlSchema = require('./schemas/url-schema.json'); | ||
var xsenv = require('@sap/xsenv'); | ||
@@ -93,3 +93,3 @@ | ||
if (err.message.includes('No enum match for:')) { // | ||
return 'User credential service is not supported by approuter' + err.message; | ||
return 'User credential service is not supported by approuter ' + err.message; | ||
} | ||
@@ -188,4 +188,3 @@ return err.message; | ||
}); | ||
} | ||
, | ||
}, | ||
@@ -196,4 +195,3 @@ validateEnvironmentSettings: function (configuration) { | ||
validator.validate(configuration, environmentSchema, 'environment-settings'); | ||
} | ||
, | ||
}, | ||
@@ -214,4 +212,3 @@ validatePlugins: function (configuration, envDestinations) { | ||
validator.validate(configuration, pluginsSchema, 'plugins'); | ||
} | ||
, | ||
}, | ||
@@ -235,4 +232,3 @@ validateHeaders: function (configuration) { | ||
validator.validate(configuration, headersSchema, 'http-headers'); | ||
} | ||
, | ||
}, | ||
@@ -255,4 +251,3 @@ validateWhitelist: function (whitelist) { | ||
validator.validate(whitelist, whitelistSchema, 'clickjack-whitelist'); | ||
} | ||
, | ||
}, | ||
@@ -274,4 +269,3 @@ validateApprouterStartOptions: function (options) { | ||
validator.validate(options, approuterOptionsSchema, 'options'); | ||
} | ||
, | ||
}, | ||
@@ -283,4 +277,3 @@ validateUaaOptions: function (options) { | ||
validator.validate(options, uaaOptionsSchema, 'uaa-configuration'); | ||
} | ||
, | ||
}, | ||
@@ -293,23 +286,21 @@ | ||
validator.validate(options, connectivitySchema, 'connectivity-configuration'); | ||
}, | ||
} | ||
, | ||
validateHTML5AppsRepoCredentials: function (options) { | ||
validateClientCredentials: function (options) { | ||
var validator = new JsonValidator(); | ||
validator.addFormat('absolute-uri', validateAbsoluteUri); | ||
if (options.uaa && options.uaa.clientid && options.uaa.clientid.length > 0 | ||
&& options.uaa.clientsecret && options.uaa.clientsecret.length > 0 | ||
&& options.uaa.url && options.uaa.url.length > 0){ | ||
validator.validate(options.uaa.url, urlSchema, 'url-schema'); | ||
return; | ||
} else if (options.clientid && options.clientid.length > 0 | ||
&& options.clientsecret && options.clientsecret.length > 0 | ||
&& options.url && options.url.length > 0){ | ||
validator.validate(options.url, urlSchema, 'url-schema'); | ||
return; | ||
} | ||
throw new Error('No clientid or clientsercret provided'); | ||
}, | ||
validator.validate(options, html5RepoCredSchema, 'html5-repo-credentials'); | ||
} | ||
, | ||
validateDestinationCredentials: function (options) { | ||
var validator = new JsonValidator(); | ||
validator.addFormat('absolute-uri', validateAbsoluteUri); | ||
validator.validate(options, destinationConfigSchema, 'destination-service-credentials-configuration'); | ||
} | ||
, | ||
validateCors: function (options) { | ||
@@ -322,4 +313,3 @@ var validator = new JsonValidator(); | ||
} | ||
} | ||
; | ||
}; | ||
@@ -326,0 +316,0 @@ function addConfigurationDefaults(configuration) { |
@@ -8,3 +8,2 @@ 'use strict'; | ||
var self = module.exports = { | ||
@@ -20,3 +19,3 @@ | ||
var destinationName = options.destinationName; | ||
destinationUtils.findDestination(destinationName, userExchangeToken, function (err, destinationLookUpResult){ | ||
destinationUtils.findDestination(destinationName, userExchangeToken, options, function (err, destinationLookUpResult){ | ||
if (err){ | ||
@@ -46,3 +45,5 @@ return cb(err); | ||
} | ||
result.destination = destinationUtils.normalizeDestination(destinationLookUpResult.destinationConfiguration); | ||
var destination = destinationUtils.normalizeDestination(destinationLookUpResult.destinationConfiguration); | ||
destinationUtils.enhanceDestination (destination); | ||
result.destination = destination; | ||
result.authToken = destinationLookUpResult.authTokens ? destinationLookUpResult.authTokens[0] : null; | ||
@@ -120,3 +121,1 @@ return cb (null, result); | ||
} | ||
@@ -12,3 +12,3 @@ 'use strict'; | ||
} | ||
var options = {destinationName: req.internalUrl.route.destination, session: req.session}; | ||
var options = {destinationName: req.internalUrl.route.destination, session: req.session, app: req.app}; | ||
if (req.routerConfig && req.routerConfig.getToken) { | ||
@@ -37,3 +37,2 @@ req.routerConfig.getToken(req, function (err, jwt) { | ||
} | ||
sessionExt.update(req.session, function(// eslint-disable-next-line | ||
@@ -57,3 +56,2 @@ session) { | ||
} | ||
return next(); | ||
@@ -64,3 +62,2 @@ }); | ||
function updateInternalUrl (req, destination) { | ||
@@ -67,0 +64,0 @@ var destinationUrl = req.internalUrl.destination.url; |
@@ -10,5 +10,6 @@ 'use strict'; | ||
module.exports = function (req, res, next) { | ||
if (!isBusinessServiceScenario(req)) { | ||
if (!isTokenExchangeRequired(req)) { | ||
return next(); | ||
} | ||
shouldAskBusinessToken(req, function(err, askForToken){ | ||
@@ -40,7 +41,6 @@ if (err) { | ||
function isBusinessServiceScenario(req){ | ||
if (!req.internalUrl || !req.internalUrl.route || !req.internalUrl.route.service) { | ||
return false; | ||
} | ||
if (req.internalUrl.route.credentials && businessServiceUtils.getGrantType(req.internalUrl.route.credentials) === 'user_token'){ | ||
function isTokenExchangeRequired(req){ | ||
if (req.internalUrl && req.internalUrl.route | ||
&& req.internalUrl.route.service && req.internalUrl.route.credentials | ||
&& businessServiceUtils.getGrantType(req.internalUrl.route.credentials) === 'user_token'){ | ||
return true; | ||
@@ -47,0 +47,0 @@ } |
@@ -14,2 +14,3 @@ 'use strict'; | ||
exports.normalizeDestination = normalizeDestination; | ||
exports.enhanceDestination = enhanceDestination; | ||
@@ -29,2 +30,12 @@ function adjustDestinationProperties(destinations) { | ||
function enhanceDestination (destination) { | ||
if (destination.proxyType === 'OnPremise') { | ||
var credentials = xsenv.cfServiceCredentials({tag: 'connectivity'}); | ||
destination['proxyHost'] = credentials.onpremise_proxy_host; | ||
destination['proxyPort'] = credentials.onpremise_proxy_port; | ||
} | ||
destination.timeout = destination.timeout ? destination.timeout : 30000; | ||
} | ||
function normalizeDestinationProperties(destinations) { | ||
@@ -49,2 +60,20 @@ if (!destinations) { | ||
function getFindDestinationReqOptions (destinationName, token, options, cb) { | ||
var credentials = getDestinationServiceCredentials (); | ||
var headers = {'accept': 'application/json;charset=utf-8'}; | ||
if (!credentials) { | ||
return cb ('Destination service is not bound'); | ||
} | ||
obtainDestinationServiceToken(token, credentials, options, function(err, token) { | ||
if (err){ | ||
return cb(err); | ||
} | ||
headers.Authorization = 'Bearer ' + token; | ||
cb (null, { | ||
url: credentials.uri + '/destination-configuration/v1/destinations/' + destinationName, | ||
headers: headers | ||
}); | ||
}); | ||
} | ||
function normalizeDestination (destination){ | ||
@@ -86,63 +115,13 @@ if (!destination) { | ||
function obtainDestinationServiceToken (token, credentials, options, cb) { | ||
function getUAAurl(serviceCredentials) { | ||
return serviceCredentials.url + '/oauth/token'; | ||
} | ||
function getUAARequestFormData(serviceCredentials) { | ||
return { | ||
'client_id': serviceCredentials.clientid, | ||
'client_secret': serviceCredentials.clientsecret, | ||
'grant_type': 'client_credentials', | ||
'response_type': 'token' | ||
}; | ||
} | ||
function obtainDestinationServiceToken (token, credentials, cb) { | ||
// this is the case when exchange token performed and we have token of destination service | ||
if (token){ | ||
return cb (null, token); | ||
} | ||
} else { | ||
// this is the case when the session is not authenticated, so there were no exchange token. | ||
// in this case we have to get the token of destination using its client credentials | ||
request.post({ | ||
url: getUAAurl(credentials), | ||
form: getUAARequestFormData(credentials) | ||
}, | ||
function (err, res, body) { | ||
try { | ||
if (err) { | ||
return cb(new Error('Error while obtaining token of destination service. ' + err)); | ||
} | ||
if (res.statusCode === 200) { | ||
if (!JSON.parse(body).access_token) { | ||
cb(new Error('Obtaining token of destination service, Error: Bad token')); | ||
} | ||
cb(null, JSON.parse(body).access_token); | ||
} else { | ||
cb(new Error('Error while obtaining token of destination service; Status: ' + res.statusCode + | ||
' Response: ' + JSON.stringify(body))); | ||
} | ||
} catch (e) { | ||
cb(new Error('Obtaining token of destination service, Error: while parsing UAA response; ' + e)); | ||
} | ||
}); | ||
} | ||
return cb (null, options.app.services['destination'].token); | ||
} | ||
function getFindDestinationReqOptions (destinationName, token, cb) { | ||
var credentials = getDestinationServiceCredentials (); | ||
var headers = {'accept': 'application/json;charset=utf-8'}; | ||
if (!credentials) { | ||
return cb ('Destination service is not bound'); | ||
} | ||
obtainDestinationServiceToken(token, credentials, function(err, token) { | ||
if (err){ | ||
return cb(err); | ||
} | ||
headers.Authorization = 'Bearer ' + token; | ||
cb (null, { | ||
url: credentials.uri + '/destination-configuration/v1/destinations/' + destinationName, | ||
headers: headers | ||
}); | ||
}); | ||
} | ||
@@ -152,3 +131,3 @@ | ||
// Search priority is destination on instance level and after that fallback to the shared destinations on subaccount level. | ||
function findDestination (destinationName, token, cb){ | ||
function findDestination (destinationName, token, options, cb){ | ||
if (!destinationName) { | ||
@@ -159,4 +138,4 @@ return cb('Cannot find destination, destination name is missing'); | ||
var errorMessage; | ||
getFindDestinationReqOptions(destinationName, token, function (error, requestOptions) { | ||
if (error) { | ||
getFindDestinationReqOptions(destinationName, token, options, function(error, requestOptions){ | ||
if (error){ | ||
return cb(error); | ||
@@ -173,3 +152,2 @@ } | ||
} | ||
try { | ||
@@ -184,3 +162,1 @@ var destinationLookUpResult = JSON.parse(res.body); | ||
} | ||
@@ -163,3 +163,3 @@ 'use strict'; | ||
if (!req.app || !req.app.html5AppsRepoToken || !req.app.html5AppsRepoToken.accessToken) { | ||
if (!req.app || !req.app.services || !req.app.services['html5-apps-repo-rt'] || !req.app.services['html5-apps-repo-rt'].token || !req.app.services['html5-apps-repo-rt'].token.accessToken) { | ||
return cb ('Missing html5AppsRepo token'); | ||
@@ -202,3 +202,3 @@ } | ||
requestOptions.headers.accept = 'application/json;charset=utf-8'; | ||
requestOptions.headers.authorization = 'Bearer ' + req.app.html5AppsRepoToken.accessToken; | ||
requestOptions.headers.authorization = 'Bearer ' + req.app.services['html5-apps-repo-rt'].token.accessToken; | ||
@@ -205,0 +205,0 @@ if (bsCredentials){ |
'use strict'; | ||
var xsenv = require('@sap/xsenv'); | ||
var businessServiceUtils = require('./business-service-utils'); | ||
var validators = require('../configuration/validators'); | ||
@@ -10,58 +9,24 @@ var passportUtils = require('../passport/utils'); | ||
var logger = loggerUtils.getLogger('/token-utils'); | ||
var VError = require('verror').VError; | ||
exports.getTokens = getBSTokens; | ||
exports.getProxyToken = getProxyToken; | ||
exports.getHTML5AppsRepoToken = getHTML5AppsRepoToken; | ||
exports.getTokens = getTokens; | ||
exports.loadDestinationTokenAsync = loadDestinationTokenAsync; | ||
function getBSTokens(app) { | ||
const DESTINATION = 'destination'; | ||
const CONNECTIVITY = 'connectivity'; | ||
const CLIENT_CREDENTIALS = 'client_credentials'; | ||
function getProxyToken(app) { | ||
try { | ||
var credentials = xsenv.cfServiceCredentials({tag: 'connectivity'}); | ||
validators.validateConnectivityCredentials(credentials); | ||
loadClientCredentialsToken(app, credentials, 'connProxyToken'); | ||
// eslint-disable-next-line no-empty | ||
} catch (e) {} | ||
} | ||
function getHTML5AppsRepoToken(app) { | ||
try { | ||
var credentials = businessServiceUtils.getCredentials('html5-apps-repo-rt'); | ||
} catch (e) {return;} | ||
try { | ||
validators.validateHTML5AppsRepoCredentials(credentials); | ||
loadClientCredentialsToken(app, credentials.uaa, 'html5AppsRepoToken'); | ||
} catch (e){ | ||
logger.error('can\'t get access token to html5 applications repository runtime service :\n', e); | ||
var services = xsenv.readCFServices(); | ||
if (services){ | ||
for (var serviceName in services){ | ||
var service = services[serviceName]; | ||
if (service.label === DESTINATION || service.label === CONNECTIVITY || service.credentials['grant_type'] === CLIENT_CREDENTIALS){ | ||
validators.validateClientCredentials(service.credentials); | ||
loadClientCredentialsToken(app, service.credentials, service.credentials['sap.cloud.service'] || service.label); | ||
} | ||
} | ||
} | ||
} | ||
function getTokens(app) { | ||
getProxyToken(app); | ||
getHTML5AppsRepoToken(app); | ||
loadDestinationTokenAsync (app); | ||
} | ||
function loadDestinationTokenAsync (app) { | ||
var credentials; | ||
var message; | ||
try { | ||
credentials = xsenv.cfServiceCredentials({tag: 'destination'}); | ||
} catch (e) { | ||
return; | ||
} | ||
try { | ||
validators.validateDestinationCredentials(credentials); | ||
} catch (e) { | ||
message = 'cannot get access token to destination service :\n' + e.message; | ||
logger.error(message); | ||
throw new VError(message); | ||
} | ||
loadClientCredentialsToken(app, credentials, 'destinationToken'); | ||
} | ||
function loadClientCredentialsToken(app, credentials, tokenName) { | ||
function loadClientCredentialsToken(app, credentials, serviceName) { | ||
var requestOptions = { | ||
url: credentials.url + '/oauth/token/?grant_type=client_credentials', | ||
url: (credentials.url || credentials.uaa.url) + '/oauth/token/?grant_type=client_credentials', | ||
headers: { | ||
@@ -72,4 +37,4 @@ 'content-type': 'application/x-www-form-urlencoded;charset=utf-8', | ||
auth: { | ||
user: credentials.clientid, | ||
pass: credentials.clientsecret | ||
user: credentials.clientid || credentials.uaa.clientid, | ||
pass: credentials.clientsecret || credentials.uaa.clientsecret | ||
} | ||
@@ -83,3 +48,2 @@ }; | ||
} | ||
if (!uaaResponse.access_token || !uaaResponse.expires_in) { | ||
@@ -90,19 +54,13 @@ return tracer.error('Bad response from UAA when getting client credentials token for ' + credentials.label + '- not all fields are present'); | ||
accessToken: uaaResponse.access_token, | ||
expiryDate: passportUtils.getExpiresAt(uaaResponse.expires_in).getTime() | ||
tokenType: uaaResponse.token_type | ||
}; | ||
app[tokenName] = options; | ||
var tokenRefreshTimestamp = options.expiryDate - toMilliseconds(5); | ||
if (!app.services) {app.services = {};} | ||
app.services[serviceName] = { token: options }; | ||
const FIVE_MINUTES = 5 * 60 * 1000; | ||
var tokenRefreshTimestamp = passportUtils.getExpiresAt(uaaResponse.expires_in).getTime() - FIVE_MINUTES; | ||
var msBeforeRetrieval = tokenRefreshTimestamp - Date.now(); | ||
executeAfter(function () { | ||
loadClientCredentialsToken(app, credentials, tokenName); | ||
}, msBeforeRetrieval); | ||
setTimeout(function () { loadClientCredentialsToken(app, credentials, serviceName);}, msBeforeRetrieval); | ||
}); | ||
} | ||
function executeAfter(fn, timeout) { | ||
setTimeout(fn, timeout); | ||
} | ||
function toMilliseconds(minutes) { | ||
return minutes * 60 * 1000; | ||
} |
@@ -1,1 +0,1 @@ | ||
{"bundleDependencies":false,"dependencies":{"@sap/audit-logging":"2.2.4","@sap/e2e-trace":"1.3.0","@sap/logging":"4.0.2","@sap/xsenv":"1.2.8","@sap/xssec":"2.1.15","agentkeepalive":"2.0.5","async":"2.0.1","base64-url":"2.0.0","basic-auth":"1.0.3","body-parser":"1.18.3","commander":"2.9.0","compression":"1.7.3","connect":"3.6.5","cookie":"0.2.2","cookie-parser":"1.3.5","cookie-signature":"1.1.0","debug":"3.1.0","deepmerge":"2.1.1","encodeurl":"1.0.2","express-session":"1.15.6","http-proxy-agent":"2.1.0","https-proxy-agent":"2.2.0","jwt-decode":"2.0.1","lodash":"4.17.11","lru-cache":"4.0.0","mime":"1.4.1","moment":"2.19.3","ms":"2.1.1","mustache":"2.2.1","node-cache":"4.1.1","passport":"0.3.2","request":"2.87.0","request-stats":"2.0.1","safe-regex":"1.1.0","scmp":"1.0.0","send":"0.16.2","serve-static":"1.13.2","tough-cookie":"2.3.3","tv4":"1.2.7","uid-safe":"2.1.5","urijs":"1.16.1","uuid":"3.2.1","verror":"1.10.0","ws":"1.1.5"},"deprecated":false,"description":"Node.js based application router","devDependencies":{"chai":"3.5.0","diveSync":"0.3.0","eslint":"3.2.2","express":"4.16.2","filter-node-package":"2.0.0","istanbul":"0.4.4","markdown-toc":"1.1.0","mocha":"3.0.2","mock-require":"3.0.2","node-build":"1.0.0","node-mocks-http":"1.5.2","node-style":"^2.0.0","proxyquire":"1.7.10","rewire":"2.5.2","rimraf":"2.5.4","sinon":"1.17.5","supertest":"3.3.0"},"engines":{"node":"^4.5.0 || ^6.0.0 "},"main":"approuter.js","name":"@sap/approuter","repository":{},"scripts":{"lint":"eslint -c node_modules/node-style/.eslintrc -f stylish lib/ approuter.js","prepareRelease":"node build/delete-extra-packages.js && clean-packages && npm prune --production","start":"node approuter.js","test":"node build/test","toc":"markdown-toc -i README.md && markdown-toc -i doc/extending.md && markdown-toc -i doc/sizingGuide.md"},"version":"5.10.1","warnings":[{"code":"ENOTSUP","required":{"node":"^4.5.0 || ^6.0.0 "},"pkgid":"@sap/approuter@5.10.1"},{"code":"ENOTSUP","required":{"node":"^4.5.0 || ^6.0.0 "},"pkgid":"@sap/approuter@5.10.1"}],"license":"SEE LICENSE IN developer-license-3.1.txt"} | ||
{"bundleDependencies":false,"dependencies":{"@sap/audit-logging":"2.2.4","@sap/e2e-trace":"1.3.0","@sap/logging":"4.0.2","@sap/xsenv":"1.2.8","@sap/xssec":"2.1.15","agentkeepalive":"2.0.5","async":"2.0.1","base64-url":"2.0.0","basic-auth":"1.0.3","body-parser":"1.18.3","commander":"2.9.0","compression":"1.7.3","connect":"3.6.5","cookie":"0.2.2","cookie-parser":"1.3.5","cookie-signature":"1.1.0","debug":"3.1.0","deepmerge":"2.1.1","encodeurl":"1.0.2","express-session":"1.15.6","http-proxy-agent":"2.1.0","https-proxy-agent":"2.2.0","jwt-decode":"2.0.1","lodash":"4.17.11","lru-cache":"4.0.0","mime":"1.4.1","moment":"2.19.3","ms":"2.1.1","mustache":"2.2.1","node-cache":"4.1.1","passport":"0.3.2","request":"2.87.0","request-stats":"2.0.1","safe-regex":"1.1.0","scmp":"1.0.0","send":"0.16.2","serve-static":"1.13.2","tough-cookie":"2.3.3","tv4":"1.2.7","uid-safe":"2.1.5","urijs":"1.16.1","uuid":"3.2.1","verror":"1.10.0","ws":"1.1.5"},"deprecated":false,"description":"Node.js based application router","devDependencies":{"chai":"3.5.0","diveSync":"0.3.0","eslint":"3.2.2","express":"4.16.2","filter-node-package":"2.0.0","istanbul":"0.4.4","markdown-toc":"1.1.0","mocha":"3.0.2","mock-require":"3.0.2","node-build":"1.0.0","node-mocks-http":"1.5.2","node-style":"^2.0.0","proxyquire":"1.7.10","rewire":"2.5.2","rimraf":"2.5.4","sinon":"1.17.5","supertest":"3.3.0"},"engines":{"node":"^4.5.0 || ^6.0.0 "},"main":"approuter.js","name":"@sap/approuter","repository":{},"scripts":{"lint":"eslint -c node_modules/node-style/.eslintrc -f stylish lib/ approuter.js","prepareRelease":"node build/delete-extra-packages.js && clean-packages && npm prune --production","start":"node approuter.js","test":"node build/test","toc":"markdown-toc -i README.md && markdown-toc -i doc/extending.md && markdown-toc -i doc/sizingGuide.md"},"version":"5.11.0","warnings":[{"code":"ENOTSUP","required":{"node":"^4.5.0 || ^6.0.0 "},"pkgid":"@sap/approuter@5.11.0"},{"code":"ENOTSUP","required":{"node":"^4.5.0 || ^6.0.0 "},"pkgid":"@sap/approuter@5.11.0"}],"license":"SEE LICENSE IN developer-license-3.1.txt"} |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
1755
1027264
5934