Exemple #1
0
 def testFromBERNullKnownValues(self):
     """BERNull(encoded="...") should give known result with known input"""
     encoded = [0x05, 0x00]
     m = s(*encoded)
     result, bytes = pureber.berDecodeObject(pureber.BERDecoderContext(), m)
     self.assertEquals(bytes, len(m))
     assert isinstance(result, pureber.BERNull)
     assert 0x05 == result.tag
Exemple #2
0
 def testSanity(self):
     """BEREnumerated(encoded=BEREnumerated(n)).value==n for -1000..1000"""
     for n in range(-1000, 1001, 10):
         encoded = str(pureber.BEREnumerated(n))
         result, bytes = pureber.berDecodeObject(
             pureber.BERDecoderContext(), encoded)
         self.assertEquals(bytes, len(encoded))
         assert isinstance(result, pureber.BEREnumerated)
         result = result.value
         assert n == result
Exemple #3
0
 def testFromBEREnumeratedKnownValues(self):
     """BEREnumerated(encoded="...") should give known result with known input"""
     for integer, encoded in self.knownValues:
         m = s(*encoded)
         result, bytes = pureber.berDecodeObject(
             pureber.BERDecoderContext(), m)
         self.assertEquals(bytes, len(m))
         assert isinstance(result, pureber.BEREnumerated)
         result = result.value
         assert integer == result
Exemple #4
0
 def testSanity(self):
     """BEROctetString(encoded=BEROctetString(n*'x')).value==n*'x' for some values of n"""
     for n in 0, 1, 2, 3, 4, 5, 6, 100, 126, 127, 128, 129, 1000, 2000:
         encoded = str(pureber.BEROctetString(n * 'x'))
         result, bytes = pureber.berDecodeObject(
             pureber.BERDecoderContext(), encoded)
         self.assertEquals(bytes, len(encoded))
         assert isinstance(result, pureber.BEROctetString)
         result = result.value
         assert n * 'x' == result
Exemple #5
0
    def testPartialBERSequenceEncodings(self):
        """BERSequence(encoded="...") with too short input should throw BERExceptionInsufficientData"""
        m = str(pureber.BERSequence([pureber.BERInteger(2)]))
        assert len(m) == 5

        self.assertRaises(pureber.BERExceptionInsufficientData,
                          pureber.berDecodeObject, pureber.BERDecoderContext(),
                          m[:4])
        self.assertRaises(pureber.BERExceptionInsufficientData,
                          pureber.berDecodeObject, pureber.BERDecoderContext(),
                          m[:3])
        self.assertRaises(pureber.BERExceptionInsufficientData,
                          pureber.berDecodeObject, pureber.BERDecoderContext(),
                          m[:2])
        self.assertRaises(pureber.BERExceptionInsufficientData,
                          pureber.berDecodeObject, pureber.BERDecoderContext(),
                          m[:1])
        self.assertEquals((None, 0),
                          pureber.berDecodeObject(pureber.BERDecoderContext(),
                                                  ''))
Exemple #6
0
 def testPartial(self):
     """LDAPClass(encoded="...") with too short input should throw BERExceptionInsufficientData"""
     for klass, args, kwargs, decoder, encoded in self.knownValues:
         if decoder is None:
             decoder = pureldap.LDAPBERDecoderContext(
                 fallback=pureber.BERDecoderContext())
         for i in xrange(1, len(encoded)):
             m = s(*encoded)[:i]
             self.assertRaises(pureber.BERExceptionInsufficientData,
                               pureber.berDecodeObject, decoder, m)
         self.assertEquals((None, 0), pureber.berDecodeObject(decoder, ''))
Exemple #7
0
 def testFromBEROctetStringKnownValues(self):
     """BEROctetString(encoded="...") should give known result with known input"""
     for st, encoded in self.knownValues:
         m = s(*encoded)
         result, bytes = pureber.berDecodeObject(
             pureber.BERDecoderContext(), m)
         self.assertEquals(bytes, len(m))
         assert isinstance(result, pureber.BEROctetString)
         result = str(result)
         result = map(ord, result)
         assert encoded == result
Exemple #8
0
 def testFromBERSequenceKnownValues(self):
     """BERSequence(encoded="...") should give known result with known input"""
     for content, encoded in self.knownValues:
         m = s(*encoded)
         result, bytes = pureber.berDecodeObject(
             pureber.BERDecoderContext(), m)
         self.assertEquals(bytes, len(m))
         assert isinstance(result, pureber.BERSequence)
         result = result.data
         assert len(content) == len(result)
         for i in xrange(len(content)):
             assert content[i] == result[i]
         assert content == result
Exemple #9
0
 def test_length(self):
     """LDAPFilter_substrings.substrings behaves like a proper list."""
     decoder = pureldap.LDAPBERDecoderContext(
         fallback=pureber.BERDecoderContext())
     filt = pureldap.LDAPFilter_substrings.fromBER(
         tag=pureldap.LDAPFilter_substrings.tag,
         content=s(0x04, 4, 'mail', 0x30, 6, 0x80, 4, 'foo@'),
         berdecoder=decoder)
     # The confusion that used to occur here was because
     # filt.substrings was left as a BERSequence, which under the
     # current str()-to-wire-protocol system had len() > 1 even
     # when empty, and that tripped e.g. entry.match()
     self.assertEquals(len(filt.substrings), 1)
Exemple #10
0
    def testFromLDAP(self):
        """LDAPClass(encoded="...") should give known result with known input"""
        for klass, args, kwargs, decoder, encoded in self.knownValues:
            if decoder is None:
                decoder = pureldap.LDAPBERDecoderContext(
                    fallback=pureber.BERDecoderContext())
            m = s(*encoded)
            result, bytes = pureber.berDecodeObject(decoder, m)
            self.assertEquals(bytes, len(m))

            shouldBe = klass(*args, **kwargs)
            #TODO shouldn't use str below
            assert str(result)==str(shouldBe), \
                   "Class %s(*%s, **%s) doesn't decode properly: " \
                   "%s != %s" % (klass.__name__,
                                 repr(args), repr(kwargs),
                                 repr(result), repr(shouldBe))
Exemple #11
0
class LDAPClient(protocol.Protocol):
    """An LDAP client"""
    debug = False

    def __init__(self):
        self.onwire = {}
        self.buffer = ''
        self.connected = None

    berdecoder = pureldap.LDAPBERDecoderContext_TopLevel(
        inherit=pureldap.LDAPBERDecoderContext_LDAPMessage(
            fallback=pureldap.LDAPBERDecoderContext(
                fallback=pureber.BERDecoderContext()),
            inherit=pureldap.LDAPBERDecoderContext(
                fallback=pureber.BERDecoderContext())))

    def dataReceived(self, recd):
        self.buffer += recd
        while 1:
            try:
                o, bytes = pureber.berDecodeObject(self.berdecoder,
                                                   self.buffer)
            except pureldap.BERExceptionInsufficientData:
                o, bytes = None, 0
            self.buffer = self.buffer[bytes:]
            if not o:
                break
            self.handle(o)

    def connectionMade(self):
        """TCP connection has opened"""
        self.connected = 1

    def connectionLost(self, reason=protocol.connectionDone):
        """Called when TCP connection has been lost"""
        self.connected = 0
        # notify handlers of operations in flight
        while self.onwire:
            k, v = self.onwire.popitem()
            d, _, _, _ = v
            d.errback(reason)

    def _send(self, op):
        if not self.connected:
            raise LDAPClientConnectionLostException()
        msg = pureldap.LDAPMessage(op)
        if self.debug:
            log.debug('C->S %s' % repr(msg))
        assert not self.onwire.has_key(msg.id)
        return msg

    def _cbSend(self, msg, d):
        d.callback(msg)
        return True

    def send(self, op):
        """
        Send an LDAP operation to the server.

        @param op: the operation to send

        @type op: LDAPProtocolRequest

        @return: the response from server

        @rtype: Deferred LDAPProtocolResponse
        """
        msg = self._send(op)
        assert op.needs_answer
        d = defer.Deferred()
        self.onwire[msg.id] = (d, None, None, None)
        self.transport.write(str(msg))
        return d

    def send_multiResponse(self, op, handler, *args, **kwargs):
        """
        Send an LDAP operation to the server, expecting one or more
        responses.

        @param op: the operation to send

        @type op: LDAPProtocolRequest

        @param handler: a callable that will be called for each
        response. It should return a boolean, whether this was the
        final response.

        @param args: positional arguments to pass to handler

        @param kwargs: keyword arguments to pass to handler

        @return: the result from the last handler as a deferred that
        completes when the last response has been received

        @rtype: Deferred LDAPProtocolResponse
        """
        msg = self._send(op)
        assert op.needs_answer
        d = defer.Deferred()
        self.onwire[msg.id] = (d, handler, args, kwargs)
        self.transport.write(str(msg))
        return d

    def send_noResponse(self, op):
        """
        Send an LDAP operation to the server, with no response
        expected.

        @param op: the operation to send
        @type op: LDAPProtocolRequest
        """
        msg = self._send(op)
        assert not op.needs_answer
        self.transport.write(str(msg))

    def unsolicitedNotification(self, msg):
        log.msg("Got unsolicited notification: %s" % repr(msg))

    def handle(self, msg):
        assert isinstance(msg.value, pureldap.LDAPProtocolResponse)
        if self.debug:
            log.debug('C<-S %s' % repr(msg))

        if msg.id == 0:
            self.unsolicitedNotification(msg.value)
        else:
            d, handler, args, kwargs = self.onwire[msg.id]

            if handler is None:
                assert args is None
                assert kwargs is None
                d.callback(msg.value)
                del self.onwire[msg.id]
            else:
                assert args is not None
                assert kwargs is not None
                # Return true to mark request as fully handled
                if handler(msg.value, *args, **kwargs):
                    del self.onwire[msg.id]

    ##Bind
    def bind(self, dn='', auth=''):
        """
        @depreciated: Use e.bind(auth).

        @todo: Remove this method when there are no callers.
        """
        if not self.connected:
            raise LDAPClientConnectionLostException()
        else:
            r = pureldap.LDAPBindRequest(dn=dn, auth=auth)
            d = self.send(r)
            d.addCallback(self._handle_bind_msg)
        return d

    def _handle_bind_msg(self, msg):
        assert isinstance(msg, pureldap.LDAPBindResponse)
        assert msg.referral is None  #TODO
        if msg.resultCode != ldaperrors.Success.resultCode:
            raise ldaperrors.get(msg.resultCode, msg.errorMessage)
        return (msg.matchedDN, msg.serverSaslCreds)

    ##Unbind
    def unbind(self):
        if not self.connected:
            raise Exception(
                "Not connected (TODO)")  #TODO make this a real object
        r = pureldap.LDAPUnbindRequest()
        self.send_noResponse(r)
        self.transport.loseConnection()

    def _cbStartTLS(self, msg, ctx):
        assert isinstance(msg, pureldap.LDAPExtendedResponse)
        assert msg.referral is None  #TODO
        if msg.resultCode != ldaperrors.Success.resultCode:
            raise ldaperrors.get(msg.resultCode, msg.errorMessage)

        self.transport.startTLS(ctx)
        return self

    def startTLS(self, ctx=None):
        """
        Start Transport Layer Security.

        It is the callers responsibility to make sure other things
        are not happening at the same time.

        @todo: server hostname check, see rfc2830 section 3.6.

        """
        if ctx is None:
            ctx = ssl.ClientContextFactory()
        # we always delay by one event loop iteration to make
        # sure the previous handler has exited and self.onwire
        # has been cleaned up
        d = defer.Deferred()
        d.addCallback(self._startTLS)
        reactor.callLater(0, d.callback, ctx)
        return d

    def _startTLS(self, ctx):
        if not self.connected:
            raise LDAPClientConnectionLostException
        elif self.onwire:
            raise LDAPStartTLSBusyError, self.onwire
        else:
            op = pureldap.LDAPStartTLSRequest()
            d = self.send(op)
            d.addCallback(self._cbStartTLS, ctx)
            return d
class ServiceBindingProxy(unittest.TestCase):
    berdecoder = pureldap.LDAPBERDecoderContext_TopLevel(
        inherit=pureldap.LDAPBERDecoderContext_LDAPMessage(
            fallback=pureldap.LDAPBERDecoderContext(
                fallback=pureber.BERDecoderContext()),
            inherit=pureldap.LDAPBERDecoderContext(
                fallback=pureber.BERDecoderContext())))

    def createServer(self, services, fallback=None, responses=[]):
        server = testutil.createServer(
            lambda config: svcbindproxy.ServiceBindingProxy(
                config=config,
                services=services,
                fallback=fallback,
            ),
            baseDN='dc=example,dc=com',
            *responses)
        server.now = '20050213140302Z'
        server.timestamp = lambda: server.now
        return server

    def test_bind_noMatchingServicesFound_noFallback(self):
        server = self.createServer(
            services=[
                'svc1',
                'svc2',
                'svc3',
            ],
            fallback=False,
            responses=[
                [pureldap.LDAPSearchResultDone(ldaperrors.Success.resultCode)],
                [pureldap.LDAPSearchResultDone(ldaperrors.Success.resultCode)],
                [pureldap.LDAPSearchResultDone(ldaperrors.Success.resultCode)],
            ])
        server.dataReceived(
            str(
                pureldap.LDAPMessage(pureldap.LDAPBindRequest(
                    dn='cn=jack,dc=example,dc=com', auth='s3krit'),
                                     id=4)))
        reactor.iterate()  #TODO
        client = server.client

        client.assertSent(
            pureldap.LDAPSearchRequest(
                baseObject='dc=example,dc=com',
                derefAliases=0,
                sizeLimit=0,
                timeLimit=0,
                typesOnly=0,
                filter=ldapfilter.parseFilter(
                    '(&' + '(objectClass=serviceSecurityObject)' +
                    '(owner=cn=jack,dc=example,dc=com)' + '(cn=svc1)' +
                    ('(|(!(validFrom=*))(validFrom<=%s))' % server.now) +
                    ('(|(!(validUntil=*))(validUntil>=%s))' % server.now) +
                    ')'),
                attributes=('1.1', )),
            pureldap.LDAPSearchRequest(
                baseObject='dc=example,dc=com',
                derefAliases=0,
                sizeLimit=0,
                timeLimit=0,
                typesOnly=0,
                filter=ldapfilter.parseFilter(
                    '(&' + '(objectClass=serviceSecurityObject)' +
                    '(owner=cn=jack,dc=example,dc=com)' + '(cn=svc2)' +
                    ('(|(!(validFrom=*))(validFrom<=%s))' % server.now) +
                    ('(|(!(validUntil=*))(validUntil>=%s))' % server.now) +
                    ')'),
                attributes=('1.1', )),
            pureldap.LDAPSearchRequest(
                baseObject='dc=example,dc=com',
                derefAliases=0,
                sizeLimit=0,
                timeLimit=0,
                typesOnly=0,
                filter=ldapfilter.parseFilter(
                    '(&' + '(objectClass=serviceSecurityObject)' +
                    '(owner=cn=jack,dc=example,dc=com)' + '(cn=svc3)' +
                    ('(|(!(validFrom=*))(validFrom<=%s))' % server.now) +
                    ('(|(!(validUntil=*))(validUntil>=%s))' % server.now) +
                    ')'),
                attributes=('1.1', )),
        )
        self.assertEquals(
            server.transport.value(),
            str(
                pureldap.LDAPMessage(pureldap.LDAPBindResponse(
                    resultCode=ldaperrors.LDAPInvalidCredentials.resultCode),
                                     id=4)))

    def test_bind_noMatchingServicesFound_fallback_success(self):
        server = self.createServer(
            services=[
                'svc1',
                'svc2',
                'svc3',
            ],
            fallback=True,
            responses=[
                [pureldap.LDAPSearchResultDone(ldaperrors.Success.resultCode)],
                [pureldap.LDAPSearchResultDone(ldaperrors.Success.resultCode)],
                [pureldap.LDAPSearchResultDone(ldaperrors.Success.resultCode)],
                [
                    pureldap.LDAPBindResponse(
                        resultCode=ldaperrors.Success.resultCode)
                ],
            ])
        server.dataReceived(
            str(
                pureldap.LDAPMessage(pureldap.LDAPBindRequest(
                    dn='cn=jack,dc=example,dc=com', auth='s3krit'),
                                     id=4)))
        reactor.iterate()  #TODO
        client = server.client

        client.assertSent(
            pureldap.LDAPSearchRequest(
                baseObject='dc=example,dc=com',
                derefAliases=0,
                sizeLimit=0,
                timeLimit=0,
                typesOnly=0,
                filter=ldapfilter.parseFilter(
                    '(&' + '(objectClass=serviceSecurityObject)' +
                    '(owner=cn=jack,dc=example,dc=com)' + '(cn=svc1)' +
                    ('(|(!(validFrom=*))(validFrom<=%s))' % server.now) +
                    ('(|(!(validUntil=*))(validUntil>=%s))' % server.now) +
                    ')'),
                attributes=('1.1', )),
            pureldap.LDAPSearchRequest(
                baseObject='dc=example,dc=com',
                derefAliases=0,
                sizeLimit=0,
                timeLimit=0,
                typesOnly=0,
                filter=ldapfilter.parseFilter(
                    '(&' + '(objectClass=serviceSecurityObject)' +
                    '(owner=cn=jack,dc=example,dc=com)' + '(cn=svc2)' +
                    ('(|(!(validFrom=*))(validFrom<=%s))' % server.now) +
                    ('(|(!(validUntil=*))(validUntil>=%s))' % server.now) +
                    ')'),
                attributes=('1.1', )),
            pureldap.LDAPSearchRequest(
                baseObject='dc=example,dc=com',
                derefAliases=0,
                sizeLimit=0,
                timeLimit=0,
                typesOnly=0,
                filter=ldapfilter.parseFilter(
                    '(&' + '(objectClass=serviceSecurityObject)' +
                    '(owner=cn=jack,dc=example,dc=com)' + '(cn=svc3)' +
                    ('(|(!(validFrom=*))(validFrom<=%s))' % server.now) +
                    ('(|(!(validUntil=*))(validUntil>=%s))' % server.now) +
                    ')'),
                attributes=('1.1', )),
            pureldap.LDAPBindRequest(dn='cn=jack,dc=example,dc=com',
                                     auth='s3krit'))
        self.assertEquals(
            server.transport.value(),
            str(
                pureldap.LDAPMessage(pureldap.LDAPBindResponse(
                    resultCode=ldaperrors.Success.resultCode),
                                     id=4)))

    def test_bind_noMatchingServicesFound_fallback_badAuth(self):
        server = self.createServer(
            services=[
                'svc1',
                'svc2',
                'svc3',
            ],
            fallback=True,
            responses=[
                [pureldap.LDAPSearchResultDone(ldaperrors.Success.resultCode)],
                [pureldap.LDAPSearchResultDone(ldaperrors.Success.resultCode)],
                [pureldap.LDAPSearchResultDone(ldaperrors.Success.resultCode)],
                [
                    pureldap.LDAPBindResponse(
                        resultCode=ldaperrors.LDAPInvalidCredentials.resultCode
                    ),
                ],
            ])
        server.dataReceived(
            str(
                pureldap.LDAPMessage(pureldap.LDAPBindRequest(
                    dn='cn=jack,dc=example,dc=com', auth='wrong-s3krit'),
                                     id=4)))
        reactor.iterate()  #TODO
        client = server.client

        client.assertSent(
            pureldap.LDAPSearchRequest(
                baseObject='dc=example,dc=com',
                derefAliases=0,
                sizeLimit=0,
                timeLimit=0,
                typesOnly=0,
                filter=ldapfilter.parseFilter(
                    '(&' + '(objectClass=serviceSecurityObject)' +
                    '(owner=cn=jack,dc=example,dc=com)' + '(cn=svc1)' +
                    ('(|(!(validFrom=*))(validFrom<=%s))' % server.now) +
                    ('(|(!(validUntil=*))(validUntil>=%s))' % server.now) +
                    ')'),
                attributes=('1.1', )),
            pureldap.LDAPSearchRequest(
                baseObject='dc=example,dc=com',
                derefAliases=0,
                sizeLimit=0,
                timeLimit=0,
                typesOnly=0,
                filter=ldapfilter.parseFilter(
                    '(&' + '(objectClass=serviceSecurityObject)' +
                    '(owner=cn=jack,dc=example,dc=com)' + '(cn=svc2)' +
                    ('(|(!(validFrom=*))(validFrom<=%s))' % server.now) +
                    ('(|(!(validUntil=*))(validUntil>=%s))' % server.now) +
                    ')'),
                attributes=('1.1', )),
            pureldap.LDAPSearchRequest(
                baseObject='dc=example,dc=com',
                derefAliases=0,
                sizeLimit=0,
                timeLimit=0,
                typesOnly=0,
                filter=ldapfilter.parseFilter(
                    '(&' + '(objectClass=serviceSecurityObject)' +
                    '(owner=cn=jack,dc=example,dc=com)' + '(cn=svc3)' +
                    ('(|(!(validFrom=*))(validFrom<=%s))' % server.now) +
                    ('(|(!(validUntil=*))(validUntil>=%s))' % server.now) +
                    ')'),
                attributes=('1.1', )),
            pureldap.LDAPBindRequest(dn='cn=jack,dc=example,dc=com',
                                     auth='wrong-s3krit'))
        self.assertEquals(
            server.transport.value(),
            str(
                pureldap.LDAPMessage(pureldap.LDAPBindResponse(
                    resultCode=ldaperrors.LDAPInvalidCredentials.resultCode),
                                     id=4)))

    def test_bind_match_success(self):
        server = self.createServer(
            services=[
                'svc1',
                'svc2',
                'svc3',
            ],
            fallback=True,
            responses=[

                # svc1
                [
                    pureldap.LDAPSearchResultEntry(
                        r'cn=svc1+owner=cn\=jack\,dc\=example\,dc\=com,dc=example,dc=com',
                        attributes=[]),
                    pureldap.LDAPSearchResultDone(
                        ldaperrors.Success.resultCode)
                ],
                [
                    pureldap.LDAPBindResponse(
                        resultCode=ldaperrors.Success.resultCode)
                ],
            ])

        server.dataReceived(
            str(
                pureldap.LDAPMessage(pureldap.LDAPBindRequest(
                    dn='cn=jack,dc=example,dc=com', auth='secret'),
                                     id=4)))
        reactor.iterate()  #TODO
        client = server.client

        client.assertSent(
            pureldap.LDAPSearchRequest(
                baseObject='dc=example,dc=com',
                derefAliases=0,
                sizeLimit=0,
                timeLimit=0,
                typesOnly=0,
                filter=ldapfilter.parseFilter(
                    '(&' + '(objectClass=serviceSecurityObject)' +
                    '(owner=cn=jack,dc=example,dc=com)' + '(cn=svc1)' +
                    ('(|(!(validFrom=*))(validFrom<=%s))' % server.now) +
                    ('(|(!(validUntil=*))(validUntil>=%s))' % server.now) +
                    ')'),
                attributes=('1.1', )),
            pureldap.LDAPBindRequest(
                dn=
                r'cn=svc1+owner=cn\=jack\,dc\=example\,dc\=com,dc=example,dc=com',
                auth='secret'),
        )
        self.assertEquals(
            server.transport.value(),
            str(
                pureldap.LDAPMessage(pureldap.LDAPBindResponse(
                    resultCode=ldaperrors.Success.resultCode,
                    matchedDN='cn=jack,dc=example,dc=com'),
                                     id=4)))

    def test_bind_match_success_later(self):
        server = self.createServer(
            services=[
                'svc1',
                'svc2',
                'svc3',
            ],
            fallback=True,
            responses=[

                # svc1
                [
                    pureldap.LDAPSearchResultEntry(
                        r'cn=svc1+owner=cn\=jack\,dc\=example\,dc\=com,dc=example,dc=com',
                        attributes=[]),
                    pureldap.LDAPSearchResultDone(
                        ldaperrors.Success.resultCode)
                ],
                [
                    pureldap.LDAPBindResponse(
                        resultCode=ldaperrors.LDAPInvalidCredentials.resultCode
                    )
                ],

                # svc2
                [pureldap.LDAPSearchResultDone(ldaperrors.Success.resultCode)],

                # svc3
                [
                    pureldap.LDAPSearchResultEntry(
                        r'cn=svc3+owner=cn\=jack\,dc\=example\,dc\=com,dc=example,dc=com',
                        attributes=[]),
                    pureldap.LDAPSearchResultDone(
                        ldaperrors.Success.resultCode)
                ],
                [
                    pureldap.LDAPBindResponse(
                        resultCode=ldaperrors.Success.resultCode)
                ],
            ])

        server.dataReceived(
            str(
                pureldap.LDAPMessage(pureldap.LDAPBindRequest(
                    dn='cn=jack,dc=example,dc=com', auth='secret'),
                                     id=4)))
        reactor.iterate()  #TODO
        client = server.client

        client.assertSent(
            pureldap.LDAPSearchRequest(
                baseObject='dc=example,dc=com',
                derefAliases=0,
                sizeLimit=0,
                timeLimit=0,
                typesOnly=0,
                filter=ldapfilter.parseFilter(
                    '(&' + '(objectClass=serviceSecurityObject)' +
                    '(owner=cn=jack,dc=example,dc=com)' + '(cn=svc1)' +
                    ('(|(!(validFrom=*))(validFrom<=%s))' % server.now) +
                    ('(|(!(validUntil=*))(validUntil>=%s))' % server.now) +
                    ')'),
                attributes=('1.1', )),
            pureldap.LDAPBindRequest(
                dn=
                r'cn=svc1+owner=cn\=jack\,dc\=example\,dc\=com,dc=example,dc=com',
                auth='secret'),
            pureldap.LDAPSearchRequest(
                baseObject='dc=example,dc=com',
                derefAliases=0,
                sizeLimit=0,
                timeLimit=0,
                typesOnly=0,
                filter=ldapfilter.parseFilter(
                    '(&' + '(objectClass=serviceSecurityObject)' +
                    '(owner=cn=jack,dc=example,dc=com)' + '(cn=svc2)' +
                    ('(|(!(validFrom=*))(validFrom<=%s))' % server.now) +
                    ('(|(!(validUntil=*))(validUntil>=%s))' % server.now) +
                    ')'),
                attributes=('1.1', )),
            pureldap.LDAPSearchRequest(
                baseObject='dc=example,dc=com',
                derefAliases=0,
                sizeLimit=0,
                timeLimit=0,
                typesOnly=0,
                filter=ldapfilter.parseFilter(
                    '(&' + '(objectClass=serviceSecurityObject)' +
                    '(owner=cn=jack,dc=example,dc=com)' + '(cn=svc3)' +
                    ('(|(!(validFrom=*))(validFrom<=%s))' % server.now) +
                    ('(|(!(validUntil=*))(validUntil>=%s))' % server.now) +
                    ')'),
                attributes=('1.1', )),
            pureldap.LDAPBindRequest(
                dn=
                'cn=svc3+owner=cn\=jack\,dc\=example\,dc\=com,dc=example,dc=com',
                auth='secret'),
        )
        self.assertEquals(
            server.transport.value(),
            str(
                pureldap.LDAPMessage(pureldap.LDAPBindResponse(
                    resultCode=ldaperrors.Success.resultCode,
                    matchedDN='cn=jack,dc=example,dc=com'),
                                     id=4)))

    def test_bind_match_badAuth(self):
        server = self.createServer(
            services=[
                'svc1',
                'svc2',
                'svc3',
            ],
            fallback=True,
            responses=[

                # svc1
                [
                    pureldap.LDAPSearchResultEntry(
                        r'cn=svc1+owner=cn\=jack\,dc\=example\,dc\=com,dc=example,dc=com',
                        attributes=[]),
                    pureldap.LDAPSearchResultDone(
                        ldaperrors.Success.resultCode)
                ],
                [
                    pureldap.LDAPBindResponse(
                        resultCode=ldaperrors.LDAPInvalidCredentials.resultCode
                    )
                ],

                # svc2
                [pureldap.LDAPSearchResultDone(ldaperrors.Success.resultCode)],

                # svc3
                [
                    pureldap.LDAPSearchResultEntry(
                        r'cn=svc3+owner=cn\=jack\,dc\=example\,dc\=com,dc=example,dc=com',
                        attributes=[]),
                    pureldap.LDAPSearchResultDone(
                        ldaperrors.Success.resultCode)
                ],
                [
                    pureldap.LDAPBindResponse(
                        resultCode=ldaperrors.LDAPInvalidCredentials.resultCode
                    )
                ],
                [
                    pureldap.LDAPBindResponse(
                        resultCode=ldaperrors.LDAPInvalidCredentials.resultCode
                    )
                ],
            ])

        server.dataReceived(
            str(
                pureldap.LDAPMessage(pureldap.LDAPBindRequest(
                    dn='cn=jack,dc=example,dc=com', auth='wrong-s3krit'),
                                     id=4)))
        reactor.iterate()  #TODO
        client = server.client

        client.assertSent(
            pureldap.LDAPSearchRequest(
                baseObject='dc=example,dc=com',
                derefAliases=0,
                sizeLimit=0,
                timeLimit=0,
                typesOnly=0,
                filter=ldapfilter.parseFilter(
                    '(&' + '(objectClass=serviceSecurityObject)' +
                    '(owner=cn=jack,dc=example,dc=com)' + '(cn=svc1)' +
                    ('(|(!(validFrom=*))(validFrom<=%s))' % server.now) +
                    ('(|(!(validUntil=*))(validUntil>=%s))' % server.now) +
                    ')'),
                attributes=('1.1', )),
            pureldap.LDAPBindRequest(
                dn=
                r'cn=svc1+owner=cn\=jack\,dc\=example\,dc\=com,dc=example,dc=com',
                auth='wrong-s3krit'),
            pureldap.LDAPSearchRequest(
                baseObject='dc=example,dc=com',
                derefAliases=0,
                sizeLimit=0,
                timeLimit=0,
                typesOnly=0,
                filter=ldapfilter.parseFilter(
                    '(&' + '(objectClass=serviceSecurityObject)' +
                    '(owner=cn=jack,dc=example,dc=com)' + '(cn=svc2)' +
                    ('(|(!(validFrom=*))(validFrom<=%s))' % server.now) +
                    ('(|(!(validUntil=*))(validUntil>=%s))' % server.now) +
                    ')'),
                attributes=('1.1', )),
            pureldap.LDAPSearchRequest(
                baseObject='dc=example,dc=com',
                derefAliases=0,
                sizeLimit=0,
                timeLimit=0,
                typesOnly=0,
                filter=ldapfilter.parseFilter(
                    '(&' + '(objectClass=serviceSecurityObject)' +
                    '(owner=cn=jack,dc=example,dc=com)' + '(cn=svc3)' +
                    ('(|(!(validFrom=*))(validFrom<=%s))' % server.now) +
                    ('(|(!(validUntil=*))(validUntil>=%s))' % server.now) +
                    ')'),
                attributes=('1.1', )),
            pureldap.LDAPBindRequest(
                dn=
                'cn=svc3+owner=cn\=jack\,dc\=example\,dc\=com,dc=example,dc=com',
                auth='wrong-s3krit'),
            pureldap.LDAPBindRequest(version=3,
                                     dn='cn=jack,dc=example,dc=com',
                                     auth='wrong-s3krit'),
        )
        self.assertEquals(
            server.transport.value(),
            str(
                pureldap.LDAPMessage(pureldap.LDAPBindResponse(
                    resultCode=ldaperrors.LDAPInvalidCredentials.resultCode),
                                     id=4)))
Exemple #13
0
class BaseLDAPServer(protocol.Protocol):
    debug = False

    def __init__(self):
        self.buffer = ''
        self.connected = None

    berdecoder = pureldap.LDAPBERDecoderContext_TopLevel(
        inherit=pureldap.LDAPBERDecoderContext_LDAPMessage(
            fallback=pureldap.LDAPBERDecoderContext(
                fallback=pureber.BERDecoderContext()),
            inherit=pureldap.LDAPBERDecoderContext(
                fallback=pureber.BERDecoderContext())))

    def dataReceived(self, recd):
        self.buffer += recd
        while 1:
            try:
                o, bytes = pureber.berDecodeObject(self.berdecoder,
                                                   self.buffer)
            except pureldap.BERExceptionInsufficientData:
                o, bytes = None, 0
            self.buffer = self.buffer[bytes:]
            if o is None:
                break
            self.handle(o)

    def connectionMade(self):
        """TCP connection has opened"""
        self.connected = 1

    def connectionLost(self, reason=protocol.connectionDone):
        """Called when TCP connection has been lost"""
        self.connected = 0

    def queue(self, id, op):
        if not self.connected:
            raise LDAPServerConnectionLostException()
        msg = pureldap.LDAPMessage(op, id=id)
        if self.debug:
            log.debug('S->C %s' % repr(msg))
        self.transport.write(str(msg))

    def unsolicitedNotification(self, msg):
        log.msg("Got unsolicited notification: %s" % repr(msg))

    def checkControls(self, controls):
        if controls is not None:
            for controlType, criticality, controlValue in controls:
                if criticality:
                    raise ldaperrors.LDAPUnavailableCriticalExtension, \
                          'Unknown control %s' % controlType

    def handleUnknown(self, request, controls, callback):
        log.msg('Unknown request: %r' % request)
        msg = pureldap.LDAPExtendedResponse(
            resultCode=ldaperrors.LDAPProtocolError.resultCode,
            responseName='1.3.6.1.4.1.1466.20036',
            errorMessage='Unknown request')
        return msg

    def _cbLDAPError(self, reason, name):
        reason.trap(ldaperrors.LDAPException)
        return self._callErrorHandler(name=name,
                                      resultCode=reason.value.resultCode,
                                      errorMessage=reason.value.message)

    def _cbHandle(self, response, id):
        if response is not None:
            self.queue(id, response)

    def failDefault(self, resultCode, errorMessage):
        return pureldap.LDAPExtendedResponse(
            resultCode=resultCode,
            responseName='1.3.6.1.4.1.1466.20036',
            errorMessage=errorMessage)

    def _callErrorHandler(self, name, resultCode, errorMessage):
        errh = getattr(self, 'fail_' + name, self.failDefault)
        return errh(resultCode=resultCode, errorMessage=errorMessage)

    def _cbOtherError(self, reason, name):
        return self._callErrorHandler(
            name=name,
            resultCode=ldaperrors.LDAPProtocolError.resultCode,
            errorMessage=reason.getErrorMessage())

    def handle(self, msg):
        assert isinstance(msg.value, pureldap.LDAPProtocolRequest)
        if self.debug:
            log.debug('S<-C %s' % repr(msg))

        if msg.id == 0:
            self.unsolicitedNotification(msg.value)
        else:
            name = msg.value.__class__.__name__
            handler = getattr(self, 'handle_' + name, self.handleUnknown)
            d = defer.maybeDeferred(
                handler, msg.value, msg.controls,
                lambda response: self._cbHandle(response, msg.id))
            d.addErrback(self._cbLDAPError, name)
            d.addErrback(defer.logError)
            d.addErrback(self._cbOtherError, name)
            d.addCallback(self._cbHandle, msg.id)
Exemple #14
0
class LDAPServer(BaseLDAPServer):
    """An LDAP server"""
    boundUser = None

    fail_LDAPBindRequest = pureldap.LDAPBindResponse

    def handle_LDAPBindRequest(self, request, controls, reply):
        if request.version != 3:
            raise ldaperrors.LDAPProtocolError, \
                  'Version %u not supported' % request.version

        self.checkControls(controls)

        if request.dn == '':
            # anonymous bind
            self.boundUser = None
            return pureldap.LDAPBindResponse(resultCode=0)
        else:
            dn = distinguishedname.DistinguishedName(request.dn)
            root = interfaces.IConnectedLDAPEntry(self.factory)
            d = root.lookup(dn)

            def _noEntry(fail):
                fail.trap(ldaperrors.LDAPNoSuchObject)
                return None

            d.addErrback(_noEntry)

            def _gotEntry(entry, auth):
                if entry is None:
                    raise ldaperrors.LDAPInvalidCredentials

                d = entry.bind(auth)

                def _cb(entry):
                    self.boundUser = entry
                    msg = pureldap.LDAPBindResponse(
                        resultCode=ldaperrors.Success.resultCode,
                        matchedDN=str(entry.dn))
                    return msg

                d.addCallback(_cb)
                return d

            d.addCallback(_gotEntry, request.auth)

            return d

    def handle_LDAPUnbindRequest(self, request, controls, reply):
        # explicitly do not check unsupported critical controls -- we
        # have no way to return an error, anyway.
        self.transport.loseConnection()

    def getRootDSE(self, request, reply):
        root = interfaces.IConnectedLDAPEntry(self.factory)
        reply(
            pureldap.LDAPSearchResultEntry(
                objectName='',
                attributes=[
                    ('supportedLDAPVersion', ['3']),
                    ('namingContexts', [str(root.dn)]),
                    ('supportedExtension', [
                        pureldap.LDAPPasswordModifyRequest.oid,
                    ]),
                ],
            ))
        return pureldap.LDAPSearchResultDone(
            resultCode=ldaperrors.Success.resultCode)

    def _cbSearchGotBase(self, base, dn, request, reply):
        def _sendEntryToClient(entry):
            reply(
                pureldap.LDAPSearchResultEntry(
                    objectName=str(entry.dn),
                    attributes=entry.items(),
                ))

        d = base.search(filterObject=request.filter,
                        attributes=request.attributes,
                        scope=request.scope,
                        derefAliases=request.derefAliases,
                        sizeLimit=request.sizeLimit,
                        timeLimit=request.timeLimit,
                        typesOnly=request.typesOnly,
                        callback=_sendEntryToClient)

        def _done(_):
            return pureldap.LDAPSearchResultDone(
                resultCode=ldaperrors.Success.resultCode)

        d.addCallback(_done)
        return d

    def _cbSearchLDAPError(self, reason):
        reason.trap(ldaperrors.LDAPException)
        return pureldap.LDAPSearchResultDone(
            resultCode=reason.value.resultCode)

    def _cbSearchOtherError(self, reason):
        return pureldap.LDAPSearchResultDone(
            resultCode=ldaperrors.other, errorMessage=reason.getErrorMessage())

    fail_LDAPSearchRequest = pureldap.LDAPSearchResultDone

    def handle_LDAPSearchRequest(self, request, controls, reply):
        self.checkControls(controls)

        if (request.baseObject == ''
                and request.scope == pureldap.LDAP_SCOPE_baseObject and
                request.filter == pureldap.LDAPFilter_present('objectClass')):
            return self.getRootDSE(request, reply)
        dn = distinguishedname.DistinguishedName(request.baseObject)
        root = interfaces.IConnectedLDAPEntry(self.factory)
        d = root.lookup(dn)
        d.addCallback(self._cbSearchGotBase, dn, request, reply)
        d.addErrback(self._cbSearchLDAPError)
        d.addErrback(defer.logError)
        d.addErrback(self._cbSearchOtherError)
        return d

    fail_LDAPDelRequest = pureldap.LDAPDelResponse

    def handle_LDAPDelRequest(self, request, controls, reply):
        self.checkControls(controls)

        dn = distinguishedname.DistinguishedName(request.value)
        root = interfaces.IConnectedLDAPEntry(self.factory)
        d = root.lookup(dn)

        def _gotEntry(entry):
            d = entry.delete()
            return d

        d.addCallback(_gotEntry)

        def _report(entry):
            return pureldap.LDAPDelResponse(resultCode=0)

        d.addCallback(_report)
        return d

    fail_LDAPAddRequest = pureldap.LDAPAddResponse

    def handle_LDAPAddRequest(self, request, controls, reply):
        self.checkControls(controls)

        attributes = {}
        for name, vals in request.attributes:
            attributes.setdefault(name.value, sets.Set())
            attributes[name.value].update([x.value for x in vals])
        dn = distinguishedname.DistinguishedName(request.entry)
        rdn = str(dn.split()[0])
        parent = dn.up()
        root = interfaces.IConnectedLDAPEntry(self.factory)
        d = root.lookup(parent)

        def _gotEntry(parent):
            d = parent.addChild(rdn, attributes)
            return d

        d.addCallback(_gotEntry)

        def _report(entry):
            return pureldap.LDAPAddResponse(resultCode=0)

        d.addCallback(_report)
        return d

    fail_LDAPModifyDNRequest = pureldap.LDAPModifyDNResponse

    def handle_LDAPModifyDNRequest(self, request, controls, reply):
        self.checkControls(controls)

        dn = distinguishedname.DistinguishedName(request.entry)
        newrdn = distinguishedname.RelativeDistinguishedName(request.newrdn)
        deleteoldrdn = bool(request.deleteoldrdn)
        if not deleteoldrdn:
            #TODO support this
            raise ldaperrors.LDAPUnwillingToPerform(
                "Cannot handle preserving old RDN yet.")
        newSuperior = request.newSuperior
        if newSuperior is None:
            newSuperior = dn.up()
        else:
            newSuperior = distinguishedname.DistinguishedName(newSuperior)
        newdn = distinguishedname.DistinguishedName(listOfRDNs=(newrdn, ) +
                                                    newSuperior.split())

        #TODO make this more atomic
        root = interfaces.IConnectedLDAPEntry(self.factory)
        d = root.lookup(dn)

        def _gotEntry(entry):
            d = entry.move(newdn)
            return d

        d.addCallback(_gotEntry)

        def _report(entry):
            return pureldap.LDAPModifyDNResponse(resultCode=0)

        d.addCallback(_report)
        return d

    fail_LDAPModifyRequest = pureldap.LDAPModifyResponse

    def handle_LDAPModifyRequest(self, request, controls, reply):
        self.checkControls(controls)

        root = interfaces.IConnectedLDAPEntry(self.factory)
        mod = delta.ModifyOp.fromLDAP(request)
        d = mod.patch(root)

        def _patched(entry):
            return entry.commit()

        d.addCallback(_patched)

        def _report(entry):
            return pureldap.LDAPModifyResponse(resultCode=0)

        d.addCallback(_report)
        return d

    fail_LDAPExtendedRequest = pureldap.LDAPExtendedResponse

    def handle_LDAPExtendedRequest(self, request, controls, reply):
        self.checkControls(controls)

        for handler in [
                getattr(self, attr) for attr in dir(self)
                if attr.startswith('extendedRequest_')
        ]:
            if getattr(handler, 'oid', None) == request.requestName:
                berdecoder = getattr(handler, 'berdecoder', None)

                if berdecoder is None:
                    values = [request.requestValue]
                else:
                    values = pureber.berDecodeMultiple(request.requestValue,
                                                       berdecoder)

                d = defer.maybeDeferred(handler, *values, **{'reply': reply})

                def eb(fail, oid):
                    fail.trap(ldaperrors.LDAPException)
                    return pureldap.LDAPExtendedResponse(
                        resultCode=fail.value.resultCode,
                        errorMessage=fail.value.message,
                        responseName=oid,
                    )

                d.addErrback(eb, request.requestName)
                return d

        raise ldaperrors.LDAPProtocolError('Unknown extended request: %s' %
                                           request.requestName)

    def extendedRequest_LDAPPasswordModifyRequest(self, data, reply):
        if not isinstance(data, pureber.BERSequence):
            raise ldaperrors.LDAPProtocolError(
                'Extended request PasswordModify expected a BERSequence.')

        userIdentity = None
        oldPasswd = None
        newPasswd = None

        for value in data:
            if isinstance(value,
                          pureldap.LDAPPasswordModifyRequest_userIdentity):
                if userIdentity is not None:
                    raise ldaperrors.LDAPProtocolError(
                        'Extended request PasswordModify received userIdentity twice.'
                    )
                userIdentity = value.value
            elif isinstance(value,
                            pureldap.LDAPPasswordModifyRequest_oldPasswd):
                if oldPasswd is not None:
                    raise ldaperrors.LDAPProtocolError(
                        'Extended request PasswordModify received oldPasswd twice.'
                    )
                oldPasswd = value.value
            elif isinstance(value,
                            pureldap.LDAPPasswordModifyRequest_newPasswd):
                if newPasswd is not None:
                    raise ldaperrors.LDAPProtocolError(
                        'Extended request PasswordModify received newPasswd twice.'
                    )
                newPasswd = value.value
            else:
                raise ldaperrors.LDAPProtocolError(
                    'Extended request PasswordModify received unexpected item.'
                )

        if self.boundUser is None:
            raise ldaperrors.LDAPStrongAuthRequired()

        if (userIdentity is not None and userIdentity != self.boundUser.dn):
            #TODO this hardcodes ACL
            log.msg('User %(actor)s tried to change password of %(target)s' % {
                'actor': str(self.boundUser.dn),
                'target': str(userIdentity),
            })
            raise ldaperrors.LDAPInsufficientAccessRights()

        if (oldPasswd is not None or newPasswd is None):
            raise ldaperrors.LDAPOperationsError(
                'Password does not support this case.')

        self.boundUser.setPassword(newPasswd)
        return pureldap.LDAPExtendedResponse(
            resultCode=ldaperrors.Success.resultCode,
            responseName=self.extendedRequest_LDAPPasswordModifyRequest.oid)

        # TODO
        if userIdentity is None:
            userIdentity = str(self.boundUser.dn)

        raise NotImplementedError('VALUE %r' % value)

    extendedRequest_LDAPPasswordModifyRequest.oid = pureldap.LDAPPasswordModifyRequest.oid
    extendedRequest_LDAPPasswordModifyRequest.berdecoder = (
        pureber.BERDecoderContext(
            inherit=pureldap.LDAPBERDecoderContext_LDAPPasswordModifyRequest(
                inherit=pureber.BERDecoderContext())))
Exemple #15
0
class KnownValues(unittest.TestCase):
    knownValues = (  # class, args, kwargs, expected_result
        (pureldap.LDAPModifyRequest, [], {
            "object":
            'cn=foo, dc=example, dc=com',
            "modification": [
                pureber.BERSequence([
                    pureber.BEREnumerated(0),
                    pureber.BERSequence([
                        pureldap.LDAPAttributeDescription('bar'),
                        pureber.BERSet([
                            pureldap.LDAPString('a'),
                            pureldap.LDAPString('b'),
                        ]),
                    ]),
                ]),
            ],
        }, None, [0x66, 50] +
         ([0x04, 0x1a] + l("cn=foo, dc=example, dc=com") + [0x30, 20] +
          ([0x30, 18] +
           ([0x0a, 0x01, 0x00] + [0x30, 13] +
            ([0x04, len("bar")] + l("bar") + [0x31, 0x06] +
             ([0x04, len("a")] + l("a") + [0x04, len("b")] + l("b"))))))),
        (pureldap.LDAPModifyRequest, [], {
            "object":
            'cn=foo, dc=example, dc=com',
            "modification": [
                pureber.BERSequence([
                    pureber.BEREnumerated(1L),
                    pureber.BERSequence([
                        pureber.BEROctetString('bar'),
                        pureber.BERSet([]),
                    ]),
                ]),
            ],
        }, None, [0x66, 0x2c] +
         ([0x04, 0x1a] + l("cn=foo, dc=example, dc=com") + [0x30, 0x0e] +
          ([0x30, 0x0c] + ([0x0a, 0x01, 0x01] + [0x30, 0x07] +
                           ([0x04, 0x03] + l("bar") + [0x31, 0x00]))))),
        (pureldap.LDAPFilter_not, [], {
            "value": pureldap.LDAPFilter_present("foo"),
        },
         pureldap.LDAPBERDecoderContext_Filter(
             fallback=pureber.BERDecoderContext()),
         [0xa2, 0x05] + [0x87] + [len("foo")] + l("foo")),
        (
            pureldap.LDAPFilter_or,
            [],
            {
                "value": [
                    pureldap.LDAPFilter_equalityMatch(
                        attributeDesc=pureldap.LDAPAttributeDescription(
                            value='cn'),
                        assertionValue=pureldap.LDAPAssertionValue(
                            value='foo')),
                    pureldap.LDAPFilter_equalityMatch(
                        attributeDesc=pureldap.LDAPAttributeDescription(
                            value='uid'),
                        assertionValue=pureldap.LDAPAssertionValue(
                            value='foo')),
                ]
            },
            pureldap.LDAPBERDecoderContext_Filter(
                fallback=pureber.BERDecoderContext()),
            [0xa1, 23] + [0xa3, 9] + [0x04] + [len("cn")] + l("cn") + [0x04] +
            [len("foo")] + l("foo") + [0xa3, 10] + [0x04] + [len("uid")] +
            l("uid") + [0x04] + [len("foo")] + l("foo"),
        ),
        (
            pureldap.LDAPFilter_and,
            [],
            {
                "value": [
                    pureldap.LDAPFilter_equalityMatch(
                        attributeDesc=pureldap.LDAPAttributeDescription(
                            value='cn'),
                        assertionValue=pureldap.LDAPAssertionValue(
                            value='foo')),
                    pureldap.LDAPFilter_equalityMatch(
                        attributeDesc=pureldap.LDAPAttributeDescription(
                            value='uid'),
                        assertionValue=pureldap.LDAPAssertionValue(
                            value='foo')),
                ]
            },
            pureldap.LDAPBERDecoderContext_Filter(
                fallback=pureber.BERDecoderContext()),
            [0xa0, 23] + [0xa3, 9] + [0x04] + [len("cn")] + l("cn") + [0x04] +
            [len("foo")] + l("foo") + [0xa3, 10] + [0x04] + [len("uid")] +
            l("uid") + [0x04] + [len("foo")] + l("foo"),
        ),
        (pureldap.LDAPModifyDNRequest, [], {
            'entry': 'cn=foo,dc=example,dc=com',
            'newrdn': 'uid=bar',
            'deleteoldrdn': 0,
        }, None, [0x6c, 0x26] + [0x04] + [len("cn=foo,dc=example,dc=com")] +
         l("cn=foo,dc=example,dc=com") + [0x04] + [len("uid=bar")] +
         l("uid=bar") + [0x01, 0x01, 0x00]),
        (pureldap.LDAPModifyDNRequest, [], {
            'entry': 'cn=aoue,dc=example,dc=com',
            'newrdn': 'uid=aoue',
            'deleteoldrdn': 0,
            'newSuperior': 'ou=People,dc=example,dc=com',
        }, None, [0x6c, 69] + [0x04] + [len("cn=aoue,dc=example,dc=com")] +
         l("cn=aoue,dc=example,dc=com") + [0x04] + [len("uid=aoue")] +
         l("uid=aoue") + [0x01, 0x01, 0x00] + [0x80] +
         [len("ou=People,dc=example,dc=com")] +
         l("ou=People,dc=example,dc=com")),
        (
            pureldap.LDAPSearchRequest,
            [],
            {
                'baseObject': 'dc=yoja,dc=example,dc=com',
            },
            None,
            [0x63, 57] + [0x04] + [len('dc=yoja,dc=example,dc=com')] +
            l('dc=yoja,dc=example,dc=com')
            # scope
            + [0x0a, 1, 2]
            # derefAliases
            + [0x0a, 1, 0]
            # sizeLimit
            + [0x02, 1, 0]
            # timeLimit
            + [0x02, 1, 0]
            # typesOnly
            + [0x01, 1, 0]
            # filter
            + [135, 11] + l('objectClass')
            # attributes
            + [48, 0]),
        (pureldap.LDAPUnbindRequest, [], {}, None, [0x42, 0x00]),
        (
            pureldap.LDAPSearchResultDone,
            [],
            {
                'resultCode': 0,
            },
            None,
            [0x65, 0x07]
            # resultCode
            + [0x0a, 0x01, 0x00]
            # matchedDN
            + [0x04] + [len('')] + l('')
            # errorMessage
            + [0x04] + [len('')] + l('')
            # referral, TODO
            + []),
        (
            pureldap.LDAPSearchResultDone,
            [],
            {
                'resultCode': 0,
                'matchedDN': 'dc=foo,dc=example,dc=com',
            },
            None,
            [0x65, 31]
            # resultCode
            + [0x0a, 0x01, 0x00]
            # matchedDN
            + [0x04] + [len('dc=foo,dc=example,dc=com')] +
            l('dc=foo,dc=example,dc=com')
            # errorMessage
            + [0x04] + [len('')] + l('')
            # referral, TODO
            + []),
        (
            pureldap.LDAPSearchResultDone,
            [],
            {
                'resultCode': 0,
                'matchedDN': 'dc=foo,dc=example,dc=com',
                'errorMessage': 'the foobar was fubar',
            },
            None,
            [0x65, 51]
            # resultCode
            + [0x0a, 0x01, 0x00]
            # matchedDN
            + [0x04] + [len('dc=foo,dc=example,dc=com')] +
            l('dc=foo,dc=example,dc=com')
            # errorMessage
            + [0x04] + [len('the foobar was fubar')] +
            l('the foobar was fubar', )
            # referral, TODO
            + []),
        (
            pureldap.LDAPSearchResultDone,
            [],
            {
                'resultCode': 0,
                'errorMessage': 'the foobar was fubar',
            },
            None,
            [0x65, 27]
            # resultCode
            + [0x0a, 0x01, 0x00]
            # matchedDN
            + [0x04] + [len('')] + l('')
            # errorMessage
            + [0x04] + [len('the foobar was fubar')] +
            l('the foobar was fubar', )
            # referral, TODO
            + []),
        (
            pureldap.LDAPMessage,
            [],
            {
                'id': 42,
                'value': pureldap.LDAPBindRequest(),
            },
            pureldap.LDAPBERDecoderContext_TopLevel(
                inherit=pureldap.LDAPBERDecoderContext_LDAPMessage(
                    fallback=pureldap.LDAPBERDecoderContext(
                        fallback=pureber.BERDecoderContext()),
                    inherit=pureldap.LDAPBERDecoderContext(
                        fallback=pureber.BERDecoderContext()))),
            [0x30, 12]
            # id
            + [0x02, 0x01, 42]
            # value
            + l(str(pureldap.LDAPBindRequest()))),
        (
            pureldap.LDAPControl,
            [],
            {
                'controlType': '1.2.3.4',
            },
            None,
            [0x30, 9]
            # controlType
            + [0x04, 7] + l("1.2.3.4")),
        (
            pureldap.LDAPControl,
            [],
            {
                'controlType': '1.2.3.4',
                'criticality': True,
            },
            None,
            [0x30, 12]
            # controlType
            + [0x04, 7] + l("1.2.3.4")
            # criticality
            + [0x01, 1, 0xFF]),
        (
            pureldap.LDAPControl,
            [],
            {
                'controlType': '1.2.3.4',
                'criticality': True,
                'controlValue': 'silly',
            },
            None,
            [0x30, 19]
            # controlType
            + [0x04, 7] + l("1.2.3.4")
            # criticality
            + [0x01, 1, 0xFF]
            # controlValue
            + [0x04, len("silly")] + l("silly")),
        (
            pureldap.LDAPMessage,
            [],
            {
                'id':
                42,
                'value':
                pureldap.LDAPBindRequest(),
                'controls': [
                    ('1.2.3.4', None, None),
                    ('2.3.4.5', False),
                    ('3.4.5.6', True, '\x00\x01\x02\xFF'),
                ],
            },
            pureldap.LDAPBERDecoderContext_TopLevel(
                inherit=pureldap.LDAPBERDecoderContext_LDAPMessage(
                    fallback=pureldap.LDAPBERDecoderContext(
                        fallback=pureber.BERDecoderContext()),
                    inherit=pureldap.LDAPBERDecoderContext(
                        fallback=pureber.BERDecoderContext()))),
            [0x30, 59]
            # id
            + [0x02, 0x01, 42]
            # value
            + l(str(pureldap.LDAPBindRequest()))
            # controls
            + l(
                str(
                    pureldap.LDAPControls(value=[
                        pureldap.LDAPControl(controlType='1.2.3.4'),
                        pureldap.LDAPControl(controlType='2.3.4.5',
                                             criticality=False),
                        pureldap.LDAPControl(controlType='3.4.5.6',
                                             criticality=True,
                                             controlValue='\x00\x01\x02\xFF'),
                    ]))),
        ),
        (pureldap.LDAPFilter_equalityMatch, [], {
            'attributeDesc': pureldap.LDAPAttributeDescription('cn'),
            'assertionValue': pureldap.LDAPAssertionValue('foo'),
        },
         pureldap.LDAPBERDecoderContext_Filter(
             fallback=pureldap.LDAPBERDecoderContext(
                 fallback=pureber.BERDecoderContext()),
             inherit=pureldap.LDAPBERDecoderContext(
                 fallback=pureber.BERDecoderContext())),
         [0xa3, 9] + ([0x04, 2] + l('cn') + [0x04, 3] + l('foo'))),
        (pureldap.LDAPFilter_or, [[
            pureldap.LDAPFilter_equalityMatch(
                attributeDesc=pureldap.LDAPAttributeDescription('cn'),
                assertionValue=pureldap.LDAPAssertionValue('foo')),
            pureldap.LDAPFilter_equalityMatch(
                attributeDesc=pureldap.LDAPAttributeDescription('uid'),
                assertionValue=pureldap.LDAPAssertionValue('foo')),
            pureldap.LDAPFilter_equalityMatch(
                attributeDesc=pureldap.LDAPAttributeDescription('mail'),
                assertionValue=pureldap.LDAPAssertionValue('foo')),
            pureldap.LDAPFilter_substrings(
                type='mail',
                substrings=[pureldap.LDAPFilter_substrings_initial('foo@')]),
        ]], {},
         pureldap.LDAPBERDecoderContext_Filter(
             fallback=pureldap.LDAPBERDecoderContext(
                 fallback=pureber.BERDecoderContext()),
             inherit=pureldap.LDAPBERDecoderContext(
                 fallback=pureber.BERDecoderContext())), [0xA1, 52] +
         ([0xa3, 9] +
          ([0x04, 2] + l('cn') + [0x04, 3] + l('foo')) + [0xa3, 10] +
          ([0x04, 3] + l('uid') + [0x04, 3] + l('foo')) + [0xa3, 11] +
          ([0x04, 4] + l('mail') + [0x04, 3] + l('foo')) + [0xa4, 14] +
          ([0x04, 4] + l('mail') + [0x30, 6] + ([0x80, 4] + l('foo@'))))),
        (pureldap.LDAPSearchRequest, [], {
            'baseObject':
            'dc=example,dc=com',
            'scope':
            pureldap.LDAP_SCOPE_wholeSubtree,
            'derefAliases':
            pureldap.LDAP_DEREF_neverDerefAliases,
            'sizeLimit':
            1,
            'timeLimit':
            0,
            'typesOnly':
            False,
            'filter':
            pureldap.LDAPFilter_or([
                pureldap.LDAPFilter_equalityMatch(
                    attributeDesc=pureldap.LDAPAttributeDescription('cn'),
                    assertionValue=pureldap.LDAPAssertionValue('foo')),
                pureldap.LDAPFilter_equalityMatch(
                    attributeDesc=pureldap.LDAPAttributeDescription('uid'),
                    assertionValue=pureldap.LDAPAssertionValue('foo')),
                pureldap.LDAPFilter_equalityMatch(
                    attributeDesc=pureldap.LDAPAttributeDescription('mail'),
                    assertionValue=pureldap.LDAPAssertionValue('foo')),
                pureldap.LDAPFilter_substrings(
                    type='mail',
                    substrings=[
                        pureldap.LDAPFilter_substrings_initial('foo@')
                    ]),
            ]),
            'attributes': [''],
        },
         pureldap.LDAPBERDecoderContext_LDAPMessage(
             fallback=pureldap.LDAPBERDecoderContext(
                 fallback=pureber.BERDecoderContext()),
             inherit=pureldap.LDAPBERDecoderContext(
                 fallback=pureber.BERDecoderContext())), [0x63, 92] +
         ([0x04, 17] + l('dc=example,dc=com') + [0x0a, 1, 0x02] +
          [0x0a, 1, 0x00] + [0x02, 1, 0x01] + [0x02, 1, 0x00] +
          [0x01, 1, 0x00] + [0xA1, 52] +
          ([0xa3, 9] +
           ([0x04, 2] + l('cn') + [0x04, 3] + l('foo')) + [0xa3, 10] +
           ([0x04, 3] + l('uid') + [0x04, 3] + l('foo')) + [0xa3, 11] +
           ([0x04, 4] + l('mail') + [0x04, 3] + l('foo')) + [0xa4, 14] +
           ([0x04, 4] + l('mail') + [0x30, 6] +
            ([0x80, 4] + l('foo@')))) + [0x30, 2] + ([0x04, 0]))),
        (
            pureldap.LDAPMessage,
            [],
            {
                'id':
                1L,
                'value':
                pureldap.LDAPSearchRequest(
                    baseObject='dc=example,dc=com',
                    scope=pureldap.LDAP_SCOPE_wholeSubtree,
                    derefAliases=pureldap.LDAP_DEREF_neverDerefAliases,
                    sizeLimit=1,
                    timeLimit=0,
                    typesOnly=False,
                    filter=pureldap.LDAPFilter_or([
                        pureldap.LDAPFilter_equalityMatch(
                            attributeDesc=pureldap.LDAPAttributeDescription(
                                'cn'),
                            assertionValue=pureldap.LDAPAssertionValue('foo')),
                        pureldap.LDAPFilter_equalityMatch(
                            attributeDesc=pureldap.LDAPAttributeDescription(
                                'uid'),
                            assertionValue=pureldap.LDAPAssertionValue('foo')),
                        pureldap.LDAPFilter_equalityMatch(
                            attributeDesc=pureldap.LDAPAttributeDescription(
                                'mail'),
                            assertionValue=pureldap.LDAPAssertionValue('foo')),
                        pureldap.LDAPFilter_substrings(
                            type='mail',
                            substrings=[
                                pureldap.LDAPFilter_substrings_initial('foo@')
                            ]),
                    ]),
                    attributes=[''],
                ),
            },
            pureldap.LDAPBERDecoderContext_TopLevel(
                inherit=pureldap.LDAPBERDecoderContext_LDAPMessage(
                    fallback=pureldap.LDAPBERDecoderContext(
                        fallback=pureber.BERDecoderContext()),
                    inherit=pureldap.LDAPBERDecoderContext(
                        fallback=pureber.BERDecoderContext()))),
            [0x30, 97]
            # id
            + [0x02, 1, 1]
            # value
            + [0x63, 92] +
            ([0x04, 17] + l('dc=example,dc=com') + [0x0a, 1, 0x02] +
             [0x0a, 1, 0x00] + [0x02, 1, 0x01] + [0x02, 1, 0x00] +
             [0x01, 1, 0x00] + [0xA1, 52] +
             ([0xa3, 9] +
              ([0x04, 2] + l('cn') + [0x04, 3] + l('foo')) + [0xa3, 10] +
              ([0x04, 3] + l('uid') + [0x04, 3] + l('foo')) + [0xa3, 11] +
              ([0x04, 4] + l('mail') + [0x04, 3] + l('foo')) + [0xa4, 14] +
              ([0x04, 4] + l('mail') + [0x30, 6] +
               ([0x80, 4] + l('foo@')))) + [0x30, 2] + ([0x04, 0]))),
        (pureldap.LDAPExtendedRequest, [], {
            'requestName': '42.42.42',
            'requestValue': 'foo',
        }, None, [0x40 | 0x20 | 23, 1 + 1 + 8 + 1 + 1 + 3] +
         ([0x80 | 0] + [len('42.42.42')] + l('42.42.42')) +
         ([0x80 | 1] + [len('foo')] + l('foo'))),
    )

    def testToLDAP(self):
        """str(LDAPClass(...)) should give known result with known input"""
        for klass, args, kwargs, decoder, encoded in self.knownValues:
            result = klass(*args, **kwargs)
            result = str(result)
            result = map(ord, result)
            if result != encoded:
                raise AssertionError, \
                      "Class %s(*%s, **%s) doesn't encode properly: " \
                      "%s != %s" % (klass.__name__,
                                    repr(args), repr(kwargs),
                                    repr(result), repr(encoded))

    def testFromLDAP(self):
        """LDAPClass(encoded="...") should give known result with known input"""
        for klass, args, kwargs, decoder, encoded in self.knownValues:
            if decoder is None:
                decoder = pureldap.LDAPBERDecoderContext(
                    fallback=pureber.BERDecoderContext())
            m = s(*encoded)
            result, bytes = pureber.berDecodeObject(decoder, m)
            self.assertEquals(bytes, len(m))

            shouldBe = klass(*args, **kwargs)
            #TODO shouldn't use str below
            assert str(result)==str(shouldBe), \
                   "Class %s(*%s, **%s) doesn't decode properly: " \
                   "%s != %s" % (klass.__name__,
                                 repr(args), repr(kwargs),
                                 repr(result), repr(shouldBe))

    def testPartial(self):
        """LDAPClass(encoded="...") with too short input should throw BERExceptionInsufficientData"""
        for klass, args, kwargs, decoder, encoded in self.knownValues:
            if decoder is None:
                decoder = pureldap.LDAPBERDecoderContext(
                    fallback=pureber.BERDecoderContext())
            for i in xrange(1, len(encoded)):
                m = s(*encoded)[:i]
                self.assertRaises(pureber.BERExceptionInsufficientData,
                                  pureber.berDecodeObject, decoder, m)
            self.assertEquals((None, 0), pureber.berDecodeObject(decoder, ''))