Пример #1
0
    def handle_error(self, error, url):
        '''Try to resolve the given error, which should be a response
        to the given URL, by discharging any macaroon contained in
        it. That is, if error.code is ERR_DISCHARGE_REQUIRED
        then it will try to discharge err.info.macaroon. If the discharge
        succeeds, the discharged macaroon will be saved to the client's cookie
        jar, otherwise an exception will be raised.
        '''
        if error.info is None or error.info.macaroon is None:
            raise BakeryException('unable to read info in discharge error '
                                  'response')

        discharges = bakery.discharge_all(
            error.info.macaroon,
            self.acquire_discharge,
            self.key,
        )
        macaroons = '[' + ','.join(
            map(utils.macaroon_to_json_string, discharges)) + ']'
        all_macaroons = base64.urlsafe_b64encode(utils.to_bytes(macaroons))

        full_path = relative_url(url, error.info.macaroon_path)
        if error.info.cookie_name_suffix is not None:
            name = 'macaroon-' + error.info.cookie_name_suffix
        else:
            name = 'macaroon-auth'
        expires = checkers.macaroons_expiry_time(checkers.Namespace(),
                                                 discharges)
        self.cookies.set_cookie(
            utils.cookie(
                name=name,
                value=all_macaroons.decode('ascii'),
                url=full_path,
                expires=expires,
            ))
Пример #2
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)
Пример #3
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)
    def __init__(self,
                 root_key,
                 id,
                 location=None,
                 version=LATEST_VERSION,
                 namespace=None):
        '''Creates a new macaroon with the given root key, id and location.

        If the version is more than the latest known version,
        the latest known version will be used. The namespace should hold the
        namespace of the service that is creating the macaroon.
        @param root_key bytes or string
        @param id bytes or string
        @param location bytes or string
        @param version the bakery version.
        @param namespace is that of the service creating it
        '''
        if version > LATEST_VERSION:
            log.info('use last known version:{} instead of: {}'.format(
                LATEST_VERSION, version))
            version = LATEST_VERSION
        # m holds the underlying macaroon.
        self._macaroon = pymacaroons.Macaroon(
            location=location,
            key=root_key,
            identifier=id,
            version=macaroon_version(version))
        # version holds the version of the macaroon.
        self._version = version
        self._caveat_data = {}
        if namespace is None:
            namespace = checkers.Namespace()
        self._namespace = namespace
        self._caveat_id_prefix = bytearray()
Пример #5
0
 def test_macaroons_expire_time(self):
     ExpireTest = namedtuple('ExpireTest', 'about macaroons expectTime')
     tests = [
         ExpireTest(
             about='no macaroons',
             macaroons=[newMacaroon()],
             expectTime=None,
         ),
         ExpireTest(
             about='single macaroon without caveats',
             macaroons=[newMacaroon()],
             expectTime=None,
         ),
         ExpireTest(
             about='multiple macaroon without caveats',
             macaroons=[newMacaroon()],
             expectTime=None,
         ),
         ExpireTest(
             about='single macaroon with time-before caveat',
             macaroons=[
                 newMacaroon([checkers.time_before_caveat(t1).condition]),
             ],
             expectTime=t1,
         ),
         ExpireTest(
             about='single macaroon with multiple time-before caveats',
             macaroons=[
                 newMacaroon([
                     checkers.time_before_caveat(t2).condition,
                     checkers.time_before_caveat(t1).condition,
                 ]),
             ],
             expectTime=t1,
         ),
         ExpireTest(
             about='multiple macaroons with multiple time-before caveats',
             macaroons=[
                 newMacaroon([
                     checkers.time_before_caveat(t3).condition,
                     checkers.time_before_caveat(t1).condition,
                 ]),
                 newMacaroon([
                     checkers.time_before_caveat(t3).condition,
                     checkers.time_before_caveat(t1).condition,
                 ]),
             ],
             expectTime=t1,
         ),
     ]
     for test in tests:
         print('test ', test.about)
         t = checkers.macaroons_expiry_time(checkers.Namespace(),
                                            test.macaroons)
         self.assertEqual(t, test.expectTime)
Пример #6
0
    def test_expiry_cookie_is_set(self):
        class _DischargerLocator(bakery.ThirdPartyLocator):
            def __init__(self):
                self.key = bakery.generate_key()

            def third_party_info(self, loc):
                if loc == 'http://1.2.3.4':
                    return bakery.ThirdPartyInfo(
                        public_key=self.key.public_key,
                        version=bakery.LATEST_VERSION,
                    )

        d = _DischargerLocator()
        b = new_bakery('loc', d, None)

        @urlmatch(path='.*/discharge')
        def discharge(url, request):
            qs = parse_qs(request.body)
            content = {q: qs[q][0] for q in qs}
            m = httpbakery.discharge(checkers.AuthContext(), content, d.key, d,
                                     alwaysOK3rd)
            return {
                'status_code': 200,
                'content': {
                    'Macaroon': m.to_dict()
                }
            }

        ages = datetime.datetime.utcnow() + datetime.timedelta(days=1)

        def handler(*args):
            GetHandler(b, 'http://1.2.3.4', None, None, None, ages, *args)
        try:
            httpd = HTTPServer(('', 0), handler)
            thread = threading.Thread(target=httpd.serve_forever)
            thread.start()
            client = httpbakery.Client()
            with HTTMock(discharge):
                resp = requests.get(
                    url='http://' + httpd.server_address[0] + ':' +
                        str(httpd.server_address[1]),
                    cookies=client.cookies,
                    auth=client.auth())
            resp.raise_for_status()
            m = bakery.Macaroon.from_dict(json.loads(
                base64.b64decode(client.cookies.get('macaroon-test')).decode('utf-8'))[0])
            t = checkers.macaroons_expiry_time(
                checkers.Namespace(), [m.macaroon])
            self.assertEquals(ages, t)
            self.assertEquals(resp.text, 'done')
        finally:
            httpd.shutdown()
Пример #7
0
    def test_register(self):
        ns = checkers.Namespace(None)
        ns.register('testns', 't')
        prefix = ns.resolve('testns')
        self.assertEquals(prefix, 't')

        ns.register('other', 'o')
        prefix = ns.resolve('other')
        self.assertEquals(prefix, 'o')

        # If we re-register the same URL, it does nothing.
        ns.register('other', 'p')
        prefix = ns.resolve('other')
        self.assertEquals(prefix, 'o')
Пример #8
0
 def test_expire_time(self):
     ExpireTest = namedtuple('ExpireTest', 'about caveats expectTime')
     tests = [
         ExpireTest(
             about='no caveats',
             caveats=[],
             expectTime=None,
         ),
         ExpireTest(
             about='single time-before caveat',
             caveats=[
                 fpcaveat(checkers.time_before_caveat(t1).condition),
             ],
             expectTime=t1,
         ),
         ExpireTest(
             about='multiple time-before caveat',
             caveats=[
                 fpcaveat(checkers.time_before_caveat(t2).condition),
                 fpcaveat(checkers.time_before_caveat(t1).condition),
             ],
             expectTime=t1,
         ),
         ExpireTest(
             about='mixed caveats',
             caveats=[
                 fpcaveat(checkers.time_before_caveat(t1).condition),
                 fpcaveat('allow bar'),
                 fpcaveat(checkers.time_before_caveat(t2).condition),
                 fpcaveat('deny foo'),
             ],
             expectTime=t1,
         ),
         ExpireTest(
             about='mixed caveats',
             caveats=[
                 fpcaveat(checkers.COND_TIME_BEFORE + ' tomorrow'),
             ],
             expectTime=None,
         ),
     ]
     for test in tests:
         print('test ', test.about)
         t = checkers.expiry_time(checkers.Namespace(), test.caveats)
         self.assertEqual(t, test.expectTime)
Пример #9
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)
Пример #10
0
 def test_v3_round_trip(self):
     tp_info = bakery.ThirdPartyInfo(version=bakery.VERSION_3,
                                     public_key=self.tp_key.public_key)
     ns = checkers.Namespace()
     ns.register('testns', 'x')
     cid = bakery.encode_caveat('is-authenticated-user', b'a random string',
                                tp_info, self.fp_key, ns)
     res = bakery.decode_caveat(self.tp_key, cid)
     self.assertEquals(
         res,
         bakery.ThirdPartyCaveatInfo(
             first_party_public_key=self.fp_key.public_key,
             root_key=b'a random string',
             condition='is-authenticated-user',
             caveat=cid,
             third_party_key_pair=self.tp_key,
             version=bakery.VERSION_3,
             id=None,
             namespace=ns))
Пример #11
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)
Пример #12
0
    def test_serialize(self):
        tests = [('empty namespace', None, b''),
                 ('standard namespace', {
                     'std': ''
                 }, b'std:'),
                 ('several elements', {
                     'std': '',
                     'http://blah.blah': 'blah',
                     'one': 'two',
                     'foo.com/x.v0.1': 'z',
                 }, b'foo.com/x.v0.1:z http://blah.blah:blah one:two std:'),
                 ('sort by URI not by field', {
                     'a': 'one',
                     'a1': 'two',
                 }, b'a:one a1:two')]
        for test in tests:
            ns = checkers.Namespace(test[1])
            data = ns.serialize_text()
            self.assertEquals(data, test[2])
            self.assertEquals(str(ns), test[2].decode('utf-8'))

        # Check that it can be deserialize to the same thing:
        ns1 = checkers.deserialize_namespace(data)
        self.assertEquals(ns1, ns)
Пример #13
0
 def test_macaroons_expire_time_skips_third_party(self):
     m1 = newMacaroon([checkers.time_before_caveat(t1).condition])
     m2 = newMacaroon()
     m2.add_third_party_caveat('https://example.com', 'a-key', '123')
     t = checkers.macaroons_expiry_time(checkers.Namespace(), [m1, m2])
     self.assertEqual(t1, t)
Пример #14
0
 def test_infer_declared(self):
     tests = [
         ('no macaroons', [], {}, None),
         ('single macaroon with one declaration',
          [[checkers.Caveat(condition='declared foo bar')]], {
              'foo': 'bar'
          }, None),
         ('only one argument to declared',
          [[checkers.Caveat(condition='declared foo')]], {}, None),
         ('spaces in value',
          [[checkers.Caveat(condition='declared foo bar bloggs')]], {
              'foo': 'bar bloggs'
          }, None),
         ('attribute with declared prefix',
          [[checkers.Caveat(condition='declaredccf foo')]], {}, None),
         ('several macaroons with different declares',
          [[
              checkers.declared_caveat('a', 'aval'),
              checkers.declared_caveat('b', 'bval')
          ],
           [
               checkers.declared_caveat('c', 'cval'),
               checkers.declared_caveat('d', 'dval')
           ]], {
               'a': 'aval',
               'b': 'bval',
               'c': 'cval',
               'd': 'dval'
           }, None),
         ('duplicate values', [[
             checkers.declared_caveat('a', 'aval'),
             checkers.declared_caveat('a', 'aval'),
             checkers.declared_caveat('b', 'bval')
         ],
                               [
                                   checkers.declared_caveat('a', 'aval'),
                                   checkers.declared_caveat('b', 'bval'),
                                   checkers.declared_caveat('c', 'cval'),
                                   checkers.declared_caveat('d', 'dval')
                               ]], {
                                   'a': 'aval',
                                   'b': 'bval',
                                   'c': 'cval',
                                   'd': 'dval'
                               }, None),
         ('conflicting values',
          [[
              checkers.declared_caveat('a', 'aval'),
              checkers.declared_caveat('a', 'conflict'),
              checkers.declared_caveat('b', 'bval')
          ],
           [
               checkers.declared_caveat('a', 'conflict'),
               checkers.declared_caveat('b', 'another conflict'),
               checkers.declared_caveat('c', 'cval'),
               checkers.declared_caveat('d', 'dval')
           ]], {
               'c': 'cval',
               'd': 'dval'
           }, None),
         ('third party caveats ignored', [[
             checkers.Caveat(condition='declared a no conflict',
                             location='location')
         ], [checkers.declared_caveat('a', 'aval')]], {
             'a': 'aval'
         }, None),
         ('unparseable caveats ignored',
          [[checkers.Caveat(condition=' bad')],
           [checkers.declared_caveat('a', 'aval')]], {
               'a': 'aval'
           }, None),
         ('infer with namespace', [[
             checkers.declared_caveat('a', 'aval'),
             caveat_with_ns(checkers.declared_caveat('a', 'aval'),
                            'testns'),
         ]], {
             'a': 'aval'
         }, None),
     ]
     for test in tests:
         uri_to_prefix = test[3]
         if uri_to_prefix is None:
             uri_to_prefix = {checkers.STD_NAMESPACE: ''}
         ns = checkers.Namespace(uri_to_prefix)
         print(test[0])
         ms = []
         for i, caveats in enumerate(test[1]):
             m = Macaroon(key=None,
                          identifier=six.int2byte(i),
                          location='',
                          version=MACAROON_V2)
             for cav in caveats:
                 cav = ns.resolve_caveat(cav)
                 if cav.location == '':
                     m.add_first_party_caveat(cav.condition)
                 else:
                     m.add_third_party_caveat(cav.location, None,
                                              cav.condition)
             ms.append(m)
         self.assertEqual(checkers.infer_declared(ms), test[2])
Пример #15
0
def legacy_namespace():
    ''' Standard namespace for pre-version3 macaroons.
    '''
    ns = checkers.Namespace(None)
    ns.register(checkers.STD_NAMESPACE, '')
    return ns
Пример #16
0
 def test_register_bad_prefix(self):
     ns = checkers.Namespace(None)
     with self.assertRaises(ValueError):
         ns.register('std', 'x:1')
Пример #17
0
 def test_register_bad_uri(self):
     ns = checkers.Namespace(None)
     with self.assertRaises(KeyError):
         ns.register('', 'x')