diff --git a/zmq/auth/asyncio.py b/zmq/auth/asyncio.py index 8b4915c12..6473b0bf4 100644 --- a/zmq/auth/asyncio.py +++ b/zmq/auth/asyncio.py @@ -17,7 +17,13 @@ class AsyncioAuthenticator(Authenticator): - """ZAP authentication for use in the asyncio IO loop""" + """ZAP authentication for use in the asyncio IO loop + + .. versionadded:: 27.2 + Multiple authenticators can now run in the same process + by specifying different socket addresses in ``start()``. + See :class:`zmq.auth.Authenticator` for details and examples. + """ __poller: Optional[Poller] __task: Any @@ -46,9 +52,20 @@ async def __handle_zap(self) -> None: msg = self.zap_socket.recv_multipart() await self.handle_zap_message(msg) - def start(self) -> None: - """Start ZAP authentication""" - super().start() + def start(self, socket_addr="inproc://zeromq.zap.01") -> None: + """Start ZAP authentication + + Parameters + ---------- + socket_addr : str, optional + The address to bind the ZAP socket to. + Default is "inproc://zeromq.zap.01" + + .. versionadded:: 27.2 + Support for custom socket addresses, enabling multiple + authenticators in the same process. + """ + super().start(socket_addr) self.__poller = Poller() self.__poller.register(self.zap_socket, zmq.POLLIN) self.__task = asyncio.ensure_future(self.__handle_zap()) diff --git a/zmq/auth/base.py b/zmq/auth/base.py index c862b60c1..40cc95387 100644 --- a/zmq/auth/base.py +++ b/zmq/auth/base.py @@ -35,6 +35,22 @@ class Authenticator: main thread, other authentication classes (such as :mod:`zmq.auth.thread`) are provided. + Multiple Authenticators + ----------------------- + + .. versionadded:: 27.2 + + Multiple authenticators can run in the same process by binding to different + ZAP socket addresses. This allows different authentication policies for + different sets of sockets within the same application:: + + # Create two authenticators with different policies + frontend_auth = zmq.auth.asyncio.AsyncioAuthenticator() + frontend_auth.start(socket_addr="inproc://zap-frontend") + + backend_auth = zmq.auth.asyncio.AsyncioAuthenticator() + backend_auth.start(socket_addr="inproc://zap-backend") + Note: - libzmq provides four levels of security: default NULL (which the Authenticator does @@ -77,11 +93,22 @@ def __init__( self.certs = {} self.log = log or logging.getLogger('zmq.auth') - def start(self) -> None: - """Create and bind the ZAP socket""" + def start(self, socket_addr="inproc://zeromq.zap.01") -> None: + """Create and bind the ZAP socket + + Parameters + ---------- + socket_addr : str, optional + The address to bind the ZAP socket to. + Default is "inproc://zeromq.zap.01" + + .. versionadded:: 27.2 + Support for custom socket addresses, enabling multiple + authenticators in the same process. + """ self.zap_socket = self.context.socket(zmq.REP, socket_class=zmq.Socket) self.zap_socket.linger = 1 - self.zap_socket.bind("inproc://zeromq.zap.01") + self.zap_socket.bind(socket_addr) self.log.debug("Starting") def stop(self) -> None: diff --git a/zmq/auth/thread.py b/zmq/auth/thread.py index a227c4bd5..dc6836df9 100644 --- a/zmq/auth/thread.py +++ b/zmq/auth/thread.py @@ -100,10 +100,21 @@ def __init__( self.pipe_endpoint = f"inproc://{id(self)}.inproc" self.thread = None # type: ignore - def start(self) -> None: - """Start the authentication thread""" + def start(self, socket_addr="inproc://zeromq.zap.01") -> None: + """Start the authentication thread + + Parameters + ---------- + socket_addr : str, optional + The address to bind the ZAP socket to. + Default is "inproc://zeromq.zap.01" + + .. versionadded:: 27.2 + Support for custom socket addresses, enabling multiple + authenticators in the same process. + """ # start the Authenticator - super().start() + super().start(socket_addr) # create a socket pair to communicate with auth thread. self.pipe = self.context.socket(zmq.PAIR, socket_class=zmq.Socket)