# Copyright 2017 Canonical Ltd. # Licensed under the LGPLv3, see LICENCE file for details. from unittest import TestCase import macaroonbakery.bakery as bakery import macaroonbakery.checkers as checkers class TestAuthorizer(TestCase): def test_authorize_func(self): def f(ctx, identity, op): self.assertEqual(identity.id(), 'bob') if op.entity == 'a': return False, None elif op.entity == 'b': return True, None elif op.entity == 'c': return True, [checkers.Caveat(location='somewhere', condition='c')] elif op.entity == 'd': return True, [checkers.Caveat(location='somewhere', condition='d')] else: self.fail('unexpected entity: ' + op.Entity) ops = [bakery.Op('a', 'x'), bakery.Op('b', 'x'), bakery.Op('c', 'x'), bakery.Op('d', 'x')] allowed, caveats = bakery.AuthorizerFunc(f).authorize( checkers.AuthContext(), bakery.SimpleIdentity('bob'), ops ) self.assertEqual(allowed, [False, True, True, True]) self.assertEqual(caveats, [ checkers.Caveat(location='somewhere', condition='c'), checkers.Caveat(location='somewhere', condition='d') ]) def test_acl_authorizer(self): ctx = checkers.AuthContext() tests = [ ('no ops, no problem', bakery.ACLAuthorizer(allow_public=True, get_acl=lambda x, y: []), None, [], []), ('identity that does not implement ACLIdentity; ' 'user should be denied except for everyone group', bakery.ACLAuthorizer( allow_public=True, get_acl=lambda ctx, op: [bakery.EVERYONE] if op.entity == 'a' else ['alice'], ), SimplestIdentity('bob'), [bakery.Op(entity='a', action='a'), bakery.Op(entity='b', action='b')], [True, False]), ('identity that does not implement ACLIdentity with user == Id; ' 'user should be denied except for everyone group', bakery.ACLAuthorizer( allow_public=True, get_acl=lambda ctx, op: [bakery.EVERYONE] if op.entity == 'a' else ['bob'], ), SimplestIdentity('bob'), [bakery.Op(entity='a', action='a'), bakery.Op(entity='b', action='b')], [True, False]), ('permission denied for everyone without AllowPublic', bakery.ACLAuthorizer( allow_public=False, get_acl=lambda x, y: [bakery.EVERYONE], ), SimplestIdentity('bob'), [bakery.Op(entity='a', action='a')], [False]), ('permission granted to anyone with no identity with AllowPublic', bakery.ACLAuthorizer( allow_public=True, get_acl=lambda x, y: [bakery.EVERYONE], ), None, [bakery.Op(entity='a', action='a')], [True]) ] for test in tests: allowed, caveats = test[1].authorize(ctx, test[2], test[3]) self.assertEqual(len(caveats), 0) self.assertEqual(allowed, test[4]) def test_context_wired_properly(self): ctx = checkers.AuthContext({'a': 'aval'}) class Visited: in_f = False in_allow = False in_get_acl = False def f(ctx, identity, op): self.assertEqual(ctx.get('a'), 'aval') Visited.in_f = True return False, None bakery.AuthorizerFunc(f).authorize( ctx, bakery.SimpleIdentity('bob'), ['op1'] ) self.assertTrue(Visited.in_f) class TestIdentity(SimplestIdentity, bakery.ACLIdentity): def allow(other, ctx, acls): self.assertEqual(ctx.get('a'), 'aval') Visited.in_allow = True return False def get_acl(ctx, acl): self.assertEqual(ctx.get('a'), 'aval') Visited.in_get_acl = True return [] bakery.ACLAuthorizer( allow_public=False, get_acl=get_acl, ).authorize(ctx, TestIdentity('bob'), ['op1']) self.assertTrue(Visited.in_get_acl) self.assertTrue(Visited.in_allow) class SimplestIdentity(bakery.Identity): # SimplestIdentity implements Identity for a string. Unlike # SimpleIdentity, it does not implement ACLIdentity. def __init__(self, user): self._identity = user def domain(self): return '' def id(self): return self._identity