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_operation_deny_caveat(self): locator = _DischargerLocator() ids = _IdService('ids', locator, self) auth = _OpAuthorizer({ bakery.Op(entity='e1', action='read'): {'bob'}, bakery.Op(entity='e1', action='write'): {'bob'}, bakery.Op(entity='e2', action='read'): {'bob'}, }) ts = _Service('myservice', auth, ids, locator) client = _Client(locator) ctx = test_context.with_value(_DISCHARGE_USER_KEY, 'bob') m = client.capability(ctx, ts, [ bakery.Op(entity='e1', action='read'), bakery.Op(entity='e1', action='write'), bakery.Op(entity='e2', action='read'), ]) # Sanity check that we can do a write. ts.do(test_context, [[m.macaroon]], [bakery.Op(entity='e1', action='write')]) m.add_caveat(checkers.deny_caveat(['write']), None, None) # A read operation should work. ts.do(test_context, [[m.macaroon]], [ bakery.Op(entity='e1', action='read'), bakery.Op(entity='e2', action='read'), ]) # A write operation should fail # even though the original macaroon allowed it. with self.assertRaises(_DischargeRequiredError): ts.do(test_context, [[m.macaroon]], [bakery.Op(entity='e1', action='write')])
def test_multiple_ops_in_id_with_version1(self): test_oven = bakery.Oven() ops = [bakery.Op('one', 'read'), bakery.Op('one', 'write'), bakery.Op('two', 'read')] m = test_oven.macaroon(bakery.VERSION_1, AGES, None, ops) got_ops, conds = test_oven.macaroon_ops([m.macaroon]) self.assertEquals(len(conds), 1) # time-before caveat. self.assertEquals(bakery.canonical_ops(got_ops), ops)
def test_multiple_ops(self): test_oven = bakery.Oven( ops_store=bakery.MemoryOpsStore()) ops = [bakery.Op('one', 'read'), bakery.Op('one', 'write'), bakery.Op('two', 'read')] m = test_oven.macaroon(bakery.LATEST_VERSION, AGES, None, ops) got_ops, conds = test_oven.macaroon_ops([m.macaroon]) self.assertEquals(len(conds), 1) # time-before caveat. self.assertEquals(bakery.canonical_ops(got_ops), ops)
def test_authorize_with_open_access_and_no_macaroons(self): locator = _DischargerLocator() ids = _IdService('ids', locator, self) auth = _OpAuthorizer( {bakery.Op(entity='something', action='read'): {bakery.EVERYONE}}) ts = _Service('myservice', auth, ids, locator) client = _Client(locator) auth_info = client.do(test_context, ts, [ bakery.Op(entity='something', action='read'), ]) self.assertEqual(len(self._discharges), 0) self.assertIsNotNone(auth_info) self.assertIsNone(auth_info.identity) self.assertEqual(len(auth_info.macaroons), 0)
def macaroon_ops(self, ms): if len(ms) == 0: raise ValueError('no macaroons provided') m_id = json.loads(ms[0].identifier_bytes.decode('utf-8')) root_key = self._root_key_store.get( base64.urlsafe_b64decode(m_id['id'].encode('utf-8'))) v = Verifier() class NoValidationOnFirstPartyCaveat(FirstPartyCaveatVerifierDelegate): def verify_first_party_caveat(self, verifier, caveat, signature): return True v.first_party_caveat_verifier_delegate = \ NoValidationOnFirstPartyCaveat() ok = v.verify(macaroon=ms[0], key=root_key, discharge_macaroons=ms[1:]) if not ok: raise bakery.VerificationError('invalid signature') conditions = [] for m in ms: cavs = m.first_party_caveats() for cav in cavs: conditions.append(cav.caveat_id_bytes.decode('utf-8')) ops = [] for op in m_id['ops']: ops.append(bakery.Op(entity=op[0], action=op[1])) return ops, conditions
def test_authorize_multiple_ops(self): locator = _DischargerLocator() ids = _IdService('ids', locator, self) auth = _OpAuthorizer({ bakery.Op(entity='something', action='read'): {'bob'}, bakery.Op(entity='otherthing', action='read'): {'bob'} }) ts = _Service('myservice', auth, ids, locator) client = _Client(locator) ctx = test_context.with_value(_DISCHARGE_USER_KEY, 'bob') client.do(ctx, ts, [ bakery.Op(entity='something', action='read'), bakery.Op(entity='otherthing', action='read') ]) self.assertEqual(self._discharges, [_DischargeRecord(location='ids', user='******')])
def test_authorize_with_authentication_required(self): locator = _DischargerLocator() ids = _IdService('ids', locator, self) auth = _OpAuthorizer( {bakery.Op(entity='something', action='read'): {'bob'}}) ts = _Service('myservice', auth, ids, locator) client = _Client(locator) ctx = test_context.with_value(_DISCHARGE_USER_KEY, 'bob') auth_info = client.do(ctx, ts, [bakery.Op(entity='something', action='read')]) self.assertEqual(self._discharges, [_DischargeRecord(location='ids', user='******')]) self.assertIsNotNone(auth_info) self.assertEqual(auth_info.identity.id(), 'bob') self.assertEqual(len(auth_info.macaroons), 1)
def agent_visit(url, request): if request.method != "POST": raise Exception('unexpected method') log.info('agent_visit url {}'.format(url)) body = json.loads(request.body.decode('utf-8')) if body['username'] != 'test-user': raise Exception('unexpected username in body {!r}'.format( request.body)) public_key = bakery.PublicKey.deserialize(body['public_key']) ms = httpbakery.extract_macaroons(request.headers) if len(ms) == 0: b = bakery.Bakery(key=discharge_key) m = b.oven.macaroon( version=bakery.LATEST_VERSION, expiry=datetime.utcnow() + timedelta(days=1), caveats=[ bakery.local_third_party_caveat( public_key, version=httpbakery.request_version( request.headers)) ], ops=[bakery.Op(entity='agent', action='login')]) content, headers = httpbakery.discharge_required_response( m, '/', 'test', 'message') resp = response(status_code=401, content=content, headers=headers) return request.hooks['response'][0](resp) return {'status_code': 200, 'content': {'agent_login': True}}
def server_get(url, request): ctx = checkers.AuthContext() test_ops = [bakery.Op(entity='test-op', action='read')] auth_checker = server_bakery.checker.auth( httpbakery.extract_macaroons(request.headers)) try: auth_checker.allow(ctx, test_ops) resp = response(status_code=200, content='done') except bakery.PermissionDenied: caveats = [ checkers.Caveat(location='http://0.3.2.1', condition='is-ok') ] m = server_bakery.oven.macaroon(version=bakery.LATEST_VERSION, expiry=datetime.utcnow() + timedelta(days=1), caveats=caveats, ops=test_ops) content, headers = httpbakery.discharge_required_response( m, '/', 'test', 'message') resp = response( status_code=401, content=content, headers=headers, ) return request.hooks['response'][0](resp)
def test_auth_with_identity_from_context(self): locator = _DischargerLocator() ids = _BasicAuthIdService() auth = _OpAuthorizer({ bakery.Op(entity='e1', action='read'): {'sherlock'}, bakery.Op(entity='e2', action='read'): {'bob'}, }) ts = _Service('myservice', auth, ids, locator) # Check that we can perform the ops with basic auth in the # context. ctx = _context_with_basic_auth(test_context, 'sherlock', 'holmes') auth_info = _Client(locator).do( ctx, ts, [bakery.Op(entity='e1', action='read')]) self.assertEqual(auth_info.identity.id(), 'sherlock') self.assertEqual(len(auth_info.macaroons), 0)
def authorize_with_tp_discharge(ctx, id, op): if (id is not None and id.id() == 'bob' and op == bakery.Op(entity='something', action='read')): return True, [ checkers.Caveat(condition='question', location='other third party') ] return False, None
def test_authorization_denied(self): locator = _DischargerLocator() ids = _IdService('ids', locator, self) auth = bakery.ClosedAuthorizer() ts = _Service('myservice', auth, ids, locator) client = _Client(locator) ctx = test_context.with_value(_DISCHARGE_USER_KEY, 'bob') with self.assertRaises(bakery.PermissionDenied): client.do(ctx, ts, [bakery.Op(entity='something', action='read')])
def test_capability(self): locator = _DischargerLocator() ids = _IdService('ids', locator, self) auth = _OpAuthorizer( {bakery.Op(entity='something', action='read'): {'bob'}}) ts = _Service('myservice', auth, ids, locator) client = _Client(locator) ctx = test_context.with_value(_DISCHARGE_USER_KEY, 'bob') m = client.discharged_capability( ctx, ts, [bakery.Op(entity='something', action='read')]) # Check that we can exercise the capability directly on the service # with no discharging required. auth_info = ts.do(test_context, [m], [ bakery.Op(entity='something', action='read'), ]) self.assertIsNotNone(auth_info) self.assertIsNone(auth_info.identity) self.assertEqual(len(auth_info.macaroons), 1) self.assertEqual(auth_info.macaroons[0][0].identifier_bytes, m[0].identifier_bytes)
def login(url, request): b = bakery.Bakery(key=discharge_key) m = b.oven.macaroon( version=bakery.LATEST_VERSION, expiry=datetime.utcnow() + timedelta(days=1), caveats=[ bakery.local_third_party_caveat( auth_info.key.public_key, version=httpbakery.request_version(request.headers)) ], ops=[bakery.Op(entity='agent', action='login')]) return {'status_code': 200, 'content': {'macaroon': m.to_dict()}}
def test_multiple_capabilities(self): locator = _DischargerLocator() ids = _IdService('ids', locator, self) auth = _OpAuthorizer({ bakery.Op(entity='e1', action='read'): {'alice'}, bakery.Op(entity='e2', action='read'): {'bob'}, }) ts = _Service('myservice', auth, ids, locator) # Acquire two capabilities as different users and check # that we can combine them together to do both operations # at once. ctx = test_context.with_value(_DISCHARGE_USER_KEY, 'alice') m1 = _Client(locator).discharged_capability(ctx, ts, [ bakery.Op(entity='e1', action='read'), ]) ctx = test_context.with_value(_DISCHARGE_USER_KEY, 'bob') m2 = _Client(locator).discharged_capability( ctx, ts, [bakery.Op(entity='e2', action='read')]) self.assertEqual(self._discharges, [ _DischargeRecord(location='ids', user='******'), _DischargeRecord(location='ids', user='******'), ]) auth_info = ts.do(test_context, [m1, m2], [ bakery.Op(entity='e1', action='read'), bakery.Op(entity='e2', action='read'), ]) self.assertIsNotNone(auth_info) self.assertIsNone(auth_info.identity) self.assertEqual(len(auth_info.macaroons), 2) self.assertEqual(auth_info.macaroons[0][0].identifier_bytes, m1[0].identifier_bytes) self.assertEqual(auth_info.macaroons[1][0].identifier_bytes, m2[0].identifier_bytes)
def test_partially_authorized_request(self): locator = _DischargerLocator() ids = _IdService('ids', locator, self) auth = _OpAuthorizer({ bakery.Op(entity='e1', action='read'): {'alice'}, bakery.Op(entity='e2', action='read'): {'bob'}, }) ts = _Service('myservice', auth, ids, locator) # Acquire a capability for e1 but rely on authentication to # authorize e2. ctx = test_context.with_value(_DISCHARGE_USER_KEY, 'alice') m = _Client(locator).discharged_capability(ctx, ts, [ bakery.Op(entity='e1', action='read'), ]) client = _Client(locator) client.add_macaroon(ts, 'authz', m) ctx = test_context.with_value(_DISCHARGE_USER_KEY, 'bob') client.discharged_capability(ctx, ts, [ bakery.Op(entity='e1', action='read'), bakery.Op(entity='e2', action='read'), ])
def test_huge_number_of_ops_gives_small_macaroon(self): test_oven = bakery.Oven( ops_store=bakery.MemoryOpsStore()) ops = [] for i in range(30000): ops.append(bakery.Op(entity='entity' + str(i), action='action' + str(i))) m = test_oven.macaroon(bakery.LATEST_VERSION, AGES, None, ops) got_ops, conds = test_oven.macaroon_ops([m.macaroon]) self.assertEquals(len(conds), 1) # time-before caveat. self.assertEquals(bakery.canonical_ops(got_ops), bakery.canonical_ops(ops)) data = m.serialize_json() self.assertLess(len(data), 300)
def login(url, request): qs = parse_qs(urlparse(request.url).query) self.assertEqual(request.method, 'GET') self.assertEqual(qs, { 'username': ['test-user'], 'public-key': [PUBLIC_KEY] }) b = bakery.Bakery(key=discharge_key) m = b.oven.macaroon( version=bakery.LATEST_VERSION, expiry=datetime.utcnow() + timedelta(days=1), caveats=[ bakery.local_third_party_caveat( PUBLIC_KEY, version=httpbakery.request_version(request.headers)) ], ops=[bakery.Op(entity='agent', action='login')]) return {'status_code': 200, 'content': {'macaroon': m.to_dict()}}
def test_auth_with_third_party_caveats(self): locator = _DischargerLocator() ids = _IdService('ids', locator, self) # We make an authorizer that requires a third party discharge # when authorizing. def authorize_with_tp_discharge(ctx, id, op): if (id is not None and id.id() == 'bob' and op == bakery.Op(entity='something', action='read')): return True, [ checkers.Caveat(condition='question', location='other third party') ] return False, None auth = bakery.AuthorizerFunc(authorize_with_tp_discharge) ts = _Service('myservice', auth, ids, locator) class _LocalDischargeChecker(bakery.ThirdPartyCaveatChecker): def check_third_party_caveat(_, ctx, info): if info.condition != 'question': raise ValueError('third party condition not recognized') self._discharges.append( _DischargeRecord(location='other third party', user=ctx.get(_DISCHARGE_USER_KEY))) return [] locator['other third party'] = _Discharger( key=bakery.generate_key(), checker=_LocalDischargeChecker(), locator=locator, ) client = _Client(locator) ctx = test_context.with_value(_DISCHARGE_USER_KEY, 'bob') client.do(ctx, ts, [bakery.Op(entity='something', action='read')]) self.assertEqual(self._discharges, [ _DischargeRecord(location='ids', user='******'), _DischargeRecord(location='other third party', user='******') ])
def test_allow_any(self): locator = _DischargerLocator() ids = _IdService('ids', locator, self) auth = _OpAuthorizer({ bakery.Op(entity='e1', action='read'): {'alice'}, bakery.Op(entity='e2', action='read'): {'bob'}, }) ts = _Service('myservice', auth, ids, locator) # Acquire a capability for e1 but rely on authentication to # authorize e2. ctx = test_context.with_value(_DISCHARGE_USER_KEY, 'alice') m = _Client(locator).discharged_capability(ctx, ts, [ bakery.Op(entity='e1', action='read'), ]) client = _Client(locator) client.add_macaroon(ts, 'authz', m) self._discharges = [] ctx = test_context.with_value(_DISCHARGE_USER_KEY, 'bob') with self.assertRaises(_DischargeRequiredError): client.do_any(ctx, ts, [ bakery.LOGIN_OP, bakery.Op(entity='e1', action='read'), bakery.Op(entity='e1', action='read') ]) self.assertEqual(len(self._discharges), 0) # Log in as bob. _, err = client.do(ctx, ts, [bakery.LOGIN_OP]) # All the previous actions should now be allowed. auth_info, allowed = client.do_any(ctx, ts, [ bakery.LOGIN_OP, bakery.Op(entity='e1', action='read'), bakery.Op(entity='e1', action='read'), ]) self.assertEqual(auth_info.identity.id(), 'bob') self.assertEqual(len(auth_info.macaroons), 2) self.assertEqual(allowed, [True, True, True])
def test_ops_stored_only_once(self): st = bakery.MemoryOpsStore() test_oven = bakery.Oven(ops_store=st) ops = [bakery.Op('one', 'read'), bakery.Op('one', 'write'), bakery.Op('two', 'read')] m = test_oven.macaroon(bakery.LATEST_VERSION, AGES, None, ops) got_ops, conds = test_oven.macaroon_ops([m.macaroon]) self.assertEquals(bakery.canonical_ops(got_ops), bakery.canonical_ops(ops)) # Make another macaroon containing the same ops in a different order. ops = [bakery.Op('one', 'write'), bakery.Op('one', 'read'), bakery.Op('one', 'read'), bakery.Op('two', 'read')] test_oven.macaroon(bakery.LATEST_VERSION, AGES, None, ops) self.assertEquals(len(st._store), 1)
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_capability_combines_first_party_caveats(self): locator = _DischargerLocator() ids = _IdService('ids', locator, self) auth = _OpAuthorizer({ bakery.Op(entity='e1', action='read'): {'alice'}, bakery.Op(entity='e2', action='read'): {'bob'}, }) ts = _Service('myservice', auth, ids, locator) # Acquire two capabilities as different users, add some first party # caveats that we can combine them together into a single capability # capable of both operations. ctx = test_context.with_value(_DISCHARGE_USER_KEY, 'alice') m1 = _Client(locator).capability( ctx, ts, [bakery.Op(entity='e1', action='read')]) m1.macaroon.add_first_party_caveat('true 1') m1.macaroon.add_first_party_caveat('true 2') ctx = test_context.with_value(_DISCHARGE_USER_KEY, 'bob') m2 = _Client(locator).capability( ctx, ts, [bakery.Op(entity='e2', action='read')]) m2.macaroon.add_first_party_caveat('true 3') m2.macaroon.add_first_party_caveat('true 4') client = _Client(locator) client.add_macaroon(ts, 'authz1', [m1.macaroon]) client.add_macaroon(ts, 'authz2', [m2.macaroon]) m = client.capability(test_context, ts, [ bakery.Op(entity='e1', action='read'), bakery.Op(entity='e2', action='read'), ]) self.assertEqual(_macaroon_conditions(m.macaroon.caveats, False), [ 'true 1', 'true 2', 'true 3', 'true 4', ])
import macaroonbakery.httpbakery as httpbakery import pymacaroons import requests import macaroonbakery._utils as utils from httmock import HTTMock, urlmatch from six.moves.urllib.parse import parse_qs from six.moves.urllib.request import Request try: from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler except ImportError: from http.server import HTTPServer, BaseHTTPRequestHandler AGES = datetime.datetime.utcnow() + datetime.timedelta(days=1) TEST_OP = bakery.Op(entity='test', action='test') class TestClient(TestCase): def test_single_service_first_party(self): b = new_bakery('loc', None, None) def handler(*args): GetHandler(b, None, None, None, None, AGES, *args) try: httpd = HTTPServer(('', 0), handler) thread = threading.Thread(target=httpd.serve_forever) thread.start() srv_macaroon = b.oven.macaroon(version=bakery.LATEST_VERSION, expiry=AGES,
def test_capability_multiple_entities(self): locator = _DischargerLocator() ids = _IdService('ids', locator, self) auth = _OpAuthorizer({ bakery.Op(entity='e1', action='read'): {'bob'}, bakery.Op(entity='e2', action='read'): {'bob'}, bakery.Op(entity='e3', action='read'): {'bob'}, }) ts = _Service('myservice', auth, ids, locator) client = _Client(locator) ctx = test_context.with_value(_DISCHARGE_USER_KEY, 'bob') m = client.discharged_capability(ctx, ts, [ bakery.Op(entity='e1', action='read'), bakery.Op(entity='e2', action='read'), bakery.Op(entity='e3', action='read'), ]) self.assertEqual(self._discharges, [_DischargeRecord(location='ids', user='******')]) # Check that we can exercise the capability directly on the service # with no discharging required. ts.do(test_context, [m], [ bakery.Op(entity='e1', action='read'), bakery.Op(entity='e2', action='read'), bakery.Op(entity='e3', action='read'), ]) # Check that we can exercise the capability to act on a subset of # the operations. ts.do(test_context, [m], [ bakery.Op(entity='e2', action='read'), bakery.Op(entity='e3', action='read'), ]) ts.do(test_context, [m], [bakery.Op(entity='e3', action='read')])
def test_combine_capabilities(self): locator = _DischargerLocator() ids = _IdService('ids', locator, self) auth = _OpAuthorizer({ bakery.Op(entity='e1', action='read'): {'alice'}, bakery.Op(entity='e2', action='read'): {'bob'}, bakery.Op(entity='e3', action='read'): {'bob', 'alice'}, }) ts = _Service('myservice', auth, ids, locator) # Acquire two capabilities as different users and check # that we can combine them together into a single capability # capable of both operations. ctx = test_context.with_value(_DISCHARGE_USER_KEY, 'alice') m1 = _Client(locator).discharged_capability(ctx, ts, [ bakery.Op(entity='e1', action='read'), bakery.Op(entity='e3', action='read'), ]) ctx = test_context.with_value(_DISCHARGE_USER_KEY, 'bob') m2 = _Client(locator).discharged_capability( ctx, ts, [bakery.Op(entity='e2', action='read')]) m = ts.capability(test_context, [m1, m2], [ bakery.Op(entity='e1', action='read'), bakery.Op(entity='e2', action='read'), bakery.Op(entity='e3', action='read'), ]) ts.do(test_context, [[m.macaroon]], [ bakery.Op(entity='e1', action='read'), bakery.Op(entity='e2', action='read'), bakery.Op(entity='e3', action='read'), ])
def test_canonical_ops(self): canonical_ops_tests = ( ('empty array', [], []), ('one element', [bakery.Op('a', 'a')], [bakery.Op('a', 'a')]), ('all in order', [bakery.Op('a', 'a'), bakery.Op('a', 'b'), bakery.Op('c', 'c')], [bakery.Op('a', 'a'), bakery.Op('a', 'b'), bakery.Op('c', 'c')]), ('out of order', [bakery.Op('c', 'c'), bakery.Op('a', 'b'), bakery.Op('a', 'a')], [bakery.Op('a', 'a'), bakery.Op('a', 'b'), bakery.Op('c', 'c')]), ('with duplicates', [bakery.Op('c', 'c'), bakery.Op('a', 'b'), bakery.Op('a', 'a'), bakery.Op('c', 'a'), bakery.Op('c', 'b'), bakery.Op('c', 'c'), bakery.Op('a', 'a')], [bakery.Op('a', 'a'), bakery.Op('a', 'b'), bakery.Op('c', 'a'), bakery.Op('c', 'b'), bakery.Op('c', 'c')]), ('make sure we\'ve got the fields right', [bakery.Op(entity='read', action='two'), bakery.Op(entity='read', action='one'), bakery.Op(entity='write', action='one')], [bakery.Op(entity='read', action='one'), bakery.Op(entity='read', action='two'), bakery.Op(entity='write', action='one')]) ) for about, ops, expected in canonical_ops_tests: new_ops = copy.copy(ops) canonical_ops = bakery.canonical_ops(new_ops) self.assertEquals(canonical_ops, expected) # Verify that the original array isn't changed. self.assertEquals(new_ops, ops)
def test_first_party_caveat_squashing(self): locator = _DischargerLocator() ids = _IdService('ids', locator, self) auth = _OpAuthorizer({ bakery.Op(entity='e1', action='read'): {'alice'}, bakery.Op(entity='e2', action='read'): {'alice'}, }) ts = _Service('myservice', auth, ids, locator) tests = [('duplicates removed', [ checkers.Caveat(condition='true 1', namespace='testns'), checkers.Caveat(condition='true 2', namespace='testns'), checkers.Caveat(condition='true 1', namespace='testns'), checkers.Caveat(condition='true 1', namespace='testns'), checkers.Caveat(condition='true 3', namespace='testns'), ], [ checkers.Caveat(condition='true 1', namespace='testns'), checkers.Caveat(condition='true 2', namespace='testns'), checkers.Caveat(condition='true 3', namespace='testns'), ]), ('earliest time before', [ checkers.time_before_caveat(epoch + timedelta(days=1)), checkers.Caveat(condition='true 1', namespace='testns'), checkers.time_before_caveat(epoch + timedelta(days=0, hours=1)), checkers.time_before_caveat( epoch + timedelta(days=0, hours=0, minutes=5)), ], [ checkers.time_before_caveat( epoch + timedelta(days=0, hours=0, minutes=5)), checkers.Caveat(condition='true 1', namespace='testns'), ]), ('operations and declared caveats removed', [ checkers.deny_caveat(['foo']), checkers.allow_caveat(['read', 'write']), checkers.declared_caveat('username', 'bob'), checkers.Caveat(condition='true 1', namespace='testns'), ], [ checkers.Caveat(condition='true 1', namespace='testns'), ])] for test in tests: print(test[0]) # Make a first macaroon with all the required first party caveats. ctx = test_context.with_value(_DISCHARGE_USER_KEY, 'alice') m1 = _Client(locator).capability( ctx, ts, [bakery.Op(entity='e1', action='read')]) m1.add_caveats(test[1], None, None) # Make a second macaroon that's not used to check that it's # caveats are not added. m2 = _Client(locator).capability( ctx, ts, [bakery.Op(entity='e1', action='read')]) m2.add_caveat( checkers.Caveat(condition='true notused', namespace='testns'), None, None) client = _Client(locator) client.add_macaroon(ts, 'authz1', [m1.macaroon]) client.add_macaroon(ts, 'authz2', [m2.macaroon]) m3 = client.capability(test_context, ts, [bakery.Op(entity='e1', action='read')]) self.assertEqual(_macaroon_conditions(m3.macaroon.caveats, False), _resolve_caveats(m3.namespace, test[2]))