From 5e198825d30c8833c5f7583fb0c40e8668de051a Mon Sep 17 00:00:00 2001 From: Caner Derici Date: Mon, 8 Jan 2024 15:30:47 -0700 Subject: [PATCH 1/2] User accounts.yaml as a backup to user provided credentials For both model and controller connections, accounts.yaml is required to have both username and password for connection to be established. This is not always true, for example, juju change-user-password actually removes the password from the accounts.yaml file. --- juju/client/connector.py | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/juju/client/connector.py b/juju/client/connector.py index e51f610cc..2ffcf0384 100644 --- a/juju/client/connector.py +++ b/juju/client/connector.py @@ -80,6 +80,15 @@ async def connect(self, **kwargs): else: if self._connection: await self._connection.close() + + account = kwargs.pop('account', {}) + # Prioritize the username and password that user provided + # If not enough, try to patch it with info from accounts.yaml + if 'username' not in kwargs and account.get('user'): + kwargs.update(username=account.get('user')) + if 'password' not in kwargs and account.get('password'): + kwargs.update(password=account.get('password')) + self._connection = await Connection.connect(**kwargs) if not self.controller_name: @@ -103,7 +112,7 @@ async def disconnect(self): await self._log_connection.close() self._log_connection = None - async def connect_controller(self, controller_name=None, specified_facades=None): + async def connect_controller(self, controller_name=None, specified_facades=None, **kwargs): """Connect to a controller by name. If the name is empty, it connect to the current controller. """ @@ -118,16 +127,15 @@ async def connect_controller(self, controller_name=None, specified_facades=None) proxy = proxy_from_config(controller.get('proxy-config', None)) - await self.connect( - endpoint=endpoints, - uuid=None, - username=accounts.get('user'), - password=accounts.get('password'), - cacert=controller.get('ca-cert'), - bakery_client=self.bakery_client_for_controller(controller_name), - specified_facades=specified_facades, - proxy=proxy, - ) + kwargs.update(endpoint=endpoints, + uuid=None, + account=accounts, + cacert=controller.get('ca-cert'), + bakery_client=self.bakery_client_for_controller(controller_name), + specified_facades=specified_facades, + proxy=proxy, + ) + await self.connect(**kwargs) self.controller_name = controller_name self.controller_uuid = controller["uuid"] @@ -176,8 +184,7 @@ async def connect_model(self, model_name=None, **kwargs): # JujuData. kwargs.update(endpoint=endpoints, uuid=model_uuid, - username=account.get('user'), - password=account.get('password'), + account=account, cacert=controller.get('ca-cert'), bakery_client=self.bakery_client_for_controller(controller_name), proxy=proxy) From 29901399b078be5b398e569e80f57a3991f4ac2f Mon Sep 17 00:00:00 2001 From: Caner Derici Date: Mon, 8 Jan 2024 15:33:12 -0700 Subject: [PATCH 2/2] Ask for credentials if some missing before connect Fixes #1001 --- juju/client/connector.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/juju/client/connector.py b/juju/client/connector.py index 2ffcf0384..a5998c6a3 100644 --- a/juju/client/connector.py +++ b/juju/client/connector.py @@ -89,6 +89,9 @@ async def connect(self, **kwargs): if 'password' not in kwargs and account.get('password'): kwargs.update(password=account.get('password')) + if not ({'username', 'password'}.issubset(kwargs)): + required = {'username', 'password'}.difference(kwargs) + raise ValueError(f'Some authentication parameters are required : {",".join(required)}') self._connection = await Connection.connect(**kwargs) if not self.controller_name: