def test_operations_checker(self): tests = [ ('all allowed', checkers.allow_caveat(['op1', 'op2', 'op4', 'op3' ]), ['op1', 'op3', 'op2'], None), ('none denied', checkers.deny_caveat(['op1', 'op2']), ['op3', 'op4'], None), ('one not allowed', checkers.allow_caveat(['op1', 'op2']), ['op1', 'op3'], 'caveat "allow op1 op2" not satisfied: op3 not allowed'), ('one not denied', checkers.deny_caveat(['op1', 'op2']), ['op4', 'op5', 'op2'], 'caveat "deny op1 op2" not satisfied: op2 not allowed'), ('no operations, allow caveat', checkers.allow_caveat(['op1']), [], 'caveat "allow op1" not satisfied: op1 not allowed'), ('no operations, deny caveat', checkers.deny_caveat(['op1']), [], None), ('no operations, empty allow caveat', checkers.Caveat(condition=checkers.COND_ALLOW), [], 'caveat "allow" not satisfied: no operations allowed'), ] checker = checkers.Checker() for test in tests: print(test[0]) ctx = checkers.context_with_operations(checkers.AuthContext(), test[2]) err = checker.check_first_party_caveat(ctx, test[1].condition) if test[3] is None: self.assertIsNone(err) continue self.assertEqual(err, test[3])
def __init__(self, location=None, locator=None, ops_store=None, key=None, identity_client=None, checker=None, root_key_store=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. If this is None, it will use checkers.Checker(None). @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(). 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 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, no third party caveats may be added. @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 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 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. ''' if checker is None: checker = checkers.Checker() root_keystore_for_ops = None if root_key_store is not None: def root_keystore_for_ops(ops): return root_key_store oven = Oven(key=key, location=location, locator=locator, namespace=checker.namespace(), root_keystore_for_ops=root_keystore_for_ops, ops_store=ops_store) self._oven = oven self._checker = Checker(checker=checker, authorizer=authorizer, identity_client=identity_client, macaroon_opstore=oven)
def __init__(self, key=None, location=None, locator=None, namespace=None, root_keystore_for_ops=None, ops_store=None): ''' @param namespace holds the namespace to use when adding first party caveats. @param root_keystore_for_ops a function that will give the macaroon storage to be used for root keys associated with macaroons created with macaroon. @param ops_store object is used to persistently store the association of multi-op entities with their associated operations when macaroon is called with multiple operations. When this is in use, operation entities with the prefix "multi-" are reserved - a "multi-"-prefixed entity represents a set of operations stored in the OpsStore. @param key holds the private nacl key pair used to encrypt third party caveats. If it is None, no third party caveats can be created. @param location string holds the location that will be associated with new macaroons (as returned by Macaroon.Location). @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. ''' self.key = key self.location = location self.locator = locator if namespace is None: namespace = checkers.Checker().namespace() self.namespace = namespace self.ops_store = ops_store self.root_keystore_for_ops = root_keystore_for_ops if root_keystore_for_ops is None: my_store = MemoryKeyStore() self.root_keystore_for_ops = lambda x: my_store
def test_register_no_registered_ns_exception(self): checker = checkers.Checker() with self.assertRaises(checkers.RegisterError) as ctx: checker.register('x', 'testns', lambda x: None) self.assertEqual( ctx.exception.args[0], 'no prefix registered for namespace testns when ' 'registering condition x')
def test_register_none_func_raise_exception(self): checker = checkers.Checker() with self.assertRaises(checkers.RegisterError) as ctx: checker.register('x', checkers.STD_NAMESPACE, None) self.assertEqual( ctx.exception.args[0], 'no check function registered for namespace std when ' 'registering condition x')
def test_register_empty_prefix_condition_with_colon(self): checker = checkers.Checker() checker.namespace().register('testns', '') with self.assertRaises(checkers.RegisterError) as ctx: checker.register('x:y', 'testns', lambda x: None) self.assertEqual( ctx.exception.args[0], 'caveat condition x:y in namespace testns contains a ' 'colon but its prefix is empty')
def test_register_twice_same_namespace(self): checker = checkers.Checker() checker.namespace().register('testns', '') checker.register('x', 'testns', lambda x: None) with self.assertRaises(checkers.RegisterError) as ctx: checker.register('x', 'testns', lambda x: None) self.assertEqual( ctx.exception.args[0], 'checker for x (namespace testns) already registered' ' in namespace testns')
def test_checker_info(self): checker = checkers.Checker(include_std_checkers=False) checker.namespace().register('one', 't') checker.namespace().register('two', 't') checker.namespace().register('three', '') checker.namespace().register('four', 's') class Called(object): val = '' def register(name, ns): def func(ctx, cond, arg): Called.val = name + ' ' + ns return None checker.register(name, ns, func) register('x', 'one') register('y', 'one') register('z', 'two') register('a', 'two') register('something', 'three') register('other', 'three') register('xxx', 'four') expect = [ checkers.CheckerInfo(ns='four', name='xxx', prefix='s'), checkers.CheckerInfo(ns='one', name='x', prefix='t'), checkers.CheckerInfo(ns='one', name='y', prefix='t'), checkers.CheckerInfo(ns='three', name='other', prefix=''), checkers.CheckerInfo(ns='three', name='something', prefix=''), checkers.CheckerInfo(ns='two', name='a', prefix='t'), checkers.CheckerInfo(ns='two', name='z', prefix='t'), ] infos = checker.info() self.assertEqual(len(infos), len(expect)) new_infos = [] for i, info in enumerate(infos): Called.val = '' info.check(None, '', '') self.assertEqual(Called.val, expect[i].name + ' ' + expect[i].ns) new_infos.append( checkers.CheckerInfo(ns=info.ns, name=info.name, prefix=info.prefix)) self.assertEqual(new_infos, expect)
def new_bakery(location, locator, checker): '''Return a new bakery instance. @param location Location of the bakery {str}. @param locator Locator for third parties {ThirdPartyLocator or None} @param checker Caveat checker {FirstPartyCaveatChecker or None} @return {Bakery} ''' if checker is None: c = checkers.Checker() c.namespace().register('testns', '') c.register('is', 'testns', check_is_something) checker = c key = bakery.generate_key() return bakery.Bakery( location=location, locator=locator, key=key, checker=checker, )
def __init__(self, checker=checkers.Checker(), authorizer=macaroonbakery.ClosedAuthorizer(), identity_client=None, macaroon_opstore=None): ''' :param checker: a first party checker implementing a :param authorizer (Authorizer): used to check whether an authenticated user is allowed to perform operations. The identity parameter passed to authorizer.allow will always have been obtained from a call to identity_client.declared_identity. :param identity_client (IdentityClient) used for interactions with the external identity service used for authentication. If this is None, no authentication will be possible. :param macaroon_opstore (object with new_macaroon and macaroon_ops method): used to retrieve macaroon root keys and other associated information. ''' self._first_party_caveat_checker = checker self._authorizer = authorizer if identity_client is None: identity_client = macaroonbakery.NoIdentities() self._identity_client = identity_client self._macaroon_opstore = macaroon_opstore
def test_checker(): c = checkers.Checker() c.namespace().register('testns', '') c.register('str', 'testns', str_check) c.register('true', 'testns', true_check) return c
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)