summaryrefslogtreecommitdiff
path: root/macaroonbakery/bakery.py
diff options
context:
space:
mode:
Diffstat (limited to 'macaroonbakery/bakery.py')
-rw-r--r--macaroonbakery/bakery.py175
1 files changed, 11 insertions, 164 deletions
diff --git a/macaroonbakery/bakery.py b/macaroonbakery/bakery.py
index 1e03191..5d9d56a 100644
--- a/macaroonbakery/bakery.py
+++ b/macaroonbakery/bakery.py
@@ -1,163 +1,10 @@
# Copyright 2017 Canonical Ltd.
# Licensed under the LGPLv3, see LICENCE file for details.
-from collections import namedtuple
-import requests
-from macaroonbakery import utils
-from macaroonbakery.discharge import discharge
from macaroonbakery.checkers import checkers
from macaroonbakery.oven import Oven
from macaroonbakery.checker import Checker
-
-
-ERR_INTERACTION_REQUIRED = 'interaction required'
-ERR_DISCHARGE_REQUIRED = 'macaroon discharge required'
-TIME_OUT = 30
-DEFAULT_PROTOCOL_VERSION = {'Bakery-Protocol-Version': '1'}
-MAX_DISCHARGE_RETRIES = 3
-
-NONCE_LEN = 24
-
-
-# A named tuple composed of the visit_url and wait_url coming from the error
-# response in discharge
-_Info = namedtuple('Info', 'visit_url wait_url')
-
-
-class DischargeException(Exception):
- '''A discharge error occurred.'''
-
-
-def discharge_all(macaroon, visit_page=None, jar=None, key=None):
- '''Gathers discharge macaroons for all the third party caveats in macaroon.
-
- All the discharge macaroons will be bound to the primary macaroon.
- The key parameter may optionally hold the key of the client, in which case
- it will be used to discharge any third party caveats with the special
- location "local". In this case, the caveat itself must be "true". This
- can be used by a server to ask a client to prove ownership of the
- private key.
- @param macaroon The macaroon to be discharged.
- @param visit_page function called when the discharge process requires
- further interaction.
- @param jar the storage for the cookies.
- @param key optional nacl key.
- @return An array with macaroon as the first element, followed by all the
- discharge macaroons.
- '''
- discharges = [macaroon]
- if visit_page is None:
- visit_page = utils.visit_page_with_browser
- if jar is None:
- jar = requests.cookies.RequestsCookieJar()
- client = _Client(visit_page, jar)
- try:
- client.discharge_caveats(macaroon, discharges, macaroon, key)
- except Exception as exc:
- raise DischargeException('unable to discharge the macaroon', exc)
- return discharges
-
-
-class _Client:
- def __init__(self, visit_page, jar):
- self._visit_page = visit_page
- self._jar = jar
-
- def discharge_caveats(self, macaroon, discharges,
- primary_macaroon, key):
- '''Gathers discharge macaroons for all the third party caveats.
-
- @param macaroon the macaroon to discharge.
- @param discharges the list of discharged macaroons.
- @param primary_macaroon used for the signature of the discharge
- macaroon.
- @param key nacl key holds the key to use to decrypt the third party
- caveat information and to encrypt any additional
- third party caveats returned by the caveat checker
- '''
- caveats = macaroon.third_party_caveats()
- for caveat in caveats:
- location = caveat.location
- b_cav_id = caveat.caveat_id
- if key is not None and location == 'local':
- # if tuple is only 2 element otherwise TODO add caveat
- dm = discharge(key, id=b_cav_id)
- else:
- dm = self._get_discharge(location, b_cav_id)
- dm = primary_macaroon.prepare_for_request(dm)
- discharges.append(dm)
- self.discharge_caveats(dm, discharges, primary_macaroon, key)
-
- def _get_discharge(self, third_party_location,
- third_party_caveat_condition):
- '''Get the discharge macaroon from the third party location.
-
- @param third_party_location where to get a discharge from.
- @param third_party_caveat_condition encoded 64 string associated to the
- discharged macaroon.
- @return a discharge macaroon.
- @raise DischargeError when an error occurs during the discharge
- process.
- '''
- headers = DEFAULT_PROTOCOL_VERSION
- payload = {'id': third_party_caveat_condition}
-
- response = requests.post(third_party_location + '/discharge',
- headers=headers,
- data=payload,
- # timeout=TIME_OUT, TODO: add a time out
- cookies=self._jar)
- status_code = response.status_code
- if status_code == 200:
- return _extract_macaroon_from_response(response)
- if (status_code == 401 and
- response.headers.get('WWW-Authenticate') == 'Macaroon'):
- error = response.json()
- if error.get('Code', '') != ERR_INTERACTION_REQUIRED:
- return DischargeException('unable to get code from discharge')
- info = _extract_urls(response)
- self._visit_page(info.visit_url)
- # Wait on the wait url and then get a macaroon if validated.
- return _acquire_macaroon_from_wait(info.wait_url)
-
-
-def _extract_macaroon_from_response(response):
- '''Extract the macaroon from a direct successful discharge.
-
- @param response from direct successful discharge.
- @return a macaroon object.
- @raises DischargeError if any error happens.
- '''
- response_json = response.json()
- return utils.deserialize(response_json['Macaroon'])
-
-
-def _acquire_macaroon_from_wait(wait_url):
- ''' Return the macaroon acquired from the wait endpoint.
-
- Note that will block until the user interaction has completed.
-
- @param wait_url the get url to call to get a macaroon.
- @return a macaroon object
- @raises DischargeError if any error happens.
- '''
- resp = requests.get(wait_url, headers=DEFAULT_PROTOCOL_VERSION)
- response_json = resp.json()
- macaroon = response_json['Macaroon']
- return utils.deserialize(macaroon)
-
-
-def _extract_urls(response):
- '''Return _Info of the visit and wait URL from response.
-
- @param response the response from the discharge endpoint.
- @return a _Info object of the visit and wait URL.
- @raises DischargeError for ant error during the process response.
- '''
- response_json = response.json()
- visit_url = response_json['Info']['VisitURL']
- wait_url = response_json['Info']['WaitURL']
- return _Info(visit_url=visit_url, wait_url=wait_url)
+from macaroonbakery.authorizer import ClosedAuthorizer
class Bakery(object):
@@ -165,36 +12,36 @@ class Bakery(object):
'''
def __init__(self, location=None, locator=None, ops_store=None, key=None,
identity_client=None, checker=None, root_key_store=None,
- authorizer=None):
+ authorizer=ClosedAuthorizer()):
'''Returns a new Bakery instance which combines an Oven with a
Checker for the convenience of callers that wish to use both
together.
- :param: checker holds the checker used to check first party caveats.
+ @param checker holds the checker used to check first party caveats.
If this is None, it will use checkers.Checker(None).
- :param: root_key_store holds the root key store to use.
+ @param root_key_store holds the root key store to use.
If you need to use a different root key store for different operations,
you'll need to pass a root_key_store_for_ops value to Oven directly.
- :param: root_key_store If this is None, it will use MemoryKeyStore().
+ @param root_key_store If this is None, it will use MemoryKeyStore().
Note that that is almost certain insufficient for production services
that are spread across multiple instances or that need
to persist keys across restarts.
- :param: locator is used to find out information on third parties when
+ @param locator is used to find out information on third parties when
adding third party caveats. If this is None, no non-local third
party caveats can be added.
- :param: key holds the private key of the oven. If this is None,
+ @param key holds the private key of the oven. If this is None,
no third party caveats may be added.
- :param: identity_client holds the identity implementation to use for
+ @param identity_client holds the identity implementation to use for
authentication. If this is None, no authentication will be possible.
- :param: authorizer is used to check whether an authenticated user is
+ @param authorizer is used to check whether an authenticated user is
allowed to perform operations. If it is None, it will use
a ClosedAuthorizer.
The identity parameter passed to authorizer.allow will
always have been obtained from a call to
IdentityClient.declared_identity.
- :param: ops_store used to persistently store the association of
+ @param ops_store used to persistently store the association of
multi-op entities with their associated operations
when oven.macaroon is called with multiple operations.
- :param: location holds the location to use when creating new macaroons.
+ @param location holds the location to use when creating new macaroons.
'''
if checker is None: