Skip to content

Commit 373b0f1

Browse files
committed
Moved config providers to the plugins extension. Permanent Fix for Issues #1688 and #1691
1 parent 4744c69 commit 373b0f1

File tree

18 files changed

+241
-104
lines changed

18 files changed

+241
-104
lines changed

doc/src/release_notes.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,11 @@ Common Changes
6666
Thin Mode Changes
6767
+++++++++++++++++
6868

69+
#) Moved centralized configuration provider support to be part of the
70+
plugins to prevent package build errors with external bundlers like
71+
webpack and esbuild. See:
72+
`Issue #1791 <https://github.com/oracle/node-oracledb/issues/1791>`__.
73+
6974
#) Fixed bug which forced node-oracledb to resolve the database host name
7075
even if a proxy was specified in the connect string. Now the proxy
7176
specified in the connect string will resolve the database host name.

examples/azureConfigProvider.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
Error.stackTraceLimit = 50;
3838

3939
const oracledb = require('oracledb');
40-
40+
require('oracledb/plugins/configProviders/azure');
4141
// This example runs in both node-oracledb Thin and Thick modes.
4242
//
4343
// Optionally run in node-oracledb Thick mode

examples/ociConfigProvider.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
Error.stackTraceLimit = 50;
3838

3939
const oracledb = require('oracledb');
40+
require('oracledb/plugins/configProviders/ociobject');
4041

4142
// This example runs in both node-oracledb Thin and Thick modes.
4243
//

examples/ociConfigProviderPool.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
Error.stackTraceLimit = 50;
3838

3939
const oracledb = require('oracledb');
40-
40+
require('oracledb/plugins/configProviders/ociobject');
4141
// This example runs in both node-oracledb Thin and Thick modes.
4242
//
4343
// Optionally run in node-oracledb Thick mode

lib/configProviders/file.js

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,14 @@
2424
//
2525
//-----------------------------------------------------------------------------
2626
const fs = require('fs').promises;
27-
const { base } = require("./base.js");
28-
class localJson extends base {
27+
const util = require('node:util');
28+
const oracledb = require('oracledb');
29+
class locaJson {
2930

3031
constructor(provider_arg, urlExtendedPart) {
31-
super(urlExtendedPart);
32-
this._addParam("filePath", provider_arg);
32+
const params = new URLSearchParams(urlExtendedPart);
33+
this.paramMap = new URLSearchParams([...params].map(([key, value]) => [key.toLowerCase(), value])); //parse the extended part and store parameters in Map
34+
this.paramMap.set('filepath', provider_arg);
3335
}
3436
//---------------------------------------------------------------------------
3537
// init()
@@ -53,4 +55,19 @@ class localJson extends base {
5355
return this.obj;
5456
}
5557
}
56-
module.exports = localJson;
58+
//---------------------------------------------------------------------------
59+
// hookFn()
60+
// hookFn will get registered to the driver while loading the plugins
61+
//---------------------------------------------------------------------------
62+
async function hookFn(args) {
63+
const configProvider = new locaJson(args.provider_arg, args.urlExtendedPart);
64+
try {
65+
configProvider.init();
66+
} catch (err) {
67+
const errmsg = util.format('Centralized Config Provider failed to load required libraries. Please install the required libraries.\n %s', err.message);
68+
throw new Error(errmsg);
69+
}
70+
return [await configProvider.returnConfig(), configProvider.credential];
71+
}
72+
oracledb.registerConfigProviderHooks('file', hookFn);
73+

lib/errors.js

Lines changed: 4 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -198,13 +198,8 @@ const ERR_INVALID_SERVICE_NAME = 518;
198198
const ERR_INVALID_SID = 519;
199199
const ERR_TNS_NAMES_FILE_MISSING = 520;
200200
const ERR_CONNECTION_EOF = 521;
201-
const ERR_AZURE_CONFIG_PROVIDER_AUTH_FAILED = 522;
202201
const ERR_CONFIG_PROVIDER_FAILED_TO_RETRIEVE_CONFIG = 523;
203-
const ERR_CONFIG_PROVIDER_NOT_SUPPORTED = 524;
204-
const ERR_CONFIG_PROVIDER_LOAD_FAILED = 525;
205-
const ERR_OCIOBJECT_CONFIG_PROVIDER_AUTH_FAILED = 526;
206-
const ERR_AZURE_VAULT_AUTH_FAILED = 527;
207-
const ERR_AZURE_SERVICE_PRINCIPAL_AUTH_FAILED = 528;
202+
const ERR_REGISTER_HOOKFN_CONFIGPROVIDER = 524;
208203
const ERR_WALLET_TYPE_NOT_SUPPORTED = 529;
209204
const ERR_HOST_NOT_FOUND = 530;
210205
const ERR_ANO_PACKET = 531;
@@ -577,20 +572,10 @@ messages.set(ERR_TNS_NAMES_FILE_MISSING, // NJS-520
577572
'cannot connect to Oracle Database. File tnsnames.ora not found in %s');
578573
messages.set(ERR_CONNECTION_EOF, // NJS-521
579574
'connection to host %s port %d received end-of-file on communication channel. (CONNECTION_ID=%s)');
580-
messages.set(ERR_AZURE_CONFIG_PROVIDER_AUTH_FAILED, // NJS-522
581-
'Azure Authentication Failed: The authentication parameter value %s may be incorrect');
582575
messages.set(ERR_CONFIG_PROVIDER_FAILED_TO_RETRIEVE_CONFIG, // NJS-523
583576
'Failed to retrieve configuration from Centralized Configuration Provider:\n %s');
584-
messages.set(ERR_CONFIG_PROVIDER_NOT_SUPPORTED, // NJS-524
585-
'Configuration Provider not supported: %s');
586-
messages.set(ERR_CONFIG_PROVIDER_LOAD_FAILED, // NJS-525
587-
'Centralized Config Provider failed to load required libraries. Please install the required libraries.\n %s');
588-
messages.set(ERR_OCIOBJECT_CONFIG_PROVIDER_AUTH_FAILED, // NJS-526
589-
'OCI authentication failed: The authentication parameter value %s may be incorrect');
590-
messages.set(ERR_AZURE_VAULT_AUTH_FAILED, // NJS-527
591-
'Azure Vault: Provide correct Azure Vault authentication details');
592-
messages.set(ERR_AZURE_SERVICE_PRINCIPAL_AUTH_FAILED, // NJS-528
593-
'Azure service principal authentication requires either a client certificate path or a client secret string');
577+
messages.set(ERR_REGISTER_HOOKFN_CONFIGPROVIDER, // NJS-524
578+
'%s Configuration provider plugin not registered. Please register the hook function for the same');
594579
messages.set(ERR_WALLET_TYPE_NOT_SUPPORTED, // NJS-529
595580
'Invalid wallet content format. Supported format is PEM');
596581
messages.set(ERR_HOST_NOT_FOUND, // NJS-530
@@ -933,14 +918,9 @@ module.exports = {
933918
ERR_NO_CONFIG_DIR,
934919
ERR_TNS_ENTRY_NOT_FOUND,
935920
ERR_CONNECTION_EOF,
936-
ERR_AZURE_CONFIG_PROVIDER_AUTH_FAILED,
937-
ERR_OCIOBJECT_CONFIG_PROVIDER_AUTH_FAILED,
938-
ERR_AZURE_VAULT_AUTH_FAILED,
939-
ERR_AZURE_SERVICE_PRINCIPAL_AUTH_FAILED,
940921
ERR_CONFIG_PROVIDER_FAILED_TO_RETRIEVE_CONFIG,
941922
ERR_CONFIG_PROVIDER_PARAM_TYPE,
942-
ERR_CONFIG_PROVIDER_NOT_SUPPORTED,
943-
ERR_CONFIG_PROVIDER_LOAD_FAILED,
923+
ERR_REGISTER_HOOKFN_CONFIGPROVIDER,
944924
ERR_DATA_COMPRESSION,
945925
ERR_WALLET_TYPE_NOT_SUPPORTED,
946926
ERR_HOST_NOT_FOUND,

lib/oracledb.js

Lines changed: 51 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ const poolCache = {};
6868
const tempUsedPoolAliases = {};
6969
const defaultPoolAlias = 'default';
7070
const registeredHooks = [];
71+
const registeredConfigProviderHooks = new Map();
7172
let configProviderCache;
7273

7374
// save arguments for call to initOracleClient()
@@ -904,7 +905,7 @@ async function shutdown(a1, a2) {
904905
await conn.close();
905906
}
906907

907-
async function _setConfigParameters(obj, configProviderObject, configProvider) {
908+
async function _setConfigParameters(obj, credential, configProvider) {
908909
const configObject = {};
909910
const pmSection = 'njs';
910911
const params = obj[pmSection];
@@ -918,21 +919,21 @@ async function _setConfigParameters(obj, configProviderObject, configProvider) {
918919

919920
configObject.user = obj.user;
920921
if (obj.password) {
921-
configObject.password = await _retrieveParamValueFromVault(obj['password'], configProviderObject.credential, configProvider);
922+
configObject.password = await _retrieveParamValueFromVault(obj['password'], credential, configProvider);
922923
}
923924
if (obj.wallet_location) {
924925
// retrieve wallet_location
925-
configObject.walletContent = await _retrieveParamValueFromVault(obj['wallet_location'], configProviderObject.credential, configProvider);
926+
configObject.walletContent = await _retrieveParamValueFromVault(obj['wallet_location'], credential, configProvider);
926927
//only Pem file supported
927-
if (!configProviderObject.isPemFile(configObject.walletContent))
928+
if (!nodbUtil.isPemFile(configObject.walletContent))
928929
errors.throwErr(errors.ERR_WALLET_TYPE_NOT_SUPPORTED);
929930
}
930931
return configObject;
931932
}
932933

933934
async function _retrieveParamValueFromVault(paramObj, credential, configProvider) {
934-
const auth = paramObj.authentication;
935935
const paramMap = new Map();
936+
const auth = paramObj.authentication;
936937
if (auth) {
937938
for (const key in auth) {
938939
if (key == 'method')
@@ -941,6 +942,7 @@ async function _retrieveParamValueFromVault(paramObj, credential, configProvider
941942
paramMap.set(key.toLowerCase(), val);
942943
}
943944
}
945+
const args = {};
944946
if (paramObj.type == "base64") {
945947
console.log("WARNING: Base64 Encoding in a JSON Password should only be used in development environments");
946948
return Buffer.from(paramObj.value, "base64").toString("utf-8");
@@ -951,27 +953,31 @@ async function _retrieveParamValueFromVault(paramObj, credential, configProvider
951953
} else
952954
errors.throwErr(errors.ERR_CONFIG_PROVIDER_PARAM_TYPE, 'password type text is only allowed in ocivault and azurevault');
953955
} else if (paramObj.type == "azurevault") {
954-
const azureVaultClass = require('../lib/configProviders/azurevault.js');
955-
const azureVaultObject = new azureVaultClass();
956-
azureVaultObject.init();
957-
paramMap.set("azuresecreturl", paramObj.value);
958-
azureVaultObject.paramMap = paramMap;
956+
paramMap.set('azuresecreturl', paramObj.value);
959957
if (!(configProvider == 'azure' || configProvider == 'azurevault'))
960958
credential = null;
961-
962-
const paramValue = await azureVaultObject.returnConfig(credential);
959+
const hookFn = registeredConfigProviderHooks['azurevault'];
960+
if (hookFn == undefined)
961+
errors.throwErr(errors.ERR_REGISTER_HOOKFN_CONFIGPROVIDER, 'azurevault');
962+
args.credential = credential;
963+
args.paramMap = paramMap;
964+
const vaultReturn = await hookFn(args);
965+
const paramValue = vaultReturn[0];
963966
return paramValue;
964967

965968
} else if (paramObj.type == "ocivault") {
966-
const ociVaultClass = require('../lib/configProviders/ocivault.js');
967-
const ociVaultObject = new ociVaultClass();
968-
ociVaultObject.init();
969-
paramMap.set("ocidvault", paramObj.value);
970-
ociVaultObject.paramMap = paramMap;
969+
970+
paramMap.set('ocidvault', paramObj.value);
971971
if (!(configProvider == 'ociobject' || configProvider == 'ocivault'))
972972
credential = null;
973-
974-
const paramValue = await ociVaultObject.returnConfig(credential);
973+
const hookFn = registeredConfigProviderHooks['ocivault'];
974+
if (hookFn == undefined)
975+
errors.throwErr(errors.ERR_REGISTER_HOOKFN_CONFIGPROVIDER, 'ocivault');
976+
const args = {};
977+
args.credential = credential;
978+
args.paramMap = paramMap;
979+
const vaultReturn = await hookFn(args);
980+
const paramValue = vaultReturn[0];
975981
return paramValue;
976982
} else {
977983
errors.throwErr(errors.ERR_CONFIG_PROVIDER_PARAM_TYPE, 'password/wallet_location');
@@ -1017,28 +1023,26 @@ async function _checkConfigProvider(options) {
10171023
if (match) {
10181024
const provider = match.groups.provider;
10191025
const provider_arg = match.groups.provider_arg;
1020-
let configPckg;
1021-
try {
1022-
configPckg = require('./configProviders/' + provider);
1023-
} catch (err) {
1024-
errors.throwErr(errors.ERR_CONFIG_PROVIDER_NOT_SUPPORTED, provider);
1025-
}
1026-
const configProvider = new configPckg(provider_arg, urlExtendedPart);
1027-
try {
1028-
configProvider.init();
1029-
} catch (err) {
1030-
errors.throwErr(errors.ERR_CONFIG_PROVIDER_LOAD_FAILED, err.message);
1031-
}
1026+
const hookFn = registeredConfigProviderHooks[provider];
1027+
if (hookFn == undefined)
1028+
errors.throwErr(errors.ERR_REGISTER_HOOKFN_CONFIGPROVIDER, provider);
1029+
const args = {};
1030+
args.provider_arg = provider_arg;
1031+
args.urlExtendedPart = urlExtendedPart;
1032+
10321033
try {
1034+
// hookFn returns an array with first element as the config stored in the configProvider
1035+
// second being the credential to the configProvider
1036+
const configProviderReturn = await hookFn(args);
1037+
secondOpts = configProviderReturn[0];
10331038
if (!configProviderCache) {
10341039
configProviderCache = new Map();
10351040
}
1036-
secondOpts = await configProvider.returnConfig();
10371041
if (!secondOpts) {
10381042
errors.throwErr(errors.ERR_CONFIG_PROVIDER_FAILED_TO_RETRIEVE_CONFIG, 'no configuration found in ' + provider);
10391043
}
10401044
if (provider != 'azure')
1041-
secondOpts = await _setConfigParameters(secondOpts, configProvider, provider);
1045+
secondOpts = await _setConfigParameters(secondOpts, configProviderReturn[1], provider);
10421046

10431047
// obfuscate password & walletcontent and store config in the cache
10441048
const cacheOpts = { ...secondOpts};
@@ -1152,6 +1156,19 @@ function registerProcessConfigurationHook(fn) {
11521156
registeredHooks.push(fn);
11531157
}
11541158

1159+
//-----------------------------------------------------------------------------
1160+
// registerConfigProviderHooks()
1161+
//
1162+
// Registers configProvider plugin modules and registered modules will be called and
1163+
// executed during pool and standalone connection creation.
1164+
//-----------------------------------------------------------------------------
1165+
function registerConfigProviderHooks(configProvider, fn) {
1166+
errors.assertArgCount(arguments, 2, 2);
1167+
errors.assertParamValue(typeof fn === 'function', 1);
1168+
errors.assertParamValue(typeof configProvider === 'string', 1);
1169+
registeredConfigProviderHooks[configProvider] = fn;
1170+
}
1171+
11551172
// module exports
11561173
module.exports = {
11571174

@@ -1183,6 +1200,7 @@ module.exports = {
11831200
getPool,
11841201
initOracleClient,
11851202
registerProcessConfigurationHook,
1203+
registerConfigProviderHooks,
11861204
shutdown: nodbUtil.callbackify(nodbUtil.wrapFn(shutdown)),
11871205
startup: nodbUtil.callbackify(nodbUtil.wrapFn(startup)),
11881206

lib/util.js

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,25 @@ const types = require('./types.js');
3434
const constants = require('./constants.js');
3535
const traceHandler = require('./traceHandler.js');
3636

37+
const pemHeaders = [
38+
'-----BEGIN CERTIFICATE-----',
39+
'-----BEGIN PUBLIC KEY-----',
40+
'-----BEGIN PRIVATE KEY-----',
41+
'-----BEGIN RSA PRIVATE KEY-----',
42+
'-----BEGIN DSA PRIVATE KEY-----',
43+
'-----BEGIN EC PRIVATE KEY-----',
44+
'-----BEGIN ENCRYPTED PRIVATE KEY-----'
45+
];
46+
const pemFooters = [
47+
'-----END CERTIFICATE-----',
48+
'-----END PUBLIC KEY-----',
49+
'-----END PRIVATE KEY-----',
50+
'-----END RSA PRIVATE KEY-----',
51+
'-----END DSA PRIVATE KEY-----',
52+
'-----END EC PRIVATE KEY-----',
53+
'-----END ENCRYPTED PRIVATE KEY-----'
54+
];
55+
3756
// set of valid network characters
3857
const validNetworkCharacterSet = new Set(['A', 'B', 'C', 'D', 'E', 'F', 'G',
3958
'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
@@ -80,6 +99,13 @@ function getInstallURL() {
8099
return ('Node-oracledb installation instructions: https://node-oracledb.readthedocs.io/en/latest/user_guide/installation.html');
81100
}
82101

102+
// check if a file has contents of a .pem file
103+
function isPemFile(content) {
104+
//remove any line breaks at the end of the content
105+
content = content.trim();
106+
return pemHeaders.some(header => content.includes(header)) &&
107+
pemFooters.some(footer => content.endsWith(footer));
108+
}
83109

84110
// getInstallHelp returns a string with installation usage tips that may be helpful
85111
function getInstallHelp() {
@@ -599,5 +625,6 @@ module.exports = {
599625
sanitize,
600626
verifySodaDoc,
601627
wrapFn,
602-
wrapFns
628+
wrapFns,
629+
isPemFile
603630
};

0 commit comments

Comments
 (0)