ubiq-security
Version:
Ubiq Client Node.js Implementation
351 lines (317 loc) • 10.1 kB
JavaScript
const fetch = require('node-fetch');
const auth = require('./auth');
const { UrlHelper } = require('./urlHelper');
const http = require('http');
const https = require('https');
class UbiqWebServices {
constructor(ubiqCredentials, ubiqConfiguration) {
this.ubiqCredentials = ubiqCredentials;
this.ubiqConfiguration = ubiqConfiguration;
this.applicationJson = 'application/json';
this.endpoint = '/api/v0/encryption/key';
this.restApiRoot = 'api/v0';
this.restApiV3Root = 'api/v3';
if (ubiqCredentials.host.indexOf('http') < 0) {
this.baseUrl = `https://${this.ubiqCredentials.host}`;
} else {
this.baseUrl = ubiqCredentials.host;
}
// node-fetch changed in Node 19 to set keepAlive: true by default
// This was causing problems with request handles were left open too long between cache TTL
// and resulted in failed subsequent calls. Changed default back to false and pass into the
// requests.
this.agent = this.baseUrl.startsWith('https')
? new https.Agent({
keepAlive: false
})
: new http.Agent({
keepAlive: false
});
}
async GetFfsConfigurationsync(ffsName) {
const key = UrlHelper.GenerateFfsUrl(ffsName, this.ubiqCredentials);
this.endpoint = `/api/v0/ffs?${key}`;
this.endpoint_base = `${this.baseUrl}${this.endpoint}`;
const headers = auth.headers(
this.ubiqCredentials.access_key_id,
this.ubiqCredentials.secret_signing_key,
this.endpoint,
null,
this.baseUrl,
'get',
);
const otherParam = {
headers,
method: 'GET',
agent: this.agent,
};
try {
var response = await fetch(this.endpoint_base, otherParam);
if (response.status < 400) {
const result = await response.json();
return result;
}
let validJson = true;
let apiBody;
try {
apiBody = await response.json();
} catch (err) {
validJson = false;
}
if (validJson && apiBody && apiBody.message) {
if (apiBody.status) {
throw new Error(`${apiBody.message} Status: ${apiBody.status}`);
} else {
throw new Error(`${apiBody.message} Status: ${response.status}`);
}
}
if (response.status === 401) {
throw new Error(`Unauthorized Request Status: ${response.status}`);
}
} catch (err) {
if (err.code === 'ENOTFOUND') {
throw new Error('URL not found.');
}
throw err;
}
}
async GetFpeEncryptionKeyAsync(ffsName, keyNumber) {
let key = '';
if (keyNumber == null) {
key = UrlHelper.GenerateFpeUrlEncrypt(ffsName, this.ubiqCredentials);
} else {
key = UrlHelper.GenerateFpeUrlDecrypt(ffsName, keyNumber, this.ubiqCredentials);
}
this.endpoint = `/${this.restApiRoot}/fpe/key?${key}`;
if (this.ubiqCredentials.isIdp()) {
await this.ubiqCredentials.renewIdpCertAsync()
this.endpoint += `&payload_cert=${this.ubiqCredentials.idp_cert_base64}`
}
this.endpoint_base = `${this.baseUrl}${this.endpoint}`;
const headers = auth.headers(
this.ubiqCredentials.access_key_id,
this.ubiqCredentials.secret_signing_key,
this.endpoint,
null,
this.baseUrl,
'get',
);
const otherParam = {
headers,
// body: JSON.stringify(body),
method: 'GET',
agent: this.agent,
};
try {
const response = await fetch(this.endpoint_base, otherParam);
if (response.status === 200) {
const result = await response.json();
return result;
}
const apiError = await response.json();
throw new Error(apiError.message);
} catch (err) {
// console.error('Error GetFfsConfigurationsync:', err);
throw err;
}
}
// Events is an array of objects, not a string
async sendBillingAsync(events) {
// const e = "usage" : [{
// datasets: '',
// dataset_groups: '',
// api_key: '9Rz/BH9C8DFgS45TMKFls52Q',
// count: 1,
// key_number: 0,
// action: 'encrypt',
// product: 'ubiq-node',
// product_version: '1.0.9',
// user_agent: 'ubiq-node/1.0.9',
// api_version: 'V3',
// date: '2023-03-16T08:18:21.474Z'
// }]
// If the array is empty, simply return
if (typeof (events !== 'undefined') && events.usage) {
if (events.usage.length == 0) {
return ""
}
}
this.endpoint = `/${this.restApiV3Root}/tracking/events`;
this.endpoint_base = `${this.baseUrl}${this.endpoint}`;
// const url = `http://localhost:8080/api/v3/billing`;
// Retrieve the necessary headers to make the request using Auth Object
const headers = auth.headers(
this.ubiqCredentials.access_key_id,
this.ubiqCredentials.secret_signing_key,
this.endpoint,
events,
this.baseUrl,
'post');
const otherParam = {
headers,
body: JSON.stringify(events),
method: 'POST',
agent: this.agent,
};
return new Promise(async (resolve, reject) => {
try {
// Wait for server response
const response = await fetch(this.endpoint_base, otherParam);
// If response status is success
if (response.status < 400) {
// NOP
}
// For any other response status code
else {
throw new Error(`HTTPError Response: status code: ${response.status}`)
}
} catch (ex) {
reject(ex)
}
resolve("");
});
}
async GetFFSAndDataKeys(ffsName) {
const key = UrlHelper.GenerateFfsUrl(ffsName, this.ubiqCredentials);
this.endpoint = `/api/v0/fpe/def_keys?${key}`;
if (this.ubiqCredentials.isIdp()) {
await this.ubiqCredentials.renewIdpCertAsync()
this.endpoint += `&payload_cert=${this.ubiqCredentials.idp_cert_base64}`
}
this.endpoint_base = `${this.baseUrl}${this.endpoint}`;
const headers = auth.headers(
this.ubiqCredentials.access_key_id,
this.ubiqCredentials.secret_signing_key,
this.endpoint,
null,
this.baseUrl,
'get',
);
const otherParam = {
headers,
method: 'GET',
agent: this.agent,
};
let response;
try {
response = await fetch(this.endpoint_base, otherParam);
if (response.status === 200) {
const result = await response.json();
return result;
}
let validJson = true;
let apiBody;
try {
apiBody = await response.json();
} catch (err) {
validJson = false;
}
if (validJson && apiBody && apiBody.message) {
throw new Error(apiBody.message);
}
if (response.status === 401) {
throw new Error(`HTTPError Response: status code: ${response.status}`)
}
} catch (err) {
if (err.code === 'ENOTFOUND') {
throw new Error('URL not found.');
}
throw err;
}
}
// If this is an IDP configuration and credentials
// Get the OAuth token
async GetOAuthToken() {
var options = {}
const agent = this.ubiqConfiguration.idp_token_endpoint_url.startsWith('https')
? new https.Agent({
keepAlive: false
})
: new http.Agent({
keepAlive: false
});
if (this.ubiqConfiguration.idp_type == 'okta') {
options = {
method: 'POST',
agent: agent,
body: new URLSearchParams({
client_id: this.ubiqConfiguration.idp_tenant_id,
client_secret: this.ubiqConfiguration.idp_client_secret,
username: this.ubiqCredentials.idp_username,
password: this.ubiqCredentials.idp_password,
grant_type: 'password',
scope: 'openid offline_access okta.users.read okta.groups.read'
}).toString(),
headers: {
Accept: 'application/json',
'Cache-control': 'no-cache',
'Content-type': 'application/x-www-form-urlencoded',
}
}
} else if (this.ubiqConfiguration.idp_type == 'entra') {
options = {
method: 'POST',
agent: agent,
body: new URLSearchParams({
client_id: this.ubiqConfiguration.idp_tenant_id,
client_secret: this.ubiqConfiguration.idp_client_secret,
username: this.ubiqCredentials.idp_username,
password: this.ubiqCredentials.idp_password,
grant_type: 'password',
scope: 'api://' + this.ubiqConfiguration.idp_tenant_id + '/.default'
}).toString(),
headers: {
Accept: 'application/json',
'Cache-control': 'no-cache',
'Content-type': 'application/x-www-form-urlencoded',
}
}
}
let response;
try {
response = await fetch(this.ubiqConfiguration.idp_token_endpoint_url, options);
if (response.status === 200) {
const result = await response.json();
return result
// return result;
} else {
throw new Error(`Status ${response.status} (${response.statusText}) Unable to fetch token from ${this.ubiqConfiguration.idp_token_endpoint_url}`)
}
}
catch (ex) {
throw ex;
}
}
// If this is an IDP configuration and credentials
// Get the OAuth token
async GetSso(access_token, csr) {
var options = {
method: 'POST',
body: JSON.stringify({ csr: csr }),
agent: this.agent,
headers: {
Authorization: `Bearer ${access_token}`,
Accept: 'application/json',
'Cache-control': 'no-cache',
"content-type": 'application/json',
}
}
let response;
try {
let url = `${this.baseUrl}/${this.ubiqConfiguration.idp_customer_id}/${this.restApiV3Root}/scim/sso`
response = await fetch(url, options);
if (response.status === 200) {
const result = await response.json();
return result
} else {
throw new Error(`Status ${response.status} (${response.statusText}) Unable to validate token ${url}`)
}
}
catch (ex) {
throw ex;
}
}
}
module.exports = {
UbiqWebServices,
};