def test_discharge_two_need_declared(self): locator = bakery.ThirdPartyStore() first_party = common.new_bakery('first', locator) third_party = common.new_bakery('third', locator) # first_party mints a macaroon with two third party caveats # with overlapping attributes. m = first_party.oven.macaroon( bakery.LATEST_VERSION, common.ages, [ checkers.need_declared_caveat( checkers.Caveat(location='third', condition='x'), ['foo', 'bar']), checkers.need_declared_caveat( checkers.Caveat(location='third', condition='y'), ['bar', 'baz']), ], [bakery.LOGIN_OP]) # The client asks for a discharge macaroon for each third party caveat. # Since no declarations are added by the discharger, def get_discharge(cav, payload): return bakery.discharge( common.test_context, cav.caveat_id_bytes, payload, third_party.oven.key, common.ThirdPartyCaveatCheckerEmpty(), third_party.oven.locator, ) d = bakery.discharge_all(m, get_discharge) declared = checkers.infer_declared(d, first_party.checker.namespace()) self.assertEqual(declared, { 'foo': '', 'bar': '', 'baz': '', }) ctx = checkers.context_with_declared(common.test_context, declared) first_party.checker.auth([d]).allow(ctx, [bakery.LOGIN_OP]) # If they return conflicting values, the discharge fails. # The client asks for a discharge macaroon for each third party caveat. # Since no declarations are added by the discharger, class ThirdPartyCaveatCheckerF(bakery.ThirdPartyCaveatChecker): def check_third_party_caveat(self, ctx, cav_info): if cav_info.condition == b'x': return [checkers.declared_caveat('foo', 'fooval1')] if cav_info.condition == b'y': return [ checkers.declared_caveat('foo', 'fooval2'), checkers.declared_caveat('baz', 'bazval') ] raise common.ThirdPartyCaveatCheckFailed('not matched') def get_discharge(cav, payload): return bakery.discharge( common.test_context, cav.caveat_id_bytes, payload, third_party.oven.key, ThirdPartyCaveatCheckerF(), third_party.oven.locator, ) d = bakery.discharge_all(m, get_discharge) declared = checkers.infer_declared(d, first_party.checker.namespace()) self.assertEqual(declared, { 'bar': '', 'baz': 'bazval', }) with self.assertRaises(bakery.AuthInitError) as exc: first_party.checker.auth([d]).allow(common.test_context, bakery.LOGIN_OP) self.assertEqual('cannot authorize login macaroon: caveat "declared ' 'foo fooval1" not satisfied: got foo=null, expected ' '"fooval1"', exc.exception.args[0])
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_need_declared(self): locator = bakery.ThirdPartyStore() first_party = common.new_bakery('first', locator) third_party = common.new_bakery('third', locator) # firstParty mints a macaroon with a third-party caveat addressed # to thirdParty with a need-declared caveat. m = first_party.oven.macaroon( bakery.LATEST_VERSION, common.ages, [ checkers.need_declared_caveat( checkers.Caveat(location='third', condition='something'), ['foo', 'bar'] ) ], [bakery.LOGIN_OP]) # The client asks for a discharge macaroon for each third party caveat. def get_discharge(cav, payload): return bakery.discharge( common.test_context, cav.caveat_id_bytes, payload, third_party.oven.key, common.ThirdPartyStrcmpChecker('something'), third_party.oven.locator, ) d = bakery.discharge_all(m, get_discharge) # The required declared attributes should have been added # to the discharge macaroons. declared = checkers.infer_declared(d, first_party.checker.namespace()) self.assertEqual(declared, { 'foo': '', 'bar': '', }) # Make sure the macaroons actually check out correctly # when provided with the declared checker. ctx = checkers.context_with_declared(common.test_context, declared) first_party.checker.auth([d]).allow(ctx, [bakery.LOGIN_OP]) # Try again when the third party does add a required declaration. # The client asks for a discharge macaroon for each third party caveat. def get_discharge(cav, payload): checker = common.ThirdPartyCheckerWithCaveats([ checkers.declared_caveat('foo', 'a'), checkers.declared_caveat('arble', 'b') ]) return bakery.discharge( common.test_context, cav.caveat_id_bytes, payload, third_party.oven.key, checker, third_party.oven.locator, ) d = bakery.discharge_all(m, get_discharge) # One attribute should have been added, the other was already there. declared = checkers.infer_declared(d, first_party.checker.namespace()) self.assertEqual(declared, { 'foo': 'a', 'bar': '', 'arble': 'b', }) ctx = checkers.context_with_declared(common.test_context, declared) first_party.checker.auth([d]).allow(ctx, [bakery.LOGIN_OP]) # Try again, but this time pretend a client is sneakily trying # to add another 'declared' attribute to alter the declarations. def get_discharge(cav, payload): checker = common.ThirdPartyCheckerWithCaveats([ checkers.declared_caveat('foo', 'a'), checkers.declared_caveat('arble', 'b'), ]) # Sneaky client adds a first party caveat. m = bakery.discharge( common.test_context, cav.caveat_id_bytes, payload, third_party.oven.key, checker, third_party.oven.locator, ) m.add_caveat(checkers.declared_caveat('foo', 'c'), None, None) return m d = bakery.discharge_all(m, get_discharge) declared = checkers.infer_declared(d, first_party.checker.namespace()) self.assertEqual(declared, { 'bar': '', 'arble': 'b', }) with self.assertRaises(bakery.AuthInitError) as exc: first_party.checker.auth([d]).allow(common.test_context, bakery.LOGIN_OP) self.assertEqual('cannot authorize login macaroon: caveat ' '"declared foo a" not satisfied: got foo=null, ' 'expected "a"', exc.exception.args[0])
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])