summaryrefslogtreecommitdiff
path: root/macaroonbakery
diff options
context:
space:
mode:
Diffstat (limited to 'macaroonbakery')
-rw-r--r--macaroonbakery/bakery/_checker.py25
-rw-r--r--macaroonbakery/bakery/_internal/id.proto14
-rw-r--r--macaroonbakery/httpbakery/_discharge.py5
-rw-r--r--macaroonbakery/tests/test_client.py92
-rw-r--r--macaroonbakery/tests/test_discharge.py10
5 files changed, 114 insertions, 32 deletions
diff --git a/macaroonbakery/bakery/_checker.py b/macaroonbakery/bakery/_checker.py
index b796502..88560cc 100644
--- a/macaroonbakery/bakery/_checker.py
+++ b/macaroonbakery/bakery/_checker.py
@@ -111,21 +111,18 @@ class AuthChecker(object):
if not self._executed:
self._init_once(ctx)
self._executed = True
- if self._init_errors:
- raise AuthInitError(self._init_errors[0])
def _init_once(self, ctx):
self._auth_indexes = {}
self._conditions = [None] * len(self._macaroons)
for i, ms in enumerate(self._macaroons):
try:
- ops, conditions = self.parent._macaroon_opstore.macaroon_ops(
- ms)
- except VerificationError:
- raise
- except Exception as exc:
- self._init_errors.append(exc.args[0])
+ ops, conditions = self.parent._macaroon_opstore.macaroon_ops(ms)
+ except VerificationError as e:
+ self._init_errors.append(str(e))
continue
+ except Exception as exc:
+ raise AuthInitError(str(exc))
# It's a valid macaroon (in principle - we haven't checked first
# party caveats).
@@ -152,8 +149,7 @@ class AuthChecker(object):
# other operations if the conditions succeed for those.
declared, err = self._check_conditions(ctx, LOGIN_OP, conditions)
if err is not None:
- self._init_errors.append('cannot authorize login macaroon: ' +
- err)
+ self._init_errors.append('cannot authorize login macaroon: ' + err)
continue
if self._identity is not None:
# We've already found a login macaroon so ignore this one
@@ -201,8 +197,13 @@ class AuthChecker(object):
authorization requests.
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.
+ be:
+
+ - DischargeRequiredError holding the operations that remain to
+ be authorized in order to allow authorization to proceed
+ - PermissionDenied when no operations can be authorized and there's
+ no third party to discharge macaroons for.
+
@param ctx AuthContext
@param ops an array of Op
:return: an AuthInfo object.
diff --git a/macaroonbakery/bakery/_internal/id.proto b/macaroonbakery/bakery/_internal/id.proto
deleted file mode 100644
index eb3d614..0000000
--- a/macaroonbakery/bakery/_internal/id.proto
+++ /dev/null
@@ -1,14 +0,0 @@
-syntax="proto3";
-
-option go_package = "macaroonpb";
-
-message MacaroonId {
- bytes nonce = 1;
- bytes storageId = 2;
- repeated Op ops = 3;
-}
-
-message Op {
- string entity = 1;
- repeated string actions = 2;
-}
diff --git a/macaroonbakery/httpbakery/_discharge.py b/macaroonbakery/httpbakery/_discharge.py
index f868d23..1873850 100644
--- a/macaroonbakery/httpbakery/_discharge.py
+++ b/macaroonbakery/httpbakery/_discharge.py
@@ -16,10 +16,13 @@ def discharge(ctx, content, key, locator, checker):
@return The discharge macaroon {macaroonbakery.Macaroon}
'''
id = content.get('id')
- if id is None:
+ if id is not None:
+ id = id.encode('utf-8')
+ else:
id = content.get('id64')
if id is not None:
id = utils.b64decode(id)
+
caveat = content.get('caveat64')
if caveat is not None:
caveat = utils.b64decode(caveat)
diff --git a/macaroonbakery/tests/test_client.py b/macaroonbakery/tests/test_client.py
index 4061a8a..9c57b78 100644
--- a/macaroonbakery/tests/test_client.py
+++ b/macaroonbakery/tests/test_client.py
@@ -55,6 +55,98 @@ class TestClient(TestCase):
finally:
httpd.shutdown()
+ def test_single_service_third_party(self):
+ class _DischargerLocator(bakery.ThirdPartyLocator):
+ def __init__(self):
+ self.key = bakery.generate_key()
+
+ def third_party_info(self, loc):
+ if loc == 'http://1.2.3.4':
+ return bakery.ThirdPartyInfo(
+ public_key=self.key.public_key,
+ version=bakery.LATEST_VERSION,
+ )
+
+ d = _DischargerLocator()
+ b = new_bakery('loc', d, None)
+
+ @urlmatch(path='.*/discharge')
+ def discharge(url, request):
+ qs = parse_qs(request.body)
+ content = {q: qs[q][0] for q in qs}
+ m = httpbakery.discharge(checkers.AuthContext(), content, d.key, d,
+ alwaysOK3rd)
+ return {
+ 'status_code': 200,
+ 'content': {
+ 'Macaroon': m.to_dict()
+ }
+ }
+
+ def handler(*args):
+ GetHandler(b, 'http://1.2.3.4', None, None, None, AGES, *args)
+ try:
+ httpd = HTTPServer(('', 0), handler)
+ server_url = 'http://' + httpd.server_address[0] + ':' + str(httpd.server_address[1])
+ thread = threading.Thread(target=httpd.serve_forever)
+ thread.start()
+ client = httpbakery.Client()
+ with HTTMock(discharge):
+ resp = requests.get(
+ url=server_url,
+ cookies=client.cookies,
+ auth=client.auth())
+ resp.raise_for_status()
+ self.assertEquals(resp.text, 'done')
+ finally:
+ httpd.shutdown()
+
+ def test_single_service_third_party_version_1_caveat(self):
+ class _DischargerLocator(bakery.ThirdPartyLocator):
+ def __init__(self):
+ self.key = bakery.generate_key()
+
+ def third_party_info(self, loc):
+ if loc == 'http://1.2.3.4':
+ return bakery.ThirdPartyInfo(
+ public_key=self.key.public_key,
+ version=bakery.VERSION_1,
+ )
+
+ d = _DischargerLocator()
+ b = new_bakery('loc', d, None)
+
+ @urlmatch(path='.*/discharge')
+ def discharge(url, request):
+ qs = parse_qs(request.body)
+ content = {q: qs[q][0] for q in qs}
+ m = httpbakery.discharge(checkers.AuthContext(), content, d.key, d,
+ alwaysOK3rd)
+ return {
+ 'status_code': 200,
+ 'content': {
+ 'Macaroon': m.to_dict()
+ }
+ }
+
+ def handler(*args):
+ GetHandler(b, 'http://1.2.3.4', None, None, None, AGES, *args)
+ try:
+ httpd = HTTPServer(('', 0), handler)
+ server_url = 'http://' + httpd.server_address[0] + ':' + str(httpd.server_address[1])
+ thread = threading.Thread(target=httpd.serve_forever)
+ thread.start()
+ client = httpbakery.Client()
+ with HTTMock(discharge):
+ resp = requests.get(
+ url=server_url,
+ cookies=client.cookies,
+ auth=client.auth())
+ resp.raise_for_status()
+ self.assertEquals(resp.text, 'done')
+ finally:
+ httpd.shutdown()
+
def test_cookie_domain_host_not_fqdn(self):
# See
# https://github.com/go-macaroon-bakery/py-macaroon-bakery/issues/53
diff --git a/macaroonbakery/tests/test_discharge.py b/macaroonbakery/tests/test_discharge.py
index 27bae63..0802070 100644
--- a/macaroonbakery/tests/test_discharge.py
+++ b/macaroonbakery/tests/test_discharge.py
@@ -153,7 +153,7 @@ class TestDischarge(unittest.TestCase):
bakery.LOGIN_OP
)
self.fail('macaroon unmet should be raised')
- except bakery.VerificationError:
+ except bakery.PermissionDenied:
pass
def test_macaroon_paper_fig6_fails_with_binding_on_tampered_sig(self):
@@ -194,7 +194,7 @@ class TestDischarge(unittest.TestCase):
d[i + 1] = tampered_macaroon.prepare_for_request(dm)
# client makes request to ts.
- with self.assertRaises(bakery.VerificationError) as exc:
+ with self.assertRaises(bakery.PermissionDenied) as exc:
ts.checker.auth([d]).allow(common.test_context,
bakery.LOGIN_OP)
self.assertEqual('verification failed: Signatures do not match',
@@ -298,7 +298,7 @@ class TestDischarge(unittest.TestCase):
'arble': 'b',
})
- with self.assertRaises(bakery.AuthInitError) as exc:
+ with self.assertRaises(bakery.PermissionDenied) as exc:
first_party.checker.auth([d]).allow(common.test_context,
bakery.LOGIN_OP)
self.assertEqual('cannot authorize login macaroon: caveat '
@@ -377,7 +377,7 @@ class TestDischarge(unittest.TestCase):
'bar': '',
'baz': 'bazval',
})
- with self.assertRaises(bakery.AuthInitError) as exc:
+ with self.assertRaises(bakery.PermissionDenied) as exc:
first_party.checker.auth([d]).allow(common.test_context,
bakery.LOGIN_OP)
self.assertEqual('cannot authorize login macaroon: caveat "declared '
@@ -416,7 +416,7 @@ class TestDischarge(unittest.TestCase):
self.assertIsNotNone(M.unbound)
# Make sure it cannot be used as a normal macaroon in the third party.
- with self.assertRaises(bakery.VerificationError) as exc:
+ with self.assertRaises(bakery.PermissionDenied) as exc:
third_party.checker.auth([[M.unbound]]).allow(
common.test_context, [bakery.LOGIN_OP])
self.assertEqual('no operations found in macaroon',