コード例 #1
0
    def test_marshal_json_latest_version(self):
        locator = bakery.ThirdPartyStore()
        bs = common.new_bakery('bs-loc', locator)
        ns = checkers.Namespace({
            'testns': 'x',
            'otherns': 'y',
        })
        m = bakery.Macaroon(root_key=b'root key',
                            id=b'id',
                            location='location',
                            version=bakery.LATEST_VERSION,
                            namespace=ns)
        m.add_caveat(checkers.Caveat(location='bs-loc', condition='something'),
                     bs.oven.key, locator)
        data = m.serialize_json()
        m1 = bakery.Macaroon.deserialize_json(data)
        # Just check the signature and version - we're not interested in fully
        # checking the macaroon marshaling here.
        self.assertEqual(m1.macaroon.signature, m.macaroon.signature)
        self.assertEqual(m1.macaroon.version, m.macaroon.version)
        self.assertEqual(len(m1.macaroon.caveats), 1)
        self.assertEqual(m1.namespace, m.namespace)
        self.assertEqual(m1._caveat_data, m._caveat_data)

        # test with the encoder, decoder
        data = json.dumps(m, cls=bakery.MacaroonJSONEncoder)
        m1 = json.loads(data, cls=bakery.MacaroonJSONDecoder)
        self.assertEqual(m1.macaroon.signature, m.macaroon.signature)
        self.assertEqual(m1.macaroon.version, m.macaroon.version)
        self.assertEqual(len(m1.macaroon.caveats), 1)
        self.assertEqual(m1.namespace, m.namespace)
        self.assertEqual(m1._caveat_data, m._caveat_data)
コード例 #2
0
 def test_add_first_party_caveat(self):
     m = bakery.Macaroon('rootkey', 'some id', 'here',
                         bakery.LATEST_VERSION)
     m.add_caveat(checkers.Caveat('test_condition'))
     caveats = m.first_party_caveats()
     self.assertEquals(len(caveats), 1)
     self.assertEquals(caveats[0].caveat_id, b'test_condition')
コード例 #3
0
    def test_add_third_party_caveat(self):
        locator = bakery.ThirdPartyStore()
        bs = common.new_bakery('bs-loc', locator)

        lbv = six.int2byte(bakery.LATEST_VERSION)
        tests = [('no existing id', b'', [], lbv + six.int2byte(0)),
                 ('several existing ids', b'', [
                     lbv + six.int2byte(0), lbv + six.int2byte(1),
                     lbv + six.int2byte(2)
                 ], lbv + six.int2byte(3)),
                 ('with base id', lbv + six.int2byte(0),
                  [lbv + six.int2byte(0)
                   ], lbv + six.int2byte(0) + six.int2byte(0)),
                 ('with base id and existing id', lbv + six.int2byte(0),
                  [lbv + six.int2byte(0) + six.int2byte(0)
                   ], lbv + six.int2byte(0) + six.int2byte(1))]

        for test in tests:
            print('test ', test[0])
            m = bakery.Macaroon(root_key=b'root key',
                                id=b'id',
                                location='location',
                                version=bakery.LATEST_VERSION)
            for id in test[2]:
                m.macaroon.add_third_party_caveat(key=None,
                                                  key_id=id,
                                                  location='')
                m._caveat_id_prefix = test[1]
            m.add_caveat(
                checkers.Caveat(location='bs-loc', condition='something'),
                bs.oven.key, locator)
            self.assertEqual(m.macaroon.caveats[len(test[2])].caveat_id,
                             test[3])
コード例 #4
0
    def _test_json_with_version(self, version):
        locator = bakery.ThirdPartyStore()
        bs = common.new_bakery('bs-loc', locator)

        ns = checkers.Namespace({
            'testns': 'x',
        })

        m = bakery.Macaroon(root_key=b'root key',
                            id=b'id',
                            location='location',
                            version=version,
                            namespace=ns)
        m.add_caveat(checkers.Caveat(location='bs-loc', condition='something'),
                     bs.oven.key, locator)

        # Sanity check that no external caveat data has been added.
        self.assertEqual(len(m._caveat_data), 0)

        data = json.dumps(m, cls=bakery.MacaroonJSONEncoder)
        m1 = json.loads(data, cls=bakery.MacaroonJSONDecoder)

        # Just check the signature and version - we're not interested in fully
        # checking the macaroon marshaling here.
        self.assertEqual(m1.macaroon.signature, m.macaroon.signature)
        self.assertEqual(m1.macaroon.version, bakery.macaroon_version(version))
        self.assertEqual(len(m1.macaroon.caveats), 1)

        # Namespace information has been thrown away.
        self.assertEqual(m1.namespace, bakery.legacy_namespace())

        self.assertEqual(len(m1._caveat_data), 0)
コード例 #5
0
 def test_new_macaroon(self):
     m = bakery.Macaroon(b'rootkey', b'some id', 'here',
                         bakery.LATEST_VERSION)
     self.assertIsNotNone(m)
     self.assertEquals(m._macaroon.identifier, b'some id')
     self.assertEquals(m._macaroon.location, 'here')
     self.assertEquals(m.version, bakery.LATEST_VERSION)
コード例 #6
0
    def macaroon(self, version, expiry, caveats, ops):
        ''' Takes a macaroon with the given version from the oven,
        associates it with the given operations and attaches the given caveats.
        There must be at least one operation specified.
        The macaroon will expire at the given time - a time_before first party
        caveat will be added with that time.

        @return: a new Macaroon object.
        '''
        if len(ops) == 0:
            raise ValueError('cannot mint a macaroon associated '
                             'with no operations')

        ops = canonical_ops(ops)
        root_key, storage_id = self.root_keystore_for_ops(ops).root_key()

        id = self._new_macaroon_id(storage_id, expiry, ops)

        id_bytes = six.int2byte(macaroonbakery.LATEST_BAKERY_VERSION) + \
            id.SerializeToString()

        if macaroonbakery.macaroon_version(version) < MACAROON_V2:
            # The old macaroon format required valid text for the macaroon id,
            # so base64-encode it.
            id_bytes = utils.raw_urlsafe_b64encode(id_bytes)

        m = macaroonbakery.Macaroon(root_key, id_bytes, self.location, version,
                                    self.namespace)
        m.add_caveat(checkers.time_before_caveat(expiry), self.key,
                     self.locator)
        m.add_caveats(caveats, self.key, self.locator)
        return m
コード例 #7
0
def discharge(ctx, id, caveat, key, checker, locator):
    ''' Creates a macaroon to discharge a third party caveat.

    The given parameters specify the caveat and how it should be checked.
    The condition implicit in the caveat is checked for validity using checker.
    If it is valid, a new macaroon is returned which discharges the caveat.
    The macaroon is created with a version derived from the version that was
    used to encode the id.

    :param id: (bytes) holds the id to give to the discharge macaroon.
    If Caveat is empty, then the id also holds the encrypted third party
    caveat.
    :param caveat: (bytes) holds the encrypted third party caveat.
    If this is None, id will be used.
    :param key: holds the key to use to decrypt the third party caveat
    information and to encrypt any additional third party caveats returned by
    the caveat checker.
    :param checker: used to check the third party caveat, and may also return
    further caveats to be added to the discharge macaroon.
    :param locator: used to information on third parties referred to by third
    party caveats returned by the Checker.
    '''
    caveat_id_prefix = []
    if caveat is None:
        # The caveat information is encoded in the id itself.
        caveat = id
    else:
        # We've been given an explicit id, so when extra third party
        # caveats are added, use that id as the prefix
        # for any more ids.
        caveat_id_prefix = id
    cav_info = macaroonbakery.decode_caveat(key, caveat)

    # Note that we don't check the error - we allow the
    # third party checker to see even caveats that we can't
    # understand.
    try:
        cond, arg = checkers.parse_caveat(cav_info.condition)
    except ValueError as exc:
        raise macaroonbakery.VerificationError(exc.args[0])

    if cond == checkers.COND_NEED_DECLARED:
        cav_info = cav_info._replace(condition=arg.encode('utf-8'))
        caveats = _check_need_declared(ctx, cav_info, checker)
    else:
        caveats = checker.check_third_party_caveat(ctx, cav_info)

    # Note that the discharge macaroon does not need to
    # be stored persistently. Indeed, it would be a problem if
    # we did, because then the macaroon could potentially be used
    # for normal authorization with the third party.
    m = macaroonbakery.Macaroon(cav_info.root_key, id, '', cav_info.version,
                                cav_info.namespace)
    m._caveat_id_prefix = caveat_id_prefix
    if caveats is not None:
        for cav in caveats:
            m.add_caveat(cav, key, locator)
    return m
コード例 #8
0
        def get_discharge(cav, payload):
            self.assertEqual(payload, None)
            m = bakery.Macaroon(root_key='root key {}'.format(
                cav.caveat_id.decode('utf-8')).encode('utf-8'),
                                id=cav.caveat_id,
                                location='',
                                version=bakery.LATEST_VERSION)

            add_caveats(m)
            return m
コード例 #9
0
 def new_macaroon(self, caveats, namespace, ops):
     root_key, id = self._root_key_store.root_key()
     m_id = {'id': base64.urlsafe_b64encode(id).decode('utf-8'), 'ops': ops}
     data = json.dumps(m_id)
     m = bakery.Macaroon(root_key=root_key,
                         id=data,
                         location='',
                         version=bakery.LATEST_VERSION,
                         namespace=namespace)
     m.add_caveats(caveats, self._key, self._locator)
     return m
コード例 #10
0
 def discharge(url, request):
     wrong_macaroon = bakery.Macaroon(
         root_key=b'some key', id=b'xxx',
         location='some other location',
         version=bakery.VERSION_0)
     return {
         'status_code': 200,
         'content': {
             'Macaroon': wrong_macaroon.to_dict()
         }
     }
コード例 #11
0
 def test_discharge_all_no_discharges(self):
     root_key = b'root key'
     m = bakery.Macaroon(root_key=root_key,
                         id=b'id0',
                         location='loc0',
                         version=bakery.LATEST_VERSION,
                         namespace=common.test_checker().namespace())
     ms = bakery.discharge_all(m, no_discharge(self))
     self.assertEqual(len(ms), 1)
     self.assertEqual(ms[0], m.macaroon)
     v = Verifier()
     v.satisfy_general(always_ok)
     v.verify(m.macaroon, root_key, None)
コード例 #12
0
    def test_discharge_all_many_discharges(self):
        root_key = b'root key'
        m0 = macaroonbakery.Macaroon(
            root_key=root_key,
            id=b'id0',
            location='loc0',
            version=macaroonbakery.LATEST_BAKERY_VERSION)

        class State(object):
            total_required = 40
            id = 1

        def add_caveats(m):
            for i in range(0, 1):
                if State.total_required == 0:
                    break
                cid = 'id{}'.format(State.id)
                m.macaroon.add_third_party_caveat(
                    location='somewhere',
                    key='root key {}'.format(cid).encode('utf-8'),
                    key_id=cid.encode('utf-8'))
                State.id += 1
                State.total_required -= 1

        add_caveats(m0)

        def get_discharge(_, cav, payload):
            self.assertEqual(payload, None)
            m = macaroonbakery.Macaroon(
                root_key='root key {}'.format(
                    cav.caveat_id.decode('utf-8')).encode('utf-8'),
                id=cav.caveat_id,
                location='',
                version=macaroonbakery.LATEST_BAKERY_VERSION)

            add_caveats(m)
            return m

        ms = macaroonbakery.discharge_all(common.test_context, m0,
                                          get_discharge)

        self.assertEqual(len(ms), 41)

        v = Verifier()
        v.satisfy_general(always_ok)
        v.verify(ms[0], root_key, ms[1:])
コード例 #13
0
    def test_json_deserialize_from_go(self):
        ns = checkers.Namespace()
        ns.register("someuri", "x")
        m = bakery.Macaroon(root_key=b'rootkey',
                            id=b'some id',
                            location='here',
                            version=bakery.LATEST_VERSION,
                            namespace=ns)
        m.add_caveat(
            checkers.Caveat(condition='something', namespace='someuri'))
        data = '{"m":{"c":[{"i":"x:something"}],"l":"here","i":"some id",' \
               '"s64":"c8edRIupArSrY-WZfa62pgZFD8VjDgqho9U2PlADe-E"},"v":3,' \
               '"ns":"someuri:x"}'
        m_go = bakery.Macaroon.deserialize_json(data)

        self.assertEqual(m.macaroon.signature_bytes,
                         m_go.macaroon.signature_bytes)
        self.assertEqual(m.macaroon.version, m_go.macaroon.version)
        self.assertEqual(len(m_go.macaroon.caveats), 1)
        self.assertEqual(m.namespace, m_go.namespace)
コード例 #14
0
 def test_clone(self):
     locator = bakery.ThirdPartyStore()
     bs = common.new_bakery("bs-loc", locator)
     ns = checkers.Namespace({
         "testns": "x",
     })
     m = bakery.Macaroon(root_key=b'root key',
                         id=b'id',
                         location='location',
                         version=bakery.LATEST_VERSION,
                         namespace=ns)
     m.add_caveat(checkers.Caveat(location='bs-loc', condition='something'),
                  bs.oven.key, locator)
     m1 = m.copy()
     self.assertEqual(len(m.macaroon.caveats), 1)
     self.assertEqual(len(m1.macaroon.caveats), 1)
     self.assertEqual(m._caveat_data, m1._caveat_data)
     m.add_caveat(checkers.Caveat(location='bs-loc', condition='something'),
                  bs.oven.key, locator)
     self.assertEqual(len(m.macaroon.caveats), 2)
     self.assertEqual(len(m1.macaroon.caveats), 1)
     self.assertNotEqual(m._caveat_data, m1._caveat_data)
コード例 #15
0
    def test_discharge_all_many_discharges_with_real_third_party_caveats(self):
        # This is the same flow as TestDischargeAllManyDischarges except that
        # we're using actual third party caveats as added by
        # Macaroon.add_caveat and we use a larger number of caveats
        # so that caveat ids will need to get larger.
        locator = bakery.ThirdPartyStore()
        bakeries = {}
        total_discharges_required = 40

        class M:
            bakery_id = 0
            still_required = total_discharges_required

        def add_bakery():
            M.bakery_id += 1
            loc = 'loc{}'.format(M.bakery_id)
            bakeries[loc] = common.new_bakery(loc, locator)
            return loc

        ts = common.new_bakery('ts-loc', locator)

        def checker(_, ci):
            caveats = []
            if ci.condition != 'something':
                self.fail('unexpected condition')
            for i in range(0, 2):
                if M.still_required <= 0:
                    break
                caveats.append(
                    checkers.Caveat(location=add_bakery(),
                                    condition='something'))
                M.still_required -= 1
            return caveats

        root_key = b'root key'
        m0 = bakery.Macaroon(root_key=root_key,
                             id=b'id0',
                             location='ts-loc',
                             version=bakery.LATEST_VERSION)

        m0.add_caveat(
            checkers.Caveat(location=add_bakery(), condition='something'),
            ts.oven.key, locator)

        # We've added a caveat (the first) so one less caveat is required.
        M.still_required -= 1

        class ThirdPartyCaveatCheckerF(bakery.ThirdPartyCaveatChecker):
            def check_third_party_caveat(self, ctx, info):
                return checker(ctx, info)

        def get_discharge(cav, payload):
            return bakery.discharge(common.test_context, cav.caveat_id,
                                    payload, bakeries[cav.location].oven.key,
                                    ThirdPartyCaveatCheckerF(), locator)

        ms = bakery.discharge_all(m0, get_discharge)

        self.assertEqual(len(ms), total_discharges_required + 1)

        v = Verifier()
        v.satisfy_general(always_ok)
        v.verify(ms[0], root_key, ms[1:])