ubiq-security
Version:
Ubiq Client Node.js Implementation
177 lines (144 loc) • 7.2 kB
JavaScript
const fs = require('fs');
const forge = require('node-forge');
const ConfigParser = require('configparser');
const { generateKeyPair, generateCsr } = require('./rsaKeys');
const { UbiqWebServices } = require('./ubiqWebServices');
const crypto = require('crypto');
class Credentials {
constructor(access_key_id, secret_signing_key, secret_crypto_access_key, host, idp_username, idp_password) {
this.initialized = false
this.access_key_id = ((access_key_id) || process.env.UBIQ_ACCESS_KEY_ID || "").trim();
this.secret_signing_key = ((secret_signing_key) || process.env.UBIQ_SECRET_SIGNING_KEY || "").trim();
this.secret_crypto_access_key = ((secret_crypto_access_key) || process.env.UBIQ_SECRET_CRYPTO_ACCESS_KEY || "").trim();
this.host = ((host) || process.env.UBIQ_SERVER || 'https://api.ubiqsecurity.com').trim()
if (this.host.indexOf('http://') !== 0 && this.host.indexOf('https://') !== 0) {
this.host = `https://${this.host}`;
}
this.idp_username = ((idp_username) || process.env.UBIQ_IDP_USERNAME || "").trim();
this.idp_password = ((idp_password) || process.env.UBIQ_IDP_PASSWORD || "").trim();
this.cert_expires = new Date(new Date() - 60000)
if ((this.secret_crypto_access_key) || ((this.idp_username) && (this.idp_password))) {
// NOP
}
else {
throw (new Error("Credentials data is incomplete"))
}
}
isIdp() {
let ret = (this.idp_username.length > 0)
// If this is IDP mode, make sure the init has been called and everything had been setup
if (ret && !this.initialized) {
throw (new Error("Credentials.init(configuration) has not been called or failed but is required when using IDP authentication"))
}
return ret
}
getEncryptedPrivateKey() {
return (this.encryptedPrivateKey)
}
// Check to see if the access token needs to be renewed or the cert needs to be
// renewed and if so, refresh both and get the signed cert
async renewIdpCertAsync() {
if (this.isIdp()) {
if (this.cert_expires < new Date()) {
await this.getIdpTokenAndCertAsync()
}
}
}
async getIdpTokenAndCertAsync() {
this.token = await this.ubiqWebServices.GetOAuthToken();
this.sso = await this.ubiqWebServices.GetSso(this.token.access_token, this.csr);
this.idp_cert_base64 = Buffer.from(this.sso.api_cert).toString('base64');
// Parse cert for expiration date
const x509 = new crypto.X509Certificate(this.sso.api_cert)
// Set cert expiration 1 minute before actual to avoid edge case
this.cert_expires = new Date(new Date(x509.validTo) - 60000);
// Private key is still the same as before
}
async initAsync(ubiqConfiguration) {
if (this.idp_username) {
this.ubiqConfiguration = ubiqConfiguration
// Webservice needed to fetch the token and sso. These are sync calls so can use
// the same webservice
this.ubiqWebServices = new UbiqWebServices(this, this.ubiqConfiguration)
this.secret_crypto_access_key = await forge.util.encode64(forge.random.getBytesSync(33));
const { publicKey: apiPublicKey_pem, privateKey: apiPrivateKey_pem } = await generateKeyPair()
this.csr = await generateCsr(apiPublicKey_pem, apiPrivateKey_pem)
await this.getIdpTokenAndCertAsync()
this.access_key_id = this.sso.public_value
this.secret_signing_key = this.sso.signing_value
let rsaPrivateKey = await forge.pki.privateKeyToAsn1(forge.pki.privateKeyFromPem(apiPrivateKey_pem));
let privateKeyInfo = await forge.pki.wrapRsaPrivateKey(rsaPrivateKey);
let encryptedPrivateKeyInfo = await forge.pki.encryptPrivateKeyInfo(
privateKeyInfo,
this.secret_crypto_access_key,
{
algorithm: 'aes256',
}
);
this.encryptedPrivateKey = await forge.pki.encryptedPrivateKeyToPem(
encryptedPrivateKeyInfo
);
// console.log("secret_crypto_access_key", this.secret_crypto_access_key, "\nencryptedPrivateKey", this.encryptedPrivateKey)
}
this.initialized = true
}
}
class ConfigCredentials {
constructor(config_file, profile) {
// If config_file is undefined or empty string,
// try to use either standard credentials else credentials.json
if (!config_file) {
config_file = `${require('os').homedir()}/.ubiq/credentials`;
if (!fs.existsSync(config_file)) {
config_file = `${require('os').homedir()}/.ubiq/credentials.json`;
if (!fs.existsSync(config_file)) {
return new Credentials(null, null, null, null, null, null)
}
}
}
return (this.load_credentials(config_file, profile));
}
process_json_credentials(credentials_data, profile) {
let def = {};
let prof = {};
if ((credentials_data.default)) {
def = credentials_data.default;
}
if (profile) {
if (credentials_data[profile]) {
prof = credentials_data[profile];
}
}
const papi = (prof.ACCESS_KEY_ID) ? prof.ACCESS_KEY_ID : def.ACCESS_KEY_ID;
const sapi = (prof.SECRET_SIGNING_KEY) ? prof.SECRET_SIGNING_KEY : def.SECRET_SIGNING_KEY;
const srsa = (prof.SECRET_CRYPTO_ACCESS_KEY) ? prof.SECRET_CRYPTO_ACCESS_KEY : def.SECRET_CRYPTO_ACCESS_KEY;
const server = (prof.SERVER) ? prof.SERVER : def.SERVER;
const idp_username = (prof.IDP_USERNAME) ? prof.IDP_USERNAME : def.IDP_USERNAME;
const idp_password = (prof.IDP_PASSWORD) ? prof.IDP_PASSWORD : def.IDP_PASSWORD;
return new Credentials(papi, sapi, srsa, server, idp_username, idp_password);
}
load_credentials_file(credentials_file, profile) {
const config = new ConfigParser();
config.read(credentials_file);
const papi = ((config.get(profile, 'ACCESS_KEY_ID')) ? config.get(profile, 'ACCESS_KEY_ID') : config.get('default', 'ACCESS_KEY_ID') || '').trim();
const sapi = ((config.get(profile, 'SECRET_SIGNING_KEY')) ? config.get(profile, 'SECRET_SIGNING_KEY') : config.get('default', 'SECRET_SIGNING_KEY') || '').trim();
const srsa = ((config.get(profile, 'SECRET_CRYPTO_ACCESS_KEY')) ? config.get(profile, 'SECRET_CRYPTO_ACCESS_KEY') : config.get('default', 'SECRET_CRYPTO_ACCESS_KEY') || '').trim();
const server = ((config.get(profile, 'SERVER')) ? config.get(profile, 'SERVER') : config.get('default', 'SERVER') || '');
const idp_username = ((config.get(profile, 'IDP_USERNAME')) ? config.get(profile, 'IDP_USERNAME') : config.get('default', 'IDP_USERNAME') || '');
const idp_password = ((config.get(profile, 'IDP_PASSWORD')) ? config.get(profile, 'IDP_PASSWORD') : config.get('default', 'IDP_PASSWORD') || '');
return new Credentials(papi, sapi, srsa, server, idp_username, idp_password);
}
load_credentials(credentials_file, profile) {
let credentials_data = fs.readFileSync(credentials_file);
let ret;
try {
credentials_data = JSON.parse(credentials_data);
ret = this.process_json_credentials(credentials_data, profile);
} catch (e) {
// config parser library requires file name, not data
ret = this.load_credentials_file(credentials_file, profile);
}
return ret;
}
}
module.exports = { ConfigCredentials, Credentials };