@sap/xssec
Advanced tools
Comparing version 2.1.16 to 2.2.3
# Change Log | ||
All notable changes to this project will be documented in this file. | ||
## 2.2.3 - 2019-08-07 | ||
- Add retry for recieving keys | ||
## 2.2.2 - 2019-06-24 | ||
- Use verification key from binding as backup if online key retrieval fails | ||
## 2.2.1 - 2019-06-17 | ||
- Fix uaaDomain comparison in key cache | ||
## 2.2.0 - 2019-06-17 | ||
- Align key cache implementation with other container security libraries | ||
## 2.1.17 - 2019-05-17 | ||
- Introduce http timeout of two seconds | ||
- Update version of module debug, lru-cache and @sap/xsenv | ||
- Fix token verification for broker master instance subscriptions | ||
## 2.1.16 - 2019-01-28 | ||
@@ -5,0 +27,0 @@ |
'use strict'; | ||
var constants = require('./constants'); | ||
var request = require('request'); | ||
var request = require('requestretry'); | ||
var LRU = require('lru-cache'); | ||
@@ -82,8 +82,8 @@ var validUrl = require('valid-url'); | ||
KeyCache.prototype.getKey = function getKey(keyId, uaaUrl, cb) { | ||
KeyCache.prototype.getKey = function getKey(tokenKeyUrl, keyId, cb) { | ||
var self = this; | ||
if ((keyId === null) || (keyId === undefined)) { | ||
if ((tokenKeyUrl === null) || (tokenKeyUrl === undefined)) { | ||
var error = new Error( | ||
'Parameter keyId null or undefined. To read a key from the KeyCache with function KeyCache.getKey, you need to specify parameter keyId.'); | ||
'Parameter tokenKeyUrl null or undefined. To enable the KeyCache reading keys from the UAA which are yet unavailable in the cache, you need to specify parameter tokenKeyUrl as a valid https URL.'); | ||
return process.nextTick(function() { | ||
@@ -93,5 +93,5 @@ cb(error, null); | ||
} | ||
if ((uaaUrl === null) || (uaaUrl === undefined)) { | ||
if (validUrl.isHttpsUri(tokenKeyUrl) === undefined) { | ||
var error = new Error( | ||
'Parameter uaaUrl null or undefined. To enable the KeyCache reading keys from the UAA which are yet unavailable in the cache, you need to specify parameter uaaUrl as a valid https URL.'); | ||
'Parameter tokenKeyUrl is not a valid https URL. To enable the KeyCache reading keys from the UAA which are yet unavailable in the cache, you need to specify parameter tokenKeyUrl as a valid https URL.'); | ||
return process.nextTick(function() { | ||
@@ -101,5 +101,5 @@ cb(error, null); | ||
} | ||
if (validUrl.isHttpsUri(uaaUrl) === undefined) { | ||
if ((keyId === null) || (keyId === undefined)) { | ||
var error = new Error( | ||
'Parameter uaaUrl is not a valid https URL. To enable the KeyCache reading keys from the UAA which are yet unavailable in the cache, you need to specify parameter uaaUrl as a valid https URL.'); | ||
'Parameter keyId null or undefined. To read a key from the KeyCache with function KeyCache.getKey, you need to specify parameter keyId.'); | ||
return process.nextTick(function() { | ||
@@ -110,8 +110,8 @@ cb(error, null); | ||
debugTrace('Looking for key with keyID: "' + keyId + '" in cache.'); | ||
var cacheKey = tokenKeyUrl + keyId; | ||
debugTrace('Looking for key "' + cacheKey + '" in cache.'); | ||
// Check whether keyid is in cache | ||
var tmpResult = this.lruCache.get(keyId); | ||
var tmpResult = this.lruCache.get(cacheKey); | ||
if (tmpResult !== undefined) { | ||
debugTrace('Key with keyID: "' + keyId | ||
+ '" found in cache. Returning key "' + tmpResult + '".'); | ||
debugTrace('Key with keyID: "' + keyId + '" found in cache. Returning key "' + tmpResult + '".'); | ||
return process.nextTick(function() { | ||
@@ -123,4 +123,3 @@ cb(null, tmpResult); | ||
// UAA | ||
var error = new Error('Key with keyID: "' + keyId | ||
+ '" not found in cache. Configuration says not to query UAA.'); | ||
var error = new Error('Key "' + cacheKey + '" not found in cache. Configuration says not to query UAA.'); | ||
return process.nextTick(function() { | ||
@@ -132,7 +131,9 @@ cb(error, null); | ||
var options = { | ||
url : uaaUrl + this.tokenKeyPath | ||
url : tokenKeyUrl, | ||
timeout: 2000, | ||
maxAttempts: 3, | ||
retryDelay: 500, | ||
retrySrategy: request.RetryStrategies.HTTPOrNetworkError | ||
}; | ||
debugTrace('Key with keyID: "' + keyId | ||
+ '" not found in cache. Querying keys from UAA via URL "' | ||
+ options.url + '".'); | ||
debugTrace('Key "' + cacheKey + '" not found in cache. Querying keys from UAA via URL "' + options.url + '".'); | ||
request | ||
@@ -142,3 +143,9 @@ .get( | ||
function(err, response, body) { | ||
if (response) { | ||
debugTrace('Finished after number of requests attempts: ' + response.attempts); | ||
} | ||
if (err) { | ||
if (err.code === 'ETIMEDOUT' && err.connect === true) { | ||
debugError('getKey: HTTP connection timeout.'); | ||
} | ||
debugError('An error occurred when reading the token keys from ' | ||
@@ -173,3 +180,3 @@ + options.url | ||
// breaks before adding the key to the cache | ||
self.addKey(json.keys[i].kid, | ||
self.addKey(tokenKeyUrl + json.keys[i].kid, | ||
json.keys[i].value.replace( | ||
@@ -187,7 +194,5 @@ /(\r\n|\n|\r)/gm, '')); | ||
} | ||
var tmpResult = self.lruCache.get(keyId); | ||
var tmpResult = self.lruCache.get(cacheKey); | ||
if (tmpResult !== undefined) { | ||
debugTrace('Key with keyID: "' + keyId | ||
+ '" found in cache. Returning key "' | ||
+ tmpResult + '".'); | ||
debugTrace('Key "' + cacheKey + '" found in cache. Returning key "' + tmpResult + '".'); | ||
return process.nextTick(function() { | ||
@@ -198,5 +203,4 @@ cb(null, tmpResult); | ||
var error = new Error( | ||
'Obtained token keys from UAA, but key with requested keyID "' | ||
+ keyId | ||
+ '" still not found in cache.'); | ||
'Obtained token keys from UAA, but key with requested keyID "' + | ||
cacheKey + '" still not found in cache.'); | ||
return process.nextTick(function() { | ||
@@ -203,0 +207,0 @@ cb(error, null); |
@@ -182,15 +182,15 @@ 'use strict'; | ||
var applicationPlan = false; | ||
if (result.cid.indexOf('!t') !== -1) { | ||
applicationPlan = true; | ||
var subscriptionsAllowed = false; | ||
if (result.cid.indexOf('!t') !== -1 || result.cid.indexOf('!b') !== -1) { | ||
subscriptionsAllowed = true; | ||
} | ||
if ((result.cid === self.config.clientid) | ||
&& (result.zid === self.config.identityzoneid || applicationPlan === true)) { | ||
&& (result.zid === self.config.identityzoneid || subscriptionsAllowed === true)) { | ||
// clientid and identity zone of the token must match with the values of the application | ||
// (if the token was issued for the xsuaa application plan it is sufficient that the clientid matches) | ||
if (applicationPlan === false) { | ||
// (if the token was issued for the xsuaa application or broker plan it is sufficient that the clientid matches) | ||
if (subscriptionsAllowed === false) { | ||
debugTrace('\nClient Id and Identity Zone of the access token match\n' | ||
+ 'with the current application\'s Client Id and Zone.'); | ||
} else { | ||
debugTrace('\nClient Id of the access token (XSUAA application plan) matches\n' | ||
debugTrace('\nClient Id of the access token (XSUAA application or broker plan) matches\n' | ||
+ 'with the current application\'s Client Id.'); | ||
@@ -572,3 +572,2 @@ } | ||
} | ||
var uaaURLString = config.url; | ||
var invalidatedTokenHeaderJSON = null; | ||
@@ -585,38 +584,17 @@ try { | ||
invalidatedTokenHeaderJSON = JSON.parse(invalidatedTokenHeaderString); | ||
if (!invalidatedTokenHeaderJSON.kid || invalidatedTokenHeaderJSON.kid == 'legacy-token-key') { | ||
if (!invalidatedTokenHeaderJSON.kid || invalidatedTokenHeaderJSON.kid == 'legacy-token-key' || !invalidatedTokenHeaderJSON.jku) { | ||
return cb(null, config.verificationkey); | ||
} | ||
var invalidatedTokenContentBuffer = new Buffer(invalidatedTokenParts[1], 'base64'); | ||
var invalidatedTokenContentString = invalidatedTokenContentBuffer.toString('utf8'); | ||
var invalidatedTokenContentJSON = JSON.parse(invalidatedTokenContentString); | ||
if (!invalidatedTokenContentJSON.iss) { | ||
var error = new Error('JWT token contains no iss field. Giving up.', null); | ||
error.statuscode = 400; | ||
return cb(error); | ||
} | ||
var tokenIssuer = invalidatedTokenContentJSON.iss; | ||
var tokenIssuerURL = url.parse(tokenIssuer); | ||
var tokenIssuerURLHostname = tokenIssuerURL.hostname; | ||
var tokenIssuerURLIDZIndex = tokenIssuerURLHostname.indexOf('.'); | ||
if (tokenIssuerURLIDZIndex < 0) { | ||
debugTrace('\nUnexpected Issuer Format in JWT. Use legacy-token-key.'); | ||
return cb(null, config.verificationkey); | ||
} | ||
var tokenIssuerIDZ = tokenIssuerURLHostname.substring(0, tokenIssuerURLIDZIndex); | ||
debugTrace('\nIdentity zone of token issuer: '+tokenIssuerIDZ+'\n'); | ||
var uaaURL = url.parse(uaaURLString); | ||
var uaaURLHostname = uaaURL.hostname; | ||
var uaaURLIDZIndex = uaaURLHostname.indexOf('.'); | ||
if (uaaURLIDZIndex < 0) { | ||
var error = new Error('Unexpected format of UAA URL in configuration. Giving up.', null); | ||
error.statuscode = 500; | ||
return cb(error); | ||
} | ||
var uaaURLHostnameWithoutIDZ = uaaURLHostname.substring(uaaURLIDZIndex, uaaURLHostname.length); | ||
var newHostname = tokenIssuerIDZ + uaaURLHostnameWithoutIDZ; | ||
uaaURL.hostname = newHostname; | ||
uaaURL.host = null; | ||
uaaURLString = url.format(uaaURL); | ||
return keyCache.getKey(invalidatedTokenHeaderJSON.kid, uaaURLString, cb); | ||
validateJku(invalidatedTokenHeaderJSON.jku, config.uaadomain, function(err) { | ||
if (err) { | ||
return cb(null, config.verificationkey); | ||
} | ||
keyCache.getKey(invalidatedTokenHeaderJSON.jku, invalidatedTokenHeaderJSON.kid, function(err, key) { | ||
if (err) { | ||
return cb(null, config.verificationkey); | ||
} else { | ||
return cb(null, key); | ||
} | ||
}); | ||
}); | ||
} catch (e) { | ||
@@ -628,2 +606,17 @@ e.statuscode = 403; | ||
function validateJku(jkuUrl, uaaDomain, cb) { | ||
if (uaaDomain === null || uaaDomain === undefined) { | ||
var errorString = 'Service is not properly configured in \'VCAP_SERVICES\', attribute \'uaadomain\' is missing. Use legacy-token-key.'; | ||
debugTrace('\n' + errorString); | ||
return cb(new Error(errorString)); | ||
} | ||
var tokenKeyUrl = url.parse(jkuUrl); | ||
if (tokenKeyUrl.hostname.substring(tokenKeyUrl.hostname.indexOf(uaaDomain), tokenKeyUrl.hostname.length) !== uaaDomain) { | ||
var errorString = 'JKU of the JWT token (' + jkuUrl + ') does not match with the uaa domain (' + uaaDomain + '). Use legacy-token-key.'; | ||
debugTrace('\n' + errorString); | ||
return cb(new Error(errorString)); | ||
} | ||
cb(null); | ||
} | ||
function checkTokenLocal(accessToken, verificationkey, ssojwt, cb) { | ||
@@ -737,3 +730,4 @@ | ||
bearer: securityContext.token | ||
} | ||
}, | ||
timeout: 2000 | ||
}; | ||
@@ -751,2 +745,5 @@ if (scopes !== null) { | ||
if (error) { | ||
if (error.code === 'ETIMEDOUT' && error.connect === true) { | ||
debugError('requestToken: HTTP connection timeout.'); | ||
} | ||
debugError(error.message); | ||
@@ -777,3 +774,4 @@ debugError(error.stack); | ||
pass: serviceCredentials.clientsecret | ||
} | ||
}, | ||
timeout: 2000 | ||
}; | ||
@@ -784,2 +782,5 @@ request.post( | ||
if (error) { | ||
if (error.code === 'ETIMEDOUT' && error.connect === true) { | ||
debugError('requestToken: HTTP connection timeout.'); | ||
} | ||
debugError(error.message); | ||
@@ -844,3 +845,4 @@ debugError(error.stack); | ||
pass: serviceCredentials.clientsecret | ||
} | ||
}, | ||
timeout: 2000 | ||
}; | ||
@@ -855,2 +857,5 @@ if (additionalAttributes !== null) { | ||
if (error) { | ||
if (error.code === 'ETIMEDOUT' && error.connect === true) { | ||
debugError('requestToken: HTTP connection timeout.'); | ||
} | ||
debugError(error.message); | ||
@@ -857,0 +862,0 @@ debugError(error.stack); |
@@ -1,1 +0,1 @@ | ||
{"bundleDependencies":false,"dependencies":{"@sap/node-jwt":"^1.4.13","@sap/xsenv":"^1.2.9","debug":"3.1.0","lru-cache":"4.1.1","request":"2.88.0","valid-url":"1.0.9"},"deprecated":false,"description":"XS Advanced Container Security API for node.js","devDependencies":{"filter-node-package":"2.0.0","istanbul":"^0.4.5","jwt-decode":"^2.2.0","mocha":"^5.1.0","should":"^13.2.1"},"keywords":["xs"],"main":"./lib","name":"@sap/xssec","repository":{"type":"git"},"scripts":{"prepareRelease":"clean-packages && npm prune --production","test":"make test"},"version":"2.1.16","license":"SEE LICENSE IN developer-license-3.1.txt"} | ||
{"bundleDependencies":false,"dependencies":{"@sap/node-jwt":"^1.5.2","@sap/xsenv":"^2.0.0","debug":"4.1.1","lru-cache":"5.1.1","request":"2.88.0","requestretry":"4.0.0","valid-url":"1.0.9"},"deprecated":false,"description":"XS Advanced Container Security API for node.js","devDependencies":{"filter-node-package":"2.0.0","istanbul":"^0.4.5","jwt-decode":"^2.2.0","mocha":"^5.1.0","should":"^13.2.1"},"keywords":["xs"],"main":"./lib","name":"@sap/xssec","repository":{"type":"git"},"scripts":{"prepareRelease":"clean-packages && npm prune --production","test":"make test"},"version":"2.2.3","license":"SEE LICENSE IN developer-license-3.1.txt"} |
@@ -181,2 +181,6 @@ @sap/xssec: XS Advanced Container Security API for node.js | ||
### Usage in Docker | ||
If you intend to use XS Advanced Container Security inside a Docker image make sure to use a base image other than **alpine**. The alpine base system is lacking needed symbols for system calls. | ||
## API Description | ||
@@ -183,0 +187,0 @@ |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
302627
1185
327
7
+ Addedrequestretry@4.0.0
+ Added@sap/xsenv@2.2.0(transitive)
+ Addeddebug@4.1.1(transitive)
+ Addedlodash@4.17.21(transitive)
+ Addedlru-cache@5.1.1(transitive)
+ Addedms@2.1.3(transitive)
+ Addedrequestretry@4.0.0(transitive)
+ Addedwhen@3.7.8(transitive)
+ Addedyallist@3.1.1(transitive)
- Removed@sap/xsenv@1.3.0(transitive)
- Removedlru-cache@4.1.1(transitive)
- Removedpseudomap@1.0.2(transitive)
- Removedyallist@2.1.2(transitive)
Updated@sap/node-jwt@^1.5.2
Updated@sap/xsenv@^2.0.0
Updateddebug@4.1.1
Updatedlru-cache@5.1.1