--- /dev/null
+import logging
+import os
+
+import macaroonbakery.bakery as bakery
+import macaroonbakery.httpbakery as httpbakery
+import macaroonbakery.httpbakery.agent as agent
+from juju.errors import JujuAPIError
+from juju.model import Model
+
+import pytest
+
+from .. import base
+
+log = logging.getLogger(__name__)
+
+
+@base.bootstrapped
+@pytest.mark.asyncio
+@pytest.mark.xfail
+async def test_macaroon_auth(event_loop):
+ auth_info, username = agent_auth_info()
+ # Create a bakery client that can do agent authentication.
+ client = httpbakery.Client(
+ key=auth_info.key,
+ interaction_methods=[agent.AgentInteractor(auth_info)],
+ )
+
+ async with base.CleanModel(bakery_client=client) as m:
+ async with await m.get_controller() as c:
+ await c.grant_model(username, m.info.uuid, 'admin')
+ async with Model(
+ jujudata=NoAccountsJujuData(m._connector.jujudata),
+ bakery_client=client,
+ ):
+ pass
+
+
+@base.bootstrapped
+@pytest.mark.asyncio
+@pytest.mark.xfail
+async def test_macaroon_auth_with_bad_key(event_loop):
+ auth_info, username = agent_auth_info()
+ # Use a random key rather than the correct key.
+ auth_info = auth_info._replace(key=bakery.generate_key())
+ # Create a bakery client can do agent authentication.
+ client = httpbakery.Client(
+ key=auth_info.key,
+ interaction_methods=[agent.AgentInteractor(auth_info)],
+ )
+
+ async with base.CleanModel(bakery_client=client) as m:
+ async with await m.get_controller() as c:
+ await c.grant_model(username, m.info.uuid, 'admin')
+ try:
+ async with Model(
+ jujudata=NoAccountsJujuData(m._connector.jujudata),
+ bakery_client=client,
+ ):
+ pytest.fail('Should not be able to connect with invalid key')
+ except httpbakery.BakeryException:
+ # We're expecting this because we're using the
+ # wrong key.
+ pass
+
+
+@base.bootstrapped
+@pytest.mark.asyncio
+async def test_macaroon_auth_with_unauthorized_user(event_loop):
+ auth_info, username = agent_auth_info()
+ # Create a bakery client can do agent authentication.
+ client = httpbakery.Client(
+ key=auth_info.key,
+ interaction_methods=[agent.AgentInteractor(auth_info)],
+ )
+ async with base.CleanModel(bakery_client=client) as m:
+ # Note: no grant of rights to the agent user.
+ try:
+ async with Model(
+ jujudata=NoAccountsJujuData(m._connector.jujudata),
+ bakery_client=client,
+ ):
+ pytest.fail('Should not be able to connect without grant')
+ except (JujuAPIError, httpbakery.DischargeError):
+ # We're expecting this because we're using the
+ # wrong user name.
+ pass
+
+
+def agent_auth_info():
+ agent_data = os.environ.get('TEST_AGENTS')
+ if agent_data is None:
+ pytest.skip('skipping macaroon_auth because no TEST_AGENTS '
+ 'environment variable is set')
+ auth_info = agent.read_auth_info(agent_data)
+ if len(auth_info.agents) != 1:
+ raise Exception('TEST_AGENTS agent data requires exactly one agent')
+ return auth_info, auth_info.agents[0].username
+
+
+class NoAccountsJujuData:
+ def __init__(self, jujudata):
+ self.__jujudata = jujudata
+
+ def __getattr__(self, name):
+ return getattr(self.__jujudata, name)
+
+ def accounts(self):
+ return {}