Skip to content

Commit 3f93955

Browse files
Added centralized configuration provider support for file based
configurations.
1 parent c9a44d4 commit 3f93955

File tree

14 files changed

+448
-13
lines changed

14 files changed

+448
-13
lines changed

doc/src/api_manual/connect_params.rst

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,31 @@ ConnectParams Methods
8989

9090
The ``connection_id_prefix`` parameter was added.
9191

92+
.. method:: ConnectParams.set_from_config(config)
93+
94+
Sets the property values based on the specified configuration. This method
95+
is intended for use with Centralized Configuration Providers.
96+
97+
The ``config`` parameter is a dictionary which consists of the following
98+
optional keys: "connect_descriptor", "user", "password", and "pyo".
99+
100+
If the key "connect_descriptor" is specified, it is expected to be a
101+
string, which will be parsed and the properties found within it are stored
102+
in the ConnectParams instance.
103+
104+
If the keys "user" or "password" are specified, and the parameters do not
105+
already have a user or password set, these values will be stored;
106+
otherwise, they will be ignored. The key "user" is expected to be a
107+
string. The "key" password may be a string or it may be a dictionary which
108+
will be examined by a :ref:`registered password type handler
109+
<registerpasswordtype>` to determine the actual password.
110+
111+
If the key "pyo" is specified, it is expected to be a dictionary containing
112+
keys corresponding to property names. Any property names accepted by the
113+
ConnectParams class will be stored in the ConnectParams instance; all other
114+
values will be ignored.
115+
116+
.. versionadded:: 3.0.0
92117

93118
.. _connparamsattr:
94119

doc/src/api_manual/module.rst

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2501,6 +2501,31 @@ Oracledb Methods
25012501
The ``connection_id_prefix`` parameter was added.
25022502

25032503

2504+
.. function:: register_password_type(password_type, hook_function)
2505+
2506+
Registers a user hook function that will be called internally by
2507+
python-oracledb when a password is supplied as a dictionary containing the
2508+
given ``password_type`` as the key "type". The hook function is called for
2509+
passwords specified as the ``password``, ``newpassword`` and
2510+
``wallet_parameter`` parameters in calls to :meth:`oracledb.connect()`,
2511+
:meth:`oracledb.create_pool()`, :meth:`oracledb.connect_async()`, and
2512+
:meth:`oracledb.create_pool_async()`.
2513+
2514+
Your hook function is expected to accept the dictionary supplied by the
2515+
application and return the valid password.
2516+
2517+
Calling :meth:`~oracledb.register_password_type()` with the
2518+
``hook_function`` parameter set to None will result in a previously
2519+
registered user function being removed and the default behavior restored.
2520+
2521+
See :ref:`registerpasswordtype`.
2522+
2523+
.. note::
2524+
2525+
This method is an extension to the DB API definition.
2526+
2527+
.. versionadded:: 3.0.0
2528+
25042529
.. function:: register_protocol(protocol, hook_function)
25052530

25062531
Registers a user hook function that will be called internally by

doc/src/release_notes.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,21 @@ Common Changes
5050
#) Added support for :ref:`naming and caching connection pools
5151
<connpoolcache>` during creation, and retrieving them later from the
5252
python-oracledb pool cache with :meth:`oracledb.get_pool()`.
53+
#) Added Centralized Configuration Provider support for :ref:`file-based
54+
configurations <builtinconfigproviders>`.
55+
#) Added :meth:`oracledb.register_password_type()` to allow users to register
56+
a function that will be called when a password is supplied as a dictionary
57+
containing the key "type".
5358
#) Added attributes :attr:`DbObjectAttribute.precision`,
5459
:attr:`DbObjectAttribute.scale`, and :attr:`DbObjectAttribute.max_size` that
5560
provide additional metadata about
5661
:ref:`database object attributes <dbobjectattr>`.
5762
#) Fixed bug where some :ref:`DbObject <dbobjecttype>` attributes for database
5863
objects defined using ANSI names (including FLOAT and REAL) may have shown
5964
as integers.
65+
#) Error ``DPY-2056: registered handler for protocol "{protocol}" failed for
66+
arg "{arg}"`` is now raised when an exception occurs when calling the
67+
registered handler for a protocol.
6068
#) Internal change: improve handling of metadata.
6169

6270

doc/src/user_guide/connection_handling.rst

Lines changed: 159 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -619,7 +619,7 @@ returned::
619619
If :attr:`~ConnectParams.config_dir` is not specified, then the following
620620
error is returned::
621621

622-
DPY-4027: no configuration directory to search for tnsnames.ora
622+
DPY-4027: no configuration directory specified
623623

624624
When creating a standalone connection or connection pool the equivalent
625625
internal extraction is done automatically when a value is passed to the ``dsn``
@@ -911,6 +911,46 @@ modes, your connection code can be like:
911911
cp.parse_connect_string(dsn)
912912
connection = oracledb.connect(user="hr", password=userpwd, params=cp)
913913
914+
.. _registerpasswordtype:
915+
916+
Using oracledb.register_password_type()
917+
---------------------------------------
918+
919+
The :meth:`oracledb.register_password_type()` method registers a user hook
920+
function that will be called internally by python-oracledb prior to connection
921+
or pool creation by :meth:`oracledb.connect()`, :meth:`oracledb.create_pool()`,
922+
:meth:`oracledb.connect_async()`, or :meth:`oracledb.create_pool_async()`. If
923+
the ``password``, ``newpassword``, or ``wallet_password`` parameters to those
924+
methods are a dictionary containing the key "type", then the registered user
925+
hook function will be invoked. Your hook function is expected to accept the
926+
dictionary and return the actual password string.
927+
928+
Below is an example of a hook function that handles passwords of type base64.
929+
Note this specific hook function is already included and registered in
930+
python-oracledb:
931+
932+
.. code-block:: python
933+
934+
def mypasswordhook(args):
935+
return base64.b64decode(args["value"].encode()).decode()
936+
937+
oracledb.register_password_type("base64", mypasswordhook)
938+
939+
When :meth:`oracledb.connect()` is called as shown below, the sample hook is
940+
invoked internally. It decodes the base64-encoded string in the key "value" and
941+
returns the password which is then used by python-oracledb to establish a
942+
connection to the database:
943+
944+
.. code-block:: python
945+
946+
connection = oracledb.connect(user="scott",
947+
password=dict(type="base64", value="dGlnZXI="),
948+
dsn="localhost/orclpdb")
949+
950+
Calling :meth:`~oracledb.register_password_type()` with the
951+
``hook_function`` parameter set to None will result in a previously
952+
registered user function being removed and the default behavior restored.
953+
914954
.. _ldapconnections:
915955

916956
LDAP Directory Naming
@@ -3991,6 +4031,124 @@ typically the same directory where the ``wallet.zip`` file was extracted.
39914031
23ai. They contain important bug fixes for using multiple wallets in
39924032
the one process.
39934033

4034+
.. _builtinconfigproviders:
4035+
4036+
Connecting Using the File Centralized Configuration Provider
4037+
============================================================
4038+
4039+
The file configuration provider enables the storage and management of Oracle
4040+
Database connection information in a JSON file on your local file system. You
4041+
must add the connect descriptor in the file. Optionally, you can add the
4042+
database user name, password, and python-oracledb specific properties in the
4043+
file. The sub-objects that can be added to the JSON file are listed in the
4044+
table below.
4045+
4046+
.. list-table-with-summary:: Sub-objects for JSON File in File Configuration Provider
4047+
:header-rows: 1
4048+
:class: wy-table-responsive
4049+
:widths: 20 60
4050+
:name: _file_configuration_provider
4051+
:summary: The first column displays the name of the sub-object. The second column displays the description of the sub-object.
4052+
4053+
* - Sub-object
4054+
- Description
4055+
* - ``user``
4056+
- The database user name.
4057+
* - ``password``
4058+
- The password of the database user or a dictionary containing "type" and password type specific properties.
4059+
* - ``connect_descriptor``
4060+
- The :ref:`connect descriptor <conndescriptor>` value.
4061+
* - ``pyo``
4062+
- The python-oracledb specific attributes.
4063+
4064+
See `Oracle Net Service Administrator’s Guide <https://www.oracle.com/pls/
4065+
topic/lookup?ctx=dblatest&id=GUID-B43EA22D-5593-40B3-87FC-C70D6DAF780E>`__ for
4066+
more information on these sub-objects.
4067+
4068+
The following sample is an example of the configuration information defined in
4069+
a JSON file that is stored in a local file system::
4070+
4071+
{
4072+
"user": "scott",
4073+
"password": {
4074+
"type": "base64",
4075+
"value": "dGlnZXI="
4076+
},
4077+
"connect_descriptor": "dbhost.example.com:1522/orclpdb",
4078+
"pyo": {
4079+
"stmtcachesize": 30,
4080+
"min": 2,
4081+
"max": 10
4082+
}
4083+
}
4084+
4085+
.. _multipleconfigurations:
4086+
4087+
Multiple configurations can be defined in a JSON file by using keys as
4088+
shown in the example below::
4089+
4090+
{
4091+
"key1": {
4092+
"connect_descriptor": "localhost/orclpdb"
4093+
},
4094+
"key2": {
4095+
"connect_descriptor": "localhost/orclpdb",
4096+
"user": "scott",
4097+
"password": {
4098+
"type": "base64",
4099+
"value": "dGlnZXI="
4100+
}
4101+
}
4102+
}
4103+
4104+
You can use python-oracledb to access the configuration information stored in
4105+
a JSON file on your local file system. For this, you must specify the
4106+
connection string URL in the ``dsn`` parameter of :meth:`oracledb.connect()`,
4107+
:meth:`oracledb.create_pool()`, :meth:`oracledb.connect_async()`, or
4108+
:meth:`oracledb.create_pool_async()` in the following format::
4109+
4110+
config-file://<file-name>?key=<key>
4111+
4112+
The parameters of the connection string are detailed in the table below.
4113+
4114+
.. list-table-with-summary:: Connection String Parameters for File Configuration Provider
4115+
:header-rows: 1
4116+
:class: wy-table-responsive
4117+
:widths: 20 60
4118+
:name: _connection_string_for_file_configuration_provider
4119+
:summary: The first column displays the name of the connection string parameter. The second column displays the description of the connection string parameter.
4120+
4121+
* - Parameter
4122+
- Description
4123+
* - ``config-file``
4124+
- Indicates that the centralized configuration provider is a file in your local system.
4125+
* - <file-name>
4126+
- The file path (absolute or relative path) and name of the JSON file that contains the configuration information. For relative paths, python-oracledb will use the ``config_dir`` value to create an absolute path.
4127+
* - ``key``
4128+
- The connection key name used to identify a specific configuration. If this parameter is specified, the file is assumed to contain multiple configurations that are indexed by the key. If not specified, the file is assumed to contain a single configuration.
4129+
4130+
For example, to access the "myfile.json" JSON file and the "key2" configuration
4131+
from the :ref:`multiple configurations sample <multipleconfigurations>` shown
4132+
above:
4133+
4134+
.. code-block:: python
4135+
4136+
configfileurl = "config-file://myfile.json?key=key2"
4137+
oracledb.connect(dsn=configfileurl)
4138+
4139+
When :meth:`oracledb.connect()` is called, the built-in hook function to handle
4140+
connection strings prefixed with ``config-file://`` is
4141+
internally invoked. This hook function parses the connection string and
4142+
extracts the name of the JSON file containing the configuration information
4143+
and the connection key name that is stored in your local file system. Using
4144+
these details, this hook function accesses the configuration information
4145+
stored in the JSON file. The hook function sets the connection information
4146+
from the JSON file in its ``connect_params`` parameter. This ConnectParams
4147+
object is used by python-oracledb to establish a connection to Oracle
4148+
Database.
4149+
4150+
Note that when using the password type handler for "base64", a warning will be
4151+
generated: "base64 encoded passwords are insecure".
39944152

39954153
.. _connsharding:
39964154

src/oracledb/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,7 @@
287287

288288
from .utils import (
289289
enable_thin_mode as enable_thin_mode,
290+
register_password_type as register_password_type,
290291
register_protocol as register_protocol,
291292
)
292293

@@ -309,6 +310,7 @@
309310
future as __future__, # noqa: F401
310311
)
311312

313+
from . import config_providers
312314

313315
IntervalYM = collections.namedtuple("IntervalYM", ["years", "months"])
314316

@@ -327,6 +329,7 @@ class JsonId(bytes):
327329
del (
328330
aq, # noqa
329331
base_impl, # noqa
332+
config_providers, # noqa
330333
connect_params, # noqa
331334
connection, # noqa
332335
constants, # noqa

src/oracledb/base_impl.pxd

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -558,9 +558,10 @@ cdef class ConnectParamsImpl:
558558
cdef int _parse_connect_string(self, str connect_string) except -1
559559
cdef int _set_access_token(self, object val, int error_num) except -1
560560
cdef int _set_access_token_param(self, object val) except -1
561-
cdef int _set_new_password(self, str password) except -1
562-
cdef int _set_password(self, str password) except -1
563-
cdef int _set_wallet_password(self, str password) except -1
561+
cdef int _set_new_password(self, object password) except -1
562+
cdef int _set_password(self, object password) except -1
563+
cdef int _set_wallet_password(self, object password) except -1
564+
cdef str _transform_password(self, object password)
564565
cdef bytearray _xor_bytes(self, bytearray a, bytearray b)
565566

566567

src/oracledb/base_impl.pyx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ import socket
5656
import ssl
5757
import string
5858
import sys
59+
import warnings
5960

6061
cydatetime.import_datetime()
6162

@@ -96,6 +97,9 @@ cdef const char* ENCODING_UTF16 = "UTF-16BE"
9697
# protocols registered with the library
9798
REGISTERED_PROTOCOLS = {}
9899

100+
# password types registered with the library
101+
REGISTERED_PASSWORD_TYPES = {}
102+
99103
include "impl/base/types.pyx"
100104
include "impl/base/constants.pxi"
101105
include "impl/base/decoders.pyx"

0 commit comments

Comments
 (0)