Skip to content

Commit beed55f

Browse files
authored
Merge pull request #1003 from cderici/no-contr-model-access-needed-for-connection
#1003 #### Description This is a follow up PR, includes changes from #1002. If you're reviewing this and #1002 hasn't landed yet, you might see some additional changes that'll land with #1002. We need those changes to be able to test this change. `ControllerAPIInfoForModels` call in `update_endpoints` after a controller connection requires the `uuid` of the controller model, and if the user doesn't have at least read access, they won't be able to have that, so the `update_endpoint` call at the end of a regular connection fails. Fixes #998 #### QA Steps We'll need a 2.9 controller, so: ``` $ juju_29 bootstrap localhost issue998 ``` Now we have the admin user on this by default, but we don't wanna use that. Let's create a new user (that doesn't have superuser so it can't read the controller model): ```sh $ juju add-user caner $ juju register ``` Now let's make a password for this user. ```sh $ juju change-user-password # to be able to access the admin user later $ juju logout $ juju login -u caner -c issue998 $ juju change-user-password <your-password> This should work just fine, we're just making sure the user is ready to go. ``` Now let's spawn a pylibjuju repl to test this change with this user (that can't access the `controller` model): Wihtout this change, you'd see the error in #998. With this change the connection should work just fine: ```python # cd pylibjuju directory $ python -m asyncio >>> from juju import controller; c=controller.Controller() >>> await c.connect(username='caner', password=<your-password>) >>> exiting asyncio REPL... # Connection works without any issues. ``` #### Notes & Discussion JUJU-5268
2 parents df709e3 + c7e4f13 commit beed55f

3 files changed

Lines changed: 38 additions & 18 deletions

File tree

juju/client/connector.py

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,18 @@ async def connect(self, **kwargs):
8080
else:
8181
if self._connection:
8282
await self._connection.close()
83+
84+
account = kwargs.pop('account', {})
85+
# Prioritize the username and password that user provided
86+
# If not enough, try to patch it with info from accounts.yaml
87+
if 'username' not in kwargs and account.get('user'):
88+
kwargs.update(username=account.get('user'))
89+
if 'password' not in kwargs and account.get('password'):
90+
kwargs.update(password=account.get('password'))
91+
92+
if not ({'username', 'password'}.issubset(kwargs)):
93+
required = {'username', 'password'}.difference(kwargs)
94+
raise ValueError(f'Some authentication parameters are required : {",".join(required)}')
8395
self._connection = await Connection.connect(**kwargs)
8496

8597
if not self.controller_name:
@@ -103,7 +115,7 @@ async def disconnect(self):
103115
await self._log_connection.close()
104116
self._log_connection = None
105117

106-
async def connect_controller(self, controller_name=None, specified_facades=None):
118+
async def connect_controller(self, controller_name=None, specified_facades=None, **kwargs):
107119
"""Connect to a controller by name. If the name is empty, it
108120
connect to the current controller.
109121
"""
@@ -118,16 +130,15 @@ async def connect_controller(self, controller_name=None, specified_facades=None)
118130

119131
proxy = proxy_from_config(controller.get('proxy-config', None))
120132

121-
await self.connect(
122-
endpoint=endpoints,
123-
uuid=None,
124-
username=accounts.get('user'),
125-
password=accounts.get('password'),
126-
cacert=controller.get('ca-cert'),
127-
bakery_client=self.bakery_client_for_controller(controller_name),
128-
specified_facades=specified_facades,
129-
proxy=proxy,
130-
)
133+
kwargs.update(endpoint=endpoints,
134+
uuid=None,
135+
account=accounts,
136+
cacert=controller.get('ca-cert'),
137+
bakery_client=self.bakery_client_for_controller(controller_name),
138+
specified_facades=specified_facades,
139+
proxy=proxy,
140+
)
141+
await self.connect(**kwargs)
131142
self.controller_name = controller_name
132143
self.controller_uuid = controller["uuid"]
133144

@@ -176,8 +187,7 @@ async def connect_model(self, model_name=None, **kwargs):
176187
# JujuData.
177188
kwargs.update(endpoint=endpoints,
178189
uuid=model_uuid,
179-
username=account.get('user'),
180-
password=account.get('password'),
190+
account=account,
181191
cacert=controller.get('ca-cert'),
182192
bakery_client=self.bakery_client_for_controller(controller_name),
183193
proxy=proxy)

juju/controller.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -139,11 +139,15 @@ async def connect(self, *args, **kwargs):
139139
await self.update_endpoints()
140140

141141
async def update_endpoints(self):
142-
info = await self.info()
143-
self._connector._connection.endpoints = [
144-
(e, info.results[0].cacert)
145-
for e in info.results[0].addresses
146-
]
142+
try:
143+
info = await self.info()
144+
self._connector._connection.endpoints = [
145+
(e, info.results[0].cacert)
146+
for e in info.results[0].addresses
147+
]
148+
except errors.JujuPermissionError:
149+
log.warning("This user doesn't have at least read access to the controller model, so endpoints are not updated after connection.")
150+
pass
147151

148152
async def connect_current(self):
149153
"""
@@ -281,6 +285,8 @@ async def info(self):
281285
"""
282286
log.debug('Getting information')
283287
uuids = await self.model_uuids()
288+
if 'controller' not in uuids:
289+
raise errors.JujuPermissionError('Requires access to controller model.')
284290
controller_facade = client.ControllerFacade.from_connection(self.connection())
285291
params = [client.Entity(tag.model(uuids["controller"]))]
286292
return await controller_facade.ControllerAPIInfoForModels(entities=params)

juju/errors.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,10 @@ class JujuUnitError(JujuError):
8282
pass
8383

8484

85+
class JujuPermissionError(JujuError):
86+
pass
87+
88+
8589
class JujuBackupError(JujuError):
8690
pass
8791

0 commit comments

Comments
 (0)