Socket
Socket
Sign inDemoInstall

@sap/instance-manager

Package Overview
Dependencies
Maintainers
1
Versions
35
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@sap/instance-manager - npm Package Compare versions

Comparing version 3.0.0 to 3.1.0

8

CHANGELOG.md

@@ -8,2 +8,10 @@ # Change Log

## 3.1.0 - 2021-12-13
### Added
- **Service Manager.** Support for mTLS authentication
- **Service Manager.** Retrieving an instance will create a binding if no binding exists for that instance.
- **Service Manager.** Creating a new instance will now also put the *tenant_id* as label.
- **Service Manager.** Optional parameters supported for get and getAll to support fallback for mTLS.
## 3.0.0 - 2021-05-05

@@ -10,0 +18,0 @@

46

lib/Manager.js

@@ -16,2 +16,3 @@ 'use strict';

this._backend = opts.sm_url ? new ServiceManager(opts) : new InstanceManager(opts);
this._isUsingServiceManager = opts.sm_url ? true : false;
this._cache = new Cache({ max: opts.cache_max_items, maxAge: (opts.cache_item_expire_seconds * 1000) });

@@ -42,5 +43,24 @@ }

Manager.prototype.get = function (tenant, callback) {
/**
* Gets the corresponding instance for the provided tenant either from cache or from server. Service Manager only: Includes a binding creation fallback if instance has no bindings.
* @param {string} tenant
* @param {object} optionalParameters - Optional. Service Manager only. When no binding available, Manager will attempt to create a new binding with default params or those provided here.
* @param {function} callback
* @returns
*/
Manager.prototype.get = function (tenant, optionalParameters, callback) {
// Input params setup is needed to meet both SM and IM requirements.
tenant = userInput.processTenant(tenant);
const inputParams = [tenant];
if (!callback) {
callback = optionalParameters;
optionalParameters = undefined;
}
if (optionalParameters && this._isUsingServiceManager) {
inputParams.push(userInput.processOptionalParameters(optionalParameters));
}
inputParams.push(callback);
if (this._cache.has(tenant)) {

@@ -52,8 +72,21 @@ debug("Getting instance for tenant '%s' from cache", tenant);

this._backend.get(tenant, callback);
this._backend.get(...inputParams);
};
Manager.prototype.getAll = function (callback) {
var self = this;
this._backend.getAll(function (err, instances) {
/**
* Gets the instances for all tenants as an array of objects. This method updates the cache. Service Manager only: Includes binding creation fallback for each instance without binding. If binding creation fails it will log an error message and continue processing.
* @param {Object} optionalParameters - Optional. Service Manager only. When no binding available, Manager will attempt to create a new binding with default params or those provided here.
* @param {function} callback
*/
Manager.prototype.getAll = function (optionalParameters, callback) {
const inputParams = [];
if (!callback) {
callback = optionalParameters;
optionalParameters = undefined;
}
if (optionalParameters && this._isUsingServiceManager) {
inputParams.push(userInput.processOptionalParameters(optionalParameters));
}
inputParams.push(function (err, instances) {
if (err) {

@@ -67,2 +100,5 @@ return callback(err);

});
var self = this;
this._backend.getAll(...inputParams);
};

@@ -69,0 +105,0 @@

@@ -60,4 +60,3 @@ 'use strict';

var instanceId = instanceOperation.resource_id;
let instanceId = instanceOperation.resource_id;
self._createBinding(tenant, params, instanceId, polling, function (err, bindingOperation) {

@@ -109,30 +108,82 @@ if (err) {

Backend.prototype.getAll = function (cb) {
Backend.prototype.getAll = function (params, cb) {
if (!cb) {
cb = params;
params = {};
}
var self = this;
this._getBindings(null, function (err, bindings) {
this._getInstances(null, function(err, instances) {
if (err) {
debug('Unable to retrieve all instances, error: %s', err);
return cb(err);
}
self._getBindings(null, function(err2, bindings) {
if (err2) {
debug('Unable to retrieve all bindings, error: %s', err2);
return cb(err2);
}
var result = [];
var tenants = Object.keys(bindings);
tenants.forEach(function (tenant) {
var latestReadyBinding = findLatestReady(bindings[tenant]);
if (latestReadyBinding) {
result.push(latestReadyBinding);
} else {
debug('No ready binding for tenant %s', tenant);
let result = [];
let bindingTenants = Object.keys(bindings);
bindingTenants.forEach(tenant => {
var latestReadyBinding = findLatestReady(bindings[tenant]);
if (latestReadyBinding) {
result.push(self._toManagedInstance(latestReadyBinding));
} else {
debug('No ready binding for tenant %s', tenant);
}
});
let instanceTenants = Object.keys(instances);
let instanceTenantsWithoutBinding = extractInstanceTenantsWithoutBinding(instanceTenants, bindingTenants);
let processedInstanceWithoutBinding = 0;
if (instanceTenantsWithoutBinding.length === 0) {
return cb(null, result);
}
});
result = result.map(function (binding) {
return self._toManagedInstance(binding);
instanceTenantsWithoutBinding.forEach(tenant => {
let instance = instances[tenant][0];
let instanceId = instance.id;
let polling = new Polling(tenant, self._options.polling_interval_millis, self._options.polling_timeout_seconds);
self._createBinding(tenant, params, instanceId, polling, function (bindingErr, bindingOperation) {
if (bindingErr) {
debug(format('Unable to create binding for tenant %s, error: %s', tenant, bindingErr));
processedInstanceWithoutBinding++;
}
else {
self._client.getBindingById(bindingOperation.resource_id, function (getBindingErr, binding) {
processedInstanceWithoutBinding++;
if (getBindingErr) {
debug(format('Unable to retrieve newly created binding for tenant %s, error: %s', tenant, getBindingErr));
}
else if (!binding.ready) {
debug('Newly created binding not ready for tenant %s', tenant);
}
else {
result.push(binding);
}
if (processedInstanceWithoutBinding === instanceTenantsWithoutBinding.length) {
return cb(null, result);
}
});
}
if (processedInstanceWithoutBinding === instanceTenantsWithoutBinding.length) {
return cb(null, result);
}
});
});
});
cb(null, result);
});
};
Backend.prototype.get = function (tenant, cb) {
Backend.prototype.get = function (tenant, params, cb) {
// Backwards compatible.
if (!cb) {
cb = params;
params = {};
}
var self = this;
var polling = new Polling(tenant, this._options.polling_interval_millis, this._options.polling_timeout_seconds);
this._getBindings(tenant, function (err, bindings) {

@@ -150,3 +201,3 @@ if (err) {

return cb(new Error(format('No ready binding for tenant %s', tenant)));
return cb(new Error(format('There are available bindings but none are ready for tenant %s', tenant)));
}

@@ -165,3 +216,16 @@

cb(new Error(format('Instance, but no bindings for tenant %s', tenant)));
let instanceId = instance.id || instance.resource_id;
self._createBinding(tenant, params, instanceId, polling, function (err, bindingOperation) {
if (err) {
cb(err);
}
self._client.getBindingById(bindingOperation.resource_id, function (err, binding) {
if (err) {
return cb(err);
}
cb(null, self._toManagedInstance(binding));
});
});
});

@@ -177,2 +241,5 @@ });

var labels = { 'tenant_id': [tenant] };
options.labels = labels;
if (params && params.provisioning_parameters) {

@@ -188,3 +255,3 @@ options.parameters = params.provisioning_parameters;

'name': this._bindingName(),
'service_instance_id': instanceId,
'service_instance_id': instanceId
};

@@ -310,2 +377,33 @@

Backend.prototype._getInstances = function (tenant, cb) {
var fieldQuery = format("service_plan_id eq '%s'", this._servicePlanId);
if (tenant) {
fieldQuery += format(" and tenant_id eq '%s'", tenant);
}
this._client.getInstances({ fieldQuery: fieldQuery }, function (err, instances) {
if (err) {
return cb(err);
}
if (instances.length === 0) {
return cb(null, {});
}
var grouped = groupByTenant(instances);
var tenants = Object.keys(grouped);
for (var i = 0; i < tenants.length; ++i) {
var tenant = tenants[i];
var tenantBindings = grouped[tenant];
var validationError = validateTimestamps(tenantBindings);
if (validationError) {
return cb(validationError);
}
grouped[tenant] = sortByTimestamp(tenantBindings);
}
cb(null, grouped);
});
};
Backend.prototype._toManagedInstance = function (binding) {

@@ -368,1 +466,18 @@ var managedInstance = clone(this._serviceMetadata);

}
function extractInstanceTenantsWithoutBinding(instanceTenants, bindingTenants) {
let instanceTenantsWithoutBinding = instanceTenants;
if (instanceTenants.length > bindingTenants.length) {
for (let i = 0; i < bindingTenants.length; i++) {
let instanceTenantIndex = instanceTenantsWithoutBinding.indexOf(bindingTenants[i]);
if (instanceTenantIndex >= 0) {
instanceTenantsWithoutBinding.splice(instanceTenantIndex, 1);
}
}
} else {
instanceTenantsWithoutBinding = instanceTenants.filter(it => !bindingTenants.includes(it));
}
return instanceTenantsWithoutBinding;
}

@@ -38,2 +38,7 @@ 'use strict';

Client.prototype.getInstances = function(query, cb) {
var resource = this._resources.instances();
resource.list(query, cb);
};
Client.prototype.createBinding = function (options, polling, cb) {

@@ -40,0 +45,0 @@ var resource = this._resources.bindings();

4

lib/service-manager/constants.js
'use strict';
var MANDATORY_OPTIONS = ['url', 'clientid', 'clientsecret', 'sm_url'];
var MANDATORY_OPTIONS = ['url', 'clientid', 'sm_url'];
var MANDATORY_OR_OPTIONS = ['clientsecret', 'certificate'];

@@ -26,2 +27,3 @@ var MIN_TOKEN_VALIDITY_SECONDS = 30;

MANDATORY_OPTIONS: MANDATORY_OPTIONS,
MANDATORY_OR_OPTIONS: MANDATORY_OR_OPTIONS,
MIN_TOKEN_VALIDITY_SECONDS: MIN_TOKEN_VALIDITY_SECONDS,

@@ -28,0 +30,0 @@ STATUS: { HTTP: HTTP_STATUS },

@@ -5,2 +5,3 @@ 'use strict';

var debug = require('debug')('instance-manager');
var https = require('https');

@@ -158,2 +159,3 @@ var HTTP = require('./constants').STATUS.HTTP;

Resource.prototype._sendRequest = function (reqOptions, cb) {
var self = this;
reqOptions.baseUrl = this._options.sm_url;

@@ -176,6 +178,16 @@ var reqContext = [reqOptions.method, reqOptions.uri];

let agent = null;
if (self._options && self._options.certificate) {
const httpsAgent = new https.Agent({
certificate: self._options.certificate,
key: self._options.key
});
agent = httpsAgent;
}
fetch(url, {
method: reqOptions.method,
headers: headers,
body: reqOptions.body
body: reqOptions.body,
agent: agent
}).then(function (response) {

@@ -182,0 +194,0 @@ res.statusCode = response.status;

'use strict';
var format = require('util').format;
var urlAppendQueryString = require('../utils').urlAppendQueryString;
var fetch = require('node-fetch');
var debug = require('debug')('instance-manager');
var requests = require('@sap/xssec').requests;
var HTTP = require('./constants').STATUS.HTTP;
var MIN_TOKEN_VALIDITY_SECONDS = require('./constants').MIN_TOKEN_VALIDITY_SECONDS;
var safeParse = require('../utils').safeJsonParse;
module.exports = TokenHandler;
var MILLISECOND = 1;
var SECOND = 1000 * MILLISECOND;
function TokenHandler(options) {
this._options = options;
this._options.timeout = 5000;
this._cached = null;

@@ -23,43 +14,9 @@ }

TokenHandler.prototype.getToken = function (cb) {
if (this._cached) {
if (this._cached.expiryTime - Date.now() > MIN_TOKEN_VALIDITY_SECONDS * SECOND) {
return cb(null, this._cached.token);
requests.requestClientCredentialsToken(null, this._options, null, null, (err, token) => {
if (err) {
return cb(err);
}
}
var self = this;
var url = this._options.url + '/oauth/token';
url = urlAppendQueryString(url, { 'grant_type': 'client_credentials' });
var headers = {
'Authorization': 'Basic ' + Buffer.from(this._options.clientid + ':' + this._options.clientsecret).toString('base64')
};
var statusCode = null;
fetch(url, {
method: 'GET',
headers: headers
}).then(function (res) {
statusCode = res.status;
return res.text();
}).then(function (body) {
if (statusCode !== HTTP.OK) {
var error = new Error(format('Status %d received while getting access token, body: %s', statusCode, body));
error.statusCode = statusCode;
return cb(error);
}
var parseResult = safeParse(body);
if (parseResult.err) {
debug('Response for getting an access token does not represent a valid JSON, body: %s', body);
return cb(parseResult.err);
}
self._cached = {
token: parseResult.data.access_token,
expiryTime: Date.now() + (parseResult.data.expires_in * SECOND)
};
cb(null, parseResult.data.access_token);
}).catch(function (err) {
cb(err);
cb(null, token);
});
};
};

@@ -6,2 +6,3 @@ 'use strict';

var MANDATORY_OPTIONS = require('./constants').MANDATORY_OPTIONS;
var MANDATORY_OR_OPTIONS = require('./constants').MANDATORY_OR_OPTIONS;
var DEFAULT_SERVICE = require('./constants').DEFAULT_SERVICE;

@@ -27,2 +28,6 @@ var DEFAULT_PLAN = require('./constants').DEFAULT_PLAN;

if (!MANDATORY_OR_OPTIONS.some((property) => options[property])) {
throw new Error(format("One of listed properties: '%s' should be a non-empty string", MANDATORY_OR_OPTIONS.join(', ')));
}
options.service = options.service || DEFAULT_SERVICE;

@@ -29,0 +34,0 @@ options.plan = options.plan || DEFAULT_PLAN;

{
"name": "@sap/instance-manager",
"version": "3.0.0",
"version": "3.1.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"@sap/xssec": {
"version": "3.2.11",
"requires": {
"axios": "^0.21.4",
"debug": "4.3.2",
"jsonwebtoken": "^8.5.1",
"lru-cache": "6.0.0",
"node-rsa": "^1.1.1",
"valid-url": "1.0.9"
},
"dependencies": {
"debug": {
"version": "4.3.2",
"requires": {
"ms": "2.1.2"
}
},
"lru-cache": {
"version": "6.0.0",
"requires": {
"yallist": "^4.0.0"
}
}
}
},
"asn1": {
"version": "0.2.6",
"requires": {
"safer-buffer": "~2.1.0"
}
},
"axios": {
"version": "0.21.4",
"requires": {
"follow-redirects": "^1.14.0"
}
},
"buffer-equal-constant-time": {
"version": "1.0.1"
},
"clone": {

@@ -16,2 +56,62 @@ "version": "2.1.1"

},
"ecdsa-sig-formatter": {
"version": "1.0.11",
"requires": {
"safe-buffer": "^5.0.1"
}
},
"follow-redirects": {
"version": "1.14.6"
},
"jsonwebtoken": {
"version": "8.5.1",
"requires": {
"jws": "^3.2.2",
"lodash.includes": "^4.3.0",
"lodash.isboolean": "^3.0.3",
"lodash.isinteger": "^4.0.4",
"lodash.isnumber": "^3.0.3",
"lodash.isplainobject": "^4.0.6",
"lodash.isstring": "^4.0.1",
"lodash.once": "^4.0.0",
"ms": "^2.1.1",
"semver": "^5.6.0"
}
},
"jwa": {
"version": "1.4.1",
"requires": {
"buffer-equal-constant-time": "1.0.1",
"ecdsa-sig-formatter": "1.0.11",
"safe-buffer": "^5.0.1"
}
},
"jws": {
"version": "3.2.2",
"requires": {
"jwa": "^1.4.1",
"safe-buffer": "^5.0.1"
}
},
"lodash.includes": {
"version": "4.3.0"
},
"lodash.isboolean": {
"version": "3.0.3"
},
"lodash.isinteger": {
"version": "4.0.4"
},
"lodash.isnumber": {
"version": "3.0.3"
},
"lodash.isplainobject": {
"version": "4.0.6"
},
"lodash.isstring": {
"version": "4.0.1"
},
"lodash.once": {
"version": "4.1.1"
},
"lru-cache": {

@@ -22,2 +122,7 @@ "version": "4.1.1",

"yallist": "^2.1.2"
},
"dependencies": {
"yallist": {
"version": "2.1.2"
}
}

@@ -31,12 +136,30 @@ },

},
"node-rsa": {
"version": "1.1.1",
"requires": {
"asn1": "^0.2.4"
}
},
"pseudomap": {
"version": "1.0.2"
},
"safe-buffer": {
"version": "5.2.1"
},
"safer-buffer": {
"version": "2.1.2"
},
"semver": {
"version": "5.7.1"
},
"uuid": {
"version": "7.0.0"
},
"valid-url": {
"version": "1.0.9"
},
"yallist": {
"version": "2.1.2"
"version": "4.0.0"
}
}
}
{
"name": "@sap/instance-manager",
"version": "3.0.0",
"version": "3.1.0",
"main": "index.js",
"license": "SEE LICENSE IN LICENSE file",
"dependencies": {
"@sap/xssec": "^3.2.11",
"clone": "2.1.1",

@@ -8,0 +9,0 @@ "debug": "4.2.0",

@@ -100,7 +100,9 @@ # @sap/instance-manager

clientid | x | Used when retrieving a token.
clientsecret | x | Used when retrieving a token.
clientsecret (not required for mTLS) | x | Used when retrieving a token.
certificate (only for mTLS) | x | Used when retrieving a token.
certurl (only for mTLS) | x | Used when retrieving a token.
service | | Defaults to 'hana'. Name of the service of which to manage instances.
plan | | Defaults to 'hdi-shared'. Name of a plan from the selected service of which to manage instances.
**Note**: A _service-manager_ binding contains all the mandatory properties mentioned above.
**Note**: A _service-manager_ binding contains all the mandatory properties mentioned above. For non-mTLS authentication *clientsecret* is required, where *certificate* and *certurl* are required for mTLS authentication.

@@ -143,5 +145,6 @@ #### Optional parameters

- `get(tenant, callback)` - gets the corresponding instance for the provided tenant either from cache or from server.
- `get(tenant, bindingParams, callback)` - gets the corresponding instance for the provided tenant either from cache or from server. Includes a binding creation fallback if instance has no bindings.
Value of `null` means that a service instance for this tenant does not exist.
- tenant | *String* | Tenant name.
- optionalParameters | *Object* | **Optional.** JSON object with parameters for provisioning or binding, as would be done with the -c options of the CLI commands. Used during binding creation fallback.
- callback | *function(err, instance)* | Callback function with the instance as second argument.

@@ -154,3 +157,4 @@

- `getAll(callback)` - gets the instances for all tenants as an array of objects. This method updates the cache.
- `getAll(optionalParameters, callback)` - gets the instances for all tenants as an array of objects. This method updates the cache. Includes binding creation fallback for each instance without binding. If binding creation fails it will log an error message and continue processing.
- optionalParameters | *Object* | **Optional.** JSON object with parameters for provisioning or binding, as would be done with the -c options of the CLI commands. Used during binding creation fallback.
- callback | *function(err, instances)* | Callback function with all instances as second argument.

@@ -157,0 +161,0 @@

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc