diff --git a/CHANGELOG.md b/CHANGELOG.md index df490ea6..6601ea2d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,21 @@ **This release is under development and information may be incomplete** +- Token-based Authentication changes: + + - Added support for [token-based + authentication](https://oracle.github.io/node-oracledb/doc/api.html#oauthtokenbasedauthentication) + using Microsoft Azure Active Directory OAuth 2.0 tokens. + + - The `accessToken` attribute for connection and connection pool creation + can now be a string, a callback function, or an object. + + - Fixed a bug that prevented async functions from being used in token callbacks. + + - Deprecated the connection pool creation attribute `accessTokenCallback`. + + - Deprecated `pool.setAccessToken()`. + - ResultSets now implement the `asyncIterator()` symbol to support asynchonous iteration. @@ -18,7 +33,7 @@ - Stated compatibility is now for Node.js 14, 16 and 18. Older releases back to Node.js 10.16 should still work. -- Added support for [token based +- Added support for [token-based authentication](https://oracle.github.io/node-oracledb/doc/api.html#tokenbasedauth) when establishing pool based connections and standalone connections. diff --git a/doc/api.md b/doc/api.md index 09bd23eb..19cfe980 100644 --- a/doc/api.md +++ b/doc/api.md @@ -112,8 +112,8 @@ For installation information, see the [Node-oracledb Installation Instructions][ - 3.3.1.1 [`createPool()`: Parameters and Attributes](#createpoolpoolattrs) - 3.3.1.1.1 [`accessToken`](#createpoolpoolattrsaccesstoken) - [`token`](#createpoolpoolattrsaccesstoken), [`privateKey`](#createpoolpoolattrsaccesstoken) - - 3.3.1.1.2 [`connectString`](#createpoolpoolattrsconnectstring), [`connectionString`](#createpoolpoolattrsconnectstring) - - 3.3.1.1.3 [`accessTokenCallback`](#createpoolpoolattrsaccesstokencallback) + - 3.3.1.1.2 [`accessTokenCallback`](#createpoolpoolattrsaccesstokencallback) + - 3.3.1.1.3 [`connectString`](#createpoolpoolattrsconnectstring), [`connectionString`](#createpoolpoolattrsconnectstring) - 3.3.1.1.4 [`edition`](#createpoolpoolattrsedition) - 3.3.1.1.5 [`enableStatistics`](#createpoolpoolattrsstats) - 3.3.1.1.6 [`events`](#createpoolpoolattrsevents) @@ -349,12 +349,12 @@ For installation information, see the [Node-oracledb Installation Instructions][ - 8.1.21 [`stmtCacheSize`](#proppoolstmtcachesize) - 8.1.22 [`user`](#proppooluser) - 8.2 [Pool Methods](#poolmethods) - - 8.2.1 [`setAccessToken()`](#poolsetaccesstoken) - - 8.2.2 [`close()`](#poolclose), [`terminate()`](#poolclose) - - 8.2.3 [`getConnection()`](#getconnectionpool) - - 8.2.4 [`getStatistics()`](#poolgetstatistics) - - 8.2.5 [`logStatistics()`](#poollogstatistics) - - 8.2.6 [`reconfigure()`](#poolreconfigure) + - 8.2.1 [`close()`](#poolclose), [`terminate()`](#poolclose) + - 8.2.2 [`getConnection()`](#getconnectionpool) + - 8.2.3 [`getStatistics()`](#poolgetstatistics) + - 8.2.4 [`logStatistics()`](#poollogstatistics) + - 8.2.5 [`reconfigure()`](#poolreconfigure) + - 8.2.6 [`setAccessToken()`](#poolsetaccesstoken) 9. [PoolStatistics Class](#poolstatisticsclass) - 9.1 [PoolStatistics Methods](#poolstatisticsmethods) - 9.1.1 [`logStatistics()`](#poolstatisticslogstatistics) @@ -463,7 +463,6 @@ For installation information, see the [Node-oracledb Installation Instructions][ - 16.1.2 [Embedded Connect Descriptor Strings](#embedtns) - 16.1.3 [Net Service Names for Connection Strings](#tnsnames) - 16.1.4 [JDBC and Oracle SQL Developer Connection Strings](#notjdbc) - - 16.1.5 [Token Based Authentication Connection Strings](#tokenbasedconnstrings) - 16.2 [Connections, Threads, and Parallelism](#numberofthreads) - 16.2.1 [Parallelism on a Connection](#parallelism) - 16.3 [Connection Pooling](#connpooling) @@ -479,12 +478,18 @@ For installation information, see the [Node-oracledb Installation Instructions][ - 16.3.7.3 [PL/SQL Session Tagging Callback](#sessiontaggingplsql) - 16.3.8 [Heterogeneous Connection Pools and Pool Proxy Authentication](#connpoolproxy) - 16.4 [External Authentication](#extauth) - - 16.5 [Token Based Authentication](#tokenbasedauth) - - 16.5.1 [Token Generation using the OCI CLI](#tokengen) - - 16.5.2 [Token and Private Key Extraction from a File](#tokenread) - - 16.5.3 [Standalone Connection Creation with Access Tokens](#tokenbasedstandaloneconn) - - 16.5.4 [Connection Pool Creation with Access Tokens](#tokenbasedpool) - - 16.5.5 [Explicitly Refreshing Pool Access Tokens](#settingpooltokens) + - 16.5 [Token-Based Authentication](#tokenbasedauthentication) + - 16.5.1 [OAuth 2.0 Token-Based Authentication](#oauthtokenbasedauthentication) + - 16.5.1.1 [OAuth 2.0 Token generation](#oauthtokengeneration) + - 16.5.1.2 [OAuth 2.0 Standalone Connections](#oauthstandalone) + - 16.5.1.3 [OAuth 2.0 Connection Pooling](#oauthpool) + - 16.5.1.4 [OAuth 2.0 Connection Strings](#oauthconnectstring) + - 16.5.2 [IAM Token-Based Authentication](#iamtokenbasedauthentication) + - 16.5.2.1 [IAM Token Generation](#iamtokengeneration) + - 16.5.2.2 [IAM Token and Private Key Extraction](#iamtokenextraction) + - 16.5.2.3 [IAM Standalone Connections](#iamstandalone) + - 16.5.2.4 [IAM Connection Pooling](#iampool) + - 16.5.2.5 [IAM Connection Strings](#iamconnectstring) - 16.6 [Database Resident Connection Pooling (DRCP)](#drcp) - 16.7 [Privileged Connections](#privconn) - 16.8 [Securely Encrypting Network Traffic to Oracle Database](#securenetwork) @@ -2237,31 +2242,92 @@ The properties of `poolAttrs` are described below. ###### 3.3.1.1.1 `accessToken`: Attributes ``` -Object accessToken +function accessToken(boolean refresh) | String accessToken | Object accessToken ``` -The `accessToken` parameter object provides token based authentication -configuration properties. +- For Microsoft Azure Active Directory OAuth 2.0 token-based authentication + `accessToken` can be: + - a callback function returning the token as a string + - an object with a `token` attribute containing the token as a string + - or the token as a string -The properties of the `accessToken` object are described below. Both properties -must be set. The values can be obtained using the Oracle Cloud Infrastructure -(OCI) Command Line Interface (CLI). + Tokens can be obtained using various approaches. For example, using the + Azure Active Directory API. -Attribute | Description -------------------- |------------------------------------------------ -`token` | The database authentication token string. -`privateKey` | The database authentication private key string. +- For Oracle Cloud Infrastructure Identity and Access Management (IAM) + token-based authentication `accessToken` can be: + - a callback function returning an object containing `token` and `privateKey` attributes + - or an object containing `token` and `privateKey` attributes -The `externalAuth` and `homogeneous` connection or pool attributes must be set -to *true* while using token based authentication. The `user` (or `username`) -and `password` properties should not be set. + The properties of the `accessToken` object are described below. -See [Token Based Authentication](#tokenbasedauth) for more information. + Attribute | Description + ------------------- |------------------------------------------------ + `token` | The database authentication token. + `privateKey` | The database authentication private key. -The `accessToken` parameter was added in node-oracledb 5.4. It should be used -with Oracle Client libraries 19.14 (or later), or 21.5 (or later). + The `token` and `privateKey` values can be obtained using various approaches. + For example the Oracle Cloud Infrastructure Command Line Interface can be + used. -###### 3.3.1.1.2 `connectString`, `connectionString` +When `accessToken` is a callback function, it will be invoked at the time the +pool is created (even if `poolMin` is 0). It is also called when the pool +needs to expand (causing new connections to be created) and the current token +has expired. The returned token is used by node-oracledb for authentication. +The `refresh` parameter is described below. + +`refresh` value | Description +-------------------|------------------------------------------------ +*false* | The application can return a token from an application-specific cache. If there is no cached token, the application must externally acquire one. +*true* | The token previously passed to driver is known to be expired, the application should externally acquire a new token. + +When the callback is first invoked, the `refresh` parameter will be set to +*false*. This indicates that the application can provide a token from its own +application managed cache, or it can generate a new token if there is no cached +value. Node-oracledb checks whether the returned token has expired. If it has +expired, then the callback function will be invoked a second time with +`refresh` set to *true*. In this case the function must externally acquire a +token, optionally add it to the application's cache, and return the token. + +For token-based authentication, the `externalAuth` and `homogeneous` pool +attributes must be set to *true* . The `user` (or `username`) and `password` +attributes should not be set. + +See [Token-Based Authentication](#tokenbasedauthentication) for more information. + +The `accessToken` attribute was added in node-oracledb 5.4 to support IAM +token-based authentication. In this release the attribute must be an Object. +Oracle Client libraries 19.14 (or later), or 21.5 (or later) must be used for +IAM token-based authentication. + +The `accessToken` attribute was extended to allow OAuth 2.0 token-based +authentication in node-oracledb 5.5. For OAuth 2.0, the attribute should be a +string, or a callback. Also Oracle Client libraries 19.15 (or later), or 21.7 +(or later) must be used. The callback usage supports both OAuth 2.0 and IAM +token-based authentication. + +###### 3.3.1.1.2 `accessTokenCallback` + +``` +Object accessTokenCallback +``` + +The optional `accessTokenCallback` attribute is a Node.js callback function. It +gets called by the connection pool if the pool needs to grow and create new +connections but the current token has expired. + +The callback function must return a JavaScript object with attributes `token` +and `privateKey` for IAM. See [Connection Pool Creation with Access Tokens for +IAM](#tokenbasedpool). + +The `accessTokenCallback` attribute was added in node-oracledb 5.4. It should be +used with Oracle Client libraries 19.14 (or later), or 21.5 (or later). + +The `accessTokenCallback` attribute is deprecated from node-oracledb 5.5. Use +[`accessToken`](#createpoolpoolattrsaccesstoken) instead, which was enhanced to +support a callback. + +###### 3.3.1.1.3 `connectString`, `connectionString` ``` String connectString @@ -2277,24 +2343,6 @@ See [Connection Strings](#connectionstrings) for examples. The alias `connectionString` was added in node-oracledb 2.1. -###### 3.3.1.1.3 `accessTokenCallback` - -``` -Object accessTokenCallback -``` - -The optional `accessTokenCallback` attribute is a Node.js callback function. It -gets called by the connection pool if the pool needs to grow and create new -connections but the current token has expired. - -The callback function must return a JavaScript object with attributes `token` -and `privateKey`, equivalent to the -[`accessToken`](#createpoolpoolattrsaccesstoken) object. See [Connection Pool -Creation with Access Tokens](#tokenbasedpool). - -The `accessTokenCallback` attribute was added in node-oracledb 5.4. It should be -used with Oracle Client libraries 19.14 (or later), or 21.5 (or later). - ###### 3.3.1.1.4 `edition` ``` @@ -2762,29 +2810,65 @@ The properties of the `connAttrs` object are described below. ###### 3.3.2.1.2.1 `accessToken`: Attributes ``` -Object accessToken +function accessToken(boolean refresh) | String accessToken | Object accessToken ``` -The `accessToken` parameter object provides token based authentication -configuration properties. +- For Microsoft Azure Active Directory OAuth 2.0 token-based authentication + `accessToken` can be: + - a callback function returning the token as a string + - or the token as a string -The properties of the `accessToken` object are described below. Both properties -must be set. The values can be obtained using the Oracle Cloud Infrastructure -(OCI) Command Line Interface (CLI). + Tokens can be obtained using various approaches. For example, using the + Azure Active Directory API. -Attribute | Description -------------------- |------------------------------------------------ -`token` | The database authentication token string. -`privateKey` | The database authentication private key string. +- For Oracle Cloud Infrastructure Identity and Access Management (IAM) + token-based authentication `accessToken` can be: + - an object containing `token` and `privateKey` attributes + - or a callback function returning an object containing `token` and `privateKey` attributes -The `externalAuth` connection attribute must be set to *true* while using token -based authentication. The `user` (or `username`) and `password` properties -should not be set. + The `token` and `privateKey` values can be obtained using various approaches. + For example the Oracle Cloud Infrastructure Command Line Interface can be + used. -See [Token Based Authentication](#tokenbasedauth) for more information. + The properties of the `accessToken` object are described below. -The `accessToken` parameter was added in node-oracledb 5.4. It should be used -with Oracle Client libraries 19.14 (or later), or 21.5 (or later). + Attribute | Description + ------------------- |------------------------------------------------ + `token` | The database authentication token. + `privateKey` | The database authentication private key. + +When `accessToken` is a callback function, the returned token is used by +node-oracledb for authentication. The `refresh` parameter is described below. + +`refresh` value | Description +-------------------|------------------------------------------------ +*false* | The application can return a token from an application-specific cache. If there is no cached token, the application must externally acquire one. +*true* | The token previously passed to driver is known to be expired, the application should externally acquire a new token. + +For each connection, the callback is invoked with the `refresh` parameter set +to *false*. This indicates that the application can provide a token from its +own application managed cache, or it can generate a new token if there is no +cached value. Node-oracledb checks whether the returned token has expired. If +it has expired, then the callback function will be invoked a second time with +`refresh` set to *true*. In this case the function must externally acquire a +token, optionally add it to the application's cache, and return the token. + +For token-based authentication, the `externalAuth` connection attribute must be +set to *true* . The `user` (or `username`) and `password` attributes should +not be set. + +See [Token-Based Authentication](#tokenbasedauthentication) for more information. + +The `accessToken` attribute was added in node-oracledb 5.4 to support IAM +token-based authentication. In this release the attribute must be an Object. +Oracle Client libraries 19.14 (or later), or 21.5 (or later) must be used for +IAM token-based authentication. + +The `accessToken` attribute was extended to allow OAuth 2.0 token-based +authentication in node-oracledb 5.5. For OAuth 2.0, the attribute should be a +string, or a callback. Also Oracle Client libraries 19.15 (or later), or 21.7 +(or later) must be used. The callback usage supports both OAuth 2.0 and IAM +token-based authentication. ###### 3.3.2.1.2.2 `connectString`, `connectionString` @@ -6553,40 +6637,7 @@ The database username for connections in the pool. ### 8.2 Pool Methods -#### 8.2.1 `pool.setAccessToken()` - -##### Description - -This method can be used to set an access token and private key after pool -creation. It is useful if the token is known to have expired, and you are not -using [`accessTokenCallback`](#createpoolpoolattrsaccesstokencallback). - -It can also be useful in tests to set an expired token so that token expiry -code paths can be tested. - -See [Explicitly Refreshing Pool Access Tokens](#settingpooltokens) for an example. - -##### Parameters - -``` -Object tokenAttrs -``` - -The `tokenAttrs` parameter object provides token based authentication -configuration properties. - -The properties of the `tokenAttrs` object are described below. Both properties -must be set. The values can be obtained using the Oracle Cloud Infrastructure -(OCI) Command Line Interface (CLI). - -Attribute | Description -------------------- |------------------------------------------------ -`token` | The database authentication token string. -`privateKey` | The database authentication private key string. - -This function was added in node-oracledb 5.4. - -#### 8.2.2 `pool.close()` +#### 8.2.1 `pool.close()` ##### Prototype @@ -6662,7 +6713,7 @@ The `drainTime` parameter was added in node-oracledb 3.0. *Error error* | If `close()` succeeds, `error` is NULL. If an error occurs, then `error` contains the [error message](#errorobj). -#### 8.2.3 `pool.getConnection()` +#### 8.2.2 `pool.getConnection()` ##### Prototype @@ -6750,7 +6801,7 @@ pools. *Error error* | If `getConnection()` succeeds, `error` is NULL. If an error occurs, then `error` contains the [error message](#errorobj). *Connection connection* | The newly created connection. If `getConnection()` fails, `connection` will be NULL. See [Connection class](#connectionclass) for more details. -#### 8.2.4 `pool.getStatistics()` +#### 8.2.3 `pool.getStatistics()` ##### Prototype @@ -6775,7 +6826,7 @@ If `getStatistics()` is called while the pool is closed, draining, or This function was added in node-oracledb 5.2. -#### 8.2.5 `pool.logStatistics()` +#### 8.2.4 `pool.logStatistics()` ##### Prototype @@ -6801,7 +6852,7 @@ This function was added in node-oracledb 5.2. The obsolete function `_logStats()` can still be used, but it will be removed in a future version of node-oracledb. -#### 8.2.6 `pool.reconfigure()` +#### 8.2.5 `pool.reconfigure()` ##### Prototype @@ -6898,6 +6949,37 @@ await pool.reconfigure({poolMin: 5, poolMax: 10, increment: 5}); ----------------------------|------------- *Error error* | If `reconfigure()` succeeds, `error` is null. If an error occurs, then `error` contains the [error message](#errorobj). +#### 8.2.6 `pool.setAccessToken()` + +##### Description + +This method can be used to set an IAM access token and private key after pool +creation. It is useful if the IAM token is known to have expired, and you are +not using [`accessTokenCallback`](#createpoolpoolattrsaccesstokencallback). + +It can also be useful in tests to set an expired token so that token expiry +code paths can be tested. + +##### Parameters + +``` +Object tokenAttrs +``` + +The `tokenAttrs` parameter object provides IAM token-based authentication +properties. + +The properties of the `tokenAttrs` object are described below. Both properties +must be set. The values can be obtained, for example, using the Oracle Cloud +Infrastructure Command Line Interface (OCI CLI). + +Attribute | Description +------------------- |------------------------------------------------ +`token` | The database authentication token string. +`privateKey` | The database authentication private key string. + +This function was added in node-oracledb 5.4 and deprecated in node-oracledb 5.5. + ## 9. PoolStatistics Class @@ -9532,64 +9614,6 @@ const connection = await oracledb.getConnection( ); ``` -#### 16.1.5 Token Based Authentication Connection Strings - -Token based authentication allows Oracle Cloud Infrastruction users to -authenticate to Oracle Database with Oracle Identity Access Manager (IAM) -tokens. Token authentication can be performed when node-oracledb uses Oracle -Client libraries 19.14 (or later), or 21.5 (or later). - -The Oracle Cloud Infrastructure command line interface (OCI-CLI) can be used -externally to get tokens and private keys from IAM, for example with `oci iam -db-token get`. - -The connection string used by node-oracledb can specify the directory where the -token and private key files are located. This syntax is usable with older -versions of node-oracledb however it is recommended to use connection and pool -creation parameters introduced in node-oracledb 5.4 instead, see [Token Based -Authentication](#tokenbasedauth). - -If you need to use the connection string syntax then the Oracle Net parameter -`TOKEN_AUTH` must be set. Also the `PROTOCOL` parameter must be `tcps` and -`SSL_SERVER_CERT_DN` should be `ON`. - -You can set `TOKEN_AUTH=OCI_TOKEN` in a [sqlnet.ora](#tnsadmin) file. -Alternatively you can specify it in a connect descriptor, for example: - -``` -db_alias = - (DESCRIPTION = - (ADDRESS = (PROTOCOL = TCPS)(PORT = 1522)(HOST = adb.us-ashburn-1.oraclecloud.com)) - (CONNECT_DATA = (SERVICE_NAME = adb.oraclecloud.com)) - (SECURITY = - (SSL_SERVER_CERT_DN = "CN=adwc.uscom-east-1.oraclecloud.com, OU=Oracle BMCS US, - O=Oracle Corporation, L=Redwood City, ST=California, C=US") - (TOKEN_AUTH = OCI_TOKEN))) -``` - -The default location for the token and private key is the same default location -that the OCI-CLI tool writes to. For example `~/.oci/db-token/` on Linux. - -If the token and private key files are not in the default location then their -directory must be specified with the `TOKEN_LOCATION` parameter in a -`sqlnet.ora` file or in a connection string. For example in a `tnsnames.ora` -file: - -``` -db_alias = - (DESCRIPTION = - (ADDRESS = (PROTOCOL = TCPS)(PORT = 1522)(HOST = adb.us-ashburn-1.oraclecloud.com)) - (CONNECT_DATA = (SERVICE_NAME = adb.oraclecloud.com)) - (SECURITY = - (SSL_SERVER_CERT_DN = "CN=adwc.uscom-east-1.oraclecloud.com, OU=Oracle BMCS US, - O=Oracle Corporation, L=Redwood City, ST=California, C=US") - (TOKEN_AUTH = OCI_TOKEN) - (TOKEN_LOCATION = '~/.oci/db-token'))) -``` - -The `TOKEN_AUTH` and `TOKEN_LOCATION` values in a connection string take -precedence over the `sqlnet.ora` settings. - ### 16.2 Connections, Threads, and Parallelism If you open more than four connections, such as via increasing @@ -10982,24 +11006,233 @@ of open connections exceeds `poolMin` and connections are idle for more than the [`poolTimeout`](#propdbpooltimeout) seconds, then the number of open connections does not fall below `poolMin`. -### 16.5 Token Based Authentication +### 16.5 Token-Based Authentication -Token based authentication allows Oracle Cloud Infrastruction users to -authenticate to Oracle Database with Oracle Identity Access Manager (IAM) -tokens. Token authentication can be performed when node-oracledb uses Oracle -Client libraries 19.14 (or later), or 21.5 (or later). +Token-Based Authentication allows users to connect to a database by using an +encrypted authentication token without having to enter a database username and +password. The authentication token must be valid and not expired for the +connection to be successful. Users already connected will be able to continue +work after their token has expired but they will not be able to reconnect +without getting a new token. -Token based authentication can be used for both standalone connections and -connection pools. Tokens can be specified using connection attributes -introduced in node-oracledb 5.4. Users of earlier node-oracledb versions can -alternatively use [Token Based Authentication Connection -Strings](#tokenbasedconnstrings). +The two authentication methods supported by node-oracledb are Open +Authorization [OAuth 2.0](#oauthtokenbasedauthentication) and Oracle Cloud +Infrastructure (OCI) Identity and Access Management +[IAM](#iamtokenbasedauthentication). -#### 16.5.1 Token Generation using the OCI CLI +Token-based authentication can be used for both standalone connections and +connection pools. -Authentication tokens are generated through execution of an Oracle Cloud -Infrastructure command line interface (OCI-CLI) command run externally to -Node.js: +#### 16.5.1 OAuth 2.0 Token-Based Authentication + +Oracle Cloud Infrastructure (OCI) users can be centrally managed in a Microsoft +Azure Active Directory (Azure AD) service. Open Authorization (OAuth 2.0) +token-based authentication allows users to authenticate to Oracle Database +using Azure AD OAuth 2.0 tokens. Your Oracle Database must be registered with +Azure AD. + +See [Authenticating and Authorizing Microsoft Azure Active Directory Users for +Oracle Autonomous Databases][206] for more information. + +OAuth 2.0 token authentication can be performed when node-oracledb uses Oracle +Client libraries 19.15 (or later), or 21.7 (or later). + +##### 16.5.1.1 OAuth 2.0 Token Generation + +Authentication tokens can be obtained in several ways. For example you can use +a curl command against the Azure Active Directory API such as: + +``` +curl -X POST -H 'Content-Type: application/x-www-form-urlencoded' +https://login.microsoftonline.com/[]/oauth2/v2.0/token +-d 'client_id = ' +-d 'scope = ' +-d 'username = ' +-d 'password = ' +-d 'grant_type = password' +-d 'client_secret = ' +``` + +Substitute your own values as appropriate for each argument. + +This returns a JSON response containing an `access_token` attribute. See +[Microsoft identity platform and OAuth 2.0 authorization code flow][208] for +more details. This attribute can be passed as the `oracledb.getConnection()` +attribute [`accessToken`](#getconnectiondbattrsaccesstoken) or as the +`oracledb.createPool()` attribute +[`accessToken`](#createpoolpoolattrsaccesstoken). + +Alternatively authentication tokens can be generated by calling the Azure +Active Directory REST API, for example: + +```javascript +function getOauthToken() { + const requestParams = { + client_id : , + client_secret : , + grant_type : 'client_credentials', + scope : , + }; + const tenantId = ; + const url = `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/token`; + return new Promise(function(resolve, reject) { + request.post({ + url : url, + body : queryString.stringify(requestParams), + headers : { 'Content-Type': 'application/x-www-form-urlencoded' } + }, function(err, response, body) { + if (err) { + reject(err); + } else { + resolve(JSON.parse(body).access_token); + } + }); + }); +} +``` + +Substitute your own values as appropriate for each argument. + +Use of `getOauthToken()` is shown in subsequent examples. + +##### 16.5.1.2 OAuth 2.0 Standalone Connections + +Standalone connections can be created using OAuth2 token-based authentication, +for example: + +```javascript +let accessTokenStr; // the token string. In this app it is also the token "cache" + +async function tokenCallback(refresh) { + if (refresh || !acccessTokenStr) { + accessTokenStr = await getOauthToken(); // getOauthToken() was shown earlier + } + return acccessTokenStr; +} + +async function init() { + try { + await oracledb.getConnection({ + accessToken : tokenCallback, // the callback returning the token + externalAuth : true, // must specify external authentication + connectString : connect_string // Oracle Autonomous Database connection string + }); + } catch (err) { + console.error(err); + } +} +``` + +In this example, the global variable `accessTokenStr` is used to "cache" the +access token string so any subsequent callback invocation will not necessarily +have to incur the expense of externally getting a token. For example, if the +application opens two connections for the same user, the token acquired for the +first connection can be reused without needing to make a second REST call. + +The `getConnection()` function's +[`accessToken`](#getconnectiondbattrsaccesstoken) attribute in this example is +set to the callback function that returns an OAuth 2.0 token used by +node-oracledb for authentication. This function `tokenCallback()` will be +invoked when `getConnection()` is called. If the returned token is found to +have expired, then `tokenCallback()` will be called a second time. If the +second invocation of `tokenCallback()` also returns an expired token, then the +connection will fail. The value of the `refresh` parameter will be different +each of the times the callback is invoked: + +- When `refresh` is *true*, the token is known to have expired so the + application must get a new token. This is then stored in the global variable + `accessTokenStr` and returned. + +- When `refresh` is *false*, the application can return the token stored in + `accessTokenStr`, if it is set. But if it is not set (meaning there is no + token cached), then the application externally acquires a token, stores it in + `accessTokenStr`, and returns it. + +##### 16.5.1.3 OAuth 2.0 Connection Pooling + +Pooled connections can be created using OAuth 2.0 token-based authentication, +for example: + +```javascript +let accessTokenStr; // The token string. In this app it is also the token "cache" + +async function tokenCallback(refresh) { + if (refresh || !acccessTokenStr) { + accessTokenStr = await getOauthToken(); // getOauthToken() was shown earlier + } + return acccessToken; +} + +async function init() { + try { + await oracledb.createPool({ + accessToken : tokenCallback, // the callback returning the token + externalAuth : true, // must specify external authentication + homogeneous : true, // must use an homogeneous pool + connectString : '...' // Oracle Autonomous Database connection string + }); + } catch (err) { + console.error(err); + } +} +``` + +See [OAuth 2.0 Standalone Connections](#oauthstandalone) for a description of +the callback and `refresh` parameter. With connection pools, the +[`accessToken`](#createpoolpoolattrsaccesstoken) attribute sets a callback +function which will be invoked at the time the pool is created (even if +`poolMin` is 0). It is also called when the pool needs to expand (causing new +connections to be created) and the current token has expired. + +##### 16.5.1.4 OAuth 2.0 Connection Strings + +Applications built with node-oracledb 5.5, or later, should use the connection +or pool creation parameters described earlier. However, if you cannot use +them, you can use OAuth 2.0 Token Authentication by configuring Oracle Net +options. This still requires Oracle Client libraries 19.15 (or later), or 21.7 +(or later). + +Save the generated access token to a file and set the connect descriptor +`TOKEN_LOCATION` option to the directory containing the token file. The +connect descriptor parameter `TOKEN_AUTH` must be set to `OAUTH`, the +`PROTOCOL` value must be `TCPS`, the `SSL_SERVER_DN_MATCH` value should be +`ON`, and the parameter `SSL_SERVER_CERT_DN` should be set. For example, your +[`tnsnames.ora`](#tnsnames) file might contain: + +``` +db_alias = + (DESCRIPTION=(ADDRESS=(PROTOCOL=TCPS)(PORT=1522)(HOST=abc.oraclecloud.com)) + (CONNECT_DATA=(SERVICE_NAME=db_low.adb.oraclecloud.com)) + (SECURITY= + (SSL_SERVER_DN_MATCH=ON) + (SSL_SERVER_CERT_DN="CN=efg.oraclecloud.com, OU=Oracle BMCS US, O=Oracle Corporation, L=Redwood City, ST=California, C=US") + (TOKEN_AUTH=OAUTH) + (TOKEN_LOCATION='/opt/oracle/token') + )) +``` + +You can alternatively set `TOKEN_AUTH` and `TOKEN_LOCATION` in a +[`sqlnet.ora`](#tnsadmin) file. The `TOKEN_AUTH` and `TOKEN_LOCATION` values +in a connection string take precedence over the `sqlnet.ora` settings. + +See [Oracle Net Services documentation][37] for more information. + + +#### 16.5.2 IAM Token-Based Authentication + +Token-based authentication allows Oracle Cloud Infrastructure users to +authenticate to Oracle Database with Oracle Identity Access Management (IAM) +tokens. This token authentication can be performed when node-oracledb uses +Oracle Client libraries 19.14 (or later), or 21.5 (or later). + +See [Configuring the Oracle Autonomous Database for IAM Integration][207] for +more information. + +##### 16.5.2.1 IAM Token Generation + +Authentication tokens can be obtained in several ways. For example you can use +the Oracle Cloud Infrastructure command line interface (OCI CLI) command run +externally to Node.js: ``` oci iam db-token get @@ -11008,22 +11241,25 @@ oci iam db-token get On Linux a folder `.oci/db-token` will be created in your home directory. It will contain the token and private key files needed by node-oracledb. -#### 16.5.2 Token and Private Key Extraction from a File +See [Working with the Command Line Interface ][209] for more information on the +OCI CLI. -Token and private key files created externally can be read by your application, -for example like: +##### 16.5.2.2 IAM Token and Private Key Extraction + +Token and private key files created externally can be read by Node.js +applications, for example like: ```javascript -function getToken() { +function getIAMToken() { const tokenPath = '/home/cjones/.oci/db-token/token'; const privateKeyPath = '/home/cjones/.oci/db-token/oci_db_key.pem'; let token = ''; let privateKey = ''; try { - // Read token file + // Read the token file token = fs.readFileSync(tokenPath, 'utf8'); - // Read private key file + // Read the private key file const privateKeyFileContents = fs.readFileSync(privateKeyPath, 'utf-8'); privateKeyFileContents.split(/\r?\n/).forEach(line => { if (line != '-----BEGIN PRIVATE KEY-----' && @@ -11034,31 +11270,37 @@ function getToken() { console.error(err); } finally { const tokenBasedAuthData = { - token : token, - privateKey : privateKey + token : token, + privateKey : privateKey }; return tokenBasedAuthData; } } ``` -The token and key can be used subsequently during authentication. +The token and key can be used during subsequent authentication. -#### 16.5.3 Standalone Connection Creation with Access Tokens +##### 16.5.2.3 IAM Standalone Connections -Standalone connections can be created using token based authentication, for +Standalone connections can be created using IAM token-based authentication, for example: ```javascript +let accessTokenObj; // the token object. In this app it is also the token "cache" + +function tokenCallback(refresh) { + if (refresh || !acccessTokenObj) { + accessTokenObj = getIAMToken(); // getIAMToken() was shown earlier + } + return acccessTokenObj; +} + async function init() { - - const accessTokenData = getToken(); - try { await oracledb.getConnection({ - accessToken : accessTokenData, - externalAuth : true, - connectString : connect_string + accessToken : tokenCallback, // the callback returns the token object + externalAuth : true, // must specify external authentication + connectString : '...' // Oracle Autonomous Database connection string }); } catch (err) { console.error(err); @@ -11066,24 +11308,54 @@ async function init() { } ``` -For a runnable example, see [`tokenbasedauth.js`][206]. +In this example, the global object `accessTokenObj` is used to "cache" the IAM +access token and private key (using the attributes `token` and `privateKey`) so +any subsequent callback invocation will not necessarily have to incur the +expense of externally getting them. For example, if the application opens two +connections for the same user, the token and private key acquired for the first +connection can be reused without needing to make a second REST call. -#### 16.5.4 Connection Pool Creation with Access Tokens +The `getConnection()` function's +[`accessToken`](#getconnectiondbattrsaccesstoken) attribute in this example is +set to the callback function that returns an IAM token and private key used by +node-oracledb for authentication. This function `tokenCallback()` will be +invoked when `getConnection()` is called. If the returned token is found to +have expired, then `tokenCallback()` will be called a second time. If the +second invocation of `tokenCallback()` also returns an expired token, then the +connection will fail. The value of the `refresh` parameter will be different +each of the times the callback is invoked: -An example creating a pool using token based authentication is: +- When `refresh` is *true*, the token is known to have expired so the + application must get a new token and private key. These are then stored in + the global object `accessTokenObj` and returned. + +- When `refresh` is *false*, the application can return the token and private + key stored in `accessTokenObj`, if it is set. But if it is not set (meaning + there is no token or key cached), then the application externally acquires a + token and private key, stores them in `accessTokenObj`, and returns it. + +##### 16.5.2.4 IAM Connection Pooling + +Pooled connections can be created using IAM token-based authentication, for +example: ```javascript +let accessTokenObj; // The token string. In this app it is also the token "cache" + +function tokenCallback(refresh) { + if (refresh || !acccessTokenObj) { + accessTokenObj = getIAMToken(); // getIAMToken() was shown earlier + } + return acccessToken; +} + async function init() { - - const accessTokenData = getToken(); - try { await oracledb.createPool({ - accessToken : accessTokenData, - accessTokenCallback : tokenCallback - externalAuth : true, - homogeneous : true, - connectString : connect_string, + accessToken : tokenCallback, // the callback returning the token + externalAuth : true, // must specify external authentication + homogeneous : true, // must use an homogeneous pool + connectString : connect_string // Oracle Autonomous Database connection string }); } catch (err) { console.error(err); @@ -11091,55 +11363,64 @@ async function init() { } ``` -The callback will be called if the connection pool needs to grow and create new -connections but the current token is invalid. The callback should return the -new, valid token: +See [IAM Standalone Connections](#iamstandalone) for a description of the +callback and `refresh` parameter. With connection pools, the +[`accessToken`](#createpoolpoolattrsaccesstoken) attribute sets a callback +function which will be invoked at the time the pool is created (even if +`poolMin` is 0). It is also called when the pool needs to expand (causing new +connections to be created) and the current token has expired. -```javascript -function tokenCallback() { - ... - // Code to get the refreshed token and private key should be here. - // For example, make an external call to 'oci iam db-token get' and then - // read the token and private key values, see getToken() - ... +##### 16.5.2.5 IAM Connection Strings - const refreshTokenData = { - token : token, - privateKey : privateKey - }; +Applications built with node-oracledb 5.4, or later, should use the connection +or pool creation parameters described earlier. However, if you cannot use +them, you can use IAM Token Authentication by configuring Oracle Net options. +This still requires Oracle Client libraries 19.14 (or later), or 21.5 (or +later). - return refreshTokenData; -} +Save the generated access token to a file and set the connect descriptor +`TOKEN_LOCATION` option to the directory containing the token file. The +connect descriptor parameter `TOKEN_AUTH` must be set to `OCI_TOKEN`, the +`PROTOCOL` value must be `TCPS`, the `SSL_SERVER_DN_MATCH` value should be +`ON`, and the parameter `SSL_SERVER_CERT_DN` should be set. For example, if +the token and private key are in the default location used by the [OCI +CLI][209], your [`tnsnames.ora`](#tnsnames) file might contain: + +``` +db_alias = + (DESCRIPTION=(ADDRESS=(PROTOCOL=TCPS)(PORT=1522)(HOST=abc.oraclecloud.com)) + (CONNECT_DATA=(SERVICE_NAME=db_low.adb.oraclecloud.com)) + (SECURITY= + (SSL_SERVER_DN_MATCH=ON) + (SSL_SERVER_CERT_DN="CN=efg.oraclecloud.com, OU=Oracle BMCS US, O=Oracle Corporation, L=Redwood City, ST=California, C=US") + (TOKEN_AUTH=OCI_TOKEN) + )) ``` -For a runnable example, see [`tokenbasedauthpool.js`][207]. +This reads the IAM token and private key from the default location, for example +`~/.oci/db-token/` on Linux. -#### 16.5.5 Explicitly Refreshing Pool Access Tokens +If the token and private key files are not in the default location then their +directory must be specified with the `TOKEN_LOCATION` parameter. For example +in a `tnsnames.ora` file: -If you did not set an access token callback with -[`accessTokenCallback`](#createpoolpoolattrsaccesstokencallback) to -automatically update an expired token, you can explicitly call -[`pool.setAccessToken()`](#poolsetaccesstoken). For example: - -```javascript -let accessTokenData = getToken(); - -let pool = await oracledb.createPool({ - accessToken : accessTokenData, - externalAuth : true, - homogeneous : true, - connectString : connect_string, -}); - -. . . // do some work, during which the token expires - -// Get and set an updated token -accessTokenData = getToken(); -pool.setAccessToken({ - token : accessTokenData.token, - privateKey : accessTokenData.privateKey -)}; ``` +db_alias = + (DESCRIPTION=(ADDRESS=(PROTOCOL=TCPS)(PORT=1522)(HOST=abc.oraclecloud.com)) + (CONNECT_DATA=(SERVICE_NAME=db_low.adb.oraclecloud.com)) + (SECURITY= + (SSL_SERVER_DN_MATCH=ON) + (SSL_SERVER_CERT_DN="CN=efg.oraclecloud.com, OU=Oracle BMCS US, O=Oracle Corporation, L=Redwood City, ST=California, C=US") + (TOKEN_AUTH=OCI_TOKEN) + (TOKEN_LOCATION='/opt/oracle/token') + )) +``` + +You can alternatively set `TOKEN_AUTH` and `TOKEN_LOCATION` in a +[`sqlnet.ora`](#tnsadmin) file. The `TOKEN_AUTH` and `TOKEN_LOCATION` values +in a connection string take precedence over the `sqlnet.ora` settings. + +See [Oracle Net Services documentation][37] for more information. ### 16.6 Database Resident Connection Pooling (DRCP) @@ -19115,5 +19396,7 @@ can be asked at [AskTom][158]. [203]: https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-39C521D4-5C6E-44B1-B7C7-DEADD7D9CAF0 [204]: https://www.oracle.com/a/tech/docs/application-checklist-for-continuous-availability-for-maa.pdf [205]: https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-8152084F-4760-4B89-A91C-9A84F81C23D1 -[206]: https://github.com/oracle/node-oracledb/tree/main/examples/tokenbasedauth.js -[207]: https://github.com/oracle/node-oracledb/tree/main/examples/tokenbasedauthpool.js +[206]: https://docs.oracle.com/en/database/oracle/oracle-database/19/dbseg/authenticating-and-authorizing-microsoft-azure-active-directory-users-oracle-autonomous-datab.html#GUID-2712902B-DD07-4A61-B336-31C504781D0F +[207]: https://docs.oracle.com/en/database/oracle/oracle-database/19/dbseg/authenticating-and-authorizing-iam-users-oracle-autonomous-databases.html#GUID-466A8800-5AF1-4202-BAFF-5AE727D242E8 +[208]: https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow +[209]: https://docs.oracle.com/en-us/iaas/Content/API/Concepts/cliconcepts.htm diff --git a/examples/README.md b/examples/README.md index 36259daa..11e80210 100644 --- a/examples/README.md +++ b/examples/README.md @@ -109,7 +109,5 @@ File Name | Description [`sessiontagging1.js`](sessiontagging1.js) | Simple pooled connection tagging for setting session state [`sessiontagging2.js`](sessiontagging2.js) | More complex example of pooled connection tagging for setting session state [`soda1.js`](soda1.js) | Basic Simple Oracle Document Access (SODA) example -[`tokenbasedauth.js`](tokenbasedauth.js) | Shows standalone connection using token based authentication -[`tokenbasedauthpool.js`](tokenbasedauthpool.js) | Shows connection pooling using token based authentication [`version.js`](version.js) | Shows the node-oracledb version attributes [`webapp.js`](webapp.js) | A simple web application using a connection pool diff --git a/examples/tokenbasedauth.js b/examples/tokenbasedauth.js deleted file mode 100644 index 3a55f1e3..00000000 --- a/examples/tokenbasedauth.js +++ /dev/null @@ -1,125 +0,0 @@ -/* Copyright (c) 2022, Oracle and/or its affiliates. */ - -/****************************************************************************** - * - * You may not use the identified files except in compliance with the Apache - * License, Version 2.0 (the "License.") - * - * You may obtain a copy of the License at - * http://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. - * - * NAME - * tokenbasedauth.js - * - * DESCRIPTION - * This script shows standalone connection using token based authentication - * to Oracle Autonomous Database from Oracle Compute Infrastructure. - * - * For more information refer to - * https://oracle.github.io/node-oracledb/doc/api.html#tokenbasedauth - * - * PREREQUISITES - * - node-oracledb 5.4 or later. - * - * - Oracle Client libraries 19.14 (or later), or 21.5 (or later). - * - * - The Oracle Cloud Infrastructure command line interface (OCI-CLI). The - * command line interface (CLI) is a tool that enables you to work with - * Oracle Cloud Infrastructure objects and services at a command line, see - * https://docs.oracle.com/en-us/iaas/Content/API/Concepts/cliconcepts.htm - * - * - Set these environment variables (see the code explanation): - * NODE_ORACLEDB_ACCESS_TOKEN_LOC - * NODE_ORACLEDB_CONNECTIONSTRING - * - *****************************************************************************/ - -const fs = require('fs'); -const oracledb = require('oracledb'); -const { execSync } = require('child_process'); - -// Execute the OCI-CLI command to generate a token. -// This should create two files "token" and "oci_db_key.pem". -// On Linux the default file location is "~/.oci/db-token". You should set -// NODE_ORACLEDB_ACCESS_TOKEN_LOC to this directory, or to the directory where -// you move the files. -try { - const cmdResult = execSync('oci iam db-token get', { encoding: 'utf-8' }); - console.log(cmdResult); -} catch (err) { - console.log(err); -} - -// User defined function for reading token and private key values generated by -// the OCI-CLI. -function getToken() { - const tokenPath = process.env.NODE_ORACLEDB_ACCESS_TOKEN_LOC + '/token'; - const privateKeyPath = process.env.NODE_ORACLEDB_ACCESS_TOKEN_LOC + - '/oci_db_key.pem'; - - let token = ''; - let privateKey = ''; - try { - // Read token file - token = fs.readFileSync(tokenPath, 'utf8'); - // Read private key file - const privateKeyFileContents = fs.readFileSync(privateKeyPath, 'utf-8'); - privateKeyFileContents.split(/\r?\n/).forEach(line => { - if (line != '-----BEGIN PRIVATE KEY-----' && - line != '-----END PRIVATE KEY-----') - privateKey = privateKey.concat(line); - }); - } catch (err) { - console.error(err); - } - - const tokenBasedAuthData = { - token : token, - privateKey : privateKey - }; - return tokenBasedAuthData; -} - -async function run() { - let connection; - // Get token and private key. - let accessTokenObj = getToken(); - - // Configuration for token based authentication: - // accessToken: The token values - // externalAuth: Must be set to true for token based authentication. - // connectString: The NODE_ORACLEDB_CONNECTIONSTRING environment variable - // set to the Oracle Net alias or connect descriptor of your - // Oracle Autonomous Database. - const config = { - accessToken : accessTokenObj, - externalAuth : true, - connectString : process.env.NODE_ORACLEDB_CONNECTIONSTRING - }; - - try { - connection = await oracledb.getConnection(config); - const sql = `SELECT TO_CHAR(current_date, 'DD-Mon-YYYY HH24:MI') AS D - FROM DUAL`; - const result = await connection.execute(sql); - console.log("Result is:\n", result); - } catch (err) { - console.error(err); - } finally { - try { - if (connection) - await connection.close(); - } catch (err) { - console.error(err.message); - } - } -} - -run(); diff --git a/examples/tokenbasedauthpool.js b/examples/tokenbasedauthpool.js deleted file mode 100644 index b56cf759..00000000 --- a/examples/tokenbasedauthpool.js +++ /dev/null @@ -1,170 +0,0 @@ -/* Copyright (c) 2022, Oracle and/or its affiliates. */ - -/****************************************************************************** - * - * You may not use the identified files except in compliance with the Apache - * License, Version 2.0 (the "License.") - * - * You may obtain a copy of the License at - * http://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. - * - * NAME - * tokenbasedauthpool.js - * - * DESCRIPTION - * This script shows connection pooling with token based authentication to - * Oracle Autonomous Database from Oracle Compute Infrastructure. It shows - * how to create a connection pool and update expired tokens. - * - * For more information refer to - * https://oracle.github.io/node-oracledb/doc/api.html#tokenbasedauth - * - * PREREQUISITES - * - node-oracledb 5.4 or later. - * - * - Oracle Client libraries 19.14 (or later), or 21.5 (or later). - * - * - The Oracle Cloud Infrastructure command line interface (OCI-CLI). The - * command line interface (CLI) is a tool that enables you to work with - * Oracle Cloud Infrastructure objects and services at a command line, see - * https://docs.oracle.com/en-us/iaas/Content/API/Concepts/cliconcepts.htm - * - * - Set these environment variables (see the code explanation): - * NODE_ORACLEDB_ACCESS_TOKEN_LOC - * NODE_ORACLEDB_CONNECTIONSTRING - * - *****************************************************************************/ - -const fs = require('fs'); -const oracledb = require('oracledb'); -const { execSync } = require('child_process'); - -// Execute the OCI-CLI command to generate a token. -// This should create two files "token" and "oci_db_key.pem" -// On Linux the default file location is "~/.oci/db-token". You should set -// NODE_ORACLEDB_ACCESS_TOKEN_LOC to this directory, or to the directory where -// you move the files. -try { - const cmdResult = execSync('oci iam db-token get', { encoding: 'utf-8' }); - console.log(cmdResult); -} catch (err) { - console.log(err); -} - -// User defined function for reading token and private key values generated by -// the OCI-CLI. -function getToken() { - const tokenPath = process.env.NODE_ORACLEDB_ACCESS_TOKEN_LOC + '/token'; - const privateKeyPath = process.env.NODE_ORACLEDB_ACCESS_TOKEN_LOC + - '/oci_db_key.pem'; - - let token = ''; - let privateKey = ''; - try { - // Read token file - token = fs.readFileSync(tokenPath, 'utf8'); - // Read private key file - const privateKeyFileContents = fs.readFileSync(privateKeyPath, 'utf-8'); - privateKeyFileContents.split(/\r?\n/).forEach(line => { - if (line != '-----BEGIN PRIVATE KEY-----' && - line != '-----END PRIVATE KEY-----') - privateKey = privateKey.concat(line); - }); - } catch (err) { - console.error(err); - } - - const tokenBasedAuthData = { - token : token, - privateKey : privateKey - }; - return tokenBasedAuthData; -} - -// User defined callback function which is invoked by node-oracledb when a -// token expires and the pool needs to create new connections -function tokenCallback() { - return getToken(); -} - -async function run() { - // Get token and private key for initial pool creation. - const accessTokenData = getToken(); - - // Configuration for token based authentication: - // accessToken: The initial token values - // accessTokenCallback: A callback function to provide a refreshed token - // externalAuth: Must be set to true for token based authentication - // homogeneous: Must be set to true for token based authentication - // connectString: The NODE_ORACLEDB_CONNECTIONSTRING environment - // variable set to the Oracle Net alias or connect - // descriptor of your Oracle Autonomous Database - const config = { - accessToken : accessTokenData, - accessTokenCallback : tokenCallback, - externalAuth : true, - homogeneous : true, - connectString : process.env.NODE_ORACLEDB_CONNECTIONSTRING, - }; - - try { - - // Create pool using token based authentication - await oracledb.createPool(config); - - // A real app would call createConnection() multiple times over a long - // period of time. During this time the pool may grow. If the initial - // token has expired, node-oracledb will automatically call the - // accessTokenCallback function allowing you to update the token. - await createConnection(); - - } catch (err) { - console.error(err); - } finally { - await closePoolAndExit(); - } -} - -async function createConnection() { - let connection; - try { - // Get a connection from the default pool - connection = await oracledb.getConnection(); - const sql = `SELECT TO_CHAR(current_date, 'DD-Mon-YYYY HH24:MI') AS D - FROM DUAL`; - const result = await connection.execute(sql); - console.log("Result is:\n", result); - } catch (err) { - console.error(err); - } finally { - if (connection) { - // Put the connection back in the pool - await connection.close(); - } - } -} - -async function closePoolAndExit() { - console.log('\nTerminating'); - try { - // Get the pool from the pool cache and close it - await oracledb.getPool().close(0); - process.exit(0); - } catch (err) { - console.error(err.message); - process.exit(1); - } -} - -process - .once('SIGTERM', closePoolAndExit) - .once('SIGINT', closePoolAndExit); - -run(); diff --git a/lib/oracledb.js b/lib/oracledb.js index ccd4f722..11037130 100644 --- a/lib/oracledb.js +++ b/lib/oracledb.js @@ -106,6 +106,7 @@ class OracleDb { this.shutdown = nodbUtil.callbackify(shutdown).bind(_oracledb); this.startup = nodbUtil.callbackify(startup).bind(_oracledb); this.initOracleClient = this.initOracleClient.bind(_oracledb); + this._accessTokenHandler = this._accessTokenHandler.bind(_oracledb); } // temporary method for determining if an object is a date until @@ -114,6 +115,22 @@ class OracleDb { return util.isDate(val); } + // handler for access token callbacks + async _accessTokenHandler(userFn, externalObj, refresh) { + let accessToken; + try { + let result = userFn(refresh); + if (result instanceof Promise) { + result = await result; + } + + _checkToken(result); + accessToken = result; + } finally { + this._returnAccessToken(externalObj, accessToken); + } + } + // retrieves a pool from the pool cache (synchronous method) getPool(poolAlias) { let pool; @@ -176,15 +193,6 @@ async function createPool(poolAttrs) { } if (poolAttrs.accessToken !== undefined) { - // token and privateKey must be set for token based - // authentication - if (poolAttrs.accessToken.token === undefined || - poolAttrs.accessToken.token === '' || - poolAttrs.accessToken.privateKey === undefined || - poolAttrs.accessToken.privateKey === '') { - throw new Error(nodbUtil.getErrorMessage('NJS-084')); - } - // cannot set username or password for token based authentication if (poolAttrs.user !== undefined || poolAttrs.password !== undefined) { @@ -199,11 +207,6 @@ async function createPool(poolAttrs) { } } - if (poolAttrs.accessToken === undefined && - poolAttrs.accessTokenCallback !== undefined) { - throw new Error(nodbUtil.getErrorMessage('NJS-084')); - } - // create an adjusted set of pool attributes to pass to the C layer; the // session callback must be removed if it is a JavaScript function and the // queue timeout is used to specify the maximum amount of time that the C @@ -220,9 +223,27 @@ async function createPool(poolAttrs) { if (adjustedPoolAttrs.queueTimeout === undefined) { adjustedPoolAttrs.queueTimeout = this.queueTimeout; } + + // token based authentication if (poolAttrs.accessToken !== undefined) { - adjustedPoolAttrs.token = poolAttrs.accessToken.token; - adjustedPoolAttrs.privateKey = poolAttrs.accessToken.privateKey; + // accessTokenCallback is depricated from node-oracledb 5.5 + if (poolAttrs.accessTokenCallback !== undefined && + typeof poolAttrs.accessToken === 'function') { + throw new Error(nodbUtil.getErrorMessage('NJS-088')); + } + + let accessToken; + if (typeof poolAttrs.accessToken === 'function') { + adjustedPoolAttrs.accessTokenCallback = poolAttrs.accessToken; + accessToken = await poolAttrs.accessToken(false); + + if (_isTokenExpired(accessToken)) { + accessToken = await poolAttrs.accessToken(true); + } + } else { + accessToken = poolAttrs.accessToken; + } + _checkToken(accessToken, adjustedPoolAttrs); } // Need to prevent another call in the same stack from succeeding, otherwise @@ -305,15 +326,6 @@ async function getConnection(a1) { // otherwise, create a new standalone connection } else { if (connAttrs.accessToken !== undefined) { - // token and privateKey must be set for token based - // authentication - if (connAttrs.accessToken.token === undefined || - connAttrs.accessToken.token === '' || - connAttrs.accessToken.privateKey === undefined || - connAttrs.accessToken.privateKey === '') { - throw new Error(nodbUtil.getErrorMessage('NJS-084')); - } - // cannot set username or password for token based authentication if (connAttrs.user !== undefined || connAttrs.password !== undefined) { @@ -325,9 +337,17 @@ async function getConnection(a1) { throw new Error(nodbUtil.getErrorMessage('NJS-086')); } - connAttrs.token = connAttrs.accessToken.token; - connAttrs.privateKey = - connAttrs.accessToken.privateKey; + let accessToken; + if (typeof connAttrs.accessToken === 'function') { + accessToken = await connAttrs.accessToken(false); + + if (_isTokenExpired(accessToken)) { + accessToken = await connAttrs.accessToken(true); + } + } else { + accessToken = connAttrs.accessToken; + } + _checkToken(accessToken, connAttrs); } try { @@ -427,6 +447,78 @@ async function startup(a1, a2) { } +//----------------------------------------------------------------------------- +// _isTokenExpiredUtil() +// Function to check validity and expiry of token +//----------------------------------------------------------------------------- +function _isTokenExpiredUtil(accessToken) { + nodbUtil.assert(typeof accessToken === 'string', 'NJS-084', 1); + + if (accessToken.split('.')[1] === undefined) { + throw new Error(nodbUtil.getErrorMessage('NJS-084')); + } + + const base64Url = accessToken.split('.')[1]; + const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/'); + const buff = Buffer.from(base64, 'base64'); + const payloadInit = buff.toString('ascii'); + + let expiry = JSON.parse(payloadInit).exp; + nodbUtil.assert(expiry != undefined, 'NJS-084'); + expiry = expiry * 1000; + + return (new Date().getTime() > expiry); +} + + +//----------------------------------------------------------------------------- +// _isTokenExpired() +// Function to check validity of token parameter +//----------------------------------------------------------------------------- +function _isTokenExpired(accessToken) { + switch (typeof accessToken) { + case 'string': + if (accessToken === '') { + throw new Error(nodbUtil.getErrorMessage('NJS-084')); + } + + return _isTokenExpiredUtil(accessToken); + case 'object': + if (accessToken.token === undefined || + accessToken.token === '' || + accessToken.privateKey === undefined || + accessToken.privateKey === '') { + throw new Error(nodbUtil.getErrorMessage('NJS-084')); + } + + return _isTokenExpiredUtil(accessToken.token); + default: + throw new Error(nodbUtil.getErrorMessage('NJS-084')); + } +} + + +//----------------------------------------------------------------------------- +// _checkToken() +// Function to check validity of token parameter +//----------------------------------------------------------------------------- +function _checkToken(accessToken, attrs) { + if (_isTokenExpired(accessToken)) { + throw new Error(nodbUtil.getErrorMessage('NJS-087')); + } + + if (attrs === undefined) + return; + + if (typeof accessToken === 'string') { + attrs.token = accessToken; + } else { + attrs.token = accessToken.token; + attrs.privateKey = accessToken.privateKey; + } +} + + // create instance which will be exported let oracleDbInst = new OracleDb(); diff --git a/lib/util.js b/lib/util.js index 5874bf70..f700e953 100644 --- a/lib/util.js +++ b/lib/util.js @@ -67,9 +67,11 @@ const errorMessages = { 'NJS-081': 'NJS-081: concurrent operations on a connection are disabled', 'NJS-082': 'NJS-082: connection pool is being reconfigured', 'NJS-083': 'NJS-083: pool statistics not enabled', - 'NJS-084': 'NJS-084: invalid or missing parameter with token based authentication. The token and privateKey attributes must contain values. Other credentials cannot be specified', + 'NJS-084': 'NJS-084: invalid access token', 'NJS-085': 'NJS-085: invalid connection pool configuration with token based authentication. The homogeneous and externalAuth attributes must be set to true', - 'NJS-086': 'NJS-086: invalid standalone configuration with token based authentication. The externalAuth attribute must be set to true' + 'NJS-086': 'NJS-086: invalid standalone configuration with token based authentication. The externalAuth attribute must be set to true', + 'NJS-087': 'NJS-087: access token has expired', + 'NJS-088': 'NJS-088: accessTokenCallback cannot be specified when accessToken is a function' }; // getInstallURL returns a string with installation URL diff --git a/package.json b/package.json index 044e031d..77cd2ce3 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,6 @@ }, "scripts": { "test": "mocha --config test/opts/.mocharc.yaml", - "testtoken": "mocha --config test/opts/.mocharctoken.yaml", "posttest": "node test/opts/version.js", "buildbinary": "node package/buildbinary.js", "buildpackage": "node package/buildpackage.js" diff --git a/src/njsErrors.c b/src/njsErrors.c index 7b8aacd8..9fbb3b9c 100644 --- a/src/njsErrors.c +++ b/src/njsErrors.c @@ -90,9 +90,11 @@ static const char *njsErrorMessages[] = { "NJS-081: concurrent operations on a connection are disabled", //errConcurrentOps "NJS-082: connection pool is being reconfigured", // errPoolReconfiguring "NJS-083: pool statistics not enabled", // errPoolStatisticsDisabled - "NJS-084: invalid or missing parameter with token based authentication. The token and privateKey attributes must contain values. Other credentials cannot be specified", // errTokenBasedAuth + "NJS-084: invalid access token", // errTokenBasedAuth "NJS-085: invalid connection pool configuration with token based authentication. The homogeneous and externalAuth attributes must be set to true", // errPoolTokenBasedAuth "NJS-086: invalid standalone configuration with token based authentication. The externalAuth attribute must be set to true", // errStandaloneTokenBasedAuth + "NJS-087: access token has expired", //errExpiredToken + "NJS-088: accessTokenCallback cannot be specified when accessToken is a function" //errAccessTokenCallback }; diff --git a/src/njsModule.h b/src/njsModule.h index 6ae7d206..9cf88314 100644 --- a/src/njsModule.h +++ b/src/njsModule.h @@ -209,6 +209,8 @@ typedef enum { errTokenBasedAuth, errPoolTokenBasedAuth, errStandaloneTokenBasedAuth, + errExpiredToken, + errAccessTokenCallback, // New ones should be added here @@ -678,6 +680,8 @@ struct njsOracleDb { napi_ref jsSodaDocumentConstructor; napi_ref jsSodaOperationConstructor; napi_ref jsSubscriptions; + napi_ref jsTokenCallbackHandler; + }; // data for class Pool exposed to JS. @@ -838,6 +842,7 @@ struct njsDbObjectAttr { // data for managing callback struct njsTokenCallback { dpiAccessToken *accessToken; + njsOracleDb *oracleDb; uv_async_t async; uv_mutex_t mutex; uv_barrier_t barrier; @@ -1139,5 +1144,7 @@ int njsTokenCallback_eventHandler(njsTokenCallback *callback, bool njsTokenCallback_new(njsBaton *baton, napi_env env); bool njsTokenCallback_startNotifications(njsTokenCallback *callback, napi_env env); +bool njsTokenCallback_returnAccessToken(njsTokenCallback *callback, + napi_env env, napi_value accessToken); bool njsTokenCallback_stopNotifications(njsTokenCallback *callback); #endif /* __NJSMODULE_H__ */ diff --git a/src/njsOracleDb.c b/src/njsOracleDb.c index 5e95ddf9..49367582 100644 --- a/src/njsOracleDb.c +++ b/src/njsOracleDb.c @@ -29,6 +29,7 @@ static NJS_NAPI_METHOD(njsOracleDb_createPool); static NJS_NAPI_METHOD(njsOracleDb_getConnection); static NJS_NAPI_METHOD(njsOracleDb_initOracleClient); +static NJS_NAPI_METHOD(njsOracleDb_returnAccessToken); // asynchronous methods static NJS_ASYNC_METHOD(njsOracleDb_createPoolAsync); @@ -349,6 +350,8 @@ static const napi_property_descriptor njsClassProperties[] = { napi_default, NULL }, { "_initOracleClient", NULL, njsOracleDb_initOracleClient, NULL, NULL, NULL, napi_default, NULL }, + { "_returnAccessToken", NULL, njsOracleDb_returnAccessToken, NULL, NULL, + NULL, napi_default, NULL }, { NULL, NULL, NULL, NULL, NULL, NULL, napi_default, NULL } }; @@ -500,7 +503,7 @@ static bool njsOracleDb_createPoolAsync(njsBaton *baton) commonParams.stmtCacheSize = baton->stmtCacheSize; // set token based auth parameters - if (baton->token && baton->privateKey) { + if (baton->token) { accessToken.token = baton->token; accessToken.tokenLength = baton->tokenLength; accessToken.privateKey = baton->privateKey; @@ -623,6 +626,7 @@ static void njsOracleDb_finalize(napi_env env, void *finalizeData, NJS_DELETE_REF_AND_CLEAR(oracleDb->jsSodaDocumentConstructor); NJS_DELETE_REF_AND_CLEAR(oracleDb->jsSodaOperationConstructor); NJS_DELETE_REF_AND_CLEAR(oracleDb->jsSubscriptions); + NJS_DELETE_REF_AND_CLEAR(oracleDb->jsTokenCallbackHandler); if (oracleDb->context) { dpiContext_destroy(oracleDb->context); oracleDb->context = NULL; @@ -706,7 +710,7 @@ static bool njsOracleDb_getConnectionAsync(njsBaton *baton) commonParams.stmtCacheSize = baton->stmtCacheSize; // set token based auth parameters - if (baton->token && baton->privateKey) { + if (baton->token) { accessToken.token = baton->token; accessToken.tokenLength = baton->tokenLength; accessToken.privateKey = baton->privateKey; @@ -1237,6 +1241,43 @@ static napi_value njsOracleDb_initOracleClient(napi_env env, } +//----------------------------------------------------------------------------- +// njsOracleDb_returnAccessTokenHelper() +// Helper method that performs the work of njsOracleDb_returnAccessToken(). +//----------------------------------------------------------------------------- +static bool njsOracleDb_returnAccessTokenHelper(napi_env env, + napi_callback_info info) +{ + njsBaseInstance *callingInstance; + napi_value callingObj, args[2]; + njsTokenCallback *callback; + + if (!njsUtils_validateArgs(env, info, 2, args, &callingObj, + &callingInstance)) + return false; + NJS_CHECK_NAPI(env, napi_get_value_external(env, args[0], + (void**) &callback)) + return njsTokenCallback_returnAccessToken(callback, env, args[1]); +} + + +//----------------------------------------------------------------------------- +// njsOracleDb_returnAccessToken() +// Returns the access token through to the callback. This needs to be done +// independently in order to handle possible asynchronous Javascript code. +// +// PARAMETERS +// - externalObj (contains native njsAccessToken structure) +// - accessToken (value to be returned through callback) +//----------------------------------------------------------------------------- +static napi_value njsOracleDb_returnAccessToken(napi_env env, + napi_callback_info info) +{ + njsOracleDb_returnAccessTokenHelper(env, info); + return NULL; +} + + //----------------------------------------------------------------------------- // njsOracleDb_initCommonCreateParams() // Initialize common creation parameters for pools and standalone @@ -1444,6 +1485,13 @@ bool njsOracleDb_prepareClass(njsOracleDb *oracleDb, napi_env env, // keep a reference to it, if requested if (clsRef) { NJS_CHECK_NAPI(env, napi_create_reference(env, cls, 1, clsRef)) + + // otherwise, acquire access token callback handler and store reference + } else { + NJS_CHECK_NAPI(env, napi_get_named_property(env, instance, + "_accessTokenHandler", &tempResult)) + NJS_CHECK_NAPI(env, napi_create_reference(env, tempResult, 1, + &oracleDb->jsTokenCallbackHandler)) } return true; diff --git a/src/njsTokenCallback.c b/src/njsTokenCallback.c index d81099c2..4c5d8550 100644 --- a/src/njsTokenCallback.c +++ b/src/njsTokenCallback.c @@ -31,7 +31,7 @@ static void njsTokenCallback_onStopNotifications(uv_handle_t *handle); static bool njsTokenCallback_onStopNotificationsHelper(napi_env env, njsTokenCallback *callback); static void njsTokenCallback_processNotification(uv_async_t *handle); -static bool njsTokenCallback_processNotificationHelper(napi_env env, +static bool njsTokenCallback_processNotificationHelper( njsTokenCallback *callback); static void njsTokenCallback_waitOnBarrier(njsTokenCallback *callback); @@ -48,23 +48,18 @@ int njsTokenCallback_eventHandler(njsTokenCallback *callback, { uv_mutex_lock(&callback->mutex); uv_barrier_init(&callback->barrier, 2); - uv_mutex_unlock(&callback->mutex); - - if (uv_async_send(&callback->async) < 0) { - njsTokenCallback_waitOnBarrier(callback); - return -1; - } - + callback->result = false; + uv_async_send(&callback->async); njsTokenCallback_waitOnBarrier(callback); if (!callback->result) - return -1; + return DPI_FAILURE; tokenRefresh->token = callback->accessToken->token; tokenRefresh->tokenLength = callback->accessToken->tokenLength; - tokenRefresh->privateKey = - callback->accessToken->privateKey; - tokenRefresh->privateKeyLength - = callback->accessToken->privateKeyLength; - return 0; + tokenRefresh->privateKey = callback->accessToken->privateKey; + tokenRefresh->privateKeyLength = callback->accessToken->privateKeyLength; + uv_mutex_unlock(&callback->mutex); + + return DPI_SUCCESS; } @@ -81,6 +76,7 @@ bool njsTokenCallback_new(njsBaton *baton, napi_env env) if (!baton->accessTokenCallback->accessToken) return njsBaton_setError(baton, errInsufficientMemory); baton->accessTokenCallback->env = env; + baton->accessTokenCallback->oracleDb = baton->oracleDb; return true; } @@ -118,14 +114,13 @@ static void njsTokenCallback_processNotification(uv_async_t *handle) { njsTokenCallback *callback = (njsTokenCallback*) handle->data; napi_handle_scope scope; + bool result; + if (napi_open_handle_scope(callback->env, &scope) != napi_ok) return; - - uv_mutex_lock(&callback->mutex); - callback->result = njsTokenCallback_processNotificationHelper(callback->env, - callback); - njsTokenCallback_waitOnBarrier(callback); - uv_mutex_unlock(&callback->mutex); + result = njsTokenCallback_processNotificationHelper(callback); + if (!result) + njsTokenCallback_waitOnBarrier(callback); napi_close_handle_scope(callback->env, scope); } @@ -135,38 +130,90 @@ static void njsTokenCallback_processNotification(uv_async_t *handle) // Helper method for processing notifications so that the scope that is // opened can be easily destroyed. //----------------------------------------------------------------------------- -static bool njsTokenCallback_processNotificationHelper(napi_env env, +static bool njsTokenCallback_processNotificationHelper( njsTokenCallback *callback) { - dpiAccessToken *accessToken = callback->accessToken; - napi_value callbackRef, global, message, result, payloadObj; - size_t tempLength; + napi_value jsCallback, jsCallbackHandler, global, refresh, result; + napi_value jsCallbackArgs[3], externalObj; + napi_env env = callback->env; - // acquire callback and refreshed tokens NJS_CHECK_NAPI(env, napi_get_global(env, &global)) NJS_CHECK_NAPI(env, napi_get_reference_value(env, callback->jsCallback, - &callbackRef)) + &jsCallback)) + NJS_CHECK_NAPI(env, napi_get_reference_value(env, + callback->oracleDb->jsTokenCallbackHandler, &jsCallbackHandler)) + NJS_CHECK_NAPI(env, napi_create_external(env, callback, NULL, NULL, + &externalObj)) + NJS_CHECK_NAPI(env, napi_get_boolean(env, true, &refresh)) + jsCallbackArgs[0] = jsCallback; + jsCallbackArgs[1] = externalObj; + jsCallbackArgs[2] = refresh; + NJS_CHECK_NAPI(env, napi_make_callback(env, NULL, global, + jsCallbackHandler, 3, jsCallbackArgs, &result)); - // perform callback - NJS_CHECK_NAPI(env, napi_make_callback(env, NULL, global, callbackRef, - 1, &message, &result)); + return true; +} - // Read "token" property - NJS_CHECK_NAPI(env, napi_get_named_property(env, result, "token", - &payloadObj)) - if (!njsUtils_setPropString(env, payloadObj, "token", - (char**) &accessToken->token, &tempLength)) - return false; - accessToken->tokenLength = (uint32_t) tempLength; - // Read "privateKey" property - NJS_CHECK_NAPI(env, napi_get_named_property(env, result, "privateKey", - &payloadObj)) - if (!njsUtils_setPropString(env, payloadObj, "privateKey", - (char**) &accessToken->privateKey, &tempLength)) - return false; - accessToken->privateKeyLength = (uint32_t) tempLength; +//----------------------------------------------------------------------------- +// njsTokenCallback_returnAccessTokenHelper() +// Helper function for njsTokenCallback_returnAccessToken(). +//----------------------------------------------------------------------------- +static bool njsTokenCallback_returnAccessTokenHelper(njsTokenCallback *callback, + napi_env env, napi_value payloadObj) +{ + dpiAccessToken *accessToken = callback->accessToken; + napi_valuetype actualType; + size_t tempLength; + napi_value temp; + NJS_CHECK_NAPI(env, napi_typeof(env, payloadObj, &actualType)) + if (actualType == napi_undefined) { + accessToken->token = NULL; + accessToken->tokenLength = 0; + accessToken->privateKey = NULL; + accessToken->privateKeyLength = 0; + } else if (actualType == napi_string) { + if (!njsUtils_setPropString(env, payloadObj, "token", + (char**) &accessToken->token, &tempLength)) + return false; + accessToken->tokenLength = (uint32_t) tempLength; + } else if (actualType == napi_object) { + + // Read "token" property + NJS_CHECK_NAPI(env, napi_get_named_property(env, payloadObj, "token", + &temp)) + if (!njsUtils_setPropString(env, temp, "token", + (char**) &accessToken->token, &tempLength)) + return false; + accessToken->tokenLength = (uint32_t) tempLength; + + // Read "privateKey" property + NJS_CHECK_NAPI(env, napi_get_named_property(env, payloadObj, + "privateKey", &temp)) + if (!njsUtils_setPropString(env, temp, "privateKey", + (char**) &accessToken->privateKey, &tempLength)) + return false; + accessToken->privateKeyLength = (uint32_t) tempLength; + + } + + return true; +} + + +//----------------------------------------------------------------------------- +// njsTokenCallback_returnAccessToken() +// Called by the JavaScript callback handler when it has completed. If an +// error has taken place, the value returned is the "undefined" Javascript +// value. +//----------------------------------------------------------------------------- +bool njsTokenCallback_returnAccessToken(njsTokenCallback *callback, + napi_env env, napi_value payloadObj) +{ + callback->result = njsTokenCallback_returnAccessTokenHelper(callback, env, + payloadObj); + njsTokenCallback_waitOnBarrier(callback); return true; } diff --git a/test/README.md b/test/README.md index 44df1074..ee782228 100644 --- a/test/README.md +++ b/test/README.md @@ -23,12 +23,9 @@ limitations under the License. - 1.3 [Build](#build) - 1.4 [Configure Database credentials](#credentials) - 1.5 [Set NODE_PATH](#nodepath) - - 1.6 [Configure OCI-CLI](#ocicli) - - 1.7 [Configuration for token based authentication](#tokenbasedauth) 2. [Run tests](#runtests) - 2.1 [Run the complete test suite](#runall) - 2.2 [Run specified tests](#runspecified) - - 2.3 [Run token based authentication tests](#runtokenbasedtest) 3. [Enable tests that require extra configuration](#enablespecified) - 3.1 [externalProxyAuth.js](#externalproxyauth) 4. [Contribute New Tests](#addtests) @@ -108,10 +105,6 @@ Set the following environment variables to provide credentials for the test suit * `NODE_ORACLEDB_QA`. This boolean environment variable serves as the toggle switch of certain tests. Some tests, such as `callTimeout.js`, use hard-coded variables as assertion condition. The test results may be inconsistent in different network situations. -* `NODE_ORACLEDB_ACCESS_TOKEN_LOC` provides the directory path where the valid Oracle Identity and Access Manager (IAM) database token and private key files are. These files are generated by the Oracle Cloud Infrastructure Command Line Interface (OCI-CLI) command oci iam db-token get. This is required in token based authentication. - -* `NODE_ORACLEDB_EXPIRED_ACCESS_TOKEN_LOC` provides the directory path where expired Oracle Identity and Access Manager (IAM) database token and private key files are. These files are generated by the Oracle Cloud Infrastructure Command Line Interface (OCI-CLI) command oci iam db-token get. This is required in token based authentication testing. - Note: the test suite requires the schema to have these privileges: CREATE TABLE, CREATE SESSION, CREATE PROCEDURE, CREATE SEQUENCE, CREATE TRIGGER, and CREATE TYPE. @@ -121,154 +114,6 @@ CREATE PROCEDURE, CREATE SEQUENCE, CREATE TRIGGER, and CREATE TYPE. export NODE_PATH=/node-oracledb/lib ``` -### 1.6 Configure OCI-CLI - -This tool is used in token based authentication for generating token. -Install OCI-CLI tool from below link: -https://docs.oracle.com/en-us/iaas/Content/API/SDKDocs/cliinstall.htm - -* Configuration steps for OCI-CLI - - Execute command `oci setup config` through command line terminal. - This command provides a walkthrough of creating a valid CLI config file. - - - Enter a location for your config : - Location where you want to place config in local machine. - Default location is ~/.oci - - - Enter user OCID: - OCID of IAM user from autonomous database. - - - Enter a tenancy OCID: - tenancy OCID of admin account from autonomous database. - - - Enter a region by index or name - Index or region name from dropdown as shown in terminal. - Region name can be found in top right corner of oracle cloud webpage. - Example: US East (Ashburn) - - - Do you want to generate a new API Signing RSA key pair? - (If you decline you will be asked to supply the path to an existing key.) [Y/n]: Y - - - Enter a directory for your keys to be created [~/.oci]: - Location where you want private/public key files to be generated. - - - Enter a name for your key [oci_api_key]: - Your public/private key filename. Default is "oci_api_key" - - - Public key written to: ~/.oci/oci_api_key_public.pem - - - Enter a passphrase for your private key (empty for no passphrase): - Can keep it as blank - - - Private key written to: ~/.oci/oci_api_key.pem - - - Config written to ~/.oci/conf - - - Upload public key to OCI cloud. - Copy public key generated to console->User->API Keys->Add API Keys. - - - Command to generate db-token - `oci iam db-token get` - -### 1.7 Configuration for token based authentication - -Steps to configure autonomous database for token based authentication. - -The steps below summarize the Oracle Autonomous Database -[Use Identity and Access Management (IAM) -Authentication with Autonomous -Database](https://docs.oracle.com/en/cloud/paas/autonomous-database/adbsa/manage-users-iam.html#GUID-4E206209-4E3B-4387-9364-BDCFB4E16E2E) -documentation. - -* Enable Identity and Access Management (IAM) Authentication on Autonomous Database - - As the privileged database user, Enable IAM token support on Autonomous - Database: - - ``` - BEGIN - DBMS_CLOUD_ADMIN.ENABLE_EXTERNAL_AUTHENTICATION (type => 'OCI_IAM'); - END; - / - ``` - Confirm the `identity_provider_type` is `OCI_IAM`: - ``` - SELECT name, value - FROM V$PARAMETER - WHERE name = 'identity_provider_type'; - ``` - -* Create Identity and Access Management (IAM) Groups and Policies for IAM Users - - To enable Autonomous Database to allow IAM users to connect using IAM - tokens, perform the Oracle Cloud Infrastructure Identity and Access - Management prerequisites by creating a group and add the IAM user to the - newly created group. - - Use the cloud console to create a group, for example `MyIAMGroupName`. - - Create a policy in the cloud console to allow any user belonging to the new group to - access Oracle Autonomous Database, for example: - - ``` - allow group MyIAMGroupName to use autonomous-database-family in tenancy - ``` - -* Create an IAM User - - Create an IAM user using the Oracle Cloud console. - -* Add the IAM User to Autonomous Database - - Map a database global user to the IAM group, for example: - - ``` - CREATE USER mydbuser IDENTIFIED GLOBALLY AS 'IAM_GROUP_NAME = MyIAMGroupName'; - ``` - -5. Add IAM Roles on Autonomous Database - - Create a role for database authorization: - ``` - CREATE ROLE myrole IDENTIFIED GLOBALLY AS 'IAM_GROUP_NAME = MyIAMGroupName'; - ``` - -6. Grant permissions - - ``` - GRANT CREATE SESSION TO myrole; - GRANT DWROLE to myrole; - ``` - -7. Download a Wallet - - Download the wallet from the Oracle Cloud console. Unzip the file. - -8. Edit the Network Configuration file - - Update the sqlnet.ora file: - - ``` - WALLET_LOCATION = (SOURCE = (METHOD = file) (METHOD_DATA = (DIRECTORY="?/network/admin"))) - SSL_SERVER_DN_MATCH = yes - SQLNET.AUTHENTICATION_SERVICE = (TCPS) - SSL_VERSION = 0 - SSL_CLIENT_AUTHENTICATION = TRUE - ``` - -9. Wallet location - - If the files extracted from the wallet zip are NOT in a default location - then also change the WALLET_LOCATION directory. Default locations include - the `network/admin` subdirectory of Oracle Instant Client. - - For example if your files are in `/opt/oracle/wallet`, then change the first - line to: - - ``` - WALLET_LOCATION = (SOURCE = (METHOD = file) (METHOD_DATA = (DIRECTORY="/opt/oracle/wallet"))) - ``` - ## 2. Run tests See [mochajs.org](http://mochajs.org/) for more information on running tests with mocha. @@ -287,13 +132,6 @@ cd node_oracledb ./node_modules/.bin/mocha test/ ``` -### 2.3 Run token based authentication tests - -``` -cd node-oracledb -npm testtoken -``` - ## 3. Enable tests that requires extra configuration The following test(s) are automatically skipped if their required environment variable(s) are not properly set. diff --git a/test/list.txt b/test/list.txt index 0c9fb277..ce34060c 100755 --- a/test/list.txt +++ b/test/list.txt @@ -5318,70 +5318,15 @@ oracledb.OUT_FORMAT_OBJECT and resultSet = true 264. methodName.js 264.1 check for methodName getConnection -265. tokenbasedAuthentication.js - 265.1 pool - 265.1.1 create pool connection - 265.1.2 acquire multiple sessions from pool - 265.1.3 acquire multiple sessions from multiple pool - 265.1.4 query execution - 265.1.5 homogeneous should be true - 265.1.6 externalAuth should be false - 265.1.7 token should be set in accessToken - 265.1.8 token length should be not be 0 in accessToken - 265.1.9 privateKey should be set in accessToken - 265.1.10 privateKey length should not be 0 in accessToken - 265.1.11 with callback having valid token - 265.1.12 with no callback and expired token - 265.1.13 with callback having invalid token - 265.1.14 with callback having missing token - 265.1.15 with callback having 0 length token - 265.1.16 with callback having missing privateKey - 265.1.17 with callback having 0 length privateKey - 265.1.18 user/password without tokens with externalAuth set to true - 265.1.19 user/password with tokens - 265.1.20 invalidate tokens using setAccessToken with missing token - 265.1.21 invalidate tokens using setAccessToken with empty token - 265.1.22 invalidate tokens using setAccessToken with empty privateKey - 265.1.23 invalidate tokens using setAccessToken with missing privateKey - 265.1.24 with callback having valid tokens in multiple pool - 265.1.25 with callback having valid and invalid tokens in multiple pool - 265.1.26 acquiring released session after token expiry - 265.1.27 acquiring released session with existing tag, with token expiry - 265.1.28 acquiring released session without existing tag, with token expiry - 265.1.29 user/password without tokens with externalAuth set to false - 265.1.30 token and private key should be private - 265.1.31 token and private key value should not be shown in logs - 265.1.32 random token and private key value - 265.1.33 pool creation with 0 connection - 265.1.34 token expires before creating connection - 265.1.35 undefined token and private key value - 265.1.36 user/password with accessTokenCallback - 265.1.37 create pool connection using expired token - - 265.2 standalone - 265.2.1 create standalone connection - 265.2.2 standalone connection with invalid data - 265.2.3 standalone connection with missing data - 265.2.4 standalone connection with missing data - 265.2.5 standalone connection with empty data - 265.2.6 standalone connection with empty data - 265.2.7 externalAuth must be set to true - 265.2.8 user/password with tokens - 265.2.9 user/password without tokens - 265.2.10 standalone connection with undefined private key and token - 265.2.11 standalone connection with random token and private key - 265.2.12 token and private key are private - 265.2.13 standalone connection with expired token - - 267. aq4.js - 267.1 empty array or no recipients - 267.2 single element in array - 267.3 Negative - numbers as recipients - 267.4 Negative - number, string, date as recipients - 267.5 Negative - null value for recipient - 267.6 Negative - undefined value for recipeint - 267.7 Negative - dequeue non-existent name - 267.8 empty recipient list with enqMany - 267.9 recipient list with enqMany - 267.10 recipient list with enqMany non-existent in dequeue - 267.11 recipient list with enqMany invalid datatype in dequeue +267. aq4.js + 267.1 empty array or no recipients + 267.2 single element in array + 267.3 Negative - numbers as recipients + 267.4 Negative - number, string, date as recipients + 267.5 Negative - null value for recipient + 267.6 Negative - undefined value for recipeint + 267.7 Negative - dequeue non-existent name + 267.8 empty recipient list with enqMany + 267.9 recipient list with enqMany + 267.10 recipient list with enqMany non-existent in dequeue + 267.11 recipient list with enqMany invalid datatype in dequeue diff --git a/test/opts/.mocharctoken.yaml b/test/opts/.mocharctoken.yaml deleted file mode 100644 index 698dbce7..00000000 --- a/test/opts/.mocharctoken.yaml +++ /dev/null @@ -1,8 +0,0 @@ -# Mocha options - -timeout: 0 -require: ['assert'] -retries: 1 -trace-warnings: true -spec: - - test/tokenBasedAuthentication.js diff --git a/test/tokenBasedAuthentication.js b/test/tokenBasedAuthentication.js deleted file mode 100644 index 395e08f4..00000000 --- a/test/tokenBasedAuthentication.js +++ /dev/null @@ -1,1874 +0,0 @@ -/* Copyright (c) 2022, Oracle and/or its affiliates. */ - -/****************************************************************************** - * - * You may not use the identified files except in compliance with the Apache - * License, Version 2.0 (the "License.") - * - * You may obtain a copy of the License at - * http://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. - * - * NAME - * 265. tokenBasedAuthentication.js - * - * DESCRIPTION - * Testing properties of pool and standalone connection using - * token based authentication. -*****************************************************************************/ -'use strict'; - -var oracledb = require('oracledb'); -const assert = require('assert'); -var dbConfig = require('./tokenBasedAuthenticationConfig.js'); - -describe('265. Token based authentication', function() { - - describe('265.1 Pool', function() { - - it('265.1.1 create pool connection', async () => { - let pool; - try { - pool = await oracledb.createPool({ - accessToken : dbConfig.accessToken, - connectString : dbConfig.connectString, - poolMin : 1, - poolMax : 1, - poolIncrement : 1, - externalAuth : true, - homogeneous : true - }); - assert.ok(pool); - } catch (err) { - assert.deepEqual(err, {}); - } finally { - try { - if (pool) - await pool.close(0); - } catch (err) { - assert.deepEqual(err, {}); - } - } - }); - - it('265.1.2 acquire multiple sessions from pool', async () => { - let pool, conn1, conn2; - try { - pool = await oracledb.createPool({ - accessToken : dbConfig.accessToken, - connectString : dbConfig.connectString, - poolMin : 1, - poolMax : 2, - poolIncrement : 1, - externalAuth : true, - homogeneous : true - }); - assert.ok(pool); - - conn1 = await pool.getConnection(); - assert.deepEqual(pool.connectionsOpen, 1); - assert.deepEqual(pool.connectionsInUse, 1); - - conn2 = await pool.getConnection(); - assert.deepEqual(pool.connectionsOpen, 2); - assert.deepEqual(pool.connectionsInUse, 2); - } catch (err) { - assert.deepEqual(err, {}); - } finally { - try { - if (conn1) - await conn1.close(); - if (conn2) - await conn2.close(); - if (pool) - await pool.close(0); - } catch (err) { - assert.deepEqual(err, {}); - } - } - }); - - it('265.1.3 acquire multiple sessions from multiple pool', async () => { - let pool1, conn1, conn2; - try { - pool1 = await oracledb.createPool({ - accessToken : dbConfig.accessToken, - connectString : dbConfig.connectString, - poolMin : 1, - poolMax : 2, - poolIncrement : 1, - externalAuth : true, - homogeneous : true - }); - assert.ok(pool1); - - conn1 = await pool1.getConnection(); - assert.deepEqual(pool1.connectionsOpen, 1); - assert.deepEqual(pool1.connectionsInUse, 1); - - conn2 = await pool1.getConnection(); - assert.deepEqual(pool1.connectionsOpen, 2); - assert.deepEqual(pool1.connectionsInUse, 2); - } catch (err) { - assert.deepEqual(err, {}); - } finally { - try { - if (conn1) - await conn1.close(); - if (conn2) - await conn2.close(); - if (pool1) - await pool1.close(0); - } catch (err) { - assert.deepEqual(err, {}); - } - } - - let pool2, conn3, conn4; - try { - pool2 = await oracledb.createPool({ - accessToken : dbConfig.accessToken, - connectString : dbConfig.connectString, - poolMin : 1, - poolMax : 2, - poolIncrement : 1, - externalAuth : true, - homogeneous : true - }); - assert.ok(pool2); - - conn3 = await pool2.getConnection(); - assert.deepEqual(pool2.connectionsOpen, 1); - assert.deepEqual(pool2.connectionsInUse, 1); - - conn4 = await pool2.getConnection(); - assert.deepEqual(pool2.connectionsOpen, 2); - assert.deepEqual(pool2.connectionsInUse, 2); - } catch (err) { - assert.deepEqual(err, {}); - } finally { - try { - if (conn3) - await conn3.close(); - if (conn4) - await conn4.close(); - if (pool2) - await pool2.close(0); - } catch (err) { - assert.deepEqual(err, {}); - } - } - }); - - it('265.1.4 query execution', async () => { - let pool, conn; - try { - pool = await oracledb.createPool({ - accessToken : dbConfig.accessToken, - connectString : dbConfig.connectString, - poolMin : 1, - poolMax : 5, - poolIncrement : 1, - externalAuth : true, - homogeneous : true - }); - assert.ok(pool); - - conn = await pool.getConnection(); - assert.deepEqual(pool.connectionsOpen, 1); - assert.deepEqual(pool.connectionsInUse, 1); - - const sql = `SELECT - TO_CHAR(current_date, 'DD-Mon-YYYY HH24:MI') AS D - FROM DUAL`; - const result = await conn.execute(sql); - assert.notEqual(result, {}); - } catch (err) { - assert.deepEqual(err, {}); - } finally { - try { - if (conn) - await conn.close(); - if (pool) - await pool.close(0); - } catch (err) { - assert.deepEqual(err, {}); - } - } - }); - - it('265.1.5 homogenous should be true', async () => { - let pool; - try { - pool = await oracledb.createPool({ - accessToken : dbConfig.accessToken, - connectString : dbConfig.connectString, - poolMin : 1, - poolMax : 5, - poolIncrement : 1, - externalAuth : true, - homogeneous : false - }); - } catch (err) { - assert.notDeepEqual(err, {}); - assert.match(err.message, /^NJS-085:/, 'regexp does not match'); - } finally { - try { - if (pool) - await pool.close(0); - } catch (err) { - assert.deepEqual(err, {}); - } - } - }); - - it('265.1.6 externalAuth should be true', async () => { - let pool; - try { - pool = await oracledb.createPool({ - accessToken : dbConfig.accessToken, - connectString : dbConfig.connectString, - poolMin : 1, - poolMax : 5, - poolIncrement : 1, - externalAuth : false, - homogeneous : true - }); - } catch (err) { - assert.notDeepEqual(err, {}); - assert.match(err.message, /^NJS-085:/, 'regexp does not match'); - } finally { - try { - if (pool) - await pool.close(0); - } catch (err) { - assert.deepEqual(err, {}); - } - } - }); - - it('265.1.7 token should be set in accessToken', async () => { - let pool; - const ResObj = { - privateKey : dbConfig.accessToken.privateKey - }; - try { - pool = await oracledb.createPool({ - accessToken : ResObj, - connectString : dbConfig.connectString, - poolMin : 1, - poolMax : 5, - poolIncrement : 1, - externalAuth : true, - homogeneous : true - }); - } catch (err) { - assert.notDeepEqual(err, {}); - assert.match(err.message, /^NJS-084:/, 'regexp does not match'); - } finally { - try { - if (pool) - await pool.close(0); - } catch (err) { - assert.deepEqual(err, {}); - } - } - }); - - it('265.1.8 token length should be not be 0 in accessToken', async () => { - let pool; - const ResObj = { - privateKey : dbConfig.accessToken.privateKey, - token: '' - }; - try { - pool = await oracledb.createPool({ - accessToken : ResObj, - connectString : dbConfig.connectString, - poolMin : 1, - poolMax : 5, - poolIncrement : 1, - externalAuth : true, - homogeneous : true - }); - } catch (err) { - assert.notDeepEqual(err, {}); - assert.match(err.message, /^NJS-084:/, 'regexp does not match'); - } finally { - try { - if (pool) - await pool.close(0); - } catch (err) { - assert.deepEqual(err, {}); - } - } - }); - - it('265.1.9 privateKey should be set in accessToken', async () => { - let pool; - const ResObj = { - token : dbConfig.accessToken.token - }; - try { - pool = await oracledb.createPool({ - accessToken : ResObj, - connectString : dbConfig.connectString, - poolMin : 1, - poolMax : 5, - poolIncrement : 1, - externalAuth : true, - homogeneous : true - }); - } catch (err) { - assert.notDeepEqual(err, {}); - assert.match(err.message, /^NJS-084:/, 'regexp does not match'); - } finally { - try { - if (pool) - await pool.close(0); - } catch (err) { - assert.deepEqual(err, {}); - } - } - }); - - it('265.1.10 privateKey length should not be 0 in accessToken', async () => { - let pool; - const ResObj = { - privateKey : '', - token : dbConfig.accessToken.token - }; - try { - pool = await oracledb.createPool({ - accessToken : ResObj, - connectString : dbConfig.connectString, - poolMin : 1, - poolMax : 5, - poolIncrement : 1, - externalAuth : true, - homogeneous : true - }); - } catch (err) { - assert.notDeepEqual(err, {}); - assert.match(err.message, /^NJS-084:/, 'regexp does not match'); - } finally { - try { - if (pool) - await pool.close(0); - } catch (err) { - assert.deepEqual(err, {}); - } - } - }); - - it('265.1.11 with callback having valid token', async () => { - function callback() { - return dbConfig.callbackValid; - } - - let pool, conn1, conn2; - try { - pool = await oracledb.createPool({ - accessToken : dbConfig.accessToken, - connectString : dbConfig.connectString, - poolMin : 1, - poolMax : 5, - poolIncrement : 1, - externalAuth : true, - homogeneous : true, - accessTokenCallback : callback - }); - assert.ok(pool); - - conn1 = await pool.getConnection(); - assert.deepEqual(pool.connectionsOpen, 1); - assert.deepEqual(pool.connectionsInUse, 1); - - // invalidate token to test callback - pool.setAccessToken(dbConfig.expiredAccessToken); - - conn2 = await pool.getConnection(); - assert.deepEqual(pool.connectionsOpen, 2); - assert.deepEqual(pool.connectionsInUse, 2); - - } catch (err) { - assert.deepEqual(err, {}); - } finally { - try { - if (conn1) - await conn1.close(); - if (conn2) - await conn2.close(); - if (pool) - await pool.close(0); - } catch (err) { - assert.deepEqual(err, {}); - } - } - }); - - it('265.1.12 no callback and expired token', async () => { - let pool, conn1, conn2; - try { - pool = await oracledb.createPool({ - accessToken : dbConfig.accessToken, - connectString : dbConfig.connectString, - poolMin : 1, - poolMax : 5, - poolIncrement : 1, - externalAuth : true, - homogeneous : true - }); - assert.ok(pool); - - conn1 = await pool.getConnection(); - assert.deepEqual(pool.connectionsOpen, 1); - assert.deepEqual(pool.connectionsInUse, 1); - - // invalidate token to test callback - pool.setAccessToken(dbConfig.expiredAccessToken); - - conn2 = await pool.getConnection(); - - } catch (err) { - assert.notDeepEqual(err, {}); - assert.match(err.message, /^ORA-25708:/, 'regexp does not match'); - } finally { - try { - if (conn1) - await conn1.close(); - if (conn2) - await conn2.close(); - if (pool) - await pool.close(0); - } catch (err) { - assert.deepEqual(err, {}); - } - } - }); - - it('265.1.13 with callback having invalid token', async () => { - function callback() { - // returns invalid token parameters values - // invalid token and private key values - return dbConfig.callbackInvalid1; - } - - let pool, conn1, conn2; - try { - pool = await oracledb.createPool({ - accessToken : dbConfig.accessToken, - connectString : dbConfig.connectString, - poolMin : 1, - poolMax : 5, - poolIncrement : 1, - externalAuth : true, - homogeneous : true, - accessTokenCallback : callback - }); - assert.ok(pool); - - conn1 = await pool.getConnection(); - assert.deepEqual(pool.connectionsOpen, 1); - assert.deepEqual(pool.connectionsInUse, 1); - - // invalidate token to test callback - pool.setAccessToken(dbConfig.expiredAccessToken); - - conn2 = await pool.getConnection(); - - } catch (err) { - assert.notDeepEqual(err, {}); - assert.match(err.message, /^ORA-25707:/, 'regexp does not match'); - } finally { - try { - if (conn1) - await conn1.close(); - if (conn2) - await conn2.close(); - if (pool) - await pool.close(0); - } catch (err) { - assert.deepEqual(err, {}); - } - } - }); - - it('265.1.14 with callback having missing token', async () => { - function callback() { - // missing token - return dbConfig.callbackInvalid2; - } - - let pool, conn1, conn2; - try { - pool = await oracledb.createPool({ - accessToken : dbConfig.accessToken, - connectString : dbConfig.connectString, - poolMin : 1, - poolMax : 5, - poolIncrement : 1, - externalAuth : true, - homogeneous : true, - accessTokenCallback : callback - }); - assert.ok(pool); - - conn1 = await pool.getConnection(); - assert.deepEqual(pool.connectionsOpen, 1); - assert.deepEqual(pool.connectionsInUse, 1); - - // invalidate token to test callback - pool.setAccessToken(dbConfig.expiredAccessToken); - - conn2 = await pool.getConnection(); - - } catch (err) { - assert.notDeepEqual(err, {}); - assert.match(err.message, /^Error: NJS-004:/, 'regexp does not match'); - } finally { - try { - if (conn1) - await conn1.close(); - if (conn2) - await conn2.close(); - if (pool) - await pool.close(0); - } catch (err) { - assert.deepEqual(err, {}); - } - } - }); - - it('265.1.15 with callback having 0 length token', async () => { - function callback() { - // empty token - return dbConfig.callbackInvalid3; - } - - let pool, conn1, conn2; - try { - pool = await oracledb.createPool({ - accessToken : dbConfig.accessToken, - connectString : dbConfig.connectString, - poolMin : 1, - poolMax : 5, - poolIncrement : 1, - externalAuth : true, - homogeneous : true, - accessTokenCallback : callback - }); - assert.ok(pool); - - conn1 = await pool.getConnection(); - assert.deepEqual(pool.connectionsOpen, 1); - assert.deepEqual(pool.connectionsInUse, 1); - - // invalidate token to test callback - pool.setAccessToken(dbConfig.expiredAccessToken); - - conn2 = await pool.getConnection(); - - } catch (err) { - assert.notDeepEqual(err, {}); - assert.match(err.message, /^ORA-01017:/, 'regexp does not match'); - } finally { - try { - if (conn1) - await conn1.close(); - if (conn2) - await conn2.close(); - if (pool) - await pool.close(0); - } catch (err) { - assert.deepEqual(err, {}); - } - } - }); - - it('265.1.16 with callback having missing privateKey', async () => { - function callback() { - // missing tokenprivatekey attribute - return dbConfig.callbackInvalid4; - } - - let pool, conn1, conn2; - try { - pool = await oracledb.createPool({ - accessToken : dbConfig.accessToken, - connectString : dbConfig.connectString, - poolMin : 1, - poolMax : 5, - poolIncrement : 1, - externalAuth : true, - homogeneous : true, - accessTokenCallback : callback - }); - assert.ok(pool); - - conn1 = await pool.getConnection(); - assert.deepEqual(pool.connectionsOpen, 1); - assert.deepEqual(pool.connectionsInUse, 1); - - // invalidate token to test callback - pool.setAccessToken(dbConfig.expiredAccessToken); - - conn2 = await pool.getConnection(); - - } catch (err) { - assert.notDeepEqual(err, {}); - assert.match(err.message, /^Error: NJS-004:/, 'regexp does not match'); - } finally { - try { - if (conn1) - await conn1.close(); - if (conn2) - await conn2.close(); - if (pool) - await pool.close(0); - } catch (err) { - assert.deepEqual(err, {}); - } - } - }); - - it('265.1.17 with callback having 0 length privateKey', async () => { - function callback() { - // empty tokenPrivatekey parameter - return dbConfig.callbackInvalid5; - } - - let pool, conn1, conn2; - try { - pool = await oracledb.createPool({ - accessToken : dbConfig.accessToken, - connectString : dbConfig.connectString, - poolMin : 1, - poolMax : 5, - poolIncrement : 1, - externalAuth : true, - homogeneous : true, - accessTokenCallback : callback - }); - assert.ok(pool); - - conn1 = await pool.getConnection(); - assert.deepEqual(pool.connectionsOpen, 1); - assert.deepEqual(pool.connectionsInUse, 1); - - // invalidate token to test callback - pool.setAccessToken(dbConfig.expiredAccessToken); - - conn2 = await pool.getConnection(); - - } catch (err) { - assert.notDeepEqual(err, {}); - assert.match(err.message, /^ORA-01017:/, 'regexp does not match'); - } finally { - try { - if (conn1) - await conn1.close(); - if (conn2) - await conn2.close(); - if (pool) - await pool.close(0); - } catch (err) { - assert.deepEqual(err, {}); - } - } - }); - - it('265.1.18 user/password without tokens and externalAuth set to true', async () => { - let pool; - try { - pool = await oracledb.createPool({ - user : dbConfig.user, - password : dbConfig.password, - connectString : dbConfig.connectString, - poolMin : 1, - poolMax : 5, - poolIncrement : 1, - externalAuth : true, - homogeneous : true - }); - assert.ok(pool); - } catch (err) { - assert.notDeepEqual(err, {}); - assert.match(err.message, /^DPI-1032:/, 'regexp does not match'); - } finally { - try { - if (pool) - await pool.close(0); - } catch (err) { - assert.deepEqual(err, {}); - } - } - }); - - it('265.1.19 user/password with tokens', async () => { - let pool; - try { - pool = await oracledb.createPool({ - user : dbConfig.user, - password : dbConfig.password, - accessToken : dbConfig.accessToken, - connectString : dbConfig.connectString, - poolMin : 1, - poolMax : 5, - poolIncrement : 1, - externalAuth : true, - homogeneous : true - }); - assert.ok(pool); - } catch (err) { - assert.notDeepEqual(err, {}); - assert.match(err.message, /^NJS-084:/, 'regexp does not match'); - } finally { - try { - if (pool) - await pool.close(0); - } catch (err) { - assert.deepEqual(err, {}); - } - } - }); - - it('265.1.20 invalidate tokens using setAccessToken with missing token', async () => { - function callback() { - // valid token and privateKey - return dbConfig.callbackValid; - } - - let pool, conn1, conn2; - try { - pool = await oracledb.createPool({ - accessToken : dbConfig.accessToken, - connectString : dbConfig.connectString, - poolMin : 1, - poolMax : 5, - poolIncrement : 1, - externalAuth : true, - homogeneous : true, - accessTokenCallback : callback - }); - assert.ok(pool); - - conn1 = await pool.getConnection(); - assert.deepEqual(pool.connectionsOpen, 1); - assert.deepEqual(pool.connectionsInUse, 1); - - // invalidate token to test callback - pool.setAccessToken({ - privateKey : dbConfig.expiredAccessToken.privateKey - }); - - conn2 = await pool.getConnection(); - assert.deepEqual(pool.connectionsOpen, 2); - assert.deepEqual(pool.connectionsInUse, 2); - - } catch (err) { - assert.deepEqual(err, {}); - } finally { - try { - if (conn1) - await conn1.close(); - if (conn2) - await conn2.close(); - if (pool) - await pool.close(0); - } catch (err) { - assert.deepEqual(err, {}); - } - } - }); - - it('265.1.21 invalidate tokens using setAccessToken with empty token', async () => { - function callback() { - // valid token and privateKey values - return dbConfig.callbackValid; - } - - let pool, conn1, conn2; - try { - pool = await oracledb.createPool({ - accessToken : dbConfig.accessToken, - connectString : dbConfig.connectString, - poolMin : 1, - poolMax : 5, - poolIncrement : 1, - externalAuth : true, - homogeneous : true, - accessTokenCallback : callback - }); - assert.ok(pool); - - conn1 = await pool.getConnection(); - assert.deepEqual(pool.connectionsOpen, 1); - assert.deepEqual(pool.connectionsInUse, 1); - - // invalidate token to test callback - pool.setAccessToken({ - token : '', - privateKey : dbConfig.expiredAccessToken.privateKey - }); - - conn2 = await pool.getConnection(); - assert.deepEqual(pool.connectionsOpen, 2); - assert.deepEqual(pool.connectionsInUse, 2); - - } catch (err) { - assert.deepEqual(err, {}); - } finally { - try { - if (conn1) - await conn1.close(); - if (conn2) - await conn2.close(); - if (pool) - await pool.close(0); - } catch (err) { - assert.deepEqual(err, {}); - } - } - }); - - it('265.1.22 invalidate tokens using setAccessToken with empty privateKey', async () => { - function callback() { - // valid token and privateKey values - return dbConfig.callbackValid; - } - - let pool, conn1, conn2; - try { - pool = await oracledb.createPool({ - accessToken : dbConfig.accessToken, - connectString : dbConfig.connectString, - poolMin : 1, - poolMax : 5, - poolIncrement : 1, - externalAuth : true, - homogeneous : true, - accessTokenCallback : callback - }); - assert.ok(pool); - - conn1 = await pool.getConnection(); - assert.deepEqual(pool.connectionsOpen, 1); - assert.deepEqual(pool.connectionsInUse, 1); - - // invalidate token to test callback - pool.setAccessToken({ - token : dbConfig.expiredAccessToken.token, - privateKey : '' - }); - - conn2 = await pool.getConnection(); - assert.deepEqual(pool.connectionsOpen, 2); - assert.deepEqual(pool.connectionsInUse, 2); - - } catch (err) { - assert.deepEqual(err, {}); - } finally { - try { - if (conn1) - await conn1.close(); - if (conn2) - await conn2.close(); - if (pool) - await pool.close(0); - } catch (err) { - assert.deepEqual(err, {}); - } - } - }); - - it('265.1.23 invalidate tokens using setAccessToken with missing privateKey', async () => { - function callback() { - // valid token and privateKey values - return dbConfig.callbackValid; - } - - let pool, conn1, conn2; - try { - pool = await oracledb.createPool({ - accessToken : dbConfig.accessToken, - connectString : dbConfig.connectString, - poolMin : 1, - poolMax : 5, - poolIncrement : 1, - externalAuth : true, - homogeneous : true, - accessTokenCallback : callback - }); - assert.ok(pool); - - conn1 = await pool.getConnection(); - assert.deepEqual(pool.connectionsOpen, 1); - assert.deepEqual(pool.connectionsInUse, 1); - - // invalidate token to test callback - pool.setAccessToken(dbConfig.expiredAccessToken); - - conn2 = await pool.getConnection(); - assert.deepEqual(pool.connectionsOpen, 2); - assert.deepEqual(pool.connectionsInUse, 2); - - } catch (err) { - assert.deepEqual(err, {}); - } finally { - try { - if (conn1) - await conn1.close(); - if (conn2) - await conn2.close(); - if (pool) - await pool.close(0); - } catch (err) { - assert.deepEqual(err, {}); - } - } - }); - - it('265.1.24 with callback having valid tokens in multiple pool', async () => { - function callback() { - // valid token and privateKey values - return dbConfig.callbackValid; - } - - let pool1, conn1, conn2; - try { - pool1 = await oracledb.createPool({ - accessToken : dbConfig.accessToken, - connectString : dbConfig.connectString, - poolMin : 1, - poolMax : 2, - poolIncrement : 1, - externalAuth : true, - homogeneous : true, - accessTokenCallback : callback - }); - assert.ok(pool1); - - conn1 = await pool1.getConnection(); - assert.deepEqual(pool1.connectionsOpen, 1); - assert.deepEqual(pool1.connectionsInUse, 1); - - pool1.setAccessToken(dbConfig.expiredAccessToken); - - conn2 = await pool1.getConnection(); - assert.deepEqual(pool1.connectionsOpen, 2); - assert.deepEqual(pool1.connectionsInUse, 2); - } catch (err) { - assert.deepEqual(err, {}); - } finally { - try { - if (conn1) - await conn1.close(); - if (conn2) - await conn2.close(); - if (pool1) - await pool1.close(0); - } catch (err) { - assert.deepEqual(err, {}); - } - } - - let pool2, conn3, conn4; - try { - pool2 = await oracledb.createPool({ - accessToken : dbConfig.accessToken, - connectString : dbConfig.connectString, - poolMin : 1, - poolMax : 2, - poolIncrement : 1, - externalAuth : true, - homogeneous : true, - accessTokenCallback : callback - }); - assert.ok(pool2); - - conn3 = await pool2.getConnection(); - assert.deepEqual(pool2.connectionsOpen, 1); - assert.deepEqual(pool2.connectionsInUse, 1); - - pool2.setAccessToken(dbConfig.expiredAccessToken); - - conn4 = await pool2.getConnection(); - assert.deepEqual(pool2.connectionsOpen, 2); - assert.deepEqual(pool2.connectionsInUse, 2); - } catch (err) { - assert.deepEqual(err, {}); - } finally { - try { - if (conn3) - await conn3.close(); - if (conn4) - await conn4.close(); - if (pool2) - await pool2.close(0); - } catch (err) { - assert.deepEqual(err, {}); - } - } - }); - - it('265.1.25 with callback having valid and invalid tokens in multiple pool', async () => { - function callback1() { - // valid token and privateKey values - return dbConfig.callbackValid; - } - - function callback2() { - return dbConfig.callbackInvalid5; - } - - let pool1, conn1, conn2; - try { - pool1 = await oracledb.createPool({ - accessToken : dbConfig.accessToken, - connectString : dbConfig.connectString, - poolMin : 1, - poolMax : 2, - poolIncrement : 1, - externalAuth : true, - homogeneous : true, - accessTokenCallback : callback1 - }); - assert.ok(pool1); - - conn1 = await pool1.getConnection(); - assert.deepEqual(pool1.connectionsOpen, 1); - assert.deepEqual(pool1.connectionsInUse, 1); - - pool1.setAccessToken(dbConfig.expiredAccessToken); - - conn2 = await pool1.getConnection(); - assert.deepEqual(pool1.connectionsOpen, 2); - assert.deepEqual(pool1.connectionsInUse, 2); - } catch (err) { - assert.deepEqual(err, {}); - } finally { - try { - if (conn1) - await conn1.close(); - if (conn2) - await conn2.close(); - if (pool1) - await pool1.close(0); - } catch (err) { - assert.deepEqual(err, {}); - } - } - - let pool2, conn3, conn4; - try { - pool2 = await oracledb.createPool({ - accessToken : dbConfig.accessToken, - connectString : dbConfig.connectString, - poolMin : 1, - poolMax : 2, - poolIncrement : 1, - externalAuth : true, - homogeneous : true, - accessTokenCallback : callback2 - }); - assert.ok(pool2); - - conn3 = await pool2.getConnection(); - assert.deepEqual(pool2.connectionsOpen, 1); - assert.deepEqual(pool2.connectionsInUse, 1); - - pool2.setAccessToken({ - token : dbConfig.expiredAccessToken.token - }); - - conn4 = await pool2.getConnection(); - } catch (err) { - assert.notDeepEqual(err, {}); - assert.match(err.message, /^ORA-01017:/, 'regexp does not match'); - } finally { - try { - if (conn3) - await conn3.close(); - if (conn4) - await conn4.close(); - if (pool2) - await pool2.close(0); - } catch (err) { - assert.deepEqual(err, {}); - } - } - }); - - it('265.1.26 acquiring released session after token expiry', async () => { - let pool, conn1; - try { - pool = await oracledb.createPool({ - accessToken : dbConfig.accessToken, - connectString : dbConfig.connectString, - poolMin : 1, - poolMax : 1, - poolIncrement : 1, - externalAuth : true, - homogeneous : true - }); - assert.ok(pool); - conn1 = await pool.getConnection(); - assert.deepEqual(pool.connectionsOpen, 1); - assert.deepEqual(pool.connectionsInUse, 1); - - await conn1.close(); - - // invalidate tokens - pool.setAccessToken(dbConfig.expiredAccessToken); - - conn1 = await pool.getConnection(); - assert.deepEqual(pool.connectionsOpen, 1); - assert.deepEqual(pool.connectionsInUse, 1); - - await conn1.close(); - - } catch (err) { - assert.notDeepEqual(err, {}); - } finally { - try { - if (pool) - await pool.close(0); - } catch (err) { - assert.deepEqual(err, {}); - } - } - }); - - it('265.1.27 acquiring released session with existing tag, with token expiry', async () => { - function initSession(connection, requestedTag, callbackFn) { - const tagParts = requestedTag.split('='); - if (tagParts[0] != 'USER_TZ') { - callbackFn(new Error('Error: Only property USER_TZ is supported')); - return; - } - connection.execute( - `ALTER SESSION SET TIME_ZONE = '${tagParts[1]}'`, - (err) => { - connection.tag = requestedTag; - callbackFn(err); - } - ); - } - - let pool, conn1, conn2, conn3; - // callback function for token refresh is not called - try { - pool = await oracledb.createPool({ - accessToken : dbConfig.accessToken, - connectString : dbConfig.connectString, - poolMin : 1, - poolMax : 2, - poolIncrement : 1, - externalAuth : true, - homogeneous : true, - sessionCallback : initSession - }); - assert.ok(pool); - - conn1 = await pool.getConnection({ - poolAlias : 'default', - tag : "USER_TZ=UTC" - }); - assert.deepEqual(pool.connectionsOpen, 1); - assert.deepEqual(pool.connectionsInUse, 1); - - conn2 = await pool.getConnection({ - poolAlias : 'default', - tag : "USER_TZ=GMT" - }); - assert.deepEqual(pool.connectionsOpen, 2); - assert.deepEqual(pool.connectionsInUse, 2); - - if (conn1) - await conn1.close(); - if (conn2) - await conn2.close(); - - // invalidate tokens - pool.setAccessToken(dbConfig.expiredAccessToken); - - // acquire session with existing tag - // doen't need token refresh - conn3 = await pool.getConnection({ - poolAlias : 'default', - tag : "USER_TZ=GMT" - }); - if (conn3) - await conn3.close(); - - } catch (err) { - assert.deepEqual(err, {}); - } finally { - try { - if (pool) - await pool.close(0); - } catch (err) { - assert.deepEqual(err, {}); - } - } - }); - - it('265.1.28 acquiring released session without existing tag, with token expiry', async () => { - function initSession(connection, requestedTag, callbackFn) { - const tagParts = requestedTag.split('='); - if (tagParts[0] != 'USER_TZ') { - callbackFn(new Error('Error: Only property USER_TZ is supported')); - return; - } - connection.execute( - `ALTER SESSION SET TIME_ZONE = '${tagParts[1]}'`, - (err) => { - connection.tag = requestedTag; - callbackFn(err); - } - ); - } - - let pool, conn1, conn2, conn3; - // callback function for token refresh is not called - try { - pool = await oracledb.createPool({ - accessToken : dbConfig.accessToken, - connectString : dbConfig.connectString, - poolMin : 1, - poolMax : 2, - poolIncrement : 1, - externalAuth : true, - homogeneous : true, - sessionCallback : initSession - }); - assert.ok(pool); - - conn1 = await pool.getConnection({ - poolAlias : 'default', - tag : "USER_TZ=UTC"}); - assert.deepEqual(pool.connectionsOpen, 1); - assert.deepEqual(pool.connectionsInUse, 1); - - conn2 = await pool.getConnection({ - poolAlias : 'default', - tag : "USER_TZ=GMT" - }); - assert.deepEqual(pool.connectionsOpen, 2); - assert.deepEqual(pool.connectionsInUse, 2); - - if (conn1) - await conn1.close(); - if (conn2) - await conn2.close(); - - // invalidate tokens - pool.setAccessToken(dbConfig.expiredAccessToken); - - conn3 = await pool.getConnection({ - poolAlias : 'default', - tag : "USER_TZ=Australia/Melbourne" - }); - } catch (err) { - assert.notDeepEqual(err, {}); - assert.match(err.message, /^ORA-25708:/, 'regexp does not match'); - } finally { - try { - if (conn3) - await conn3.close(); - if (pool) - await pool.close(0); - } catch (err) { - assert.deepEqual(err, {}); - } - } - }); - - it('265.1.29 user/password without tokens and externalAuth set to false', async () => { - let pool; - try { - pool = await oracledb.createPool({ - user : dbConfig.user, - password : dbConfig.password, - connectString : dbConfig.connectString, - poolMin : 1, - poolMax : 5, - poolIncrement : 1, - externalAuth : false, - homogeneous : true - }); - assert.ok(pool); - } catch (err) { - assert.deepEqual(err, {}); - } finally { - try { - if (pool) - await pool.close(0); - } catch (err) { - assert.deepEqual(err, {}); - } - } - }); - - it('265.1.30 token and private key should be private', async () => { - let pool; - try { - pool = await oracledb.createPool({ - accessToken : dbConfig.accessToken, - connectString : dbConfig.connectString, - poolMin : 1, - poolMax : 2, - poolIncrement : 1, - externalAuth : true, - homogeneous : true - }); - assert.ok(pool); - assert.deepEqual(pool.token, undefined); - assert.deepEqual(pool.privateKey, undefined); - } catch (err) { - assert.deepEqual(err, {}); - } finally { - try { - if (pool) - await pool.close(0); - } catch (err) { - assert.deepEqual(err, {}); - } - } - }); - - it('265.1.31 token and private key value should not be shown in logs', async () => { - let pool; - try { - pool = await oracledb.createPool({ - accessToken : dbConfig.accessToken, - connectString : dbConfig.connectString, - poolMin : 1, - poolMax : 2, - poolIncrement : 1, - externalAuth : true, - homogeneous : true, - enableStatistics : true - }); - assert.ok(pool); - const poolstatistics = pool.getStatistics(); - assert.deepEqual(poolstatistics.token, undefined); - assert.deepEqual(poolstatistics.privateKey, undefined); - } catch (err) { - assert.deepEqual(err, {}); - } finally { - try { - if (pool) - await pool.close(0); - } catch (err) { - assert.deepEqual(err, {}); - } - } - }); - - it('265.1.32 random token and private key value', async () => { - const testData = { - token : 'Hello', - privateKey : 'World' - }; - - let pool; - try { - pool = await oracledb.createPool({ - accessToken : testData, - connectString : dbConfig.connectString, - poolMin : 1, - poolMax : 2, - poolIncrement : 1, - externalAuth : true, - homogeneous : true - }); - } catch (err) { - assert.notDeepEqual(err, {}); - assert.match(err.message, /^ORA-25707:/, 'regexp does not match'); - } finally { - try { - if (pool) - await pool.close(0); - } catch (err) { - assert.deepEqual(err, {}); - } - } - }); - - it('265.1.33 pool creation with 0 connection', async () => { - let pool; - try { - pool = await oracledb.createPool({ - accessToken : dbConfig.accessToken, - connectString : dbConfig.connectString, - poolMin : 0, - poolMax : 2, - poolIncrement : 1, - externalAuth : true, - homogeneous : true - }); - assert.ok(pool); - } catch (err) { - assert.deepEqual(err, {}); - } finally { - try { - if (pool) - await pool.close(0); - } catch (err) { - assert.deepEqual(err, {}); - } - } - }); - - it('265.1.34 token expires before creating connection', async () => { - function callback() { - return dbConfig.callbackValid; - } - - let pool, conn1, conn2; - try { - pool = await oracledb.createPool({ - accessToken : dbConfig.accessToken, - connectString : dbConfig.connectString, - poolMin : 0, - poolMax : 1, - poolIncrement : 1, - externalAuth : true, - homogeneous : true, - accessTokenCallback : callback - }); - assert.ok(pool); - - // invalidate token to test callback - pool.setAccessToken(dbConfig.expiredAccessToken); - - conn1 = await pool.getConnection(); - assert.deepEqual(pool.connectionsOpen, 1); - assert.deepEqual(pool.connectionsInUse, 1); - } catch (err) { - assert.deepEqual(err, {}); - } finally { - try { - if (conn1) - await conn1.close(); - if (conn2) - await conn2.close(); - if (pool) - await pool.close(0); - } catch (err) { - assert.deepEqual(err, {}); - } - } - }); - - it('265.1.35 undefined token and private key value', async () => { - const testData = { - token : undefined, - privateKey : undefined - }; - - let pool; - try { - pool = await oracledb.createPool({ - accessToken : testData, - connectString : dbConfig.connectString, - poolMin : 1, - poolMax : 2, - poolIncrement : 1, - externalAuth : true, - homogeneous : true - }); - } catch (err) { - assert.notDeepEqual(err, {}); - assert.match(err.message, /^NJS-084:/, 'regexp does not match'); - } finally { - try { - if (pool) - await pool.close(0); - } catch (err) { - assert.deepEqual(err, {}); - } - } - }); - - it('265.1.36 user/password with accessTokenCallback', async () => { - function callback() { - return dbConfig.callbackValid; - } - let pool; - try { - pool = await oracledb.createPool({ - user : dbConfig.user, - password : dbConfig.password, - accessTokenCallback : callback, - connectString : dbConfig.connectString, - poolMin : 1, - poolMax : 5, - poolIncrement : 1, - externalAuth : false, - homogeneous : true - }); - assert.ok(pool); - } catch (err) { - assert.notDeepEqual(err, {}); - assert.match(err.message, /^NJS-084:/, 'regexp does not match'); - } finally { - try { - if (pool) - await pool.close(0); - } catch (err) { - assert.deepEqual(err, {}); - } - } - }); - - it('265.1.37 create pool connection using expired token', async () => { - let pool; - - try { - pool = await oracledb.createPool({ - accessToken : dbConfig.expiredAccessToken, - connectString : dbConfig.connectString, - poolMin : 1, - poolMax : 1, - poolIncrement : 1, - externalAuth : true, - homogeneous : true - }); - assert.ok(pool); - } catch (err) { - assert.notDeepEqual(err, {}); - assert.match(err.message, /^ORA-25708:/, 'regexp does not match'); - } finally { - try { - if (pool) - await pool.close(0); - } catch (err) { - assert.deepEqual(err, {}); - } - } - }); - }); - - describe('265.2 Standalone', function() { - - it('265.2.1 create standalone connection', async () => { - let conn; - try { - conn = await oracledb.getConnection({ - accessToken : dbConfig.accessToken, - connectString : dbConfig.connectString, - externalAuth : true - }); - } catch (err) { - assert.notDeepEqual(err, {}); - } finally { - try { - if (conn) - await conn.close(); - } catch (err) { - assert.deepEqual(err, {}); - } - } - }); - - it('265.2.2 standalone connection with invalid data', async () => { - let conn; - const ResObj = { - token : dbConfig.accessToken.privateKey, - privateKey : dbConfig.accessToken.token - }; - try { - conn = await oracledb.getConnection({ - accessToken : ResObj, - connectString : dbConfig.connectString, - externalAuth : true - }); - } catch (err) { - assert.notDeepEqual(err, {}); - assert.match(err.message, /^ORA-25707:/, 'regexp does not match'); - } finally { - try { - if (conn) - await conn.close(); - } catch (err) { - assert.deepEqual(err, {}); - } - } - }); - - it('265.2.3 standalone connection with missing data', async () => { - let conn; - const ResObj = { - privateKey : dbConfig.accessToken.token - }; - try { - conn = await oracledb.getConnection({ - accessToken : ResObj, - connectString : dbConfig.connectString, - externalAuth : true - }); - } catch (err) { - assert.notDeepEqual(err, {}); - assert.match(err.message, /^NJS-084:/, 'regexp does not match'); - } finally { - try { - if (conn) - await conn.close(); - } catch (err) { - assert.deepEqual(err, {}); - } - } - }); - - it('265.2.4 standalone connection with missing data', async () => { - let conn; - const ResObj = { - token: dbConfig.accessToken.privateKey - }; - try { - conn = await oracledb.getConnection({ - accessToken : ResObj, - connectString : dbConfig.connectString, - externalAuth : true - }); - } catch (err) { - assert.notDeepEqual(err, {}); - assert.match(err.message, /^NJS-084:/, 'regexp does not match'); - } finally { - try { - if (conn) - await conn.close(); - } catch (err) { - assert.deepEqual(err, {}); - } - } - }); - - it('265.2.5 standalone connection with empty data', async () => { - let conn; - const ResObj = { - token : dbConfig.accessToken.token, - privateKey : '' - }; - try { - conn = await oracledb.getConnection({ - accessToken : ResObj, - connectString : dbConfig.connectString, - externalAuth : true - }); - } catch (err) { - assert.notDeepEqual(err, {}); - assert.match(err.message, /^NJS-084:/, 'regexp does not match'); - } finally { - try { - if (conn) - await conn.close(); - } catch (err) { - assert.deepEqual(err, {}); - } - } - }); - - - it('265.2.6 standalone connection with empty data', async () => { - let conn; - const ResObj = { - token : '', - privateKey : dbConfig.accessToken.privateKey - }; - try { - conn = await oracledb.getConnection({ - accessToken : ResObj, - connectString : dbConfig.connectString, - externalAuth : true - }); - } catch (err) { - assert.notDeepEqual(err, {}); - assert.match(err.message, /^NJS-084:/, 'regexp does not match'); - } finally { - try { - if (conn) - await conn.close(); - } catch (err) { - assert.deepEqual(err, {}); - } - } - }); - - it('265.2.7 externalAuth must be set to true', async () => { - let conn; - try { - conn = await oracledb.getConnection({ - accessToken : dbConfig.accessToken, - connectString : dbConfig.connectString, - externalAuth : false - }); - } catch (err) { - assert.notDeepEqual(err, {}); - assert.match(err.message, /^NJS-086:/, 'regexp does not match'); - } finally { - try { - if (conn) - await conn.close(); - } catch (err) { - assert.deepEqual(err, {}); - } - } - }); - - it('265.2.8 user/password with tokens', async () => { - let conn; - try { - conn = await oracledb.getConnection({ - user : dbConfig.user, - password : dbConfig.password, - accessToken : dbConfig.accessToken, - connectString : dbConfig.connectString, - externalAuth : true - }); - } catch (err) { - assert.notDeepEqual(err, {}); - assert.match(err.message, /^NJS-084:/, 'regexp does not match'); - } finally { - try { - if (conn) - await conn.close(); - } catch (err) { - assert.deepEqual(err, {}); - } - } - }); - - it('265.2.9 user/password without tokens', async () => { - let conn; - try { - conn = await oracledb.getConnection({ - user : dbConfig.user, - password : dbConfig.password, - connectString : dbConfig.connectString, - externalAuth : false - }); - } catch (err) { - assert.notDeepEqual(err, {}); - assert.match(err.message, /^NJS-084:/, 'regexp does not match'); - } finally { - try { - if (conn) - await conn.close(); - } catch (err) { - assert.deepEqual(err, {}); - } - } - }); - - it('265.2.10 standalone connection with undefined private key and token', async () => { - let conn; - const ResObj = { - token : undefined, - privateKey : undefined - }; - try { - conn = await oracledb.getConnection({ - accessToken : ResObj, - connectString : dbConfig.connectString, - externalAuth : true - }); - } catch (err) { - assert.notDeepEqual(err, {}); - assert.match(err.message, /^NJS-084:/, 'regexp does not match'); - } finally { - try { - if (conn) - await conn.close(); - } catch (err) { - assert.deepEqual(err, {}); - } - } - }); - - it('265.2.11 standalone connection with random token and private key', async () => { - let conn; - const ResObj = { - token : 'Hello', - privateKey : 'World' - }; - try { - conn = await oracledb.getConnection({ - accessToken : ResObj, - connectString : dbConfig.connectString, - externalAuth : true - }); - } catch (err) { - assert.notDeepEqual(err, {}); - assert.match(err.message, /^ORA-25707:/, 'regexp does not match'); - } finally { - try { - if (conn) - await conn.close(); - } catch (err) { - assert.deepEqual(err, {}); - } - } - }); - - it('265.2.12 token and private key are private', async () => { - let conn; - const ResObj = { - token : dbConfig.accessToken.token, - privateKey : dbConfig.accessToken.privateKey, - }; - try { - conn = await oracledb.getConnection({ - accessToken : ResObj, - connectString : dbConfig.connectString, - externalAuth : true - }); - assert.deepEqual(conn.token, undefined); - assert.deepEqual(conn.privateKey, undefined); - } catch (err) { - assert.deepEqual(err, {}); - } finally { - try { - if (conn) - await conn.close(); - } catch (err) { - assert.deepEqual(err, {}); - } - } - }); - - it('265.2.13 standalone connection with expired token', async () => { - let conn; - const ResObj = { - token : dbConfig.expiredAccessToken.token, - privateKey : dbConfig.expiredAccessToken.privateKey - }; - try { - conn = await oracledb.getConnection({ - accessToken : ResObj, - connectString : dbConfig.connectString, - externalAuth : true - }); - } catch (err) { - assert.notDeepEqual(err, {}); - assert.match(err.message, /^ORA-25708:/, 'regexp does not match'); - } finally { - try { - if (conn) - await conn.close(); - } catch (err) { - assert.deepEqual(err, {}); - } - } - }); - }); -}); diff --git a/test/tokenBasedAuthenticationConfig.js b/test/tokenBasedAuthenticationConfig.js deleted file mode 100644 index d34378e3..00000000 --- a/test/tokenBasedAuthenticationConfig.js +++ /dev/null @@ -1,195 +0,0 @@ -/* Copyright (c) 2022, Oracle and/or its affiliates. */ - -/****************************************************************************** - * - * You may not use the identified files except in compliance with the Apache - * License, Version 2.0 (the "License.") - * - * You may obtain a copy of the License at - * http://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. - * - * NAME - * dbConfigTokenBasedAuth.js - * - * PREREQUISITES - * Install Oracle cloud infrastructure command line interface (OCI-CLI). - * The command line interface (CLI) is a tool that enables you to work with - * Oracle Cloud Infrastructure objects and services at a command line. - * https://docs.oracle.com/en-us/iaas/Content/API/Concepts/cliconcepts.htm - * Oracle Client libraries 19.14 (or later), or 21.5 (or later). - * node-oracledb 5.4 or later. - * - * DESCRIPTION - * This file conduct the configuration work for token based authentication - * tests. - * - * Environment variables used: - * NODE_ORACLEDB_USER, - * NODE_ORACLEDB_PASSWORD, - * NODE_ORACLEDB_CONNECTIONSTRING, - * NODE_ORACLEDB_ACCESS_TOKEN_LOC - * NODE_ORACLEDB_EXPIRED_ACCESS_TOKEN_LOC - * -******************************************************************************/ - -const fs = require('fs'); -const { execSync } = require('child_process'); - -var config = { - test : { - printDebugMsg : false - } -}; - -// Execute the OCI-CLI command to generate a token. -// This should create two files "token" and "oci_db_key.pem" -// Default file location is ~/.oci/db-token -execSync('oci iam db-token get', { encoding: 'utf-8' }); - -if (process.env.NODE_ORACLEDB_CONNECTIONSTRING) { - config.connectString = process.env.NODE_ORACLEDB_CONNECTIONSTRING; -} else { - throw new Error('Database Connect String is not Set! Try Set Environment Variable NODE_ORACLEDB_CONNECTIONSTRING.'); -} - -if (process.env.NODE_ORACLEDB_ACCESS_TOKEN_LOC) { - config.tokenLocation = process.env.NODE_ORACLEDB_ACCESS_TOKEN_LOC; -} else { - throw new Error('Db-token file location is not set! Try Set Environment Variable NODE_ORACLEDB_ACCESS_TOKEN_LOC'); -} - -if (process.env.NODE_ORACLEDB_USER) { - config.user = process.env.NODE_ORACLEDB_USER; -} else { - throw new Error('User is not Set! Try Set Environment Variable NODE_ORACLEDB_USER.'); -} - -if (process.env.NODE_ORACLEDB_PASSWORD) { - config.password = process.env.NODE_ORACLEDB_PASSWORD; -} else { - throw new Error('Password is not Set! Try Set Environment Variable NODE_ORACLEDB_PASSWORD.'); -} - -if (process.env.NODE_ORACLEDB_EXPIRED_ACCESS_TOKEN_LOC) { - config.expiredToken = process.env.NODE_ORACLEDB_EXPIRED_ACCESS_TOKEN_LOC; -} else { - throw new Error('Expired token location is not Set! Try Set Environment Variable NODE_ORACLEDB_EXPIRED_ACCESS_TOKEN_LOC.'); -} - -if (process.env.NODE_PRINT_DEBUG_MESSAGE) { - var printDebugMsg = process.env.NODE_PRINT_DEBUG_MESSAGE; - printDebugMsg = String(printDebugMsg); - printDebugMsg = printDebugMsg.toLowerCase(); - if (printDebugMsg == 'true') { - config.test.printDebugMsg = true; - } -} - -// User defined function for reading token and private key values generated by -// the OCI-CLI. -// NODE_ORACLEDB_DBTOKEN_LOC environment variable is used to provide directory -// path where token and private key files are stored. -function getToken(envName) { - const tokenPath = process.env[envName] + '/' + 'token'; - const privateKeyPath = process.env[envName] + '/' + 'oci_db_key.pem'; - - let token = ''; - let privateKey = ''; - try { - // Read token file - token = fs.readFileSync(tokenPath, 'utf8'); - // Read private key file - const privateKeyFileContents = fs.readFileSync(privateKeyPath, 'utf-8'); - privateKeyFileContents.split(/\r?\n/).forEach(line => { - if (line != '-----BEGIN PRIVATE KEY-----' && - line != '-----END PRIVATE KEY-----') - privateKey = privateKey.concat(line); - }); - } catch (err) { - console.error(err); - } - - const tokenBasedAuthData = { - token : token, - privateKey : privateKey - }; - return tokenBasedAuthData; -} - -config.accessToken = getToken('NODE_ORACLEDB_ACCESS_TOKEN_LOC'); -config.expiredAccessToken = getToken('NODE_ORACLEDB_EXPIRED_ACCESS_TOKEN_LOC'); - -// callback function with vlid data -// privateKey and token having valid data -function callbackValid() { - return config.accessToken; -} - -// callback function with invalid data -// privateKey and token having invalid data -function callbackInvalid1() { - const obj1 = config.accessToken; - const obj2 = { - token : obj1.privateKey, - privateKey : obj1.token - }; - return obj2; -} - -// callback function with invalid data -// dbtoken attribute is missing -function callbackInvalid2() { - const obj1 = config.accessToken; - const obj2 = { - privateKey : obj1.privateKey - }; - return obj2; -} - -// callback function with invalid data -// dbtoken is empty -function callbackInvalid3() { - const obj1 = config.accessToken; - const obj2 = { - token : '', - privateKey : obj1.privateKey - }; - return obj2; -} - -// callback function with invalid data -// dbtokenPrivateKeyattribute is missing -function callbackInvalid4() { - const obj1 = config.accessToken; - const obj2 = { - token: obj1.token - }; - return obj2; -} - -// callback function with invalid data -// dbtokenPrivateKey is empty -function callbackInvalid5() { - const obj1 = config.accessToken; - const obj2 = { - token : obj1.token, - privateKey : '' - }; - return obj2; -} - -config.callbackValid = callbackValid(); -config.callbackInvalid1 = callbackInvalid1(); -config.callbackInvalid2 = callbackInvalid2(); -config.callbackInvalid3 = callbackInvalid3(); -config.callbackInvalid4 = callbackInvalid4(); -config.callbackInvalid5 = callbackInvalid5(); - -module.exports = config;