oracledb
Version:
A Node.js module for Oracle Database access from JavaScript and TypeScript
211 lines (191 loc) • 8.86 kB
JavaScript
// Copyright (c) 2025, 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.
//
//-----------------------------------------------------------------------------
;
const oracledb = require('oracledb');
const identitydataplane = require("oci-identitydataplane");
const common = require("oci-common");
const { generateKeyPair } = require('crypto');
const fs = require('fs');
async function getToken(params) {
switch (params.authType.toLowerCase()) {
case 'configfilebasedauthentication':
return await configFileBasedAuthentication(params);
case 'simpleauthentication':
return await simpleAuthentication(params);
case 'instanceprincipal':
return await instancePrincipalAuthentication(params);
default:
throwErr(`Invalid authentication type ${params.authType} in extensionOci plugins.`);
}
}
//---------------------------------------------------------------------------
// throwErr()
//---------------------------------------------------------------------------
function throwErr(message) {
throw new Error(message);
}
//---------------------------------------------------------------------------
// Requests an access token from the dataplane service
// Generating security token
//---------------------------------------------------------------------------
async function generateAccessToken(provider, scope) {
// Scope uses the * character to identify all databases in the cloud
// tenancy of the authenticated user. urn:oracle:db::id::*, default
// A scope that authorizes access to all databases within a compartment has
// the form: urn:oracle:db::id::<compartment-ocid>
// String scope = "urn:oracle:db::id::ocid1.compartment.oc1..xxxxxxxx"
// A scope that authorizes access to a single database within a compartment
// has the form: urn:oracle:db::id::<compartment-ocid>::<database-ocid>
// String scope = "urn:oracle:db::id::ocid1.compartment.oc1..xxxxxx::ocid1.autonomousdatabase.oc1.phx.xxxxxx"
const client = new identitydataplane.DataplaneClient({
authenticationDetailsProvider: provider
});
const keyPair = await _getKeyPair();
const generateScopedAccessTokenRequest = {
generateScopedAccessTokenDetails: {
scope: scope ?? "urn:oracle:db::id::*",
publicKey: keyPair.publicKey
}
};
const generateScopedAccessTokenResponse =
await client.generateScopedAccessToken(generateScopedAccessTokenRequest);
return {
token: generateScopedAccessTokenResponse.securityToken.token,
privateKey: keyPair.privateKey
};
}
//---------------------------------------------------------------------------
// Generates a public-private key pair for proof of possession when token
// requested by this provider is presented for validation.
//---------------------------------------------------------------------------
async function _getKeyPair() {
return await new Promise((resolve, reject) => {
generateKeyPair('rsa', {
modulusLength: 4096,
publicKeyEncoding: {
type: 'spki',
format: 'pem'
},
privateKeyEncoding: {
type: 'pkcs8',
format: 'pem',
}
}, (err, publicKey, privateKey) => {
if (err) return reject(err);
resolve({publicKey, privateKey});
});
});
}
//---------------------------------------------------------------------------
// User defined function for reading token and private key values
// generated by the OCI SDK.
// Returns the OCI SDK's token request details object for the given
// config parameters, where a scope parameter specifies the
// scope of requested access. The request will specify a public key
// as being paired with a private key that the presenter of the token must
// prove to be in possession of.
//
// The path and profile of the config file may be configured by optional
// parameters in object accessTokenConfig. If values not provided in
// accessTokenConfig then this method will read the DEFAULT
// profile from $HOME/.oci/config.
// accessTokenConfig.configFileLocation, Not null.
// accessTokenConfig.profile, Not null.
//
// Return API Key-Based Authentication.
//---------------------------------------------------------------------------
async function configFileBasedAuthentication(accessTokenConfig) {
const provider =
new common.ConfigFileAuthenticationDetailsProvider(accessTokenConfig.configFileLocation, accessTokenConfig.profile);
return await generateAccessToken(provider, accessTokenConfig.scope);
}
//---------------------------------------------------------------------------
// simpleAuthentication()
// Returns authentication details for the provided credentials.
// tenancy: OCID of the tenancy
// user: OCID of the user
// fingerprint: Fingerprint of the public key
// privateKey: Private key
// passPhrase: Passphrase that is used to encrypt the private key.
// null if not used.
// Return API Key-Based Authentication.
//---------------------------------------------------------------------------
async function simpleAuthentication(accessTokenConfig) {
const tenancy = accessTokenConfig.tenancy ??
throwErr("Token based authentication config parameter tenancy is missing for simpleauthentication in extensionOci plugins.");
const user = accessTokenConfig.user ??
throwErr("Token based authentication config parameter user is missing for simpleauthentication in extensionOci plugins.");
const fingerprint = accessTokenConfig.fingerprint ??
throwErr("Token based authentication config parameter fingerprint is missing for simpleauthentication in extensionOci plugins.");
const passphrase = accessTokenConfig.passphrase |= null; // optional
const privateKeyLocation = accessTokenConfig.privateKeyLocation ??
throwErr("Token based authentication config parameter privateKeyLocation is missing for simpleauthentication in extensionOci plugins.");
const privateKey = fs.readFileSync(privateKeyLocation, 'utf-8'); // ~/.oci/oci_api_key.pem
const regionId = accessTokenConfig.regionId ?? // ex : us-ashburn-1
throwErr("Token based authentication config parameter regionId is missing for simpleauthentication in extensionOci plugins.");
let region;
const regionsList = common.Region.values();
regionsList.forEach(regions => {
if (regions.regionId === regionId) {
region = regions;
return;
}
});
const provider = new common.SimpleAuthenticationDetailsProvider(
tenancy,
user,
fingerprint,
privateKey,
passphrase,
region
);
return await generateAccessToken(provider, accessTokenConfig.scope);
}
//---------------------------------------------------------------------------
// instancePrincipalAuthentication()
//
// Authentication in a Compute Instance, without credentials, as an instance
// principal. This authentication method should only work on compute
// instances where internal network endpoints are reachable.
// Return Instance Principal Authentication.
//---------------------------------------------------------------------------
async function instancePrincipalAuthentication(accessTokenConfig) {
const provider = await new common.InstancePrincipalsAuthenticationDetailsProviderBuilder().build();
return await generateAccessToken(provider, accessTokenConfig.scope);
}
//---------------------------------------------------------------------------
// hookFn()
// hookFn will get registerd to driver while loading plugins.
//---------------------------------------------------------------------------
function hookFn(options) {
if (options.tokenAuthConfigOci) {
// eslint-disable-next-line no-unused-vars
options.accessToken = async function callbackFn(refresh, config) {
return await getToken(config);
};
options.accessTokenConfig = options.tokenAuthConfigOci;
}
}
oracledb.registerProcessConfigurationHook(hookFn);