Skip to content

Commit 0be9ba3

Browse files
committed
Refine TLS certificate handling
* Improve API docs. * Document missing classes from the mTLS feature in preview. * Improve wording and references. * Made `neo4j.auth_management.RotatingClientCertificateProvider` an abstract class. This means it can no longer be instantiated directly. Please use the provided factory method `neo4j.auth_management.RotatingClientCertificateProvider.rotating` instead. * Analogously for the async APIs. * Fix missing type hint for parameter of `TrustCustomCAs.__init__`.
1 parent 4acb10f commit 0be9ba3

File tree

9 files changed

+119
-86
lines changed

9 files changed

+119
-86
lines changed

CHANGELOG.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,12 @@
33
See also https://github.com/neo4j/neo4j-python-driver/wiki for a full changelog.
44

55
## NEXT RELEASE
6-
- No breaking or major changes.
6+
- Made `neo4j.auth_management.RotatingClientCertificateProvider` and
7+
`...AsyncRotatingClientCertificateProvider` (in preview)
8+
abstract classes, meaning they can no longer be instantiated directly.
9+
Please use the provided factory methods instead:
10+
`neo4j.auth_management.RotatingClientCertificateProvider.rotating()` and
11+
`....AsyncRotatingClientCertificateProvider.rotating()` respectively.
712

813

914
## Version 5.23

docs/source/api.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -664,8 +664,17 @@ https://github.com/neo4j/neo4j-python-driver/wiki/preview-features
664664
.. versionadded:: 5.19
665665

666666
.. autoclass:: neo4j.auth_management.ClientCertificate
667+
:members:
667668

668669
.. autoclass:: neo4j.auth_management.ClientCertificateProvider
670+
:members:
671+
672+
.. autoclass:: neo4j.auth_management.ClientCertificateProviders
673+
:members:
674+
675+
.. autoclass:: neo4j.auth_management.RotatingClientCertificateProvider
676+
:show-inheritance:
677+
:members:
669678

670679

671680
.. _user-agent-ref:

docs/source/async_api.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,14 @@ https://github.com/neo4j/neo4j-python-driver/wiki/preview-features
457457
.. versionadded:: 5.19
458458

459459
.. autoclass:: neo4j.auth_management.AsyncClientCertificateProvider
460+
:members:
461+
462+
.. autoclass:: neo4j.auth_management.AsyncClientCertificateProviders
463+
:members:
464+
465+
.. autoclass:: neo4j.auth_management.AsyncRotatingClientCertificateProvider
466+
:show-inheritance:
467+
:members:
460468

461469

462470

src/neo4j/_async/auth_management.py

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
from __future__ import annotations
1818

19+
import abc
1920
import typing as t
2021
from logging import getLogger
2122

@@ -110,7 +111,8 @@ async def handle_security_exception(
110111

111112

112113
class AsyncAuthManagers:
113-
"""A collection of :class:`.AsyncAuthManager` factories.
114+
"""
115+
A collection of :class:`.AsyncAuthManager` factories.
114116
115117
.. versionadded:: 5.8
116118
@@ -124,7 +126,8 @@ class AsyncAuthManagers:
124126

125127
@staticmethod
126128
def static(auth: _TAuth) -> AsyncAuthManager:
127-
"""Create a static auth manager.
129+
"""
130+
Create a static auth manager.
128131
129132
The manager will always return the auth info provided at its creation.
130133
@@ -163,7 +166,8 @@ def static(auth: _TAuth) -> AsyncAuthManager:
163166
def basic(
164167
provider: t.Callable[[], t.Awaitable[_TAuth]]
165168
) -> AsyncAuthManager:
166-
"""Create an auth manager handling basic auth password rotation.
169+
"""
170+
Create an auth manager handling basic auth password rotation.
167171
168172
This factory wraps the provider function in an auth manager
169173
implementation that caches the provided auth info until the server
@@ -230,7 +234,8 @@ async def wrapped_provider() -> ExpiringAuth:
230234
def bearer(
231235
provider: t.Callable[[], t.Awaitable[ExpiringAuth]]
232236
) -> AsyncAuthManager:
233-
"""Create an auth manager for potentially expiring bearer auth tokens.
237+
"""
238+
Create an auth manager for potentially expiring bearer auth tokens.
234239
235240
This factory wraps the provider function in an auth manager
236241
implementation that caches the provided auth info until either the
@@ -310,10 +315,9 @@ async def get_certificate(self) -> t.Optional[ClientCertificate]:
310315
return cert
311316

312317

313-
@preview("Mutual TLS is a preview feature.")
314318
class AsyncRotatingClientCertificateProvider(AsyncClientCertificateProvider):
315319
"""
316-
Implementation of a certificate provider that can rotate certificates.
320+
Abstract base class for certificate providers that can rotate certificates.
317321
318322
The provider will make the driver use the initial certificate for all
319323
connections until the certificate is updated using the
@@ -367,10 +371,27 @@ class AsyncRotatingClientCertificateProvider(AsyncClientCertificateProvider):
367371
# rotated again
368372
...
369373
370-
:param initial_cert: The certificate to use initially.
371-
372374
.. versionadded:: 5.19
375+
376+
.. versionchanged:: 5.24
377+
378+
Turned this class into an abstract class to make the actual
379+
implementation internal. This entails removing the possibility to
380+
directly instantiate this class. Please use the factory method
381+
:meth:`AsyncClientCertificateProviders.rotating` instead.
373382
"""
383+
384+
@abc.abstractmethod
385+
async def update_certificate(self, cert: ClientCertificate) -> None:
386+
"""
387+
Update the certificate to use for new connections.
388+
"""
389+
390+
391+
@preview("Mutual TLS is a preview feature.")
392+
class _AsyncNeo4jRotatingClientCertificateProvider(
393+
AsyncRotatingClientCertificateProvider
394+
):
374395
def __init__(self, initial_cert: ClientCertificate) -> None:
375396
self._cert: t.Optional[ClientCertificate] = initial_cert
376397
self._lock = AsyncCooperativeLock()
@@ -381,15 +402,13 @@ async def get_certificate(self) -> t.Optional[ClientCertificate]:
381402
return cert
382403

383404
async def update_certificate(self, cert: ClientCertificate) -> None:
384-
"""
385-
Update the certificate to use for new connections.
386-
"""
387405
async with self._lock:
388406
self._cert = cert
389407

390408

391409
class AsyncClientCertificateProviders:
392-
"""A collection of :class:`.AsyncClientCertificateProvider` factories.
410+
"""
411+
A collection of :class:`.AsyncClientCertificateProvider` factories.
393412
394413
**This is a preview** (see :ref:`filter-warnings-ref`).
395414
It might be changed without following the deprecation policy.
@@ -419,4 +438,4 @@ def rotating(
419438
420439
.. seealso:: :class:`.AsyncRotatingClientCertificateProvider`
421440
"""
422-
return AsyncRotatingClientCertificateProvider(initial_cert)
441+
return _AsyncNeo4jRotatingClientCertificateProvider(initial_cert)

src/neo4j/_auth_management.py

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@
3737

3838
@dataclass
3939
class ExpiringAuth:
40-
"""Represents potentially expiring authentication information.
40+
"""
41+
Represents potentially expiring authentication information.
4142
4243
This class is used with :meth:`.AuthManagers.bearer` and
4344
:meth:`.AsyncAuthManagers.bearer`.
@@ -68,7 +69,8 @@ class ExpiringAuth:
6869
expires_at: t.Optional[float] = None
6970

7071
def expires_in(self, seconds: float) -> ExpiringAuth:
71-
"""Return a (flat) copy of this object with a new expiration time.
72+
"""
73+
Return a (flat) copy of this object with a new expiration time.
7274
7375
This is a convenience method for creating an :class:`.ExpiringAuth`
7476
for a relative expiration time ("expires in" instead of "expires at").
@@ -96,7 +98,8 @@ def expiring_auth_has_expired(auth: ExpiringAuth) -> bool:
9698

9799

98100
class AuthManager(metaclass=abc.ABCMeta):
99-
"""Baseclass for authentication information managers.
101+
"""
102+
Abstract base class for authentication information managers.
100103
101104
The driver provides some default implementations of this class in
102105
:class:`.AuthManagers` for convenience.
@@ -132,7 +135,8 @@ class AuthManager(metaclass=abc.ABCMeta):
132135

133136
@abc.abstractmethod
134137
def get_auth(self) -> _TAuth:
135-
"""Return the current authentication information.
138+
"""
139+
Return the current authentication information.
136140
137141
The driver will call this method very frequently. It is recommended
138142
to implement some form of caching to avoid unnecessary overhead.
@@ -151,7 +155,8 @@ def get_auth(self) -> _TAuth:
151155
def handle_security_exception(
152156
self, auth: _TAuth, error: Neo4jError
153157
) -> bool:
154-
"""Handle the server indicating authentication failure.
158+
"""
159+
Handle the server indicating authentication failure.
155160
156161
The driver will call this method when the server returns any
157162
`Neo.ClientError.Security.*` error. The error will then be processed
@@ -174,7 +179,8 @@ def handle_security_exception(
174179

175180

176181
class AsyncAuthManager(_Protocol, metaclass=abc.ABCMeta):
177-
"""Async version of :class:`.AuthManager`.
182+
"""
183+
Async version of :class:`.AuthManager`.
178184
179185
.. seealso:: :class:`.AuthManager`
180186
@@ -234,10 +240,14 @@ class ClientCertificate:
234240

235241
class ClientCertificateProvider(_Protocol, metaclass=abc.ABCMeta):
236242
"""
237-
Provides a client certificate to the driver for mutual TLS.
243+
Interface for providing a client certificate to the driver for mutual TLS.
244+
245+
This is an abstract base class (:class:`abc.ABC`) as well as a protocol
246+
(:class:`typing.Protocol`). Meaning you can either inherit from it or just
247+
implement all required method on a class to satisfy the type constraints.
238248
239249
The package provides some default implementations of this class in
240-
:class:`.AsyncClientCertificateProviders` for convenience.
250+
:class:`.ClientCertificateProviders` for convenience.
241251
242252
The driver will call :meth:`.get_certificate` to check if the client wants
243253
the driver to use as new certificate for mutual TLS.
@@ -286,12 +296,17 @@ class AsyncClientCertificateProvider(_Protocol, metaclass=abc.ABCMeta):
286296
"""
287297
Async version of :class:`.ClientCertificateProvider`.
288298
299+
The package provides some default implementations of this class in
300+
:class:`.AsyncClientCertificateProviders` for convenience.
301+
289302
**This is a preview** (see :ref:`filter-warnings-ref`).
290303
It might be changed without following the deprecation policy.
291304
See also
292305
https://github.com/neo4j/neo4j-python-driver/wiki/preview-features
293306
294-
.. seealso:: :class:`.ClientCertificateProvider`
307+
.. seealso::
308+
:class:`.ClientCertificateProvider`,
309+
:class:`.AsyncClientCertificateProviders`
295310
296311
.. versionadded:: 5.19
297312
"""

src/neo4j/_conf.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ class TrustCustomCAs(TrustStore):
102102
authority at the specified paths. This option is primarily intended for
103103
self-signed and custom certificates.
104104
105-
:param certificates (str): paths to the certificates to trust.
105+
:param certificates: paths to the certificates to trust.
106106
Those are not the certificates you expect to see from the server but
107107
the CA certificates you expect to be used to sign the server's
108108
certificate.
@@ -118,7 +118,7 @@ class TrustCustomCAs(TrustStore):
118118
)
119119
)
120120
"""
121-
def __init__(self, *certificates):
121+
def __init__(self, *certificates: str):
122122
self.certs = certificates
123123

124124

src/neo4j/_sync/auth_management.py

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
from __future__ import annotations
1818

19+
import abc
1920
import typing as t
2021
from logging import getLogger
2122

@@ -110,7 +111,8 @@ def handle_security_exception(
110111

111112

112113
class AuthManagers:
113-
"""A collection of :class:`.AuthManager` factories.
114+
"""
115+
A collection of :class:`.AuthManager` factories.
114116
115117
.. versionadded:: 5.8
116118
@@ -124,7 +126,8 @@ class AuthManagers:
124126

125127
@staticmethod
126128
def static(auth: _TAuth) -> AuthManager:
127-
"""Create a static auth manager.
129+
"""
130+
Create a static auth manager.
128131
129132
The manager will always return the auth info provided at its creation.
130133
@@ -163,7 +166,8 @@ def static(auth: _TAuth) -> AuthManager:
163166
def basic(
164167
provider: t.Callable[[], t.Union[_TAuth]]
165168
) -> AuthManager:
166-
"""Create an auth manager handling basic auth password rotation.
169+
"""
170+
Create an auth manager handling basic auth password rotation.
167171
168172
This factory wraps the provider function in an auth manager
169173
implementation that caches the provided auth info until the server
@@ -230,7 +234,8 @@ def wrapped_provider() -> ExpiringAuth:
230234
def bearer(
231235
provider: t.Callable[[], t.Union[ExpiringAuth]]
232236
) -> AuthManager:
233-
"""Create an auth manager for potentially expiring bearer auth tokens.
237+
"""
238+
Create an auth manager for potentially expiring bearer auth tokens.
234239
235240
This factory wraps the provider function in an auth manager
236241
implementation that caches the provided auth info until either the
@@ -310,10 +315,9 @@ def get_certificate(self) -> t.Optional[ClientCertificate]:
310315
return cert
311316

312317

313-
@preview("Mutual TLS is a preview feature.")
314318
class RotatingClientCertificateProvider(ClientCertificateProvider):
315319
"""
316-
Implementation of a certificate provider that can rotate certificates.
320+
Abstract base class for certificate providers that can rotate certificates.
317321
318322
The provider will make the driver use the initial certificate for all
319323
connections until the certificate is updated using the
@@ -367,10 +371,27 @@ class RotatingClientCertificateProvider(ClientCertificateProvider):
367371
# rotated again
368372
...
369373
370-
:param initial_cert: The certificate to use initially.
371-
372374
.. versionadded:: 5.19
375+
376+
.. versionchanged:: 5.24
377+
378+
Turned this class into an abstract class to make the actual
379+
implementation internal. This entails removing the possibility to
380+
directly instantiate this class. Please use the factory method
381+
:meth:`ClientCertificateProviders.rotating` instead.
373382
"""
383+
384+
@abc.abstractmethod
385+
def update_certificate(self, cert: ClientCertificate) -> None:
386+
"""
387+
Update the certificate to use for new connections.
388+
"""
389+
390+
391+
@preview("Mutual TLS is a preview feature.")
392+
class _Neo4jRotatingClientCertificateProvider(
393+
RotatingClientCertificateProvider
394+
):
374395
def __init__(self, initial_cert: ClientCertificate) -> None:
375396
self._cert: t.Optional[ClientCertificate] = initial_cert
376397
self._lock = CooperativeLock()
@@ -381,15 +402,13 @@ def get_certificate(self) -> t.Optional[ClientCertificate]:
381402
return cert
382403

383404
def update_certificate(self, cert: ClientCertificate) -> None:
384-
"""
385-
Update the certificate to use for new connections.
386-
"""
387405
with self._lock:
388406
self._cert = cert
389407

390408

391409
class ClientCertificateProviders:
392-
"""A collection of :class:`.ClientCertificateProvider` factories.
410+
"""
411+
A collection of :class:`.ClientCertificateProvider` factories.
393412
394413
**This is a preview** (see :ref:`filter-warnings-ref`).
395414
It might be changed without following the deprecation policy.
@@ -419,4 +438,4 @@ def rotating(
419438
420439
.. seealso:: :class:`.RotatingClientCertificateProvider`
421440
"""
422-
return RotatingClientCertificateProvider(initial_cert)
441+
return _Neo4jRotatingClientCertificateProvider(initial_cert)

0 commit comments

Comments
 (0)