示例#1
0
    def _write_discharge_error(self, exc):
        version = httpbakery.request_version(self.headers)
        if version < bakery.LATEST_VERSION:
            self._server_version = version

        caveats = []
        if self._auth_location != '':
            caveats = [
                checkers.Caveat(location=self._auth_location,
                                condition='is-ok')
            ]
        if self._caveats is not None:
            caveats.extend(self._caveats)

        m = self._bakery.oven.macaroon(version=bakery.LATEST_VERSION,
                                       expiry=self._expiry,
                                       caveats=caveats,
                                       ops=[TEST_OP])

        content, headers = httpbakery.discharge_required_response(
            m, '/', 'test', exc.args[0])
        self.send_response(401)
        for h in headers:
            self.send_header(h, headers[h])
        self.send_header('Connection', 'close')
        self.end_headers()
        self.wfile.write(content)
示例#2
0
 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)
示例#3
0
    def test_discharge_with_version1_macaroon(self):
        locator = bakery.ThirdPartyStore()
        bs = common.new_bakery('bs-loc', locator)
        ts = common.new_bakery('ts-loc', locator)

        # ts creates a old-version macaroon.
        ts_macaroon = ts.oven.macaroon(bakery.VERSION_1, common.ages, None,
                                       [bakery.LOGIN_OP])
        ts_macaroon.add_caveat(
            checkers.Caveat(condition='something', location='bs-loc'),
            ts.oven.key, ts.oven.locator)

        # client asks for a discharge macaroon for each third party caveat

        def get_discharge(cav, payload):
            # Make sure that the caveat id really is old-style.
            try:
                cav.caveat_id_bytes.decode('utf-8')
            except UnicodeDecodeError:
                self.fail('caveat id is not utf-8')
            return bakery.discharge(
                common.test_context,
                cav.caveat_id_bytes,
                payload,
                bs.oven.key,
                common.ThirdPartyStrcmpChecker('something'),
                bs.oven.locator,
            )

        d = bakery.discharge_all(ts_macaroon, get_discharge)

        ts.checker.auth([d]).allow(common.test_context, [bakery.LOGIN_OP])

        for m in d:
            self.assertEqual(m.version, MACAROON_V1)
示例#4
0
 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])
示例#5
0
    def test_macaroon_paper_fig6_fails_without_discharges(self):
        ''' Runs a similar test as test_macaroon_paper_fig6 without the client
        discharging the third party caveats.
        '''
        locator = bakery.ThirdPartyStore()
        ts = common.new_bakery('ts-loc', locator)
        fs = common.new_bakery('fs-loc', locator)
        common.new_bakery('as-loc', locator)

        # ts creates a macaroon.
        ts_macaroon = ts.oven.macaroon(bakery.LATEST_VERSION, common.ages,
                                       None, [bakery.LOGIN_OP])

        # ts somehow sends the macaroon to fs which adds a third party
        # caveat to be discharged by as.
        ts_macaroon.add_caveat(
            checkers.Caveat(location='as-loc', condition='user==bob'),
            fs.oven.key, fs.oven.locator)

        # client makes request to ts
        try:
            ts.checker.auth([[ts_macaroon.macaroon]
                             ]).allow(common.test_context, bakery.LOGIN_OP)
            self.fail('macaroon unmet should be raised')
        except bakery.PermissionDenied:
            pass
示例#6
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)
示例#7
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])
示例#8
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')
示例#9
0
 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)
示例#10
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)
示例#11
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
示例#12
0
 def check_third_party_caveat(self, ctx, info):
     if self._loc == 'as1-loc':
         return [checkers.Caveat(condition='something',
                                 location='as2-loc')]
     if self._loc == 'as2-loc':
         return []
     raise common.ThirdPartyCaveatCheckFailed(
         'unknown location {}'.format(self._loc))
    def add_caveat(self, cav, key=None, loc=None):
        '''Add a caveat to the macaroon.

        It encrypts it using the given key pair
        and by looking up the location using the given locator.
        As a special case, if the caveat's Location field has the prefix
        "local " the caveat is added as a client self-discharge caveat using
        the public key base64-encoded in the rest of the location. In this
        case, the Condition field must be empty. The resulting third-party
        caveat will encode the condition "true" encrypted with that public
        key.

        @param cav the checkers.Caveat to be added.
        @param key the public key to encrypt third party caveat.
        @param loc locator to find information on third parties when adding
        third party caveats. It is expected to have a third_party_info method
        that will be called with a location string and should return a
        ThirdPartyInfo instance holding the requested information.
        '''
        if cav.location is None:
            self._macaroon.add_first_party_caveat(
                self.namespace.resolve_caveat(cav).condition)
            return
        if key is None:
            raise ValueError('no private key to encrypt third party caveat')
        local_info = _parse_local_location(cav.location)
        if local_info is not None:
            if cav.condition:
                raise ValueError('cannot specify caveat condition in '
                                 'local third-party caveat')
            info = local_info
            cav = checkers.Caveat(location='local', condition='true')
        else:
            if loc is None:
                raise ValueError('no locator when adding third party caveat')
            info = loc.third_party_info(cav.location)

        root_key = os.urandom(24)

        # Use the least supported version to encode the caveat.
        if self._version < info.version:
            info = ThirdPartyInfo(
                version=self._version,
                public_key=info.public_key,
            )

        caveat_info = encode_caveat(cav.condition, root_key, info, key,
                                    self._namespace)
        if info.version < VERSION_3:
            # We're encoding for an earlier client or third party which does
            # not understand bundled caveat info, so use the encoded
            # caveat information as the caveat id.
            id = caveat_info
        else:
            id = self._new_caveat_id(self._caveat_id_prefix)
            self._caveat_data[id] = caveat_info

        self._macaroon.add_third_party_caveat(cav.location, root_key, id)
示例#14
0
def _identity_caveats():
    """Caveats required for user authentication using Candid."""
    return [
        checkers.need_declared_caveat(
            checkers.Caveat(location='https://api.staging.jujucharms.com/identity',
                            condition='is-authenticated-user'),
            ['username']
        )
    ]
示例#15
0
def local_third_party_caveat(key, version):
    ''' Returns a third-party caveat that, when added to a macaroon with
    add_caveat, results in a caveat with the location "local", encrypted with
    the given PublicKey.
    This can be automatically discharged by discharge_all passing a local key.
    '''
    if version >= VERSION_2:
        loc = 'local {} {}'.format(version, key)
    else:
        loc = 'local {}'.format(key)
    return checkers.Caveat(location=loc, condition='')
def local_third_party_caveat(key, version):
    ''' Returns a third-party caveat that, when added to a macaroon with
    add_caveat, results in a caveat with the location "local", encrypted with
    the given PublicKey.
    This can be automatically discharged by discharge_all passing a local key.
    '''
    encoded_key = key.encode().decode('utf-8')
    loc = 'local {}'.format(encoded_key)
    if version >= macaroonbakery.BAKERY_V2:
        loc = 'local {} {}'.format(version, encoded_key)
    return checkers.Caveat(location=loc, condition='')
示例#17
0
 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
示例#18
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)
示例#19
0
    def test_third_party_discharge_macaroon_ids_are_small(self):
        locator = bakery.ThirdPartyStore()
        bakeries = {
            'ts-loc': common.new_bakery('ts-loc', locator),
            'as1-loc': common.new_bakery('as1-loc', locator),
            'as2-loc': common.new_bakery('as2-loc', locator),
        }
        ts = bakeries['ts-loc']

        ts_macaroon = ts.oven.macaroon(bakery.LATEST_VERSION,
                                       common.ages,
                                       None, [bakery.LOGIN_OP])
        ts_macaroon.add_caveat(checkers.Caveat(condition='something',
                                               location='as1-loc'),
                               ts.oven.key, ts.oven.locator)

        class ThirdPartyCaveatCheckerF(bakery.ThirdPartyCaveatChecker):
            def __init__(self, loc):
                self._loc = loc

            def check_third_party_caveat(self, ctx, info):
                if self._loc == 'as1-loc':
                    return [checkers.Caveat(condition='something',
                                            location='as2-loc')]
                if self._loc == 'as2-loc':
                    return []
                raise common.ThirdPartyCaveatCheckFailed(
                    'unknown location {}'.format(self._loc))

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

        d = bakery.discharge_all(ts_macaroon, get_discharge)
        ts.checker.auth([d]).allow(common.test_context,
                                   [bakery.LOGIN_OP])

        for i, m in enumerate(d):
            for j, cav in enumerate(m.caveats):
                if (cav.verification_key_id is not None and
                        len(cav.caveat_id) > 3):
                    self.fail('caveat id on caveat {} of macaroon {} '
                              'is too big ({})'.format(j, i, cav.id))
示例#20
0
 def test_single_service_first_party(self):
     ''' Creates a single service with a macaroon with one first party
     caveat.
     It creates a request with this macaroon and checks that the service
     can verify this macaroon as valid.
     '''
     oc = common.new_bakery('bakerytest')
     primary = oc.oven.macaroon(bakery.LATEST_VERSION,
                                common.ages, None,
                                [bakery.LOGIN_OP])
     self.assertEqual(primary.macaroon.location, 'bakerytest')
     primary.add_caveat(checkers.Caveat(condition='str something',
                                        namespace='testns'),
                        oc.oven.key, oc.oven.locator)
     oc.checker.auth([[primary.macaroon]]).allow(
         common.str_context('something'), [bakery.LOGIN_OP])
示例#21
0
    def test_macaroon_paper_fig6(self):
        ''' Implements an example flow as described in the macaroons paper:
        http://theory.stanford.edu/~ataly/Papers/macaroons.pdf
        There are three services, ts, fs, bs:
        ts is a store service which has deligated authority to a forum
        service fs.
        The forum service wants to require its users to be logged into to an
        authentication service bs.

        The client obtains a macaroon from fs (minted by ts, with a third party
         caveat addressed to bs).
        The client obtains a discharge macaroon from bs to satisfy this caveat.
        The target service verifies the original macaroon it delegated to fs
        No direct contact between bs and ts is required
        '''
        locator = bakery.ThirdPartyStore()
        bs = common.new_bakery('bs-loc', locator)
        ts = common.new_bakery('ts-loc', locator)
        fs = common.new_bakery('fs-loc', locator)

        # ts creates a macaroon.
        ts_macaroon = ts.oven.macaroon(bakery.LATEST_VERSION,
                                       common.ages,
                                       None, [bakery.LOGIN_OP])

        # ts somehow sends the macaroon to fs which adds a third party caveat
        # to be discharged by bs.
        ts_macaroon.add_caveat(checkers.Caveat(location='bs-loc',
                                               condition='user==bob'),
                               fs.oven.key, fs.oven.locator)

        # client asks for a discharge macaroon for each third party caveat
        def get_discharge(cav, payload):
            self.assertEqual(cav.location, 'bs-loc')
            return bakery.discharge(
                common.test_context,
                cav.caveat_id_bytes,
                payload,
                bs.oven.key,
                common.ThirdPartyStrcmpChecker('user==bob'),
                bs.oven.locator,
            )

        d = bakery.discharge_all(ts_macaroon, get_discharge)

        ts.checker.auth([d]).allow(common.test_context,
                                   [bakery.LOGIN_OP])
示例#22
0
    def test_third_party_discharge_macaroon_wrong_root_key_and_third_party_caveat(
            self):

        root_keys = bakery.MemoryKeyStore()
        ts = bakery.Bakery(
            key=bakery.generate_key(),
            checker=common.test_checker(),
            root_key_store=root_keys,
            identity_client=common.OneIdentity(),
        )
        locator = bakery.ThirdPartyStore()
        bs = common.new_bakery('bs-loc', locator)

        # ts creates a macaroon with a third party caveat addressed to bs.
        ts_macaroon = ts.oven.macaroon(bakery.LATEST_VERSION, common.ages,
                                       None, [bakery.LOGIN_OP])
        ts_macaroon.add_caveat(
            checkers.Caveat(location='bs-loc', condition='true'),
            ts.oven.key,
            locator,
        )

        def get_discharge(cav, payload):
            return bakery.discharge(
                common.test_context,
                cav.caveat_id_bytes,
                payload,
                bs.oven.key,
                common.ThirdPartyStrcmpChecker('true'),
                bs.oven.locator,
            )

        d = bakery.discharge_all(ts_macaroon, get_discharge)

        # The authorization should succeed at first.
        ts.checker.auth([d]).allow(common.test_context, [bakery.LOGIN_OP])
        # Corrupt the root key and try again.
        # We should get a DischargeRequiredError because the verification has failed.
        root_keys._key = os.urandom(24)
        with self.assertRaises(bakery.PermissionDenied) as err:
            ts.checker.auth([d]).allow(common.test_context, [bakery.LOGIN_OP])
        self.assertEqual(
            str(err.exception),
            'verification failed: Decryption failed. Ciphertext failed verification'
        )
示例#23
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)
示例#24
0
    def test_macaroon_paper_fig6_fails_with_binding_on_tampered_sig(self):
        ''' Runs a similar test as test_macaroon_paper_fig6 with the discharge
        macaroon binding being done on a tampered signature.
        '''
        locator = bakery.ThirdPartyStore()
        bs = common.new_bakery('bs-loc', locator)
        ts = common.new_bakery('ts-loc', locator)

        # ts creates a macaroon.
        ts_macaroon = ts.oven.macaroon(bakery.LATEST_VERSION,
                                       common.ages, None,
                                       [bakery.LOGIN_OP])
        # ts somehow sends the macaroon to fs which adds a third party caveat
        # to be discharged by as.
        ts_macaroon.add_caveat(checkers.Caveat(condition='user==bob',
                                               location='bs-loc'),
                               ts.oven.key, ts.oven.locator)

        # client asks for a discharge macaroon for each third party caveat
        def get_discharge(cav, payload):
            self.assertEqual(cav.location, 'bs-loc')
            return bakery.discharge(
                common.test_context,
                cav.caveat_id_bytes,
                payload,
                bs.oven.key,
                common.ThirdPartyStrcmpChecker('user==bob'),
                bs.oven.locator,
            )

        d = bakery.discharge_all(ts_macaroon, get_discharge)
        # client has all the discharge macaroons. For each discharge macaroon
        # bind it to our ts_macaroon and add it to our request.
        tampered_macaroon = Macaroon()
        for i, dm in enumerate(d[1:]):
            d[i + 1] = tampered_macaroon.prepare_for_request(dm)

        # client makes request to ts.
        with self.assertRaises(bakery.VerificationError) as exc:
            ts.checker.auth([d]).allow(common.test_context,
                                       bakery.LOGIN_OP)
        self.assertEqual('verification failed: Signatures do not match',
                         exc.exception.args[0])
示例#25
0
    def test_discharge_macaroon_cannot_be_used_as_normal_macaroon(self):
        locator = bakery.ThirdPartyStore()
        first_party = common.new_bakery('first', locator)
        third_party = common.new_bakery('third', locator)

        # First party mints a macaroon with a 3rd party caveat.
        m = first_party.oven.macaroon(bakery.LATEST_VERSION,
                                      common.ages, [
                                          checkers.Caveat(location='third',
                                                          condition='true')],
                                      [bakery.LOGIN_OP])

        # Acquire the discharge macaroon, but don't bind it to the original.
        class M:
            unbound = None

        def get_discharge(cav, payload):
            m = bakery.discharge(
                common.test_context,
                cav.caveat_id_bytes,
                payload,
                third_party.oven.key,
                common.ThirdPartyStrcmpChecker('true'),
                third_party.oven.locator,
            )
            M.unbound = m.macaroon.copy()
            return m

        bakery.discharge_all(m, get_discharge)
        self.assertIsNotNone(M.unbound)

        # Make sure it cannot be used as a normal macaroon in the third party.
        with self.assertRaises(bakery.VerificationError) as exc:
            third_party.checker.auth([[M.unbound]]).allow(
                common.test_context, [bakery.LOGIN_OP])
        self.assertEqual('no operations found in macaroon',
                         exc.exception.args[0])
示例#26
0
    def test_need_declared(self):
        locator = bakery.ThirdPartyStore()
        first_party = common.new_bakery('first', locator)
        third_party = common.new_bakery('third', locator)

        # firstParty mints a macaroon with a third-party caveat addressed
        # to thirdParty with a need-declared caveat.
        m = first_party.oven.macaroon(
            bakery.LATEST_VERSION, common.ages, [
                checkers.need_declared_caveat(
                    checkers.Caveat(location='third', condition='something'),
                    ['foo', 'bar']
                )
            ], [bakery.LOGIN_OP])

        # The client asks for a discharge macaroon for each third party caveat.
        def get_discharge(cav, payload):
            return bakery.discharge(
                common.test_context,
                cav.caveat_id_bytes,
                payload,
                third_party.oven.key,
                common.ThirdPartyStrcmpChecker('something'),
                third_party.oven.locator,
            )

        d = bakery.discharge_all(m, get_discharge)

        # The required declared attributes should have been added
        # to the discharge macaroons.
        declared = checkers.infer_declared(d, first_party.checker.namespace())
        self.assertEqual(declared, {
            'foo': '',
            'bar': '',
        })

        # Make sure the macaroons actually check out correctly
        # when provided with the declared checker.
        ctx = checkers.context_with_declared(common.test_context, declared)
        first_party.checker.auth([d]).allow(ctx, [bakery.LOGIN_OP])

        # Try again when the third party does add a required declaration.

        # The client asks for a discharge macaroon for each third party caveat.
        def get_discharge(cav, payload):
            checker = common.ThirdPartyCheckerWithCaveats([
                checkers.declared_caveat('foo', 'a'),
                checkers.declared_caveat('arble', 'b')
            ])
            return bakery.discharge(
                common.test_context,
                cav.caveat_id_bytes,
                payload,
                third_party.oven.key,
                checker,
                third_party.oven.locator,
            )

        d = bakery.discharge_all(m, get_discharge)

        # One attribute should have been added, the other was already there.
        declared = checkers.infer_declared(d, first_party.checker.namespace())
        self.assertEqual(declared, {
            'foo': 'a',
            'bar': '',
            'arble': 'b',
        })

        ctx = checkers.context_with_declared(common.test_context, declared)
        first_party.checker.auth([d]).allow(ctx, [bakery.LOGIN_OP])

        # Try again, but this time pretend a client is sneakily trying
        # to add another 'declared' attribute to alter the declarations.

        def get_discharge(cav, payload):
            checker = common.ThirdPartyCheckerWithCaveats([
                checkers.declared_caveat('foo', 'a'),
                checkers.declared_caveat('arble', 'b'),
            ])

            # Sneaky client adds a first party caveat.
            m = bakery.discharge(
                common.test_context, cav.caveat_id_bytes,
                payload,
                third_party.oven.key, checker,
                third_party.oven.locator,
            )
            m.add_caveat(checkers.declared_caveat('foo', 'c'), None, None)
            return m

        d = bakery.discharge_all(m, get_discharge)

        declared = checkers.infer_declared(d, first_party.checker.namespace())
        self.assertEqual(declared, {
            'bar': '',
            'arble': 'b',
        })

        with self.assertRaises(bakery.AuthInitError) as exc:
            first_party.checker.auth([d]).allow(common.test_context,
                                                bakery.LOGIN_OP)
        self.assertEqual('cannot authorize login macaroon: caveat '
                         '"declared foo a" not satisfied: got foo=null, '
                         'expected "a"', exc.exception.args[0])
示例#27
0
def _get_authentication_caveat(location, domain=""):
    """Return a Caveat requiring the user to be authenticated."""
    condition = "is-authenticated-user"
    if domain:
        condition += " @" + domain
    return checkers.Caveat(condition, location=location)
示例#28
0
    def test_discharge_two_need_declared(self):
        locator = bakery.ThirdPartyStore()
        first_party = common.new_bakery('first', locator)
        third_party = common.new_bakery('third', locator)

        # first_party mints a macaroon with two third party caveats
        # with overlapping attributes.
        m = first_party.oven.macaroon(
            bakery.LATEST_VERSION,
            common.ages, [
                checkers.need_declared_caveat(
                    checkers.Caveat(location='third', condition='x'),
                    ['foo', 'bar']),
                checkers.need_declared_caveat(
                    checkers.Caveat(location='third', condition='y'),
                    ['bar', 'baz']),
            ], [bakery.LOGIN_OP])

        # The client asks for a discharge macaroon for each third party caveat.
        # Since no declarations are added by the discharger,

        def get_discharge(cav, payload):
            return bakery.discharge(
                common.test_context,
                cav.caveat_id_bytes,
                payload,
                third_party.oven.key,
                common.ThirdPartyCaveatCheckerEmpty(),
                third_party.oven.locator,
            )

        d = bakery.discharge_all(m, get_discharge)
        declared = checkers.infer_declared(d, first_party.checker.namespace())
        self.assertEqual(declared, {
            'foo': '',
            'bar': '',
            'baz': '',
        })
        ctx = checkers.context_with_declared(common.test_context, declared)
        first_party.checker.auth([d]).allow(ctx, [bakery.LOGIN_OP])

        # If they return conflicting values, the discharge fails.
        # The client asks for a discharge macaroon for each third party caveat.
        # Since no declarations are added by the discharger,
        class ThirdPartyCaveatCheckerF(bakery.ThirdPartyCaveatChecker):
            def check_third_party_caveat(self, ctx, cav_info):
                if cav_info.condition == b'x':
                    return [checkers.declared_caveat('foo', 'fooval1')]
                if cav_info.condition == b'y':
                    return [
                        checkers.declared_caveat('foo', 'fooval2'),
                        checkers.declared_caveat('baz', 'bazval')
                    ]
                raise common.ThirdPartyCaveatCheckFailed('not matched')

        def get_discharge(cav, payload):
            return bakery.discharge(
                common.test_context,
                cav.caveat_id_bytes,
                payload,
                third_party.oven.key,
                ThirdPartyCaveatCheckerF(),
                third_party.oven.locator,
            )

        d = bakery.discharge_all(m, get_discharge)

        declared = checkers.infer_declared(d, first_party.checker.namespace())
        self.assertEqual(declared, {
            'bar': '',
            'baz': 'bazval',
        })
        with self.assertRaises(bakery.AuthInitError) as exc:
            first_party.checker.auth([d]).allow(common.test_context,
                                                bakery.LOGIN_OP)
        self.assertEqual('cannot authorize login macaroon: caveat "declared '
                         'foo fooval1" not satisfied: got foo=null, expected '
                         '"fooval1"', exc.exception.args[0])
 def identity_from_context(self, ctx):
     return None, [
         checkers.Caveat(location=self._location,
                         condition='is-authenticated-user')
     ]
示例#30
0
def is_something_caveat():
    return checkers.Caveat(condition='is something', namespace='testns')