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 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 test_operation_error_caveat(self):
     tests = [('empty allow', checkers.allow_caveat(None),
               'error no operations allowed'),
              ('allow: invalid operation name',
               checkers.allow_caveat(['op1', 'operation number 2']),
               'error invalid operation name "operation number 2"'),
              ('deny: invalid operation name',
               checkers.deny_caveat(['op1', 'operation number 2']),
               'error invalid operation name "operation number 2"')]
     for test in tests:
         print(test[0])
         self.assertEqual(test[1].condition, test[2])
 def test_operation_error_caveat(self):
     tests = [
         ('empty allow', checkers.allow_caveat(None),
          'error no operations allowed'),
         ('allow: invalid operation name',
          checkers.allow_caveat(['op1', 'operation number 2']),
          'error invalid operation name "operation number 2"'),
         ('deny: invalid operation name',
          checkers.deny_caveat(['op1', 'operation number 2']),
          'error invalid operation name "operation number 2"')
     ]
     for test in tests:
         print(test[0])
         self.assertEqual(test[1].condition, test[2])
    def test_operation_allow_caveat(self):
        locator = _DischargerLocator()
        ids = _IdService('ids', locator, self)
        auth = _OpAuthorizer({
            macaroonbakery.Op(entity='e1', action='read'): {'bob'},
            macaroonbakery.Op(entity='e1', action='write'): {'bob'},
            macaroonbakery.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, [
            macaroonbakery.Op(entity='e1', action='read'),
            macaroonbakery.Op(entity='e1', action='write'),
            macaroonbakery.Op(entity='e2', action='read')
        ])

        # Sanity check that we can do a write.
        ts.do(test_context, [[m.macaroon]],
              [macaroonbakery.Op(entity='e1', action='write')])

        m.add_caveat(checkers.allow_caveat(['read']), None, None)

        # A read operation should work.
        ts.do(test_context, [[m.macaroon]], [
            macaroonbakery.Op(entity='e1', action='read'),
            macaroonbakery.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]],
                  [macaroonbakery.Op(entity='e1', action='write')])
    def test_operation_allow_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.allow_caveat(['read']), 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_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]))
    def test_first_party_caveat_squashing(self):
        locator = _DischargerLocator()
        ids = _IdService('ids', locator, self)
        auth = _OpAuthorizer({
            macaroonbakery.Op(entity='e1', action='read'): {'alice'},
            macaroonbakery.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, [macaroonbakery.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, [macaroonbakery.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,
                [macaroonbakery.Op(entity='e1', action='read')])
            self.assertEqual(_macaroon_conditions(m3.macaroon.caveats, False),
                             _resolve_caveats(m3.namespace, test[2]))