def _check_conditions(self, ctx, op, conds): declared = checkers.infer_declared_from_conditions( conds, self.parent.namespace()) ctx = checkers.context_with_operations(ctx, [op.action]) ctx = checkers.context_with_declared(ctx, declared) for cond in conds: err = self.parent._first_party_caveat_checker.\ check_first_party_caveat(ctx, cond) if err is not None: return None, err return declared, None
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_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_checkers(self): tests = [ ('nothing in context, no extra checkers', [ ('something', 'caveat "something" not satisfied: caveat not recognized'), ('', 'cannot parse caveat "": empty caveat'), (' hello', 'cannot parse caveat " hello": caveat starts with' ' space character'), ], None), ('one failed caveat', [ ('t:a aval', None), ('t:b bval', None), ('t:a wrong', 'caveat "t:a wrong" not satisfied: wrong arg'), ], None), ('time from clock', [ (checkers.time_before_caveat( datetime.utcnow() + timedelta(0, 1)).condition, None), (checkers.time_before_caveat(NOW).condition, 'caveat "time-before 2006-01-02T15:04:05.000123Z" ' 'not satisfied: macaroon has expired'), (checkers.time_before_caveat(NOW - timedelta(0, 1)).condition, 'caveat "time-before 2006-01-02T15:04:04.000123Z" ' 'not satisfied: macaroon has expired'), ('time-before bad-date', 'caveat "time-before bad-date" not satisfied: ' 'cannot parse "bad-date" as RFC 3339'), (checkers.time_before_caveat(NOW).condition + " ", 'caveat "time-before 2006-01-02T15:04:05.000123Z " ' 'not satisfied: ' 'cannot parse "2006-01-02T15:04:05.000123Z " as RFC 3339'), ], lambda x: checkers.context_with_clock(ctx, TestClock())), ('real time', [ (checkers.time_before_caveat(datetime( year=2010, month=1, day=1)).condition, 'caveat "time-before 2010-01-01T00:00:00.000000Z" not ' 'satisfied: macaroon has expired'), (checkers.time_before_caveat(datetime( year=3000, month=1, day=1)).condition, None), ], None), ('declared, no entries', [ (checkers.declared_caveat('a', 'aval').condition, 'caveat "declared a aval" not satisfied: got a=null, ' 'expected "aval"'), (checkers.COND_DECLARED, 'caveat "declared" not satisfied: ' 'declared caveat has no value'), ], None), ('declared, some entries', [ (checkers.declared_caveat('a', 'aval').condition, None), (checkers.declared_caveat('b', 'bval').condition, None), (checkers.declared_caveat('spc', ' a b').condition, None), (checkers.declared_caveat('a', 'bval').condition, 'caveat "declared a bval" not satisfied: ' 'got a="aval", expected "bval"'), (checkers.declared_caveat('a', ' aval').condition, 'caveat "declared a aval" not satisfied: ' 'got a="aval", expected " aval"'), (checkers.declared_caveat('spc', 'a b').condition, 'caveat "declared spc a b" not satisfied: ' 'got spc=" a b", expected "a b"'), (checkers.declared_caveat('', 'a b').condition, 'caveat "error invalid caveat \'declared\' key """ ' 'not satisfied: bad caveat'), (checkers.declared_caveat('a b', 'a b').condition, 'caveat "error invalid caveat \'declared\' key "a b"" ' 'not satisfied: bad caveat'), ], lambda x: checkers.context_with_declared(x, { 'a': 'aval', 'b': 'bval', 'spc': ' a b'})), ] checker = checkers.Checker() checker.namespace().register('testns', 't') checker.register('a', 'testns', arg_checker(self, 't:a', 'aval')) checker.register('b', 'testns', arg_checker(self, 't:b', 'bval')) ctx = checkers.AuthContext() for test in tests: print(test[0]) if test[2] is not None: ctx1 = test[2](ctx) else: ctx1 = ctx for check in test[1]: err = checker.check_first_party_caveat(ctx1, check[0]) if check[1] is not None: self.assertEqual(err, check[1]) else: self.assertIsNone(err)
def test_checkers(self): tests = [ ('nothing in context, no extra checkers', [ ('something', 'caveat "something" not satisfied: caveat not recognized'), ('', 'cannot parse caveat "": empty caveat'), (' hello', 'cannot parse caveat " hello": caveat starts with' ' space character'), ], None), ('one failed caveat', [ ('t:a aval', None), ('t:b bval', None), ('t:a wrong', 'caveat "t:a wrong" not satisfied: wrong arg'), ], None), ('time from clock', [ (checkers.time_before_caveat(datetime.utcnow() + timedelta(0, 1)).condition, None), (checkers.time_before_caveat(NOW).condition, 'caveat "time-before 2006-01-02T15:04:05.000123Z" ' 'not satisfied: macaroon has expired'), (checkers.time_before_caveat(NOW - timedelta(0, 1)).condition, 'caveat "time-before 2006-01-02T15:04:04.000123Z" ' 'not satisfied: macaroon has expired'), ('time-before bad-date', 'caveat "time-before bad-date" not satisfied: ' 'cannot parse "bad-date" as RFC 3339'), (checkers.time_before_caveat(NOW).condition + " ", 'caveat "time-before 2006-01-02T15:04:05.000123Z " ' 'not satisfied: ' 'cannot parse "2006-01-02T15:04:05.000123Z " as RFC 3339'), ], lambda x: checkers.context_with_clock(ctx, TestClock())), ('real time', [ (checkers.time_before_caveat( datetime(year=2010, month=1, day=1)).condition, 'caveat "time-before 2010-01-01T00:00:00.000000Z" not ' 'satisfied: macaroon has expired'), (checkers.time_before_caveat( datetime(year=3000, month=1, day=1)).condition, None), ], None), ('declared, no entries', [ (checkers.declared_caveat('a', 'aval').condition, 'caveat "declared a aval" not satisfied: got a=null, ' 'expected "aval"'), (checkers.COND_DECLARED, 'caveat "declared" not satisfied: ' 'declared caveat has no value'), ], None), ('declared, some entries', [ (checkers.declared_caveat('a', 'aval').condition, None), (checkers.declared_caveat('b', 'bval').condition, None), (checkers.declared_caveat('spc', ' a b').condition, None), (checkers.declared_caveat('a', 'bval').condition, 'caveat "declared a bval" not satisfied: ' 'got a="aval", expected "bval"'), (checkers.declared_caveat('a', ' aval').condition, 'caveat "declared a aval" not satisfied: ' 'got a="aval", expected " aval"'), (checkers.declared_caveat('spc', 'a b').condition, 'caveat "declared spc a b" not satisfied: ' 'got spc=" a b", expected "a b"'), (checkers.declared_caveat('', 'a b').condition, 'caveat "error invalid caveat \'declared\' key """ ' 'not satisfied: bad caveat'), (checkers.declared_caveat('a b', 'a b').condition, 'caveat "error invalid caveat \'declared\' key "a b"" ' 'not satisfied: bad caveat'), ], lambda x: checkers.context_with_declared(x, { 'a': 'aval', 'b': 'bval', 'spc': ' a b' })), ] checker = checkers.Checker() checker.namespace().register('testns', 't') checker.register('a', 'testns', arg_checker(self, 't:a', 'aval')) checker.register('b', 'testns', arg_checker(self, 't:b', 'bval')) ctx = checkers.AuthContext() for test in tests: print(test[0]) if test[2] is not None: ctx1 = test[2](ctx) else: ctx1 = ctx for check in test[1]: err = checker.check_first_party_caveat(ctx1, check[0]) if check[1] is not None: self.assertEqual(err, check[1]) else: self.assertIsNone(err)