summaryrefslogtreecommitdiff
path: root/macaroonbakery/httpbakery/agent
diff options
context:
space:
mode:
authorColin Watson <cjwatson@debian.org>2017-12-12 15:20:49 +0000
committerColin Watson <cjwatson@debian.org>2017-12-12 15:20:49 +0000
commit9e4403035a9953c99117083e6373ae3c441a76b5 (patch)
treed91b137df6767bfb8cb72de6b9fd21efb0c3dee4 /macaroonbakery/httpbakery/agent
parent949b7072cabce0daed6c94993ad44c8ea8648dbd (diff)
Import py-macaroon-bakery_1.1.0.orig.tar.gz
Diffstat (limited to 'macaroonbakery/httpbakery/agent')
-rw-r--r--macaroonbakery/httpbakery/agent/__init__.py8
-rw-r--r--macaroonbakery/httpbakery/agent/_agent.py (renamed from macaroonbakery/httpbakery/agent/agent.py)115
2 files changed, 63 insertions, 60 deletions
diff --git a/macaroonbakery/httpbakery/agent/__init__.py b/macaroonbakery/httpbakery/agent/__init__.py
index db252de..c0a7523 100644
--- a/macaroonbakery/httpbakery/agent/__init__.py
+++ b/macaroonbakery/httpbakery/agent/__init__.py
@@ -1,8 +1,9 @@
# Copyright 2017 Canonical Ltd.
# Licensed under the LGPLv3, see LICENCE file for details.
-from macaroonbakery.httpbakery.agent.agent import (
- load_agent_file,
+from ._agent import (
+ load_auth_info,
+ read_auth_info,
Agent,
AgentInteractor,
AgentFileFormatError,
@@ -13,5 +14,6 @@ __all__ = [
'AgentFileFormatError',
'AgentInteractor',
'AuthInfo',
- 'load_agent_file',
+ 'load_auth_info',
+ 'read_auth_info',
]
diff --git a/macaroonbakery/httpbakery/agent/agent.py b/macaroonbakery/httpbakery/agent/_agent.py
index ad56015..b717261 100644
--- a/macaroonbakery/httpbakery/agent/agent.py
+++ b/macaroonbakery/httpbakery/agent/_agent.py
@@ -1,20 +1,18 @@
# Copyright 2017 Canonical Ltd.
# Licensed under the LGPLv3, see LICENCE file for details.
-import base64
-from collections import namedtuple
+import copy
import json
+import logging
+from collections import namedtuple
-import nacl.public
-import nacl.encoding
-import nacl.exceptions
+import macaroonbakery.bakery as bakery
+import macaroonbakery.httpbakery as httpbakery
+import macaroonbakery._utils as utils
import requests.cookies
-import six
-from six.moves.urllib.parse import urlparse
+
from six.moves.urllib.parse import urljoin
-import macaroonbakery as bakery
-import macaroonbakery.utils as utils
-import macaroonbakery.httpbakery as httpbakery
+log = logging.getLogger(__name__)
class AgentFileFormatError(Exception):
@@ -24,40 +22,41 @@ class AgentFileFormatError(Exception):
pass
-def load_agent_file(filename, cookies=None):
- ''' Loads agent information from the specified file.
-
- The agent cookies are added to cookies, or a newly created cookie jar
- if cookies is not specified. The updated cookies is returned along
- with the private key associated with the agent. These can be passed
- directly as the cookies and key parameter to BakeryAuth.
+def load_auth_info(filename):
+ '''Loads agent authentication information from the specified file.
+ The returned information is suitable for passing as an argument
+ to the AgentInteractor constructor.
+ @param filename The name of the file to open (str)
+ @return AuthInfo The authentication information
+ @raises AgentFileFormatError when the file format is bad.
'''
-
with open(filename) as f:
- data = json.load(f)
+ return read_auth_info(f.read())
+
+
+def read_auth_info(agent_file_content):
+ '''Loads agent authentication information from the
+ specified content string, as read from an agents file.
+ The returned information is suitable for passing as an argument
+ to the AgentInteractor constructor.
+ @param agent_file_content The agent file content (str)
+ @return AuthInfo The authentication information
+ @raises AgentFileFormatError when the file format is bad.
+ '''
try:
- key = nacl.public.PrivateKey(
- data['key']['private'],
- nacl.encoding.Base64Encoder,
+ data = json.loads(agent_file_content)
+ return AuthInfo(
+ key=bakery.PrivateKey.deserialize(data['key']['private']),
+ agents=list(
+ Agent(url=a['url'], username=a['username'])
+ for a in data.get('agents', [])
+ ),
)
- if cookies is None:
- cookies = requests.cookies.RequestsCookieJar()
- for agent in data['agents']:
- u = urlparse(agent['url'])
- value = {'username': agent['username'],
- 'public_key': data['key']['public']}
- jv = json.dumps(value)
- if six.PY3:
- jv = jv.encode('utf-8')
- v = base64.b64encode(jv)
- if six.PY3:
- v = v.decode('utf-8')
- cookie = requests.cookies.create_cookie('agent-login', v,
- domain=u.netloc,
- path=u.path)
- cookies.set_cookie(cookie)
- return cookies, key
- except (KeyError, ValueError, nacl.exceptions.TypeError) as e:
+ except (
+ KeyError,
+ ValueError,
+ TypeError,
+ ) as e:
raise AgentFileFormatError('invalid agent file', e)
@@ -110,9 +109,10 @@ class AgentInteractor(httpbakery.Interactor, httpbakery.LegacyInteractor):
if not location.endswith('/'):
location += '/'
login_url = urljoin(location, p.login_url)
+ # TODO use client to make the request.
resp = requests.get(login_url, json={
'Username': agent.username,
- 'PublicKey': self._auth_info.key.encode().decode('utf-8'),
+ 'PublicKey': str(self._auth_info.key),
})
if resp.status_code != 200:
raise httpbakery.InteractionError(
@@ -144,30 +144,31 @@ class AgentInteractor(httpbakery.Interactor, httpbakery.LegacyInteractor):
the discharge macaroon using the client's private key
'''
agent = self._find_agent(location)
- pk_encoded = self._auth_info.key.public_key.encode().decode('utf-8')
- value = {
- 'username': agent.username,
- 'public_key': pk_encoded,
- }
- # TODO(rogpeppe) use client passed into interact method.
- client = httpbakery.Client(key=self._auth_info.key)
- client.cookies.set_cookie(utils.cookie(
+ # Shallow-copy the client so that we don't unexpectedly side-effect
+ # it by changing the key. Another possibility might be to
+ # set up agent authentication differently, in such a way that
+ # we're sure that client.key is the same as self._auth_info.key.
+ client = copy.copy(client)
+ client.key = self._auth_info.key
+ resp = client.request(
+ method='POST',
url=visit_url,
- name='agent-login',
- value=base64.urlsafe_b64encode(
- json.dumps(value).encode('utf-8')).decode('utf-8'),
- ))
- resp = requests.get(url=visit_url, cookies=client.cookies, auth=client.auth())
+ json={
+ 'username': agent.username,
+ 'public_key': str(self._auth_info.key.public_key),
+ },
+ )
if resp.status_code != 200:
raise httpbakery.InteractionError(
- 'cannot acquire agent macaroon: {}'.format(resp.status_code))
- if not resp.json().get('agent-login', False):
+ 'cannot acquire agent macaroon from {}: {} (response body: {!r})'.format(visit_url, resp.status_code, resp.text))
+ if not resp.json().get('agent_login', False):
raise httpbakery.InteractionError('agent login failed')
class Agent(namedtuple('Agent', 'url, username')):
''' Represents an agent that can be used for agent authentication.
- @param url holds the URL of the discharger that knows about the agent (string).
+ @param url(string) holds the URL of the discharger that knows about
+ the agent.
@param username holds the username agent (string).
'''