diff options
Diffstat (limited to 'macaroonbakery/bakery.py')
-rw-r--r-- | macaroonbakery/bakery.py | 148 |
1 files changed, 68 insertions, 80 deletions
diff --git a/macaroonbakery/bakery.py b/macaroonbakery/bakery.py index a3fcf88..1e03191 100644 --- a/macaroonbakery/bakery.py +++ b/macaroonbakery/bakery.py @@ -1,16 +1,14 @@ # Copyright 2017 Canonical Ltd. # Licensed under the LGPLv3, see LICENCE file for details. - -import base64 from collections import namedtuple -import json import requests -from macaroonbakery import utils -import nacl.utils -from nacl.public import Box +from macaroonbakery import utils +from macaroonbakery.discharge import discharge +from macaroonbakery.checkers import checkers +from macaroonbakery.oven import Oven +from macaroonbakery.checker import Checker -from pymacaroons import Macaroon ERR_INTERACTION_REQUIRED = 'interaction required' ERR_DISCHARGE_REQUIRED = 'macaroon discharge required' @@ -18,11 +16,6 @@ TIME_OUT = 30 DEFAULT_PROTOCOL_VERSION = {'Bakery-Protocol-Version': '1'} MAX_DISCHARGE_RETRIES = 3 -BAKERY_V0 = 0 -BAKERY_V1 = 1 -BAKERY_V2 = 2 -BAKERY_V3 = 3 -LATEST_BAKERY_VERSION = BAKERY_V3 NONCE_LEN = 24 @@ -65,32 +58,6 @@ def discharge_all(macaroon, visit_page=None, jar=None, key=None): return discharges -def discharge(key, id, caveat=None, checker=None, locator=None): - '''Creates a macaroon to discharge a third party caveat. - - @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 - @param id bytes holding the id to give to the discharge macaroon. - If caveat is empty, then the id also holds the encrypted third party caveat - @param caveat bytes holding the encrypted third party caveat. - If this is None, id will be used - @param checker used to check the third party caveat, - and may also return further caveats to be added to - the discharge macaroon. object that will have a function - check_third_party_caveat taking a dict of third party caveat info - as parameter. - @param locator used to retrieve information on third parties - referred to by third party caveats returned by the checker. Object that - will have a third_party_info function taking a location as a string. - @return macaroon with third party caveat discharged. - ''' - if caveat is None: - caveat = id - cav_info = _decode_caveat(key, caveat) - return Macaroon(location='', key=cav_info['RootKey'], identifier=id) - - class _Client: def __init__(self, visit_page, jar): self._visit_page = visit_page @@ -111,7 +78,7 @@ class _Client: caveats = macaroon.third_party_caveats() for caveat in caveats: location = caveat.location - b_cav_id = caveat.caveat_id.encode('utf-8') + 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) @@ -154,39 +121,6 @@ class _Client: return _acquire_macaroon_from_wait(info.wait_url) -def _decode_caveat(key, caveat): - '''Attempts to decode caveat by decrypting the encrypted part using key. - - @param key a nacl key. - @param caveat bytes to be decoded. - @return a dict of third party caveat info. - ''' - data = base64.b64decode(caveat).decode('utf-8') - tpid = json.loads(data) - third_party_public_key = nacl.public.PublicKey( - base64.b64decode(tpid['ThirdPartyPublicKey'])) - if key.public_key != third_party_public_key: - return 'some error' - if tpid.get('FirstPartyPublicKey', None) is None: - return 'target service public key not specified' - # The encrypted string is base64 encoded in the JSON representation. - secret = base64.b64decode(tpid['Id']) - first_party_public_key = nacl.public.PublicKey( - base64.b64decode(tpid['FirstPartyPublicKey'])) - box = Box(key, - first_party_public_key) - c = box.decrypt(secret, base64.b64decode(tpid['Nonce'])) - record = json.loads(c.decode('utf-8')) - return { - 'Condition': record['Condition'], - 'FirstPartyPublicKey': first_party_public_key, - 'ThirdPartyKeyPair': key, - 'RootKey': base64.b64decode(record['RootKey']), - 'Caveat': caveat, - 'MacaroonId': id, - } - - def _extract_macaroon_from_response(response): '''Extract the macaroon from a direct successful discharge. @@ -226,12 +160,66 @@ def _extract_urls(response): return _Info(visit_url=visit_url, wait_url=wait_url) -class ThirdPartyInfo: - def __init__(self, version, public_key): - ''' - @param version holds latest the bakery protocol version supported - by the discharger. - @param public_key holds the public nacl key of the third party. +class Bakery(object): + '''Convenience class that contains both an Oven and a Checker. + ''' + def __init__(self, location=None, locator=None, ops_store=None, key=None, + identity_client=None, checker=None, root_key_store=None, + authorizer=None): + '''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. + If this is None, it will use checkers.Checker(None). + :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(). + 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 + 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, + no third party caveats may be added. + :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 + 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 + 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. ''' - self.version = version - self.public_key = public_key + + if checker is None: + checker = checkers.Checker() + root_keystore_for_ops = None + if root_key_store is not None: + def root_keystore_for_ops(ops): + return root_key_store + + oven = Oven(key=key, + location=location, + locator=locator, + namespace=checker.namespace(), + root_keystore_for_ops=root_keystore_for_ops, + ops_store=ops_store) + self._oven = oven + + self._checker = Checker(checker=checker, authorizer=authorizer, + identity_client=identity_client, + macaroon_opstore=oven) + + @property + def oven(self): + return self._oven + + @property + def checker(self): + return self._checker |