oracledb
Version:
A Node.js module for Oracle Database access from JavaScript and TypeScript
212 lines (202 loc) • 9.09 kB
JavaScript
// Copyright (c) 2024, Oracle and/or its affiliates.
//-----------------------------------------------------------------------------
//
// This software is dual-licensed to you under the Universal Permissive License
// (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
// 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose
// either license.
//
// If you elect to accept the software under the Apache License, Version 2.0,
// the following applies:
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//-----------------------------------------------------------------------------
'use strict';
const errors = require("../errors.js");
const { base } = require("./base.js");
const fs = require('fs');
const cloud_net_naming_pattern_oci = new RegExp("(?<objservername>[A-Za-z0-9._-]+)/n/" + "(?<namespace>[A-Za-z0-9._-]+)/b/" + "(?<bucketname>[A-Za-z0-9._-]+)/o/" + "(?<filename>[A-Za-z0-9._-]+)$");
// object to store module references that will be populated by init()
const oci = {};
class OCIProvider extends base {
constructor(provider_arg, urlExtendedPart) {
super(urlExtendedPart);
const match = provider_arg.match(cloud_net_naming_pattern_oci);
if (match) {
this._addParam("objservername", match.groups.objservername);
this._addParam("namespace", match.groups.namespace);
this._addParam("bucketname", match.groups.bucketname);
this._addParam("filename", match.groups.filename);
}
}
//---------------------------------------------------------------------------
// init()
//
// Require/import modules from ociobject
//---------------------------------------------------------------------------
init() {
oci.common = require('oci-common');
oci.objectstorage = require('oci-objectstorage');
}
//---------------------------------------------------------------------------
// _streamToString()
//
// Converts data stored in a Readable stream to string
//---------------------------------------------------------------------------
async _streamToString(stream) {
const chunks = [];
for await (const chunk of stream) {
chunks.push(Buffer.from(chunk));
}
return Buffer.concat(chunks).toString("utf-8");
}
//---------------------------------------------------------------------------
// _returnCredential()
//
// Returns credential to access OCI Object Store on the basis of
// authentication parameters given by the user.
//---------------------------------------------------------------------------
async _returnCredential() {
let provider = null;
let auth = null;
if (this.paramMap.get('authentication')) {
auth = this.paramMap.get('authentication').toUpperCase();
}
// authentication parameter given and its not OCI_DEFAULT
if (auth && !(auth == 'OCI_DEFAULT')) {
if (auth == 'OCI_INSTANCE_PRINCIPAL') {
provider = await new oci.common.InstancePrincipalsAuthenticationDetailsProviderBuilder().build();
} else if (auth == 'OCI_RESOURCE_PRINCIPAL') {
provider = await new oci.common.ResourcePrincipalAuthenticationDetailsProvider.builder();
} else
errors.throwErr(errors.ERR_OCIOBJECT_CONFIG_PROVIDER_AUTH_FAILED, auth);
} else {
// default authentication
try {
//authentication parameters exist in the configurationFile
provider = new oci.common.ConfigFileAuthenticationDetailsProvider(
this.paramMap.get("oci_profile_path"), //default path ~/.oci/config
this.paramMap.get("oci_profile")
);
} catch (err) {
//throw error for wrong profile or wrong path
if (!this.paramMap.get("oci_tenancy") || !this.paramMap.get("oci_user")) {
throw (err);
}
// authentication parameters are directly given in the connectString
const publicKey = fs.readFileSync(this.paramMap.get('oci_key_file'), { encoding: "utf8" });
const region = this.retrieveRegion(this.paramMap.get('objservername'));
provider = new oci.common.SimpleAuthenticationDetailsProvider(
this.paramMap.get("oci_tenancy"),
this.paramMap.get("oci_user"),
this.paramMap.get("oci_fingerprint"),
publicKey,
undefined,
oci.common.Region[region]
);
}
}
return provider;
}
//---------------------------------------------------------------------------
// returnConfig()
//
// Returns config stored in the OCI Object Store and
// parses and gets password field stored in OCI/Azure Vault
//---------------------------------------------------------------------------
async returnConfig() {
const configObject = {};
this.credential = await this._returnCredential();
// oci object store
const client_oci = new (oci.objectstorage).ObjectStorageClient({
authenticationDetailsProvider: this.credential
});
const getObjectRequest = {
objectName: this.paramMap.get('filename'),
bucketName: this.paramMap.get('bucketname'),
namespaceName: this.paramMap.get('namespace')
};
const getObjectResponse = await client_oci.getObject(getObjectRequest);
const resp = await this._streamToString(getObjectResponse.value);
// Entire object we get from OCI Object Storage
this.obj = JSON.parse(resp);
const userAlias = this.paramMap.get('key'); // alias
if (userAlias) {
this.obj = this.obj[userAlias];
}
const pmSection = 'node-oracledb';
const params = this.obj[pmSection];
for (const key in params) {
var val = params[key];
configObject[key] = val;
}
configObject.connectString = this.obj.connect_descriptor;
configObject.user = this.obj.user;
if (this.obj.password) {
configObject.password = await this.retrieveParamValueFromVault('password');
}
if (this.obj.wallet_location) {
// retrieve wallet_location
configObject.walletContent = await this.retrieveParamValueFromVault('wallet_location');
//only Pem file supported
if (!this.isPemFile(configObject.walletContent))
errors.throwErr(errors.ERR_WALLET_TYPE_NOT_SUPPORTED);
}
return configObject;
}
//---------------------------------------------------------------------------
// retrieveRegion(objservername)
//
// returns region from the given objservername.
//---------------------------------------------------------------------------
retrieveRegion(objservername) {
const arr = objservername.split(".");
return arr[1].toUpperCase().replaceAll('-', '_');
}
async retrieveParamValueFromVault(param) {
if (this.obj[param].type == "azurevault") {
if (this.obj[param].authentication) {
const { SecretClient } = require("@azure/keyvault-secrets");
const {ClientSecretCredential, ClientCertificateCredential} = require("@azure/identity");
if (this.obj[param].authentication.azure_client_secret)
this.credential = new ClientSecretCredential(this.obj[param].authentication.azure_tenant_id, this.obj[param].authentication.azure_client_id, this.obj[param].authentication.azure_client_secret);
else if (this.obj[param].authentication.azure_client_certificate_path)
this.credential = new ClientCertificateCredential(this.obj[param].authentication.azure_tenant_id, this.obj[param].authentication.azure_client_id, this.obj[param].authentication.azure_client_certificate_path);
else
errors.throwErr(errors.ERR_AZURE_VAULT_AUTH_FAILED);
const vault_detail = await this._parsePwd(this.obj[param].value);
const client1 = new SecretClient(vault_detail[0], this.credential);
return (await client1.getSecret(vault_detail[1])).value;
} else {
errors.throwErr(errors.ERR_AZURE_VAULT_AUTH_FAILED);
}
} else if (this.obj[param].type == "ocivault") {
const secrets = require('oci-secrets');
const secretClientOci = new secrets.SecretsClient({
authenticationDetailsProvider: this.credential
});
const getSecretBundleRequest = {
secretId: this.obj[param].value
};
const getSecretBundleResponse = await secretClientOci.getSecretBundle(getSecretBundleRequest);
const base64content = getSecretBundleResponse.secretBundle.secretBundleContent.content;
// decode base64 content
const returnVal = Buffer.from(base64content, "base64").toString("utf-8");
return returnVal;
} else {
return this.obj[param];
}
}
}
module.exports = OCIProvider;