[Python-modules-commits] [py-macaroon-bakery] 01/05: Import py-macaroon-bakery_0.0.5.orig.tar.gz

Colin Watson cjwatson at moszumanska.debian.org
Mon Nov 6 10:44:07 UTC 2017


This is an automated email from the git hooks/post-receive script.

cjwatson pushed a commit to branch master
in repository py-macaroon-bakery.

commit 37d61d0415f6cc96a7a9abe057e1ae0f89fd977e
Author: Colin Watson <cjwatson at debian.org>
Date:   Mon Nov 6 10:04:48 2017 +0000

    Import py-macaroon-bakery_0.0.5.orig.tar.gz
---
 docs/conf.py                                |   2 +-
 macaroonbakery/__init__.py                  |  97 ++++--
 macaroonbakery/authorizer.py                |   4 +-
 macaroonbakery/bakery.py                    | 175 +----------
 macaroonbakery/checker.py                   |  42 +--
 macaroonbakery/checkers/__init__.py         |  54 +++-
 macaroonbakery/checkers/time.py             |  53 +++-
 macaroonbakery/checkers/utils.py            |   2 +-
 macaroonbakery/codec.py                     |  50 ++--
 macaroonbakery/discharge.py                 |  55 ++--
 macaroonbakery/httpbakery/__init__.py       |  48 ++-
 macaroonbakery/httpbakery/agent.py          |  52 ----
 macaroonbakery/httpbakery/agent/__init__.py |  17 ++
 macaroonbakery/httpbakery/agent/agent.py    | 180 +++++++++++
 macaroonbakery/httpbakery/browser.py        |  86 ++++++
 macaroonbakery/httpbakery/client.py         | 442 +++++++++++++++++++--------
 macaroonbakery/httpbakery/discharge.py      |  33 +++
 macaroonbakery/httpbakery/error.py          | 151 +++++++++-
 macaroonbakery/httpbakery/interactor.py     |  73 +++++
 macaroonbakery/httpbakery/keyring.py        |  26 +-
 macaroonbakery/identity.py                  |  10 +-
 macaroonbakery/macaroon.py                  |  81 ++---
 macaroonbakery/oven.py                      |  44 ++-
 macaroonbakery/tests/common.py              |  30 +-
 macaroonbakery/tests/test_agent.py          | 331 ++++++++++++++++++++-
 macaroonbakery/tests/test_authorizer.py     |  72 ++---
 macaroonbakery/tests/test_bakery.py         |  88 +++++-
 macaroonbakery/tests/test_checker.py        | 443 +++++++++++++---------------
 macaroonbakery/tests/test_client.py         | 388 ++++++++++++++++++++++++
 macaroonbakery/tests/test_codec.py          | 115 ++++----
 macaroonbakery/tests/test_discharge.py      | 274 +++++++++--------
 macaroonbakery/tests/test_discharge_all.py  |  71 +++--
 macaroonbakery/tests/test_keyring.py        |  34 +--
 macaroonbakery/tests/test_macaroon.py       |  69 +++--
 macaroonbakery/tests/test_namespace.py      |   2 +
 macaroonbakery/tests/test_oven.py           | 127 ++++----
 macaroonbakery/tests/test_store.py          |   4 +-
 macaroonbakery/tests/test_time.py           | 129 ++++++++
 macaroonbakery/third_party.py               |  38 +--
 macaroonbakery/utils.py                     | 106 +++++--
 macaroonbakery/versions.py                  |  10 +-
 setup.py                                    |   6 +-
 tox.ini                                     |   7 +-
 43 files changed, 2904 insertions(+), 1217 deletions(-)

diff --git a/docs/conf.py b/docs/conf.py
index 75593c5..df296f1 100755
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -31,7 +31,7 @@ project_root = os.path.dirname(cwd)
 # version is used.
 sys.path.insert(0, project_root)
 
-import macaroonbakery
+import macaroonbakery as bakery
 
 # -- General configuration ---------------------------------------------
 
diff --git a/macaroonbakery/__init__.py b/macaroonbakery/__init__.py
index dd2e6df..6397a19 100644
--- a/macaroonbakery/__init__.py
+++ b/macaroonbakery/__init__.py
@@ -1,63 +1,96 @@
 # Copyright 2017 Canonical Ltd.
 # Licensed under the LGPLv3, see LICENCE file for details.
 
-from __future__ import unicode_literals
-try:
-    import urllib3.contrib.pyopenssl
-except ImportError:
-    pass
-else:
-    urllib3.contrib.pyopenssl.inject_into_urllib3()
-
 from macaroonbakery.versions import (
-    LATEST_BAKERY_VERSION, BAKERY_V3, BAKERY_V2, BAKERY_V1, BAKERY_V0
+    VERSION_0,
+    VERSION_1,
+    VERSION_2,
+    VERSION_3,
+    LATEST_VERSION,
 )
 from macaroonbakery.authorizer import (
-    ClosedAuthorizer, EVERYONE, AuthorizerFunc, Authorizer, ACLAuthorizer
+    ACLAuthorizer,
+    Authorizer,
+    AuthorizerFunc,
+    ClosedAuthorizer,
+    EVERYONE,
 )
 from macaroonbakery.codec import (
-    encode_caveat, decode_caveat, encode_uvarint
+    decode_caveat,
+    encode_caveat,
+    encode_uvarint,
 )
 from macaroonbakery.checker import (
-    Op, LOGIN_OP, AuthInfo, AuthChecker, Checker
+    AuthChecker,
+    AuthInfo,
+    Checker,
+    LOGIN_OP,
+    Op,
 )
 from macaroonbakery.error import (
-    ThirdPartyCaveatCheckFailed, CaveatNotRecognizedError, AuthInitError,
-    PermissionDenied, IdentityError, DischargeRequiredError, VerificationError,
-    ThirdPartyInfoNotFound
+    AuthInitError,
+    CaveatNotRecognizedError,
+    DischargeRequiredError,
+    IdentityError,
+    PermissionDenied,
+    ThirdPartyCaveatCheckFailed,
+    ThirdPartyInfoNotFound,
+    VerificationError,
 )
 from macaroonbakery.identity import (
-    Identity, ACLIdentity, SimpleIdentity, IdentityClient, NoIdentities
+    ACLIdentity,
+    Identity,
+    IdentityClient,
+    NoIdentities,
+    SimpleIdentity,
+)
+from macaroonbakery.keys import (
+    generate_key,
+    PrivateKey,
+    PublicKey,
+)
+from macaroonbakery.store import (
+    MemoryOpsStore,
+    MemoryKeyStore,
 )
-from macaroonbakery.keys import generate_key, PrivateKey, PublicKey
-from macaroonbakery.store import MemoryOpsStore, MemoryKeyStore
 from macaroonbakery.third_party import (
-    ThirdPartyCaveatInfo, ThirdPartyInfo, legacy_namespace
+    ThirdPartyCaveatInfo,
+    ThirdPartyInfo,
+    legacy_namespace,
 )
 from macaroonbakery.macaroon import (
-    Macaroon, MacaroonJSONDecoder, MacaroonJSONEncoder, ThirdPartyStore,
-    ThirdPartyLocator, macaroon_version
+    Macaroon,
+    MacaroonJSONDecoder,
+    MacaroonJSONEncoder,
+    ThirdPartyLocator,
+    ThirdPartyStore,
+    macaroon_version,
 )
 from macaroonbakery.discharge import (
-    discharge_all, discharge, local_third_party_caveat, ThirdPartyCaveatChecker
+    ThirdPartyCaveatChecker,
+    discharge,
+    discharge_all,
+    local_third_party_caveat,
+)
+from macaroonbakery.oven import (
+    Oven,
+    canonical_ops,
 )
-from macaroonbakery.oven import Oven, canonical_ops
 from macaroonbakery.bakery import Bakery
-
+from macaroonbakery.utils import b64decode
 
 __all__ = [
-    'ACLIdentity',
     'ACLAuthorizer',
+    'ACLIdentity',
     'AuthChecker',
     'AuthInfo',
     'AuthInitError',
     'Authorizer',
     'AuthorizerFunc',
-    'Bakery',
-    'BAKERY_V0',
-    'BAKERY_V1',
-    'BAKERY_V2',
-    'BAKERY_V3',
+    'VERSION_0',
+    'VERSION_1',
+    'VERSION_2',
+    'VERSION_3',
     'Bakery',
     'CaveatNotRecognizedError',
     'Checker',
@@ -67,7 +100,7 @@ __all__ = [
     'Identity',
     'IdentityClient',
     'IdentityError',
-    'LATEST_BAKERY_VERSION',
+    'LATEST_VERSION',
     'LOGIN_OP',
     'Macaroon',
     'MacaroonJSONDecoder',
@@ -80,7 +113,6 @@ __all__ = [
     'PermissionDenied',
     'PrivateKey',
     'PublicKey',
-    'NoIdentities',
     'SimpleIdentity',
     'ThirdPartyCaveatCheckFailed',
     'ThirdPartyCaveatChecker',
@@ -91,6 +123,7 @@ __all__ = [
     'ThirdPartyStore',
     'VERSION',
     'VerificationError',
+    'b64decode',
     'canonical_ops',
     'decode_caveat',
     'discharge',
diff --git a/macaroonbakery/authorizer.py b/macaroonbakery/authorizer.py
index b7128c0..ae84104 100644
--- a/macaroonbakery/authorizer.py
+++ b/macaroonbakery/authorizer.py
@@ -2,7 +2,7 @@
 # Licensed under the LGPLv3, see LICENCE file for details.
 import abc
 
-import macaroonbakery
+import macaroonbakery as bakery
 
 
 # EVERYONE is recognized by ACLAuthorizer as the name of a
@@ -90,7 +90,7 @@ class ACLAuthorizer(Authorizer):
             # Anyone is allowed to do nothing.
             return [], []
         allowed = [False] * len(ops)
-        has_allow = isinstance(identity, macaroonbakery.ACLIdentity)
+        has_allow = isinstance(identity, bakery.ACLIdentity)
         for i, op in enumerate(ops):
             acl = self._get_acl(ctx, op)
             if has_allow:
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:
diff --git a/macaroonbakery/checker.py b/macaroonbakery/checker.py
index b73c92f..568fd7c 100644
--- a/macaroonbakery/checker.py
+++ b/macaroonbakery/checker.py
@@ -6,7 +6,7 @@ from threading import Lock
 
 import pyrfc3339
 
-import macaroonbakery
+import macaroonbakery as bakery
 import macaroonbakery.checkers as checkers
 
 
@@ -38,7 +38,7 @@ class Checker(object):
     See the Oven type (TODO) for one way of doing that.
     '''
     def __init__(self, checker=checkers.Checker(),
-                 authorizer=macaroonbakery.ClosedAuthorizer(),
+                 authorizer=bakery.ClosedAuthorizer(),
                  identity_client=None,
                  macaroon_opstore=None):
         '''
@@ -57,7 +57,7 @@ class Checker(object):
         self._first_party_caveat_checker = checker
         self._authorizer = authorizer
         if identity_client is None:
-            identity_client = macaroonbakery.NoIdentities()
+            identity_client = bakery.NoIdentities()
         self._identity_client = identity_client
         self._macaroon_opstore = macaroon_opstore
 
@@ -106,16 +106,18 @@ class AuthChecker(object):
                 self._init_once(ctx)
                 self._executed = True
         if self._init_errors is not None and len(self._init_errors) > 0:
-            raise macaroonbakery.AuthInitError(self._init_errors[0])
+            raise bakery.AuthInitError(self._init_errors[0])
 
     def _init_once(self, ctx):
         self._auth_indexes = {}
-        self._conditions = [None]*len(self._macaroons)
+        self._conditions = [None] * len(self._macaroons)
         for i, ms in enumerate(self._macaroons):
             try:
                 ops, conditions = self.parent._macaroon_opstore.macaroon_ops(
                     ms)
-            except macaroonbakery.VerificationError as exc:
+            except bakery.VerificationError:
+                raise
+            except Exception as exc:
                 self._init_errors.append(exc.args[0])
                 continue
 
@@ -155,7 +157,7 @@ class AuthChecker(object):
             try:
                 identity = self.parent._identity_client.declared_identity(
                     ctx, declared)
-            except macaroonbakery.IdentityError as exc:
+            except bakery.IdentityError as exc:
                 self._init_errors.append(
                     'cannot decode declared identity: {}'.format(exc.args[0]))
                 continue
@@ -169,7 +171,7 @@ class AuthChecker(object):
             try:
                 identity, cavs = self.parent.\
                     _identity_client.identity_from_context(ctx)
-            except macaroonbakery.IdentityError:
+            except bakery.IdentityError:
                 self._init_errors.append('could not determine identity')
             if cavs is None:
                 cavs = []
@@ -195,8 +197,8 @@ class AuthChecker(object):
         If an operation was not allowed, an exception will be raised which may
         be DischargeRequiredError holding the operations that remain to
         be authorized in order to allow authorization to proceed.
-        :param: ctx AuthContext
-        :param: ops an array of Op
+        @param ctx AuthContext
+        @param ops an array of Op
         :return: an AuthInfo object.
         '''
         auth_info, _ = self.allow_any(ctx, ops)
@@ -217,8 +219,8 @@ class AuthChecker(object):
 
         The LOGIN_OP operation is treated specially - it is always required if
         present in ops.
-        :param: ctx AuthContext
-        :param: ops an array of Op
+        @param ctx AuthContext
+        @param ops an array of Op
         :return: an AuthInfo object and the auth used as an array of int.
         '''
         authed, used = self._allow_any(ctx, ops)
@@ -233,8 +235,8 @@ class AuthChecker(object):
 
     def _allow_any(self, ctx, ops):
         self._init(ctx)
-        used = [False]*len(self._macaroons)
-        authed = [False]*len(ops)
+        used = [False] * len(self._macaroons)
+        authed = [False] * len(ops)
         num_authed = 0
         errors = []
         for i, op in enumerate(ops):
@@ -269,7 +271,7 @@ class AuthChecker(object):
             return authed, used
         # There are some unauthorized operations.
         need = []
-        need_index = [0]*(len(ops)-num_authed)
+        need_index = [0] * (len(ops) - num_authed)
         for i, ok in enumerate(authed):
             if not ok:
                 need_index[len(need)] = i
@@ -290,7 +292,7 @@ class AuthChecker(object):
             # no caveats to be discharged.
             return authed, used
         if self._identity is None and len(self._identity_caveats) > 0:
-            raise macaroonbakery.DischargeRequiredError(
+            raise bakery.DischargeRequiredError(
                 msg='authentication required',
                 ops=[LOGIN_OP],
                 cavs=self._identity_caveats)
@@ -301,8 +303,8 @@ class AuthChecker(object):
             err = ''
             if len(all_errors) > 0:
                 err = all_errors[0]
-            raise macaroonbakery.PermissionDenied(err)
-        raise macaroonbakery.DischargeRequiredError(
+            raise bakery.PermissionDenied(err)
+        raise bakery.DischargeRequiredError(
             msg='some operations have extra caveats', ops=ops, cavs=caveats)
 
     def allow_capability(self, ctx, ops):
@@ -352,11 +354,11 @@ class AuthChecker(object):
 class AuthInfo(namedtuple('AuthInfo', 'identity macaroons')):
     '''AuthInfo information about an authorization decision.
 
-    :param: identity: holds information on the authenticated user as
+    @param identity: holds information on the authenticated user as
     returned identity_client. It may be None after a successful
     authorization if LOGIN_OP access was not required.
 
-    :param: macaroons: holds all the macaroons that were used for the
+    @param macaroons: holds all the macaroons that were used for the
     authorization. Macaroons that were invalid or unnecessary are
     not included.
     '''
diff --git a/macaroonbakery/checkers/__init__.py b/macaroonbakery/checkers/__init__.py
index 9f0b022..25c6b7d 100644
--- a/macaroonbakery/checkers/__init__.py
+++ b/macaroonbakery/checkers/__init__.py
@@ -1,24 +1,53 @@
 # Copyright 2017 Canonical Ltd.
 # Licensed under the LGPLv3, see LICENCE file for details.
 from macaroonbakery.checkers.conditions import (
-    STD_NAMESPACE, COND_DECLARED, COND_TIME_BEFORE, COND_ERROR, COND_ALLOW,
-    COND_DENY, COND_NEED_DECLARED
+    STD_NAMESPACE,
+    COND_DECLARED,
+    COND_TIME_BEFORE,
+    COND_ERROR,
+    COND_ALLOW,
+    COND_DENY,
+    COND_NEED_DECLARED,
 )
 from macaroonbakery.checkers.caveat import (
-    allow_caveat, deny_caveat, declared_caveat, parse_caveat,
-    time_before_caveat, Caveat
+    allow_caveat,
+    deny_caveat,
+    declared_caveat,
+    parse_caveat,
+    time_before_caveat,
+    Caveat,
 )
 from macaroonbakery.checkers.declared import (
-    context_with_declared, infer_declared, infer_declared_from_conditions,
-    need_declared_caveat
+    context_with_declared,
+    infer_declared,
+    infer_declared_from_conditions,
+    need_declared_caveat,
+)
+from macaroonbakery.checkers.operation import (
+    context_with_operations,
+)
+from macaroonbakery.checkers.namespace import (
+    Namespace,
+    deserialize_namespace
+)
+from macaroonbakery.checkers.time import (
+    context_with_clock,
+    expiry_time,
+    macaroons_expiry_time,
 )
-from macaroonbakery.checkers.operation import context_with_operations
-from macaroonbakery.checkers.namespace import Namespace, deserialize_namespace
-from macaroonbakery.checkers.time import context_with_clock
 from macaroonbakery.checkers.checkers import (
-    Checker, CheckerInfo, RegisterError
+    Checker,
+    CheckerInfo,
+    RegisterError,
+)
+from macaroonbakery.checkers.auth_context import (
+    AuthContext,
+    ContextKey,
+)
+
+from macaroonbakery.checkers.utils import (
+    condition_with_prefix,
 )
-from macaroonbakery.checkers.auth_context import AuthContext, ContextKey
 
 __all__ = [
     'AuthContext',
@@ -36,14 +65,17 @@ __all__ = [
     'Namespace',
     'RegisterError',
     'allow_caveat',
+    'condition_with_prefix',
     'context_with_declared',
     'context_with_operations',
     'context_with_clock',
     'declared_caveat',
     'deny_caveat',
     'deserialize_namespace',
+    'expiry_time',
     'infer_declared',
     'infer_declared_from_conditions',
+    'macaroons_expiry_time',
     'need_declared_caveat',
     'parse_caveat',
     'time_before_caveat',
diff --git a/macaroonbakery/checkers/time.py b/macaroonbakery/checkers/time.py
index 052d983..0b52131 100644
--- a/macaroonbakery/checkers/time.py
+++ b/macaroonbakery/checkers/time.py
@@ -1,14 +1,20 @@
 # Copyright 2017 Canonical Ltd.
 # Licensed under the LGPLv3, see LICENCE file for details.
+
+import pyrfc3339
+
 from macaroonbakery.checkers.auth_context import ContextKey
+from macaroonbakery.checkers.conditions import COND_TIME_BEFORE, STD_NAMESPACE
+from macaroonbakery.checkers.utils import condition_with_prefix
+from macaroonbakery.checkers.caveat import parse_caveat
 
 
 TIME_KEY = ContextKey('time-key')
 
 
 def context_with_clock(ctx, clock):
-    ''' Returns a copy of ctx with a key added that associates it with the given
-    clock implementation, which will be used by the time-before checker
+    ''' Returns a copy of ctx with a key added that associates it with the
+    given clock implementation, which will be used by the time-before checker
     to determine the current time.
     The clock should have a utcnow method that returns the current time
     as a datetime value in UTC.
@@ -16,3 +22,46 @@ def context_with_clock(ctx, clock):
     if clock is None:
         return ctx
     return ctx.with_value(TIME_KEY, clock)
+
+
+def macaroons_expiry_time(ns, ms):
+    ''' Returns the minimum time of any time-before caveats found in the given
+    macaroons or None if no such caveats were found.
+    :param ns: a Namespace, used to resolve caveats.
+    :param ms: a list of pymacaroons.Macaroon
+    :return: datetime.DateTime or None.
+    '''
+    t = None
+    for m in ms:
+        et = expiry_time(ns, m.caveats)
+        if et is not None and (t is None or et < t):
+            t = et
+    return t
+
+
+def expiry_time(ns, cavs):
+    ''' Returns the minimum time of any time-before caveats found
+    in the given list or None if no such caveats were found.
+
+    The ns parameter is
+    :param ns: used to determine the standard namespace prefix - if
+    the standard namespace is not found, the empty prefix is assumed.
+    :param cavs: a list of pymacaroons.Caveat
+    :return: datetime.DateTime or None.
+    '''
+    prefix = ns.resolve(STD_NAMESPACE)
+    time_before_cond = condition_with_prefix(
+        prefix, COND_TIME_BEFORE)
+    t = None
+    for cav in cavs:
+        cav = cav.caveat_id_bytes.decode('utf-8')
+        name, rest = parse_caveat(cav)
+        if name != time_before_cond:
+            continue
+        try:
+            et = pyrfc3339.parse(rest)
+            if t is None or et < t:
+                t = et
+        except ValueError:
+            continue
+    return t
diff --git a/macaroonbakery/checkers/utils.py b/macaroonbakery/checkers/utils.py
index f2e51b1..925e8c7 100644
--- a/macaroonbakery/checkers/utils.py
+++ b/macaroonbakery/checkers/utils.py
@@ -7,7 +7,7 @@ def condition_with_prefix(prefix, condition):
 
     If the prefix is non-empty, a colon is used to separate them.
     '''
-    if prefix == '':
+    if prefix == '' or prefix is None:
         return condition
 
     return prefix + ':' + condition
diff --git a/macaroonbakery/codec.py b/macaroonbakery/codec.py
index d9340b7..2946da9 100644
--- a/macaroonbakery/codec.py
+++ b/macaroonbakery/codec.py
@@ -6,7 +6,7 @@ import json
 import six
 import nacl.public
 
-import macaroonbakery
+import macaroonbakery as bakery
 import macaroonbakery.checkers as checkers
 
 _PUBLIC_KEY_PREFIX_LEN = 4
@@ -33,11 +33,11 @@ def encode_caveat(condition, root_key, third_party_info, key, ns):
     @param ns not used yet
     @return bytes
     '''
-    if third_party_info.version == macaroonbakery.BAKERY_V1:
+    if third_party_info.version == bakery.VERSION_1:
         return _encode_caveat_v1(condition, root_key,
                                  third_party_info.public_key, key)
-    if (third_party_info.version == macaroonbakery.BAKERY_V2 or
-            third_party_info.version == macaroonbakery.BAKERY_V3):
+    if (third_party_info.version == bakery.VERSION_2 or
+            third_party_info.version == bakery.VERSION_3):
         return _encode_caveat_v2_v3(third_party_info.version, condition,
                                     root_key, third_party_info.public_key,
                                     key, ns)
@@ -99,7 +99,7 @@ def _encode_caveat_v2_v3(version, condition, root_key, third_party_pub_key,
         condition [rest of encrypted part]
     '''
     ns_data = bytearray()
-    if version >= macaroonbakery.BAKERY_V3:
+    if version >= bakery.VERSION_3:
         ns_data = ns.serialize_text()
     data = bytearray()
     data.append(version)
@@ -131,7 +131,7 @@ def _encode_secret_part_v2_v3(version, condition, root_key, ns):
     data.append(version)
     encode_uvarint(len(root_key), data)
     data.extend(root_key)
-    if version >= macaroonbakery.BAKERY_V3:
+    if version >= bakery.VERSION_3:
         encode_uvarint(len(ns), data)
         data.extend(ns)
     data.extend(condition.encode('utf-8'))
@@ -146,7 +146,7 @@ def decode_caveat(key, caveat):
     @return ThirdPartyCaveatInfo
     '''
     if len(caveat) == 0:
-        raise macaroonbakery.VerificationError('empty third party caveat')
+        raise bakery.VerificationError('empty third party caveat')
 
     first = caveat[:1]
     if first == b'e':
@@ -154,17 +154,17 @@ def decode_caveat(key, caveat):
         # encoded JSON object.
         return _decode_caveat_v1(key, caveat)
     first_as_int = six.byte2int(first)
-    if (first_as_int == macaroonbakery.BAKERY_V2 or
-            first_as_int == macaroonbakery.BAKERY_V3):
+    if (first_as_int == bakery.VERSION_2 or
+            first_as_int == bakery.VERSION_3):
         if (len(caveat) < _VERSION3_CAVEAT_MIN_LEN
-                and first_as_int == macaroonbakery.BAKERY_V3):
+                and first_as_int == bakery.VERSION_3):
             # If it has the version 3 caveat tag and it's too short, it's
             # almost certainly an id, not an encrypted payload.
-            raise macaroonbakery.VerificationError(
+            raise bakery.VerificationError(
                 'caveat id payload not provided for caveat id {}'.format(
                     caveat))
         return _decode_caveat_v2_v3(first_as_int, key, caveat)
-    raise macaroonbakery.VerificationError('unknown version for caveat')
+    raise bakery.VerificationError('unknown version for caveat')
 
 
 def _decode_caveat_v1(key, caveat):
@@ -196,14 +196,15 @@ def _decode_caveat_v1(key, caveat):
     record = json.loads(c.decode('utf-8'))
     fp_key = nacl.public.PublicKey(
         base64.b64decode(wrapper.get('FirstPartyPublicKey')))
-    return macaroonbakery.ThirdPartyCaveatInfo(
+    return bakery.ThirdPartyCaveatInfo(
         condition=record.get('Condition'),
-        first_party_public_key=macaroonbakery.PublicKey(fp_key),
+        first_party_public_key=bakery.PublicKey(fp_key),
         third_party_key_pair=key,
         root_key=base64.b64decode(record.get('RootKey')),
         caveat=caveat,
-        version=macaroonbakery.BAKERY_V1,
-        namespace=macaroonbakery.legacy_namespace()
+        id=None,
+        version=bakery.VERSION_1,
+        namespace=bakery.legacy_namespace()
     )
 
 
@@ -212,14 +213,14 @@ def _decode_caveat_v2_v3(version, key, caveat):
     '''
     if (len(caveat) < 1 + _PUBLIC_KEY_PREFIX_LEN +
             _KEY_LEN + nacl.public.Box.NONCE_SIZE + 16):
-        raise macaroonbakery.VerificationError('caveat id too short')
+        raise bakery.VerificationError('caveat id too short')
     original_caveat = caveat
     caveat = caveat[1:]  # skip version (already checked)
 
     pk_prefix = caveat[:_PUBLIC_KEY_PREFIX_LEN]
     caveat = caveat[_PUBLIC_KEY_PREFIX_LEN:]
     if key.public_key.encode(raw=True)[:_PUBLIC_KEY_PREFIX_LEN] != pk_prefix:
-        raise macaroonbakery.VerificationError('public key mismatch')
+        raise bakery.VerificationError('public key mismatch')
 
     first_party_pub = caveat[:_KEY_LEN]
     caveat = caveat[_KEY_LEN:]
@@ -229,38 +230,39 @@ def _decode_caveat_v2_v3(version, key, caveat):
     box = nacl.public.Box(key.key, fp_public_key)
     data = box.decrypt(caveat, nonce)
     root_key, condition, ns = _decode_secret_part_v2_v3(version, data)
-    return macaroonbakery.ThirdPartyCaveatInfo(
+    return bakery.ThirdPartyCaveatInfo(
         condition=condition.decode('utf-8'),
-        first_party_public_key=macaroonbakery.PublicKey(fp_public_key),
+        first_party_public_key=bakery.PublicKey(fp_public_key),
         third_party_key_pair=key,
         root_key=root_key,
         caveat=original_caveat,
         version=version,
+        id=None,
         namespace=ns
     )
 
 
 def _decode_secret_part_v2_v3(version, data):
     if len(data) < 1:
-        raise macaroonbakery.VerificationError('secret part too short')
+        raise bakery.VerificationError('secret part too short')
     got_version = six.byte2int(data[:1])
     data = data[1:]
     if version != got_version:
-        raise macaroonbakery.VerificationError(
+        raise bakery.VerificationError(
             'unexpected secret part version, got {} want {}'.format(
                 got_version, version))
     root_key_length, read = decode_uvarint(data)
     data = data[read:]
     root_key = data[:root_key_length]
     data = data[root_key_length:]
-    if version >= macaroonbakery.BAKERY_V3:
+    if version >= bakery.VERSION_3:
         namespace_length, read = decode_uvarint(data)
         data = data[read:]
         ns_data = data[:namespace_length]
         data = data[namespace_length:]
         ns = checkers.deserialize_namespace(ns_data)
     else:
-        ns = macaroonbakery.legacy_namespace()
+        ns = bakery.legacy_namespace()
     return root_key, data, ns
 
 
diff --git a/macaroonbakery/discharge.py b/macaroonbakery/discharge.py
index d4c0e5a..f54fc97 100644
--- a/macaroonbakery/discharge.py
+++ b/macaroonbakery/discharge.py
@@ -3,11 +3,13 @@
 import abc
 from collections import namedtuple
 
-import macaroonbakery
+import macaroonbakery as bakery
 import macaroonbakery.checkers as checkers
 
+emptyContext = checkers.AuthContext()
 
-def discharge_all(ctx, m, get_discharge, local_key=None):
+
+def discharge_all(m, get_discharge, local_key=None):
     '''Gathers discharge macaroons for all the third party caveats in m
     (and any subsequent caveats required by those) using get_discharge to
     acquire each discharge macaroon.
@@ -46,13 +48,14 @@ def discharge_all(ctx, m, get_discharge, local_key=None):
         need = need[1:]
         if local_key is not None and cav.cav.location == 'local':
             # TODO use a small caveat id.
-            dm = discharge(ctx=ctx, key=local_key,
+            dm = discharge(ctx=emptyContext,
+                           key=local_key,
                            checker=_LocalDischargeChecker(),
                            caveat=cav.encrypted_caveat,
                            id=cav.cav.caveat_id_bytes,
                            locator=_EmptyLocator())
         else:
-            dm = get_discharge(ctx, cav.cav, cav.encrypted_caveat)
+            dm = get_discharge(cav.cav, cav.encrypted_caveat)
         # It doesn't matter that we're invalidating dm here because we're
         # about to throw it away.
         discharge_m = dm.macaroon
@@ -87,7 +90,7 @@ class ThirdPartyCaveatChecker(object):
 class _LocalDischargeChecker(ThirdPartyCaveatChecker):
     def check_third_party_caveat(self, ctx, info):
         if info.condition != 'true':
-            raise macaroonbakery.CaveatNotRecognizedError()
+            raise bakery.CaveatNotRecognizedError()
         return []
 
 
@@ -122,15 +125,24 @@ def discharge(ctx, id, caveat, key, checker, locator):
         # caveats are added, use that id as the prefix
         # for any more ids.
         caveat_id_prefix = id
-    cav_info = macaroonbakery.decode_caveat(key, caveat)
-
+    cav_info = bakery.decode_caveat(key, caveat)
+    cav_info = bakery.ThirdPartyCaveatInfo(
+        condition=cav_info.condition,
+        first_party_public_key=cav_info.first_party_public_key,
+        third_party_key_pair=cav_info.third_party_key_pair,
+        root_key=cav_info.root_key,
+        caveat=cav_info.caveat,
+        version=cav_info.version,
... 5722 lines suppressed ...

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/py-macaroon-bakery.git



More information about the Python-modules-commits mailing list