summaryrefslogtreecommitdiff
path: root/macaroonbakery/bakery.py
diff options
context:
space:
mode:
Diffstat (limited to 'macaroonbakery/bakery.py')
-rw-r--r--macaroonbakery/bakery.py148
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