def test_verify_third_party_caveats(self): m = Macaroon( location='http://mybank/', identifier='we used our other secret key', key='this is a different super-secret key; \ never use the same secret twice' ) m.add_first_party_caveat('account = 3735928559') caveat_key = '4; guaranteed random by a fair toss of the dice' identifier = 'this was how we remind auth of key/pred' m.add_third_party_caveat('http://auth.mybank/', caveat_key, identifier) discharge = Macaroon( location='http://auth.mybank/', key=caveat_key, identifier=identifier ) discharge.add_first_party_caveat('time < 2015-01-01T00:00') protected = m.prepare_for_request(discharge) v = Verifier() v.satisfy_exact('account = 3735928559') v.satisfy_exact('time < 2015-01-01T00:00') verified = v.verify( m, 'this is a different super-secret key; \ never use the same secret twice', discharge_macaroons=[protected] ) assert_true(verified)
def test_prepare_for_request(self, rand_nonce): # use a fixed nonce to ensure the same signature rand_nonce.return_value = truncate_or_pad( b'\0', size=crypto_box_NONCEBYTES ) m = Macaroon( location='http://mybank/', identifier='we used our other secret key', key='this is a different super-secret key; \ never use the same secret twice' ) m.add_first_party_caveat('account = 3735928559') caveat_key = '4; guaranteed random by a fair toss of the dice' identifier = 'this was how we remind auth of key/pred' m.add_third_party_caveat( 'http://auth.mybank/', caveat_key, identifier ) discharge = Macaroon( location='http://auth.mybank/', key=caveat_key, identifier=identifier ) discharge.add_first_party_caveat('time < 2015-01-01T00:00') protected = m.prepare_for_request(discharge) assert_equal( protected.signature, '2eb01d0dd2b4475330739140188648cf25dda0425ea9f661f1574ca0a9eac54e' )
def test_verify_third_party_caveats_multi_level(self): # See https://github.com/ecordell/pymacaroons/issues/37 root = Macaroon(location="", identifier="root-id", key="root-key") root.add_third_party_caveat("bob", "bob-caveat-root-key", "bob-is-great") # Create a discharge macaroon that requires a secondary discharge. discharge1 = Macaroon(location="bob", identifier="bob-is-great", key="bob-caveat-root-key") discharge1.add_third_party_caveat("barbara", "barbara-caveat-root-key", "barbara-is-great") # Create the secondary discharge macaroon. discharge2 = Macaroon(location="barbara", identifier="barbara-is-great", key="barbara-caveat-root-key") # Prepare the discharge macaroons for request. discharge1 = root.prepare_for_request(discharge1) discharge2 = root.prepare_for_request(discharge2) verified = Verifier( discharge_macaroons=[discharge1, discharge2]).verify( root, "root-key") assert_true(verified)
def test_verify_third_party_caveats(self): m = Macaroon( location='http://mybank/', identifier='we used our other secret key', key='this is a different super-secret key; \ never use the same secret twice' ) m.add_first_party_caveat('account = 3735928559') caveat_key = '4; guaranteed random by a fair toss of the dice' identifier = 'this was how we remind auth of key/pred' m.add_third_party_caveat('http://auth.mybank/', caveat_key, identifier) discharge = Macaroon( location='http://auth.mybank/', key=caveat_key, identifier=identifier ) discharge.add_first_party_caveat('time < 2015-01-01T00:00') protected = m.prepare_for_request(discharge) v = Verifier() v.satisfy_exact('account = 3735928559') v.satisfy_exact('time < 2015-01-01T00:00') verified = v.verify( m, 'this is a different super-secret key; \ never use the same secret twice', discharge_macaroons=[protected] ) assert_true(verified)
def test_mutual_discharge(self): m1 = Macaroon(location="", identifier="root-id", key="root-key") m1.add_third_party_caveat("bob", "bob-caveat-root-key", "bob-is-great") m2 = Macaroon(location="bob", identifier="bob-is-great", key="bob-caveat-root-key") m2.add_third_party_caveat("charlie", "bob-caveat-root-key", "bob-is-great") m2 = m1.prepare_for_request(m2) Verifier(discharge_macaroons=[m2]).verify(m1, "root-key")
def test_prepare_for_request(self, rand_nonce): # use a fixed nonce to ensure the same signature rand_nonce.return_value = truncate_or_pad( b'\0', size=crypto_box_NONCEBYTES ) m = Macaroon( location='http://mybank/', identifier='we used our other secret key', key='this is a different super-secret key; \ never use the same secret twice' ) m.add_first_party_caveat('account = 3735928559') caveat_key = '4; guaranteed random by a fair toss of the dice' identifier = 'this was how we remind auth of key/pred' m.add_third_party_caveat( 'http://auth.mybank/', caveat_key, identifier ) discharge = Macaroon( location='http://auth.mybank/', key=caveat_key, identifier=identifier ) discharge.add_first_party_caveat('time < 2015-01-01T00:00') protected = m.prepare_for_request(discharge) assert_equal( protected.signature, '2eb01d0dd2b4475330739140188648cf25dda0425ea9f661f1574ca0a9eac54e' )
def generate_macaroon(self, nonce): m = Macaroon( location='http://mybank/', identifier='we used our other secret key', key='this is a different super-secret key; \ never use the same secret twice' ) m.add_first_party_caveat('account = 3735928559') caveat_key = '4; guaranteed random by a fair toss of the dice' identifier = 'this was how we remind auth of key/pred' m.add_third_party_caveat( 'http://auth.mybank/', caveat_key, identifier, nonce=nonce, ) discharge = Macaroon( location='http://auth.mybank/', key=caveat_key, identifier=identifier ) discharge.add_first_party_caveat('time < 2015-01-01T00:00') protected = m.prepare_for_request(discharge) return protected.signature
def test_logging(self): r = Request() root_key = hashlib.sha256("root").hexdigest() root_macaroon = Macaroon(key=root_key) discharge_key = hashlib.sha256("discharge").hexdigest() discharge_caveat_id = '{"secret": "thing"}' root_macaroon.add_third_party_caveat( "sso.example", discharge_key, discharge_caveat_id) root_macaroon.add_first_party_caveat( "store.example|package_id|{}".format( json.dumps(["example-package"]))) unbound_discharge_macaroon = Macaroon( location="sso.example", key=discharge_key, identifier=discharge_caveat_id) unbound_discharge_macaroon.add_first_party_caveat( "sso.example|account|{}".format( base64.b64encode(json.dumps({ "openid": "1234567", "email": "*****@*****.**", })))) logger = BufferLogger() MacaroonAuth( root_macaroon.serialize(), unbound_discharge_macaroon.serialize(), logger=logger)(r) self.assertEqual( ['DEBUG root macaroon: snap-ids: ["example-package"]', 'DEBUG discharge macaroon: OpenID identifier: 1234567'], logger.getLogBuffer().splitlines())
def test_mutual_discharge(self): m1 = Macaroon(location="", identifier="root-id", key="root-key") m1.add_third_party_caveat("bob", "bob-caveat-root-key", "bob-is-great") m2 = Macaroon(location="bob", identifier="bob-is-great", key="bob-caveat-root-key") m2.add_third_party_caveat("charlie", "bob-caveat-root-key", "bob-is-great") m2 = m1.prepare_for_request(m2) Verifier(discharge_macaroons=[m2]).verify(m1, "root-key")
def test_serializing_macaroon_with_first_and_third_caveats(self): m = Macaroon(location='http://mybank/', identifier='we used our other secret key', key='this is a different super-secret key; \ never use the same secret twice') m.add_first_party_caveat('account = 3735928559') caveat_key = '4; guaranteed random by a fair toss of the dice' identifier = 'this was how we remind auth of key/pred' m.add_third_party_caveat('http://auth.mybank/', caveat_key, identifier) n = Macaroon.deserialize(m.serialize()) assert_equal(m.signature, n.signature)
def test_third_party_caveat(self, rand_nonce): # use a fixed nonce to ensure the same signature rand_nonce.return_value = truncate_or_pad(b'\0', size=crypto_box_NONCEBYTES) m = Macaroon(location='http://mybank/', identifier='we used our other secret key', key='this is a different super-secret key; \ never use the same secret twice') m.add_first_party_caveat('account = 3735928559') caveat_key = '4; guaranteed random by a fair toss of the dice' identifier = 'this was how we remind auth of key/pred' m.add_third_party_caveat('http://auth.mybank/', caveat_key, identifier) assert_equal( m.signature, 'd27db2fd1f22760e4c3dae8137e2d8fc1df6c0741c18aed4b97256bf78d1f55c')
def test_third_party_caveat(self): m = Macaroon(location='http://mybank/', identifier='we used our other secret key', key='this is a different super-secret key; \ never use the same secret twice') m.add_first_party_caveat('account = 3735928559') caveat_key = '4; guaranteed random by a fair toss of the dice' identifier = 'this was how we remind auth of key/pred' m.add_third_party_caveat('http://auth.mybank/', caveat_key, identifier, nonce=ZERO_NONCE) assert_equal( m.signature, 'd27db2fd1f22760e4c3dae8137e2d8fc1df6c0741c18aed4b97256bf78d1f55c')
def _make_store_secrets(self): self.root_key = hashlib.sha256( self.factory.getUniqueString()).hexdigest() root_macaroon = Macaroon(key=self.root_key) self.discharge_key = hashlib.sha256( self.factory.getUniqueString()).hexdigest() self.discharge_caveat_id = self.factory.getUniqueString() root_macaroon.add_third_party_caveat( "sso.example", self.discharge_key, self.discharge_caveat_id) unbound_discharge_macaroon = Macaroon( location="sso.example", key=self.discharge_key, identifier=self.discharge_caveat_id) return { "root": root_macaroon.serialize(), "discharge": unbound_discharge_macaroon.serialize(), }
def test_verify_third_party_caveats_multi_level(self): # See https://github.com/ecordell/pymacaroons/issues/37 root = Macaroon(location="", identifier="root-id", key="root-key") root.add_third_party_caveat("bob", "bob-caveat-root-key", "bob-is-great") # Create a discharge macaroon that requires a secondary discharge. discharge1 = Macaroon(location="bob", identifier="bob-is-great", key="bob-caveat-root-key") discharge1.add_third_party_caveat("barbara", "barbara-caveat-root-key", "barbara-is-great") # Create the secondary discharge macaroon. discharge2 = Macaroon(location="barbara", identifier="barbara-is-great", key="barbara-caveat-root-key") # Prepare the discharge macaroons for request. discharge1 = root.prepare_for_request(discharge1) discharge2 = root.prepare_for_request(discharge2) verified = Verifier(discharge_macaroons=[discharge1, discharge2]).verify(root, "root-key") assert_true(verified)
def test_login_handler_redirect(self): m = Macaroon() m.add_third_party_caveat("login.ubuntu.com", "key", "id") serialized_macaroon = m.serialize() responses.add( responses.Response( method="POST", url=self.api_url, json={"macaroon": serialized_macaroon}, status=200, )) response = self.client.get(self.endpoint_url) assert len(responses.calls) == 1 assert response.status_code == 302
def test_serializing_macaroon_with_first_and_third_caveats(self): m = Macaroon( location='http://mybank/', identifier='we used our other secret key', key='this is a different super-secret key; \ never use the same secret twice' ) m.add_first_party_caveat('account = 3735928559') caveat_key = '4; guaranteed random by a fair toss of the dice' identifier = 'this was how we remind auth of key/pred' m.add_third_party_caveat('http://auth.mybank/', caveat_key, identifier) n = Macaroon.deserialize(m.serialize()) assert_equal( m.signature, n.signature )
def test_good(self): r = Request() root_key = hashlib.sha256("root").hexdigest() root_macaroon = Macaroon(key=root_key) discharge_key = hashlib.sha256("discharge").hexdigest() discharge_caveat_id = '{"secret": "thing"}' root_macaroon.add_third_party_caveat( "sso.example", discharge_key, discharge_caveat_id) unbound_discharge_macaroon = Macaroon( location="sso.example", key=discharge_key, identifier=discharge_caveat_id) MacaroonAuth( root_macaroon.serialize(), unbound_discharge_macaroon.serialize())(r) auth_value = r.headers["Authorization"] self.assertThat(auth_value, StartsWith("Macaroon ")) self.assertThat( parse_dict_header(auth_value[len("Macaroon "):]), MacaroonsVerify(root_key))
def alice_server_get_macaroon(): m = Macaroon(location='alices-server.example.com', identifier='key-for-bob', key=alice_server_keys['key-for-bob']) # should be random caveat_key = 'randomKey' predicate = 'Bob' identifier = auth_server_get_identifier(caveat_key, predicate) # location is unused m.add_third_party_caveat('http://auth-server.example.com/', caveat_key, identifier) serialized = m.serialize() resp = make_response( render_template("auth_demo.html", macaroon=m.inspect().replace("\n", "<br/>"), caveat_key=caveat_key, identifier=identifier)) resp.set_cookie('macaroonCookie', serialized) return resp
def test_login_handler_redirect(self): m = Macaroon() m.add_third_party_caveat("login.ubuntu.com", "key", "id") serialized_macaroon = m.serialize() responses.add( responses.Response( method="POST", url=self.api_url, json={"macaroon": serialized_macaroon}, status=200, ) ) response = self.client.get(self.endpoint_url) assert len(responses.calls) == 1 assert response.status_code == 302
def test_third_party_caveat(self): m = Macaroon( location='http://mybank/', identifier='we used our other secret key', key='this is a different super-secret key; \ never use the same secret twice' ) m.add_first_party_caveat('account = 3735928559') caveat_key = '4; guaranteed random by a fair toss of the dice' identifier = 'this was how we remind auth of key/pred' m.add_third_party_caveat( 'http://auth.mybank/', caveat_key, identifier, nonce=ZERO_NONCE) assert_equal( m.signature, 'd27db2fd1f22760e4c3dae8137e2d8fc1df6c0741c18aed4b97256bf78d1f55c' )
def test_third_party_caveat(self, rand_nonce): # use a fixed nonce to ensure the same signature rand_nonce.return_value = truncate_or_pad( b'\0', size=crypto_box_NONCEBYTES ) m = Macaroon( location='http://mybank/', identifier='we used our other secret key', key='this is a different super-secret key; \ never use the same secret twice' ) m.add_first_party_caveat('account = 3735928559') caveat_key = '4; guaranteed random by a fair toss of the dice' identifier = 'this was how we remind auth of key/pred' m.add_third_party_caveat('http://auth.mybank/', caveat_key, identifier) assert_equal( m.signature, 'd27db2fd1f22760e4c3dae8137e2d8fc1df6c0741c18aed4b97256bf78d1f55c' )
def test_inspect(self): m = Macaroon( location='http://mybank/', identifier='we used our secret key', key='this is our super secret key; only we should know it') m.add_first_party_caveat('test = caveat') caveat_key = '4; guaranteed random by a fair toss of the dice' identifier = 'this was how we remind auth of key/pred' m.add_third_party_caveat('http://auth.mybank/', caveat_key, identifier, nonce=ZERO_NONCE) assert_equal(m.inspect(), ( 'location http://mybank/\n' 'identifier we used our secret key\n' 'cid test = caveat\n' 'cid this was how we remind auth of key/pred\n' 'vid AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA68NYajhiFuHnKGSNcVhkAwgbs0VZ0yK2o+q0Aq9+bONkXw7ky7HAuhCLO9hhaMMc\n' 'cl http://auth.mybank/\n' 'signature 7a9289bfbb92d725f748bbcb4f3e04e56b7021513ebeed8411bfba10a16a662e' ))
def generate_macaroon(self, nonce): m = Macaroon(location='http://mybank/', identifier='we used our other secret key', key='this is a different super-secret key; \ never use the same secret twice') m.add_first_party_caveat('account = 3735928559') caveat_key = '4; guaranteed random by a fair toss of the dice' identifier = 'this was how we remind auth of key/pred' m.add_third_party_caveat( 'http://auth.mybank/', caveat_key, identifier, nonce=nonce, ) discharge = Macaroon(location='http://auth.mybank/', key=caveat_key, identifier=identifier) discharge.add_first_party_caveat('time < 2015-01-01T00:00') protected = m.prepare_for_request(discharge) return protected.signature
def test_inspect(self): m = Macaroon( location='http://mybank/', identifier='we used our secret key', key='this is our super secret key; only we should know it' ) m.add_first_party_caveat('test = caveat') caveat_key = '4; guaranteed random by a fair toss of the dice' identifier = 'this was how we remind auth of key/pred' m.add_third_party_caveat( 'http://auth.mybank/', caveat_key, identifier, nonce=ZERO_NONCE ) assert_equal(m.inspect(), ( 'location http://mybank/\n' 'identifier we used our secret key\n' 'cid test = caveat\n' 'cid this was how we remind auth of key/pred\n' 'vid AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA68NYajhiFuHnKGSNcVhkAwgbs0VZ0yK2o+q0Aq9+bONkXw7ky7HAuhCLO9hhaMMc\n' 'cl http://auth.mybank/\n' 'signature 7a9289bfbb92d725f748bbcb4f3e04e56b7021513ebeed8411bfba10a16a662e'))
def test_infer_declared(self): tests = [ ('no macaroons', [], {}, None), ('single macaroon with one declaration', [ [checkers.Caveat(condition='declared foo bar')] ], {'foo': 'bar'}, None), ('only one argument to declared', [ [checkers.Caveat(condition='declared foo')] ], {}, None), ('spaces in value', [ [checkers.Caveat(condition='declared foo bar bloggs')] ], {'foo': 'bar bloggs'}, None), ('attribute with declared prefix', [ [checkers.Caveat(condition='declaredccf foo')] ], {}, None), ('several macaroons with different declares', [ [ checkers.declared_caveat('a', 'aval'), checkers.declared_caveat('b', 'bval') ], [ checkers.declared_caveat('c', 'cval'), checkers.declared_caveat('d', 'dval') ] ], {'a': 'aval', 'b': 'bval', 'c': 'cval', 'd': 'dval'}, None), ('duplicate values', [ [ checkers.declared_caveat('a', 'aval'), checkers.declared_caveat('a', 'aval'), checkers.declared_caveat('b', 'bval') ], [ checkers.declared_caveat('a', 'aval'), checkers.declared_caveat('b', 'bval'), checkers.declared_caveat('c', 'cval'), checkers.declared_caveat('d', 'dval') ] ], {'a': 'aval', 'b': 'bval', 'c': 'cval', 'd': 'dval'}, None), ('conflicting values', [ [ checkers.declared_caveat('a', 'aval'), checkers.declared_caveat('a', 'conflict'), checkers.declared_caveat('b', 'bval') ], [ checkers.declared_caveat('a', 'conflict'), checkers.declared_caveat('b', 'another conflict'), checkers.declared_caveat('c', 'cval'), checkers.declared_caveat('d', 'dval') ] ], {'c': 'cval', 'd': 'dval'}, None), ('third party caveats ignored', [ [checkers.Caveat(condition='declared a no conflict', location='location')], [checkers.declared_caveat('a', 'aval')] ], {'a': 'aval'}, None), ('unparseable caveats ignored', [ [checkers.Caveat(condition=' bad')], [checkers.declared_caveat('a', 'aval')] ], {'a': 'aval'}, None), ('infer with namespace', [ [ checkers.declared_caveat('a', 'aval'), caveat_with_ns(checkers.declared_caveat('a', 'aval'), 'testns'), ] ], {'a': 'aval'}, None), ] for test in tests: uri_to_prefix = test[3] if uri_to_prefix is None: uri_to_prefix = {checkers.STD_NAMESPACE: ''} ns = checkers.Namespace(uri_to_prefix) print(test[0]) ms = [] for i, caveats in enumerate(test[1]): m = Macaroon(key=None, identifier=six.int2byte(i), location='', version=MACAROON_V2) for cav in caveats: cav = ns.resolve_caveat(cav) if cav.location == '': m.add_first_party_caveat(cav.condition) else: m.add_third_party_caveat(cav.location, None, cav.condition) ms.append(m) self.assertEqual(checkers.infer_declared(ms), test[2])
def test_infer_declared(self): tests = [ ('no macaroons', [], {}, None), ('single macaroon with one declaration', [[checkers.Caveat(condition='declared foo bar')]], { 'foo': 'bar' }, None), ('only one argument to declared', [[checkers.Caveat(condition='declared foo')]], {}, None), ('spaces in value', [[checkers.Caveat(condition='declared foo bar bloggs')]], { 'foo': 'bar bloggs' }, None), ('attribute with declared prefix', [[checkers.Caveat(condition='declaredccf foo')]], {}, None), ('several macaroons with different declares', [[ checkers.declared_caveat('a', 'aval'), checkers.declared_caveat('b', 'bval') ], [ checkers.declared_caveat('c', 'cval'), checkers.declared_caveat('d', 'dval') ]], { 'a': 'aval', 'b': 'bval', 'c': 'cval', 'd': 'dval' }, None), ('duplicate values', [[ checkers.declared_caveat('a', 'aval'), checkers.declared_caveat('a', 'aval'), checkers.declared_caveat('b', 'bval') ], [ checkers.declared_caveat('a', 'aval'), checkers.declared_caveat('b', 'bval'), checkers.declared_caveat('c', 'cval'), checkers.declared_caveat('d', 'dval') ]], { 'a': 'aval', 'b': 'bval', 'c': 'cval', 'd': 'dval' }, None), ('conflicting values', [[ checkers.declared_caveat('a', 'aval'), checkers.declared_caveat('a', 'conflict'), checkers.declared_caveat('b', 'bval') ], [ checkers.declared_caveat('a', 'conflict'), checkers.declared_caveat('b', 'another conflict'), checkers.declared_caveat('c', 'cval'), checkers.declared_caveat('d', 'dval') ]], { 'c': 'cval', 'd': 'dval' }, None), ('third party caveats ignored', [[ checkers.Caveat(condition='declared a no conflict', location='location') ], [checkers.declared_caveat('a', 'aval')]], { 'a': 'aval' }, None), ('unparseable caveats ignored', [[checkers.Caveat(condition=' bad')], [checkers.declared_caveat('a', 'aval')]], { 'a': 'aval' }, None), ('infer with namespace', [[ checkers.declared_caveat('a', 'aval'), caveat_with_ns(checkers.declared_caveat('a', 'aval'), 'testns'), ]], { 'a': 'aval' }, None), ] for test in tests: uri_to_prefix = test[3] if uri_to_prefix is None: uri_to_prefix = {checkers.STD_NAMESPACE: ''} ns = checkers.Namespace(uri_to_prefix) print(test[0]) ms = [] for i, caveats in enumerate(test[1]): m = Macaroon(key=None, identifier=six.int2byte(i), location='', version=MACAROON_V2) for cav in caveats: cav = ns.resolve_caveat(cav) if cav.location == '': m.add_first_party_caveat(cav.condition) else: m.add_third_party_caveat(cav.location, None, cav.condition) ms.append(m) self.assertEqual(checkers.infer_declared(ms), test[2])