コード例 #1
0
    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 = [
            macaroonbakery.Op('a', 'x'),
            macaroonbakery.Op('b', 'x'),
            macaroonbakery.Op('c', 'x'),
            macaroonbakery.Op('d', 'x')
        ]
        allowed, caveats = macaroonbakery.AuthorizerFunc(f).authorize(
            checkers.AuthContext(), macaroonbakery.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')
        ])
コード例 #2
0
    def test_operation_deny_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.deny_caveat(['write']), 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')])
コード例 #3
0
 def test_multiple_ops_in_id_with_version1(self):
     test_oven = macaroonbakery.Oven()
     ops = [macaroonbakery.Op('one', 'read'),
            macaroonbakery.Op('one', 'write'),
            macaroonbakery.Op('two', 'read')]
     m = test_oven.macaroon(macaroonbakery.BAKERY_V1, AGES, None, ops)
     got_ops, conds = test_oven.macaroon_ops([m.macaroon])
     self.assertEquals(len(conds), 1)  # time-before caveat.
     self.assertEquals(macaroonbakery.canonical_ops(got_ops), ops)
コード例 #4
0
 def test_multiple_ops(self):
     test_oven = macaroonbakery.Oven(
         ops_store=macaroonbakery.MemoryOpsStore())
     ops = [macaroonbakery.Op('one', 'read'),
            macaroonbakery.Op('one', 'write'),
            macaroonbakery.Op('two', 'read')]
     m = test_oven.macaroon(macaroonbakery.LATEST_BAKERY_VERSION, AGES,
                            None, ops)
     got_ops, conds = test_oven.macaroon_ops([m.macaroon])
     self.assertEquals(len(conds), 1)  # time-before caveat.
     self.assertEquals(macaroonbakery.canonical_ops(got_ops), ops)
コード例 #5
0
 def test_multiple_ops_in_id(self):
     test_oven = bakery.Oven()
     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)
コード例 #6
0
 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)
コード例 #7
0
    def test_authorize_with_authentication_required(self):
        locator = _DischargerLocator()
        ids = _IdService('ids', locator, self)
        auth = _OpAuthorizer(
            {macaroonbakery.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, [macaroonbakery.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)
コード例 #8
0
 def test_authorize_multiple_ops(self):
     locator = _DischargerLocator()
     ids = _IdService('ids', locator, self)
     auth = _OpAuthorizer({
         macaroonbakery.Op(entity='something', action='read'): {'bob'},
         macaroonbakery.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, [
         macaroonbakery.Op(entity='something', action='read'),
         macaroonbakery.Op(entity='otherthing', action='read')
     ])
     self.assertEqual(self._discharges,
                      [_DischargeRecord(location='ids', user='******')])
コード例 #9
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 macaroonbakery.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(macaroonbakery.Op(entity=op[0], action=op[1]))
        return ops, conditions
コード例 #10
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)
コード例 #11
0
    def test_auth_with_identity_from_context(self):
        locator = _DischargerLocator()
        ids = _BasicAuthIdService()
        auth = _OpAuthorizer({
            macaroonbakery.Op(entity='e1', action='read'): {'sherlock'},
            macaroonbakery.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, [macaroonbakery.Op(entity='e1', action='read')])
        self.assertEqual(auth_info.identity.id(), 'sherlock')
        self.assertEqual(len(auth_info.macaroons), 0)
コード例 #12
0
        def visit(url, request):
            if request.headers.get('Accept') == 'application/json':
                return {'status_code': 200, 'content': {'agent': request.url}}
            cs = SimpleCookie()
            cookies = request.headers.get('Cookie')
            if cookies is not None:
                cs.load(str(cookies))
            public_key = None
            for c in cs:
                if c == 'agent-login':
                    json_cookie = json.loads(
                        base64.b64decode(cs[c].value).decode('utf-8'))
                    public_key = bakery.PublicKey.deserialize(
                        json_cookie.get('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}}
コード例 #13
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
コード例 #14
0
 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')])
コード例 #15
0
    def test_capability(self):
        locator = _DischargerLocator()
        ids = _IdService('ids', locator, self)
        auth = _OpAuthorizer(
            {macaroonbakery.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, [macaroonbakery.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],
            [macaroonbakery.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)
コード例 #16
0
 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(
                 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()}}
コード例 #17
0
    def test_multiple_capabilities(self):
        locator = _DischargerLocator()
        ids = _IdService('ids', locator, self)
        auth = _OpAuthorizer({
            macaroonbakery.Op(entity='e1', action='read'): {'alice'},
            macaroonbakery.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, [macaroonbakery.Op(entity='e1', action='read')])
        ctx = test_context.with_value(_DISCHARGE_USER_KEY, 'bob')
        m2 = _Client(locator).discharged_capability(
            ctx, ts, [macaroonbakery.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], [
            macaroonbakery.Op(entity='e1', action='read'),
            macaroonbakery.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)
コード例 #18
0
def _decode_macaroon_id(id):
    storage_id = b''
    base64_decoded = False
    first = id[:1]
    if first == b'A':
        # The first byte is not a version number and it's 'A', which is the
        # base64 encoding of the top 6 bits (all zero) of the version number 2
        # or 3, so we assume that it's the base64 encoding of a new-style
        # macaroon id, so we base64 decode it.
        #
        # Note that old-style ids always start with an ASCII character >= 4
        # (> 32 in fact) so this logic won't be triggered for those.
        try:
            dec = utils.raw_b64decode(id.decode('utf-8'))
            # Set the id only on success.
            id = dec
            base64_decoded = True
        except:
            # if it's a bad encoding, we'll get an error which is fine
            pass

    # Trim any extraneous information from the id before retrieving
    # it from storage, including the UUID that's added when
    # creating macaroons to make all macaroons unique even if
    # they're using the same root key.
    first = six.byte2int(id[:1])
    if first == macaroonbakery.BAKERY_V2:
        # Skip the UUID at the start of the id.
        storage_id = id[1 + 16:]
    if first == macaroonbakery.BAKERY_V3:
        try:
            id1 = id_pb2.MacaroonId.FromString(id[1:])
        except google.protobuf.message.DecodeError:
            raise macaroonbakery.VerificationError(
                'no operations found in macaroon')
        if len(id1.ops) == 0 or len(id1.ops[0].actions) == 0:
            raise macaroonbakery.VerificationError(
                'no operations found in macaroon')

        ops = []
        for op in id1.ops:
            for action in op.actions:
                ops.append(macaroonbakery.Op(op.entity, action))
        return id1.storageId, ops

    if not base64_decoded and _is_lower_case_hex_char(first):
        # It's an old-style id, probably with a hyphenated UUID.
        # so trim that off.
        last = id.rfind(b'-')
        if last >= 0:
            storage_id = id[0:last]
    return storage_id, [macaroonbakery.LOGIN_OP]
コード例 #19
0
    def test_partially_authorized_request(self):
        locator = _DischargerLocator()
        ids = _IdService('ids', locator, self)
        auth = _OpAuthorizer({
            macaroonbakery.Op(entity='e1', action='read'): {'alice'},
            macaroonbakery.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, [macaroonbakery.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, [
            macaroonbakery.Op(entity='e1', action='read'),
            macaroonbakery.Op(entity='e2', action='read')
        ])
コード例 #20
0
    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)
コード例 #21
0
 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])
コード例 #22
0
    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 == macaroonbakery.Op(entity='something',
                                                action='read')):
                return True, [
                    checkers.Caveat(condition='question',
                                    location='other third party')
                ]
            return False, None

        auth = macaroonbakery.AuthorizerFunc(authorize_with_tp_discharge)
        ts = _Service('myservice', auth, ids, locator)

        class _LocalDischargeChecker(macaroonbakery.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=macaroonbakery.generate_key(),
            checker=_LocalDischargeChecker(),
            locator=locator,
        )
        client = _Client(locator)
        ctx = test_context.with_value(_DISCHARGE_USER_KEY, 'bob')
        client.do(ctx, ts,
                  [macaroonbakery.Op(entity='something', action='read')])
        self.assertEqual(self._discharges, [
            _DischargeRecord(location='ids', user='******'),
            _DischargeRecord(location='other third party', user='******')
        ])
コード例 #23
0
    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])
コード例 #24
0
    def test_ops_stored_only_once(self):
        st = macaroonbakery.MemoryOpsStore()
        test_oven = macaroonbakery.Oven(ops_store=st)

        ops = [macaroonbakery.Op('one', 'read'),
               macaroonbakery.Op('one', 'write'),
               macaroonbakery.Op('two', 'read')]

        m = test_oven.macaroon(macaroonbakery.LATEST_BAKERY_VERSION, AGES,
                               None, ops)
        got_ops, conds = test_oven.macaroon_ops([m.macaroon])
        self.assertEquals(macaroonbakery.canonical_ops(got_ops),
                          macaroonbakery.canonical_ops(ops))

        # Make another macaroon containing the same ops in a different order.
        ops = [macaroonbakery.Op('one', 'write'),
               macaroonbakery.Op('one', 'read'),
               macaroonbakery.Op('one', 'read'),
               macaroonbakery.Op('two', 'read')]
        test_oven.macaroon(macaroonbakery.LATEST_BAKERY_VERSION, AGES, None,
                           ops)
        self.assertEquals(len(st._store), 1)
コード例 #25
0
    def test_capability_combines_first_party_caveats(self):
        locator = _DischargerLocator()
        ids = _IdService('ids', locator, self)
        auth = _OpAuthorizer({
            macaroonbakery.Op(entity='e1', action='read'): {'alice'},
            macaroonbakery.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, [macaroonbakery.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, [macaroonbakery.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, [
            macaroonbakery.Op(entity='e1', action='read'),
            macaroonbakery.Op(entity='e2', action='read')
        ])
        self.assertEqual(_macaroon_conditions(m.macaroon.caveats, False), [
            'true 1',
            'true 2',
            'true 3',
            'true 4',
        ])
コード例 #26
0
 def test_canonical_ops(self):
     canonical_ops_tests = (
         ('empty array', [], []),
         ('one element', [macaroonbakery.Op('a', 'a')],
          [macaroonbakery.Op('a', 'a')]),
         ('all in order',
          [macaroonbakery.Op('a', 'a'), macaroonbakery.Op('a', 'b'),
           macaroonbakery.Op('c', 'c')],
          [macaroonbakery.Op('a', 'a'), macaroonbakery.Op('a', 'b'),
           macaroonbakery.Op('c', 'c')]),
         ('out of order',
          [macaroonbakery.Op('c', 'c'), macaroonbakery.Op('a', 'b'),
           macaroonbakery.Op('a', 'a')],
          [macaroonbakery.Op('a', 'a'), macaroonbakery.Op('a', 'b'),
           macaroonbakery.Op('c', 'c')]),
         ('with duplicates',
          [macaroonbakery.Op('c', 'c'), macaroonbakery.Op('a', 'b'),
           macaroonbakery.Op('a', 'a'), macaroonbakery.Op('c', 'a'),
           macaroonbakery.Op('c', 'b'), macaroonbakery.Op('c', 'c'),
           macaroonbakery.Op('a', 'a')],
          [macaroonbakery.Op('a', 'a'), macaroonbakery.Op('a', 'b'),
           macaroonbakery.Op('c', 'a'), macaroonbakery.Op('c', 'b'),
           macaroonbakery.Op('c', 'c')]),
         ('make sure we\'ve got the fields right',
          [macaroonbakery.Op(entity='read', action='two'),
           macaroonbakery.Op(entity='read', action='one'),
           macaroonbakery.Op(entity='write', action='one')],
          [macaroonbakery.Op(entity='read', action='one'),
           macaroonbakery.Op(entity='read', action='two'),
           macaroonbakery.Op(entity='write', action='one')])
     )
     for about, ops, expected in canonical_ops_tests:
         new_ops = copy.copy(ops)
         canonical_ops = macaroonbakery.canonical_ops(new_ops)
         self.assertEquals(canonical_ops, expected)
         # Verify that the original array isn't changed.
         self.assertEquals(new_ops, ops)
コード例 #27
0
    def test_capability_multiple_entities(self):
        locator = _DischargerLocator()
        ids = _IdService('ids', locator, self)
        auth = _OpAuthorizer({
            macaroonbakery.Op(entity='e1', action='read'): {'bob'},
            macaroonbakery.Op(entity='e2', action='read'): {'bob'},
            macaroonbakery.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, [
            macaroonbakery.Op(entity='e1', action='read'),
            macaroonbakery.Op(entity='e2', action='read'),
            macaroonbakery.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], [
            macaroonbakery.Op(entity='e1', action='read'),
            macaroonbakery.Op(entity='e2', action='read'),
            macaroonbakery.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], [
            macaroonbakery.Op(entity='e2', action='read'),
            macaroonbakery.Op(entity='e3', action='read')
        ])
        ts.do(test_context, [m],
              [macaroonbakery.Op(entity='e3', action='read')])
コード例 #28
0
    from http.server import HTTPServer, BaseHTTPRequestHandler
import threading

from httmock import (
    HTTMock,
    urlmatch
)
import requests
from six.moves.urllib.parse import parse_qs

import macaroonbakery as bakery
import macaroonbakery.httpbakery as httpbakery
import macaroonbakery.checkers as checkers

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, *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,
                caveats=None, ops=[TEST_OP])
コード例 #29
0
    def test_combine_capabilities(self):
        locator = _DischargerLocator()
        ids = _IdService('ids', locator, self)
        auth = _OpAuthorizer({
            macaroonbakery.Op(entity='e1', action='read'): {'alice'},
            macaroonbakery.Op(entity='e2', action='read'): {'bob'},
            macaroonbakery.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, [
            macaroonbakery.Op(entity='e1', action='read'),
            macaroonbakery.Op(entity='e3', action='read')
        ])
        ctx = test_context.with_value(_DISCHARGE_USER_KEY, 'bob')
        m2 = _Client(locator).discharged_capability(
            ctx, ts, [macaroonbakery.Op(entity='e2', action='read')])

        m = ts.capability(test_context, [m1, m2], [
            macaroonbakery.Op(entity='e1', action='read'),
            macaroonbakery.Op(entity='e2', action='read'),
            macaroonbakery.Op(entity='e3', action='read')
        ])
        ts.do(test_context, [[m.macaroon]], [
            macaroonbakery.Op(entity='e1', action='read'),
            macaroonbakery.Op(entity='e2', action='read'),
            macaroonbakery.Op(entity='e3', action='read')
        ])
コード例 #30
0
    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]))