ldaptor-0.0.43/0000755000175000017500000000000011306701242011341 5ustar janjanldaptor-0.0.43/ldaptor/0000755000175000017500000000000010403234314013004 5ustar janjanldaptor-0.0.43/ldaptor/samba/0000755000175000017500000000000010403234305014067 5ustar janjanldaptor-0.0.43/ldaptor/samba/__init__.py0000644000175000017500000000000007542426337016210 0ustar janjanldaptor-0.0.43/ldaptor/samba/smbpassword.py0000644000175000017500000000464210104536552017022 0ustar janjanimport string, warnings from ldaptor import md4, config lower='abcdefghijklmnopqrstuvwxyz' upper=lower.upper() toupper=string.maketrans(lower, upper) def nthash(password=''): """Generates nt md4 password hash for a given password.""" password=password[:128] password=''.join([c+'\000' for c in password]) return md4.new(password).hexdigest().translate(toupper); def lmhash_locked(password=''): """ Generates a lanman password hash that matches no password. Note that the author thinks LanMan hashes should be banished from the face of the earth. """ return 32*'X' def _no_lmhash(password=''): if config.useLMhash(): warnings.warn("Cannot import Crypto.Cipher.DES, lmhash passwords disabled.") return lmhash_locked() def _have_lmhash(password=''): """ Generates lanman password hash for a given password. Note that the author thinks LanMan hashes should be banished from the face of the earth. """ if not config.useLMhash(): return lmhash_locked() password = (password+14*'\0')[:14] password = password.upper() return _deshash(password[:7]) + _deshash(password[7:]) try: from Crypto.Cipher import DES except ImportError: lmhash = _no_lmhash else: lmhash = _have_lmhash LM_MAGIC = "KGS!@#$%" def _deshash(p): # Insert parity bits. I'm not going to bother myself with smart # implementations. bits = [] for byte in [ord(c) for c in p]: bits.extend([bool(byte & 128), bool(byte & 64), bool(byte & 32), bool(byte & 16), bool(byte & 8), bool(byte & 4), bool(byte & 2), bool(byte & 1)]) def _pack(bits): x = ((bits[0] << 7) + (bits[1] << 6) + (bits[2] << 5) + (bits[3] << 4) + (bits[4] << 3) + (bits[5] << 2) + (bits[6] << 1)) return x bytes = (_pack(bits[:7]), _pack(bits[7:14]), _pack(bits[14:21]), _pack(bits[21:28]), _pack(bits[28:35]), _pack(bits[35:42]), _pack(bits[42:49]), _pack(bits[49:])) bytes = ''.join([chr(x) for x in bytes]) cipher = DES.new(bytes, DES.MODE_ECB) raw = cipher.encrypt(LM_MAGIC) l = ['%02X' % ord(x) for x in raw] return ''.join(l) ldaptor-0.0.43/ldaptor/weave.py0000644000175000017500000001112610345073240014472 0ustar janjanfrom zope.interface import implements from nevow import tags, compy, inevow, flat from ldaptor.protocols.ldap import ldapsyntax, distinguishedname from ldaptor import interfaces def keyvalue(context, data): """ Render items in a mapping using patterns found in the children of the element. Keyvalue recognizes the following patterns: - header: Rendered at the start, before the first item. If multiple header patterns are provided they are rendered together in the order they were defined. - footer: Just like the header only renderer at the end, after the last item. - item: Rendered once for each item in the sequence. Can contain subpatterns key and value. If multiple item patterns are provided then the pattern is cycled in the order defined. - divider: Rendered once between each item in the sequence. Multiple divider patterns are cycled. - empty: Rendered instead of item and divider patterns when the sequence contains no items. Example::
name email
name goes here email goes here
name goes here email goes here
they've all gone!
""" headers = context.tag.allPatterns('header') item = context.tag.patternGenerator('item') divider = context.tag.patternGenerator('divider', default=tags.invisible) content = [] for key, value in data.items(): content.append(item(data=(key, value))) content.append(divider(data=(key, value))) if not content: content = context.tag.allPatterns('empty') else: ## No divider after the last thing. del content[-1] footers = context.tag.allPatterns('footer') return context.tag.clear()[ headers, content, footers ] def keyvalue_item(context, data): key, value = data k = context.tag.patternGenerator('key') v = context.tag.patternGenerator('value') return context.tag.clear()[ k(data=key), v(data=value) ] class _DictLike(object): implements(inevow.IContainer) def __init__(self, original): self.original = original def child(self, context, name): return self.original.get(name, None) def items(self): return self.original.items() class LDAPEntryContainer(object): implements(inevow.IContainer) def __init__(self, original): self.original = original def child(self, context, name): if name == 'dn': return self.original.dn elif name == 'attributes': return _DictLike(self.original) else: return None compy.registerAdapter(LDAPEntryContainer, ldapsyntax.LDAPEntryWithClient, inevow.IContainer) def dnSerializer(original, context): return flat.serialize(str(original), context) flat.registerFlattener(dnSerializer, distinguishedname.DistinguishedName) def entrySerializer(original, context): ul = tags.ul() for a,l in original.items(): if len(l)==0: ul[tags.li[a, ': none']] elif len(l)==1: for attr in l: first = attr break ul[tags.li[a, ': ', first]] else: li=tags.li[a, ':'] ul[li] liul=tags.ul() li[liul] for i in l: liul[tags.li[i]] return flat.serialize(ul, context) flat.registerFlattener(entrySerializer, interfaces.ILDAPEntry) class IZebraStyle(compy.Interface): """Marker interface for zebra.""" pass def zebra(styles=['zebra-odd', 'zebra-even']): """ Provide alternating background colors for e.g. zebra tables. @param styles: Two or more CSS class names to iterate. Use like this:: render_zebra = weave.zebra()
foo
bar
baz
""" styles = list(styles) def f(self, ctx, data): request = inevow.IRequest(ctx) state = IZebraStyle(request, styles) r = ctx.tag(class_="%s" % state[0]) request.setComponent(IZebraStyle, state[1:]+state[:1]) return r return f ldaptor-0.0.43/ldaptor/dns.py0000644000175000017500000000263610344535643014166 0ustar janjan"""DNS-related utilities.""" from socket import inet_aton, inet_ntoa def aton_octets(ip): s=inet_aton(ip) octets=map(None, s) n=0L for o in octets: n=n<<8 n+=ord(o) return n def aton_numbits(num): n=0L while num>0: n>>=1 n |= 2**31 num-=1 return n def aton(ip): try: i=int(ip) except ValueError: return aton_octets(ip) else: return aton_numbits(i) def ntoa(n): s=( chr((n>>24)&0xFF) + chr((n>>16)&0xFF) + chr((n>>8)&0xFF) + chr(n&0xFF) ) ip=inet_ntoa(s) return ip def netmaskToNumbits(netmask): bits = aton(netmask) i = 2**31 n = 0 while bits and i > 0: if (bits & i) == 0: if bits: raise RuntimeError, "Invalid netmask: %s" % netmask n += 1 bits -= i i = i >> 1 return n def ptrSoaName(ip, netmask): """ Convert an IP address and netmask to a CIDR delegation -style zone name. """ net = aton(ip) & aton(netmask) nmBits = netmaskToNumbits(netmask) bytes, bits = divmod(nmBits, 8) octets = ntoa(net).split('.') octets.reverse() if not bits: octets = octets[-bytes:] else: partial = octets[-bytes-1] octets = octets[-bytes:] octets.insert(0, '%s/%d' % (partial, nmBits)) return '.'.join(octets)+'.in-addr.arpa.' ldaptor-0.0.43/ldaptor/ldapfilter.py0000644000175000017500000001756410344535643015536 0ustar janjan#!/usr/bin/python from ldaptor.protocols import pureldap """ RFC2254: filter = "(" filtercomp ")" filtercomp = and / or / not / item and = "&" filterlist or = "|" filterlist not = "!" filter filterlist = 1*filter item = simple / present / substring / extensible simple = attr filtertype value filtertype = equal / approx / greater / less equal = "=" approx = "~=" greater = ">=" less = "<=" extensible = attr [":dn"] [":" matchingrule] ":=" value / [":dn"] ":" matchingrule ":=" value present = attr "=*" substring = attr "=" [initial] any [final] initial = value any = "*" *(value "*") final = value attr = AttributeDescription from Section 4.1.5 of [1] matchingrule = MatchingRuleId from Section 4.1.9 of [1] value = AttributeValue from Section 4.1.6 of [1] """ class InvalidLDAPFilter(Exception): def __init__(self, msg, loc, text): Exception.__init__(self) self.msg=msg self.loc=loc self.text=text def __str__(self): return "Invalid LDAP filter: %s at point %d in %r" \ % (self.msg, self.loc, self.text) def parseExtensible(attr, s): raise NotImplementedError from pyparsing import Word, Literal, Optional, ZeroOrMore, Suppress, \ Group, Forward, OneOrMore, ParseException, \ CharsNotIn, Combine, StringStart, \ StringEnd, delimitedList import copy, string filter_ = Forward() attr = Word(string.ascii_letters, string.ascii_letters + string.digits + ';-',) attr.leaveWhitespace() attr.setName('attr') hexdigits = Word(string.hexdigits, exact=2) hexdigits.setName('hexdigits') escaped = Suppress(Literal('\\'))+hexdigits escaped.setName('escaped') def _p_escaped(s,l,t): text=t[0] return chr(int(text, 16)) escaped.setParseAction(_p_escaped) value = Combine(OneOrMore(CharsNotIn('*()\\\0') | escaped)) value.setName('value') equal = Literal("=") equal.setParseAction(lambda s,l,t: pureldap.LDAPFilter_equalityMatch) approx = Literal("~=") approx.setParseAction(lambda s,l,t: pureldap.LDAPFilter_approxMatch) greater = Literal(">=") greater.setParseAction(lambda s,l,t: pureldap.LDAPFilter_greaterOrEqual) less = Literal("<=") less.setParseAction(lambda s,l,t: pureldap.LDAPFilter_lessOrEqual) filtertype = equal | approx | greater | less filtertype.setName('filtertype') simple = attr + filtertype + value simple.leaveWhitespace() simple.setName('simple') def _p_simple(s,l,t): attr, filtertype, value = t return filtertype(attributeDesc=pureldap.LDAPAttributeDescription(attr), assertionValue=pureldap.LDAPAssertionValue(value)) simple.setParseAction(_p_simple) present = attr + "=*" present.setParseAction(lambda s,l,t: pureldap.LDAPFilter_present(t[0])) initial = copy.copy(value) initial.setParseAction(lambda s,l,t: pureldap.LDAPFilter_substrings_initial(t[0])) initial.setName('initial') any_value = value + Suppress(Literal("*")) any_value.setParseAction(lambda s,l,t: pureldap.LDAPFilter_substrings_any(t[0])) any = Suppress(Literal("*")) + ZeroOrMore(any_value) any.setName('any') final = copy.copy(value) final.setName('final') final.setParseAction(lambda s,l,t: pureldap.LDAPFilter_substrings_final(t[0])) substring = attr + Suppress(Literal("=")) + Group(Optional(initial) + any + Optional(final)) substring.setName('substring') def _p_substring(s,l,t): attrtype, substrings = t return pureldap.LDAPFilter_substrings( type=attrtype, substrings=substrings) substring.setParseAction(_p_substring) keystring = Word(string.ascii_letters, string.ascii_letters + string.digits + ';-') keystring.setName('keystring') numericoid = delimitedList(Word(string.digits), delim='.', combine=True) numericoid.setName('numericoid') oid = numericoid | keystring oid.setName('oid') matchingrule = copy.copy(oid) matchingrule.setName('matchingrule') extensible_dn = Optional(":dn") def _p_extensible_dn(s,l,t): return bool(t) extensible_dn.setParseAction(_p_extensible_dn) matchingrule_or_none = Optional(Suppress(":") + matchingrule) def _p_matchingrule_or_none(s,l,t): if not t: return [None] else: return t[0] matchingrule_or_none.setParseAction(_p_matchingrule_or_none) extensible_attr = attr + extensible_dn + matchingrule_or_none + Suppress(":=") + value extensible_attr.setName('extensible_attr') def _p_extensible_attr(s,l,t): return list(t) extensible_attr.setParseAction(_p_extensible_attr) extensible_noattr = extensible_dn + Suppress(":") + matchingrule + Suppress(":=") + value extensible_noattr.setName('extensible_noattr') def _p_extensible_noattr(s,l,t): return [None]+list(t) extensible_noattr.setParseAction(_p_extensible_noattr) extensible = extensible_attr | extensible_noattr extensible.setName('extensible') def _p_extensible(s,l,t): attr, dn, matchingRule, value = t return pureldap.LDAPFilter_extensibleMatch( matchingRule=matchingRule, type=attr, matchValue=value, dnAttributes=dn) extensible.setParseAction(_p_extensible) item = simple ^ present ^ substring ^ extensible item.setName('item') item.leaveWhitespace() not_ = Suppress(Literal('!')) + filter_ not_.setParseAction(lambda s,l,t: pureldap.LDAPFilter_not(t[0])) not_.setName('not') filterlist = OneOrMore(filter_) or_ = Suppress(Literal('|')) + filterlist or_.setParseAction(lambda s,l,t: pureldap.LDAPFilter_or(t)) or_.setName('or') and_ = Suppress(Literal('&')) + filterlist and_.setParseAction(lambda s,l,t: pureldap.LDAPFilter_and(t)) and_.setName('and') filtercomp = and_ | or_ | not_ | item filtercomp.setName('filtercomp') filter_ << (Suppress(Literal('(').leaveWhitespace()) + filtercomp + Suppress(Literal(')').leaveWhitespace())) filter_.setName('filter') filtercomp.leaveWhitespace() filter_.leaveWhitespace() toplevel = (StringStart().leaveWhitespace() + filter_ + StringEnd().leaveWhitespace()) toplevel.leaveWhitespace() toplevel.setName('toplevel') def parseFilter(s): try: x=toplevel.parseString(s) except ParseException, e: raise InvalidLDAPFilter, (e.msg, e.loc, e.line) assert len(x)==1 return x[0] maybeSubString_value = Combine(OneOrMore(CharsNotIn('*\\\0') | escaped)) maybeSubString_simple = copy.copy(maybeSubString_value) def _p_maybeSubString_simple(s,l,t): return (lambda attr: pureldap.LDAPFilter_equalityMatch( attributeDesc=pureldap.LDAPAttributeDescription(attr), assertionValue=pureldap.LDAPAssertionValue(t[0]))) maybeSubString_simple.setParseAction(_p_maybeSubString_simple) maybeSubString_present = Literal("*") def _p_maybeSubString_present(s,l,t): return (lambda attr: pureldap.LDAPFilter_present(attr)) maybeSubString_present.setParseAction(_p_maybeSubString_present) maybeSubString_substring = Optional(initial) + any + Optional(final) def _p_maybeSubString_substring(s,l,t): return (lambda attr: pureldap.LDAPFilter_substrings( type=attr, substrings=t)) maybeSubString_substring.setParseAction(_p_maybeSubString_substring) maybeSubString = (maybeSubString_simple ^ maybeSubString_present ^ maybeSubString_substring ) def parseMaybeSubstring(attrType, s): try: x=maybeSubString.parseString(s) except ParseException, e: raise InvalidLDAPFilter, (e.msg, e.loc, e.line) assert len(x)==1 fn = x[0] return fn(attrType) if __name__=='__main__': import sys for filt in sys.argv[1:]: print repr(parseFilter(filt)) print ldaptor-0.0.43/ldaptor/inmemory.py0000644000175000017500000001500510352535700015224 0ustar janjanfrom zope.interface import implements from twisted.internet import defer, error from twisted.python.failure import Failure from ldaptor import interfaces, entry, entryhelpers from ldaptor.protocols.ldap import distinguishedname, ldaperrors, ldifprotocol class LDAPCannotRemoveRootError(ldaperrors.LDAPNamingViolation): """Cannot remove root of LDAP tree""" class ReadOnlyInMemoryLDAPEntry(entry.EditableLDAPEntry, entryhelpers.DiffTreeMixin, entryhelpers.SubtreeFromChildrenMixin, entryhelpers.MatchMixin, entryhelpers.SearchByTreeWalkingMixin, ): implements(interfaces.IConnectedLDAPEntry) def __init__(self, *a, **kw): entry.BaseLDAPEntry.__init__(self, *a, **kw) self._parent = None self._children = [] def parent(self): return self._parent def children(self, callback=None): if callback is None: return defer.succeed(self._children[:]) else: for c in self._children: callback(c) return defer.succeed(None) def _lookup(self, dn): if not self.dn.contains(dn): raise ldaperrors.LDAPNoSuchObject(dn) if dn == self.dn: return defer.succeed(self) for c in self._children: if c.dn.contains(dn): return c.lookup(dn) raise ldaperrors.LDAPNoSuchObject(dn) def lookup(self, dn): return defer.maybeDeferred(self._lookup, dn) def fetch(self, *attributes): return defer.succeed(self) def addChild(self, rdn, attributes): """TODO ugly API. Returns the created entry.""" rdn = distinguishedname.RelativeDistinguishedName(rdn) for c in self._children: if c.dn.split()[0] == rdn: raise ldaperrors.LDAPEntryAlreadyExists, c.dn dn = distinguishedname.DistinguishedName(listOfRDNs= (rdn,) +self.dn.split()) e = ReadOnlyInMemoryLDAPEntry(dn, attributes) e._parent = self self._children.append(e) return e def _delete(self): if self._parent is None: raise LDAPCannotRemoveRootError if self._children: raise ldaperrors.LDAPNotAllowedOnNonLeaf, self.dn return self._parent.deleteChild(self.dn.split()[0]) def delete(self): return defer.maybeDeferred(self._delete) def _deleteChild(self, rdn): if not isinstance(rdn, distinguishedname.RelativeDistinguishedName): rdn = distinguishedname.RelativeDistinguishedName(stringValue=rdn) for c in self._children: if c.dn.split()[0] == rdn: self._children.remove(c) return c raise ldaperrors.LDAPNoSuchObject, rdn def deleteChild(self, rdn): return defer.maybeDeferred(self._deleteChild, rdn) def _move(self, newDN): if not isinstance(newDN, distinguishedname.DistinguishedName): newDN = distinguishedname.DistinguishedName(stringValue=newDN) if newDN.up() != self.dn.up(): # climb up the tree to root root = self while root._parent is not None: root = root._parent d = defer.maybeDeferred(root.lookup, newDN.up()) else: d = defer.succeed(None) d.addCallback(self._move2, newDN) return d def _move2(self, newParent, newDN): if newParent is not None: newParent._children.append(self) self._parent._children.remove(self) # remove old RDN attributes for attr in self.dn.split()[0].split(): self[attr.attributeType].remove(attr.value) # add new RDN attributes for attr in newDN.split()[0].split(): # TODO what if the key does not exist? self[attr.attributeType].add(attr.value) self.dn = newDN return self def move(self, newDN): return defer.maybeDeferred(self._move, newDN) def commit(self): return defer.succeed(self) class InMemoryLDIFProtocol(ldifprotocol.LDIF): """ Receive LDIF data and gather results into an ReadOnlyInMemoryLDAPEntry. You can override lookupFailed and addFailed to provide smarter error handling. They are called as Deferred errbacks; returning the reason causes error to pass onward and abort the whole operation. Returning None from lookupFailed skips that entry, but continues loading. When the full LDIF data has been read, the completed Deferred will trigger. """ def __init__(self): self.db = None #do not access this via db, just to make sure you respect the ordering self._deferred = defer.Deferred() self.completed = defer.Deferred() def _addEntry(self, db, entry): d = db.lookup(entry.dn.up()) d.addErrback(self.lookupFailed, entry) def _add(parent, entry): if parent is not None: parent.addChild(rdn=entry.dn.split()[0], attributes=entry) d.addCallback(_add, entry) d.addErrback(self.addFailed, entry) def _passDB(_, db): return db d.addCallback(_passDB, db) return d def gotEntry(self, entry): if self.db is None: # first entry, create the db, prepare to process the rest self.db = ReadOnlyInMemoryLDAPEntry( dn=entry.dn, attributes=entry) self._deferred.callback(self.db) else: self._deferred.addCallback(self._addEntry, entry) def lookupFailed(self, reason, entry): return reason # pass the error (abort) by default def addFailed(self, reason, entry): return reason # pass the error (abort) by default def connectionLost(self, reason): super(InMemoryLDIFProtocol, self).connectionLost(reason) if not reason.check(error.ConnectionDone): self._deferred.addCallback(lambda db: reason) else: self._deferred.chainDeferred(self.completed) del self._deferred # invalidate it to flush out bugs def fromLDIFFile(f): """Read LDIF data from a file.""" p = InMemoryLDIFProtocol() while 1: data = f.read() if not data: break p.dataReceived(data) p.connectionLost(Failure(error.ConnectionDone())) return p.completed ldaptor-0.0.43/ldaptor/testutil.py0000644000175000017500000001031410345261504015240 0ustar janjan"""Utilities for writing Twistedy unit tests and debugging.""" from twisted.internet import defer from twisted.trial import unittest from twisted.test import proto_helpers from ldaptor import config def mustRaise(dummy): raise unittest.FailTest('Should have raised an exception.') def calltrace(): """Print out all function calls. For debug use only.""" def printfuncnames(frame, event, arg): print "|%s: %s:%d:%s" % (event, frame.f_code.co_filename, frame.f_code.co_firstlineno, frame.f_code.co_name) import sys sys.setprofile(printfuncnames) class FakeTransport: def __init__(self, proto): self.proto = proto def loseConnection(self): self.proto.connectionLost() class LDAPClientTestDriver: """ A test driver that looks somewhat like a real LDAPClient. Pass in a list of lists of LDAPProtocolResponses. For each sent LDAP message, the first item of said list is iterated through, and all the items are sent as responses to the callback. The sent LDAP messages are stored in self.sent, so you can assert that the sent messages are what they are supposed to be. """ def __init__(self, *responses): self.sent=[] self.responses=list(responses) self.connected = None self.transport = FakeTransport(self) def send(self, op): self.sent.append(op) l = self._response() assert len(l) == 1, \ "got %d responses for a .send()" % len(l) return defer.succeed(l[0]) def send_multiResponse(self, op, handler, *args, **kwargs): self.sent.append(op) responses = self._response() while responses: r = responses.pop(0) ret = handler(r, *args, **kwargs) if responses: assert not ret, \ "got %d responses still to give, but handler wants none (got %r)." % (len(responses), ret) else: assert ret, \ "no more responses to give, but handler still wants more (got %r)." % ret def send_noResponse(self, op): responses = self.responses.pop(0) assert not responses self.sent.append(op) def _response(self): assert self.responses, 'Ran out of responses' responses = self.responses.pop(0) return responses def assertNothingSent(self): # just a bit more explicit self.assertSent() def assertSent(self, *shouldBeSent): shouldBeSent = list(shouldBeSent) assert self.sent == shouldBeSent, \ '%s expected to send %r but sent %r' % ( self.__class__.__name__, shouldBeSent, self.sent) sentStr = ''.join([str(x) for x in self.sent]) shouldBeSentStr = ''.join([str(x) for x in shouldBeSent]) assert sentStr == shouldBeSentStr, \ '%s expected to send data %r but sent %r' % ( self.__class__.__name__, shouldBeSentStr, sentStr) def connectionMade(self): """TCP connection has opened""" self.connected = 1 def connectionLost(self, reason=None): """Called when TCP connection has been lost""" assert not self.responses, \ "connectionLost called even when have responses left: %r" % self.responses self.connected = 0 def unbind(self): assert self.connected r='fake-unbind-by-LDAPClientTestDriver' self.send_noResponse(r) self.transport.loseConnection() def createServer(proto, *responses, **kw): def createClient(factory): factory.doStart() #TODO factory.startedConnecting(c) proto = factory.buildProtocol(addr=None) proto.connectionMade() cfg = config.loadConfig( configFiles=[], reload=True) overrides = kw.setdefault('serviceLocationOverrides', {}) overrides.setdefault('', createClient) conf = config.LDAPConfig(**kw) server = proto(conf) server.protocol = lambda : LDAPClientTestDriver(*responses) server.transport = proto_helpers.StringTransport() server.connectionMade() return server ldaptor-0.0.43/ldaptor/__init__.py0000644000175000017500000000000007516345760015126 0ustar janjanldaptor-0.0.43/ldaptor/attributeset.py0000644000175000017500000000322110344535643016110 0ustar janjanimport sets from copy import deepcopy class LDAPAttributeSet(sets.Set): def __init__(self, key, *a, **kw): self.key = key super(LDAPAttributeSet, self).__init__(*a, **kw) def __repr__(self): values=list(self) values.sort() attributes=', '.join([repr(x) for x in values]) return '%s(%r, [%s])' % ( self.__class__.__name__, self.key, attributes) def __eq__(self, other): """ Note that LDAPAttributeSets can also be compared against any iterator. In that case the attributeType will be ignored. """ if isinstance(other, LDAPAttributeSet): if self.key != other.key: return False return super(LDAPAttributeSet, self).__eq__(other) else: me=list(self) me.sort() him=list(other) him.sort() return me == him def __ne__(self, other): return not self==other def difference(self, other): return sets.Set(self) - sets.Set(other) def union(self, other): return sets.Set(self) | sets.Set(other) def intersection(self, other): return sets.Set(self) & sets.Set(other) def symmetric_difference(self, other): return sets.Set(self) ^ sets.Set(other) def copy(self): result = self.__class__(self.key) result.update(self) return result __copy__ = copy def __deepcopy__(self, memo): result = self.__class__(self.key) memo[id(self)] = result data = deepcopy(sets.Set(self), memo) result.update(data) return result ldaptor-0.0.43/ldaptor/test/0000755000175000017500000000000010403234303013761 5ustar janjanldaptor-0.0.43/ldaptor/test/test_proxy.py0000644000175000017500000000777410344407651016606 0ustar janjan""" Test cases for ldaptor.protocols.ldap.proxy module. """ from twisted.trial import unittest from twisted.internet import reactor, error from ldaptor.protocols.ldap import proxy, ldaperrors from ldaptor.protocols import pureldap from ldaptor import testutil class Proxy(unittest.TestCase): def createServer(self, *responses): return testutil.createServer(proxy.Proxy, *responses) def test_bind(self): server = self.createServer([ pureldap.LDAPBindResponse(resultCode=0), ]) server.dataReceived(str(pureldap.LDAPMessage(pureldap.LDAPBindRequest(), id=4))) reactor.iterate() #TODO self.assertEquals(server.transport.value(), str(pureldap.LDAPMessage(pureldap.LDAPBindResponse(resultCode=0), id=4))) def test_search(self): server = self.createServer([ pureldap.LDAPBindResponse(resultCode=0), ], [ pureldap.LDAPSearchResultEntry('cn=foo,dc=example,dc=com', [('a', ['b'])]), pureldap.LDAPSearchResultEntry('cn=bar,dc=example,dc=com', [('b', ['c'])]), pureldap.LDAPSearchResultDone(ldaperrors.Success.resultCode), ], ) server.dataReceived(str(pureldap.LDAPMessage(pureldap.LDAPBindRequest(), id=2))) server.dataReceived(str(pureldap.LDAPMessage(pureldap.LDAPSearchRequest(), id=3))) reactor.iterate() #TODO self.assertEquals(server.transport.value(), str(pureldap.LDAPMessage(pureldap.LDAPBindResponse(resultCode=0), id=2)) +str(pureldap.LDAPMessage(pureldap.LDAPSearchResultEntry('cn=foo,dc=example,dc=com', [('a', ['b'])]), id=3)) +str(pureldap.LDAPMessage(pureldap.LDAPSearchResultEntry('cn=bar,dc=example,dc=com', [('b', ['c'])]), id=3)) +str(pureldap.LDAPMessage(pureldap.LDAPSearchResultDone(ldaperrors.Success.resultCode), id=3))) def test_unbind_clientUnbinds(self): server = self.createServer([ pureldap.LDAPBindResponse(resultCode=0), ], [], ) server.dataReceived(str(pureldap.LDAPMessage(pureldap.LDAPBindRequest(), id=2))) reactor.iterate() #TODO client = server.client client.assertSent(pureldap.LDAPBindRequest()) self.assertEquals(server.transport.value(), str(pureldap.LDAPMessage(pureldap.LDAPBindResponse(resultCode=0), id=2))) server.dataReceived(str(pureldap.LDAPMessage(pureldap.LDAPUnbindRequest(), id=3))) server.connectionLost(error.ConnectionDone) reactor.iterate() #TODO client.assertSent(pureldap.LDAPBindRequest(), pureldap.LDAPUnbindRequest()) self.assertEquals(server.transport.value(), str(pureldap.LDAPMessage(pureldap.LDAPBindResponse(resultCode=0), id=2))) def test_unbind_clientEOF(self): server = self.createServer([ pureldap.LDAPBindResponse(resultCode=0), ], [], ) server.dataReceived(str(pureldap.LDAPMessage(pureldap.LDAPBindRequest(), id=2))) reactor.iterate() #TODO client = server.client client.assertSent(pureldap.LDAPBindRequest()) self.assertEquals(server.transport.value(), str(pureldap.LDAPMessage(pureldap.LDAPBindResponse(resultCode=0), id=2))) server.connectionLost(error.ConnectionDone) reactor.iterate() #TODO client.assertSent(pureldap.LDAPBindRequest(), 'fake-unbind-by-LDAPClientTestDriver') self.assertEquals(server.transport.value(), str(pureldap.LDAPMessage(pureldap.LDAPBindResponse(resultCode=0), id=2))) ldaptor-0.0.43/ldaptor/test/test_smbpassword.py0000644000175000017500000000431010344535643017753 0ustar janjan""" Test cases for the ldaptor.samba.smbpassword module. """ from twisted.trial import unittest from ldaptor.samba import smbpassword from ldaptor import config class TestNTHash(unittest.TestCase): knownValues=( # password, expected_result ('', '31D6CFE0D16AE931B73C59D7E0C089C0'), ('foo', 'AC8E657F83DF82BEEA5D43BDAF7800CC'), (127*'x', '25900FAB94A048BCF438615217776562'), (128*'x', '65681023D0CB5E7E96FF662150EF060D'), (129*'x', '65681023D0CB5E7E96FF662150EF060D'), (1000*'x', '65681023D0CB5E7E96FF662150EF060D'), ) def testKnownValues(self): """nthash(...) gives known results""" for password, expected_result in self.knownValues: result = smbpassword.nthash(password) if result != expected_result: raise AssertionError, 'nthash(%s)=%s, expected %s' \ % (repr(password), repr(result), repr(expected_result)) class TestLMHash(unittest.TestCase): knownValues=( # password, expected_result ('', 'AAD3B435B51404EEAAD3B435B51404EE'), ('foo', '5BFAFBEBFB6A0942AAD3B435B51404EE'), (13*'x', '3AA62DBBEFDB676366B4159AF5A7C45C'), (14*'x', '3AA62DBBEFDB67633AA62DBBEFDB6763'), (15*'x', '3AA62DBBEFDB67633AA62DBBEFDB6763'), (100*'x', '3AA62DBBEFDB67633AA62DBBEFDB6763'), ('1234567abcdefg', '0182BD0BD4444BF8E0C510199CC66ABD'), ('XXXXXXXabcdefg', '3AA62DBBEFDB6763E0C510199CC66ABD'), ('1234567XXXXXXX', '0182BD0BD4444BF83AA62DBBEFDB6763'), ) def testKnownValues(self): """lmhash(...) gives known results""" cfg = config.loadConfig() for password, expected_result in self.knownValues: cfg.set('samba', 'use-lmhash', 'no') disabled = smbpassword.lmhash(password) self.assertEquals(disabled, 32*'X', "Disabled lmhash must be X's: %r" % disabled) cfg.set('samba', 'use-lmhash', 'yes') result = smbpassword.lmhash(password) if result != expected_result: raise AssertionError, 'lmhash(%s)=%s, expected %s' \ % (repr(password), repr(result), repr(expected_result)) ldaptor-0.0.43/ldaptor/test/test_config.py0000644000175000017500000000352210173563735016663 0ustar janjan""" Test cases for the ldaptor.config module. """ from twisted.trial import unittest import os from ldaptor import config def writeFile(path, content): f = file(path, 'w') f.write(content) f.close() class TestConfig(unittest.TestCase): def testSomething(self): self.dir = self.mktemp() os.mkdir(self.dir) self.f1 = os.path.join(self.dir, 'one.cfg') writeFile(self.f1, """\ [fooSection] fooVar = val [barSection] barVar = anotherVal """) self.f2 = os.path.join(self.dir, 'two.cfg') writeFile(self.f2, """\ [fooSection] fooVar = val2 """) self.cfg = config.loadConfig( configFiles=[self.f1, self.f2], reload=True) val = self.cfg.get('fooSection', 'fooVar') self.assertEquals(val, 'val2') val = self.cfg.get('barSection', 'barVar') self.assertEquals(val, 'anotherVal') class IdentitySearch(unittest.TestCase): def setUp(self): self.dir = self.mktemp() os.mkdir(self.dir) self.f1 = os.path.join(self.dir, 'one.cfg') writeFile(self.f1, """\ [authentication] identity-search = (something=%(name)s) """) self.cfg = config.loadConfig( configFiles=[self.f1], reload=True) self.config = config.LDAPConfig() def testConfig(self): self.assertEquals(self.config.getIdentitySearch('foo'), '(something=foo)') def testCopy(self): conf = self.config.copy(identitySearch='(&(bar=baz)(quux=%(name)s))') self.assertEquals(conf.getIdentitySearch('foo'), '(&(bar=baz)(quux=foo))') def testInitArg(self): conf = config.LDAPConfig(identitySearch='(&(bar=thud)(quux=%(name)s))') self.assertEquals(conf.getIdentitySearch('foo'), '(&(bar=thud)(quux=foo))') ldaptor-0.0.43/ldaptor/test/test_ldiftree.py0000644000175000017500000006345210402661536017215 0ustar janjan""" Test cases for LDIF directory tree writing/reading. """ from twisted.trial import unittest import os, random, errno, shutil, sets from ldaptor import ldiftree, entry, delta, testutil from ldaptor.entry import BaseLDAPEntry from ldaptor.protocols.ldap import ldaperrors, ldifprotocol def writeFile(path, content): f = file(path, 'w') f.write(content) f.close() class RandomizeListdirMixin(object): def randomListdir(self, *args, **kwargs): r = self.__listdir(*args, **kwargs) random.shuffle(r) return r def setUpClass(self): self.__listdir = os.listdir os.listdir = self.randomListdir def tearDownClass(self): os.listdir = self.__listdir class Dir2LDIF(RandomizeListdirMixin, unittest.TestCase): def setUp(self): self.tree = self.mktemp() os.mkdir(self.tree) com = os.path.join(self.tree, 'dc=com.dir') os.mkdir(com) example = os.path.join(com, 'dc=example.dir') os.mkdir(example) writeFile(os.path.join(example, 'cn=foo.ldif'), """\ dn: cn=foo,dc=example,dc=com cn: foo objectClass: top """) writeFile(os.path.join(example, 'cn=bad-two-entries.ldif'), """\ dn: cn=bad-two-entries,dc=example,dc=com cn: bad-two-entries objectClass: top dn: cn=more,dc=example,dc=com cn: more objectClass: top """) writeFile(os.path.join(example, 'cn=bad-missing-end.ldif'), """\ dn: cn=bad-missing-end,dc=example,dc=com cn: bad-missing-end objectClass: top """) writeFile(os.path.join(example, 'cn=bad-empty.ldif'), '') writeFile(os.path.join(example, 'cn=bad-only-newline.ldif'), '\n') sales = os.path.join(example, 'ou=Sales.dir') os.mkdir(sales) writeFile(os.path.join(sales, 'cn=sales-thingie.ldif'), """\ dn: cn=sales-thingie,ou=Sales,dc=example,dc=com cn: sales-thingie objectClass: top """) def testSimpleRead(self): want = BaseLDAPEntry(dn='cn=foo,dc=example,dc=com', attributes={ 'objectClass': ['top'], 'cn': ['foo'], }) d = ldiftree.get(self.tree, want.dn) d.addCallback(self.failUnlessEqual, want) return d def testNoAccess(self): os.chmod(os.path.join(self.tree, 'dc=com.dir', 'dc=example.dir', 'cn=foo.ldif'), 0) d = ldiftree.get(self.tree, 'cn=foo,dc=example,dc=com') def eb(fail): fail.trap(IOError) self.assertEquals(fail.value.errno, errno.EACCES) d.addCallbacks(testutil.mustRaise, eb) return d if os.getuid() == 0: testNoAccess.skip = "Can't test as root" def gettingDNRaises(self, dn, exceptionClass): d = ldiftree.get(self.tree, dn) def eb(fail): fail.trap(exceptionClass) d.addCallbacks(testutil.mustRaise, eb) return d def testMultipleError(self): return self.gettingDNRaises( 'cn=bad-two-entries,dc=example,dc=com', ldiftree.LDIFTreeEntryContainsMultipleEntries) def testMissingEndError(self): return self.gettingDNRaises( 'cn=bad-missing-end,dc=example,dc=com', ldiftree.LDIFTreeEntryContainsNoEntries) def testEmptyError(self): return self.gettingDNRaises( 'cn=bad-empty,dc=example,dc=com', ldiftree.LDIFTreeEntryContainsNoEntries) def testOnlyNewlineError(self): return self.gettingDNRaises( 'cn=bad-only-newline,dc=example,dc=com', ldifprotocol.LDIFLineWithoutSemicolonError) def testTreeBranches(self): want = BaseLDAPEntry(dn='cn=sales-thingie,ou=Sales,dc=example,dc=com', attributes={ 'objectClass': ['top'], 'cn': ['sales-thingie'], }) d = ldiftree.get(self.tree, want.dn) d.addCallback(self.failUnlessEqual, want) return d class LDIF2Dir(RandomizeListdirMixin, unittest.TestCase): def setUp(self): self.tree = self.mktemp() os.mkdir(self.tree) com = os.path.join(self.tree, 'dc=com.dir') os.mkdir(com) example = os.path.join(com, 'dc=example.dir') os.mkdir(example) writeFile(os.path.join(example, 'cn=pre-existing.ldif'), """\ dn: cn=pre-existing,dc=example,dc=com cn: pre-existing objectClass: top """) writeFile(os.path.join(example, 'ou=OrgUnit.ldif'), """\ dn: ou=OrgUnit,dc=example,dc=com ou: OrgUnit objectClass: organizationalUnit """) def testSimpleWrite(self): e = BaseLDAPEntry(dn='cn=foo,dc=example,dc=com', attributes={ 'objectClass': ['top'], 'cn': ['foo'], }) d = ldiftree.put(self.tree, e) d.addCallback(self._cb_testSimpleWrite) return d def _cb_testSimpleWrite(self, entry): path = os.path.join(self.tree, 'dc=com.dir', 'dc=example.dir', 'cn=foo.ldif') self.failUnless(os.path.isfile(path)) self.failUnlessEqual(file(path).read(), """\ dn: cn=foo,dc=example,dc=com objectClass: top cn: foo """) def testDirCreation(self): e = BaseLDAPEntry(dn='cn=create-me,ou=OrgUnit,dc=example,dc=com', attributes={ 'objectClass': ['top'], 'cn': ['create-me'], }) d = ldiftree.put(self.tree, e) d.addCallback(self._cb_testDirCreation) return d def _cb_testDirCreation(self, entry): path = os.path.join(self.tree, 'dc=com.dir', 'dc=example.dir', 'ou=OrgUnit.dir', 'cn=create-me.ldif') self.failUnless(os.path.isfile(path)) self.failUnlessEqual(file(path).read(), """\ dn: cn=create-me,ou=OrgUnit,dc=example,dc=com objectClass: top cn: create-me """) def testDirExists(self): e = BaseLDAPEntry(dn='cn=create-me,ou=OrgUnit,dc=example,dc=com', attributes={ 'objectClass': ['top'], 'cn': ['create-me'], }) dirpath = os.path.join(self.tree, 'dc=com.dir', 'dc=example.dir', 'ou=OrgUnit.dir') os.mkdir(dirpath) d = ldiftree.put(self.tree, e) d.addCallback(self._cb_testDirExists, dirpath) return d def _cb_testDirExists(self, entry, dirpath): path = os.path.join(dirpath, 'cn=create-me.ldif') self.failUnless(os.path.isfile(path)) self.failUnlessEqual(file(path).read(), """\ dn: cn=create-me,ou=OrgUnit,dc=example,dc=com objectClass: top cn: create-me """) def testMissingLinkError(self): e = BaseLDAPEntry(dn='cn=bad-create,ou=NoSuchOrgUnit,dc=example,dc=com', attributes={ 'objectClass': ['top'], 'cn': ['bad-create'], }) d = ldiftree.put(self.tree, e) d.addCallbacks(self._cb_testMissingLinkError, self._eb_testMissingLinkError) return d def _cb_testMissingLinkError(self): raise unittest.FailTest('Should have raised an exception.') def _eb_testMissingLinkError(self, fail): fail.trap(ldiftree.LDIFTreeNoSuchObject) def testAddTopLevel(self): e = BaseLDAPEntry(dn='dc=org', attributes={ 'objectClass': ['dcObject'], 'dc': ['org'], }) d = ldiftree.put(self.tree, e) d.addCallback(self._cb_testAddTopLevel) return d def _cb_testAddTopLevel(self, entry): path = os.path.join(self.tree, 'dc=org.ldif') self.failUnless(os.path.isfile(path)) self.failUnlessEqual(file(path).read(), """\ dn: dc=org objectClass: dcObject dc: org """) class Tree(RandomizeListdirMixin, unittest.TestCase): # TODO share the actual tests with inmemory and any other # implementations of the same interface def setUp(self): self.tree = self.mktemp() os.mkdir(self.tree) com = os.path.join(self.tree, 'dc=com.dir') os.mkdir(com) example = os.path.join(com, 'dc=example.dir') os.mkdir(example) meta = os.path.join(example, 'ou=metasyntactic.dir') os.mkdir(meta) writeFile(os.path.join(example, 'ou=metasyntactic.ldif'), """\ dn: ou=metasyntactic,dc=example,dc=com objectClass: a objectClass: b ou: metasyntactic """) foo = os.path.join(meta, 'cn=foo.dir') writeFile(os.path.join(meta, 'cn=foo.ldif'), """\ dn: cn=foo,ou=metasyntactic,dc=example,dc=com objectClass: a objectClass: b cn: foo """) bar = os.path.join(meta, 'cn=bar.dir') writeFile(os.path.join(meta, 'cn=bar.ldif'), """\ dn: cn=bar,ou=metasyntactic,dc=example,dc=com objectClass: a objectClass: b cn: bar """) empty = os.path.join(example, 'ou=empty.dir') writeFile(os.path.join(example, 'ou=empty.ldif'), """\ dn: ou=empty,dc=example,dc=com objectClass: a objectClass: b ou: empty """) oneChild = os.path.join(example, 'ou=oneChild.dir') os.mkdir(oneChild) writeFile(os.path.join(example, 'ou=oneChild.ldif'), """\ dn: ou=oneChild,dc=example,dc=com objectClass: a objectClass: b ou: oneChild """) theChild = os.path.join(oneChild, 'cn=theChild.dir') writeFile(os.path.join(oneChild, 'cn=theChild.ldif'), """\ dn: cn=theChild,ou=oneChild,dc=example,dc=com objectClass: a objectClass: b cn: theChild """) self.root = ldiftree.LDIFTreeEntry(self.tree) self.example = ldiftree.LDIFTreeEntry(example, 'dc=example,dc=com') self.empty = ldiftree.LDIFTreeEntry(empty, 'ou=empty,dc=example,dc=com') self.meta = ldiftree.LDIFTreeEntry(meta, 'ou=metasyntactic,dc=example,dc=com') self.foo = ldiftree.LDIFTreeEntry(foo, 'cn=foo,ou=metasyntactic,dc=example,dc=com') self.bar = ldiftree.LDIFTreeEntry(bar, 'cn=bar,ou=metasyntactic,dc=example,dc=com') self.oneChild = ldiftree.LDIFTreeEntry(oneChild, 'ou=oneChild,dc=example,dc=com') self.theChild = ldiftree.LDIFTreeEntry(theChild, 'cn=theChild,ou=oneChild,dc=example,dc=com') def test_children_empty(self): d = self.empty.children() def cb(children): self.assertEquals(children, []) d.addCallback(cb) return d def test_children_oneChild(self): d = self.oneChild.children() d.addCallback(self._cb_test_children_oneChild) return d def _cb_test_children_oneChild(self, children): self.assertEquals(len(children), 1) got = [e.dn for e in children] want = ['cn=theChild,ou=oneChild,dc=example,dc=com'] got.sort() want.sort() self.assertEquals(got, want) def test_children_repeat(self): """Test that .children() returns a copy of the data so that modifying it does not affect behaviour.""" d = self.oneChild.children() d.addCallback(self._cb_test_children_repeat_1) return d def _cb_test_children_repeat_1(self, children1): self.assertEquals(len(children1), 1) children1.pop() d = self.oneChild.children() d.addCallback(self._cb_test_children_repeat_2) return d def _cb_test_children_repeat_2(self, children2): self.assertEquals(len(children2), 1) def test_children_twoChildren(self): d = self.meta.children() d.addCallback(self._cb_test_children_twoChildren) return d def _cb_test_children_twoChildren(self, children): self.assertEquals(len(children), 2) want = [ 'cn=foo,ou=metasyntactic,dc=example,dc=com', 'cn=bar,ou=metasyntactic,dc=example,dc=com', ] got = [e.dn for e in children] got.sort() want.sort() self.assertEquals(got, want) def test_children_twoChildren_callback(self): children = [] d = self.meta.children(callback=children.append) d.addCallback(self._cb_test_children_twoChildren_callback, children) return d def _cb_test_children_twoChildren_callback(self, r, children): self.assertIdentical(r, None) self.assertEquals(len(children), 2) want = [ 'cn=foo,ou=metasyntactic,dc=example,dc=com', 'cn=bar,ou=metasyntactic,dc=example,dc=com', ] got = [e.dn for e in children] got.sort() want.sort() self.assertEquals(got, want) def test_children_noAccess_dir_noRead(self): os.chmod(self.meta.path, 0300) d = self.meta.children() def eb(fail): fail.trap(OSError) self.assertEquals(fail.value.errno, errno.EACCES) os.chmod(self.meta.path, 0755) d.addCallbacks(testutil.mustRaise, eb) return d if os.getuid() == 0: test_children_noAccess_dir_noRead.skip = "Can't test as root" def test_children_noAccess_dir_noExec(self): os.chmod(self.meta.path, 0600) d = self.meta.children() def eb(fail): fail.trap(IOError) self.assertEquals(fail.value.errno, errno.EACCES) os.chmod(self.meta.path, 0755) d.addCallbacks(testutil.mustRaise, eb) return d if os.getuid() == 0: test_children_noAccess_dir_noExec.skip = "Can't test as root" def test_children_noAccess_file(self): os.chmod(os.path.join(self.meta.path, 'cn=foo.ldif'), 0) d = self.meta.children() def eb(fail): fail.trap(IOError) self.assertEquals(fail.value.errno, errno.EACCES) d.addCallbacks(testutil.mustRaise, eb) return d if os.getuid() == 0: test_children_noAccess_file.skip = "Can't test as root" def test_addChild(self): self.empty.addChild( rdn='a=b', attributes={ 'objectClass': ['a', 'b'], 'a': 'b', }) d = self.empty.children() d.addCallback(self._cb_test_addChild) return d def _cb_test_addChild(self, children): self.assertEquals(len(children), 1) got = [e.dn for e in children] want = [ 'a=b,ou=empty,dc=example,dc=com', ] got.sort() want.sort() self.assertEquals(got, want) def test_addChild_Exists(self): self.assertRaises(ldaperrors.LDAPEntryAlreadyExists, self.meta.addChild, rdn='cn=foo', attributes={ 'objectClass': ['a'], 'cn': 'foo', }) def test_parent(self): self.assertEquals(self.foo.parent(), self.meta) self.assertEquals(self.meta.parent(), self.example) self.assertEquals(self.root.parent(), None) def test_subtree_empty(self): d = self.empty.subtree() d.addCallback(self._cb_test_subtree_empty) return d def _cb_test_subtree_empty(self, entries): self.assertEquals(len(entries), 1) def test_subtree_oneChild(self): d = self.oneChild.subtree() d.addCallback(self._cb_test_subtree_oneChild) return d def _cb_test_subtree_oneChild(self, results): got = results want = [ self.oneChild, self.theChild, ] self.assertEquals(got, want) def test_subtree_oneChild_cb(self): got = [] d = self.oneChild.subtree(got.append) d.addCallback(self._cb_test_subtree_oneChild_cb, got) return d def _cb_test_subtree_oneChild_cb(self, r, got): self.assertEquals(r, None) want = [ self.oneChild, self.theChild, ] self.assertEquals(got, want) def test_subtree_many(self): d = self.example.subtree() d.addCallback(self._cb_test_subtree_many) return d def _cb_test_subtree_many(self, results): got = results want = [ self.example, self.oneChild, self.theChild, self.empty, self.meta, self.bar, self.foo, ] got.sort() want.sort() self.assertEquals(got, want) def test_subtree_many_cb(self): got = [] d = self.example.subtree(callback=got.append) d.addCallback(self._cb_test_subtree_many_cb, got) return d def _cb_test_subtree_many_cb(self, r, got): self.assertEquals(r, None) want = [ self.example, self.oneChild, self.theChild, self.empty, self.meta, self.bar, self.foo, ] got.sort() want.sort() self.assertEquals(got, want) def test_lookup_fail(self): dn = 'cn=thud,ou=metasyntactic,dc=example,dc=com' d = self.root.lookup(dn) def eb(fail): fail.trap(ldaperrors.LDAPNoSuchObject) self.assertEquals(fail.value.message, dn) d.addCallbacks(testutil.mustRaise, eb) return d def test_lookup_fail_outOfTree(self): dn = 'dc=invalid' d = self.root.lookup(dn) def eb(fail): fail.trap(ldaperrors.LDAPNoSuchObject) self.assertEquals(fail.value.message, dn) d.addCallbacks(testutil.mustRaise, eb) return d def test_lookup_fail_outOfTree_2(self): dn = 'dc=invalid' d = self.example.lookup(dn) def eb(fail): fail.trap(ldaperrors.LDAPNoSuchObject) self.assertEquals(fail.value.message, dn) d.addCallbacks(testutil.mustRaise, eb) def test_lookup_fail_multipleError(self): writeFile(os.path.join(self.example.path, 'cn=bad-two-entries.ldif'), """\ dn: cn=bad-two-entries,dc=example,dc=com cn: bad-two-entries objectClass: top dn: cn=more,dc=example,dc=com cn: more objectClass: top """) self.assertRaises( ldiftree.LDIFTreeEntryContainsMultipleEntries, self.example.lookup, 'cn=bad-two-entries,dc=example,dc=com') def test_lookup_fail_emptyError(self): writeFile(os.path.join(self.example.path, 'cn=bad-empty.ldif'), "") self.assertRaises( ldiftree.LDIFTreeEntryContainsNoEntries, self.example.lookup, 'cn=bad-empty,dc=example,dc=com') def test_lookup_deep(self): dn = 'cn=bar,ou=metasyntactic,dc=example,dc=com' d = self.root.lookup(dn) d.addCallback(self._cb_test_lookup_deep) return d def _cb_test_lookup_deep(self, r): self.assertEquals(r, self.bar) def test_delete_root(self): d = self.root.delete() def eb(fail): fail.trap(ldiftree.LDAPCannotRemoveRootError) d.addCallbacks(testutil.mustRaise, eb) return d def test_delete_nonLeaf(self): d = self.meta.delete() def eb(fail): fail.trap(ldaperrors.LDAPNotAllowedOnNonLeaf) d.addCallbacks(testutil.mustRaise, eb) return d def test_delete(self): d = self.foo.delete() d.addCallback(self._cb_test_delete_1) return d def _cb_test_delete_1(self, r): self.assertEquals(r, self.foo) d = self.meta.children() d.addCallback(self._cb_test_delete_2) return d def _cb_test_delete_2(self, r): self.assertEquals(r, [self.bar]) def test_deleteChild(self): d = self.meta.deleteChild('cn=bar') d.addCallback(self._cb_test_deleteChild_1) return d def _cb_test_deleteChild_1(self, r): self.assertEquals(r, self.bar) d = self.meta.children() d.addCallback(self._cb_test_deleteChild_2) return d def _cb_test_deleteChild_2(self, r): self.assertEquals(r, [self.foo]) def test_deleteChild_NonExisting(self): d = self.root.deleteChild('cn=not-exist') def eb(fail): fail.trap(ldaperrors.LDAPNoSuchObject) d.addCallbacks(testutil.mustRaise, eb) return d def test_setPassword(self): self.foo.setPassword('s3krit', salt='\xf2\x4a') self.failUnless('userPassword' in self.foo) self.assertEquals(self.foo['userPassword'], ['{SSHA}0n/Iw1NhUOKyaI9gm9v5YsO3ZInySg==']) def test_setPassword_noSalt(self): self.foo.setPassword('s3krit') self.failUnless('userPassword' in self.foo) d = self.foo.bind('s3krit') d.addCallback(self.assertIdentical, self.foo) d.addCallback(lambda _: self.foo.bind('s4krit')) def eb(fail): fail.trap(ldaperrors.LDAPInvalidCredentials) d.addCallbacks(testutil.mustRaise, eb) return d def test_diffTree_self(self): d = self.root.diffTree(self.root) d.addCallback(self.assertEquals, []) return d def test_diffTree_copy(self): otherDir = self.mktemp() shutil.copytree(self.tree, otherDir) other = ldiftree.LDIFTreeEntry(otherDir) d = self.root.diffTree(other) d.addCallback(self.assertEquals, []) return d def test_diffTree_addChild(self): otherDir = self.mktemp() shutil.copytree(self.tree, otherDir) other = ldiftree.LDIFTreeEntry(otherDir) e = entry.BaseLDAPEntry(dn='cn=foo,dc=example,dc=com') d = ldiftree.put(otherDir, e) def cb1(dummy): return other.lookup('cn=foo,dc=example,dc=com') d.addCallback(cb1) def cb2(r): d = self.root.diffTree(other) d.addCallback(self.assertEquals, [delta.AddOp(r)]) return d d.addCallback(cb2) return d def test_diffTree_delChild(self): otherDir = self.mktemp() shutil.copytree(self.tree, otherDir) other = ldiftree.LDIFTreeEntry(otherDir) d = other.lookup('ou=empty,dc=example,dc=com') def cb1(otherEmpty): return otherEmpty.delete() d.addCallback(cb1) def cb2(dummy): return self.root.diffTree(other) d.addCallback(cb2) def cb3(got): self.assertEquals(got, [delta.DeleteOp(self.empty)]) d.addCallback(cb3) return d def test_diffTree_edit(self): otherDir = self.mktemp() shutil.copytree(self.tree, otherDir) other = ldiftree.LDIFTreeEntry(otherDir) d = other.lookup('ou=empty,dc=example,dc=com') def cb1(otherEmpty): otherEmpty['foo'] = ['bar'] return otherEmpty.commit() d.addCallback(cb1) def cb2(dummy): return self.root.diffTree(other) d.addCallback(cb2) def cb3(got): self.assertEquals(got, [ delta.ModifyOp(self.empty.dn, [delta.Add('foo', ['bar'])], ), ]) d.addCallback(cb3) return d def test_move_noChildren_sameSuperior(self): d = self.empty.move('ou=moved,dc=example,dc=com') def getChildren(dummy): return self.example.children() d.addCallback(getChildren) d.addCallback(sets.Set) d.addCallback(self.assertEquals, sets.Set([ self.meta, BaseLDAPEntry( dn='ou=moved,dc=example,dc=com', attributes={ 'objectClass': ['a', 'b'], 'ou': ['moved'], }), self.oneChild, ])) return d def test_move_children_sameSuperior(self): d = self.meta.move('ou=moved,dc=example,dc=com') def getChildren(dummy): return self.example.children() d.addCallback(getChildren) d.addCallback(sets.Set) d.addCallback(self.assertEquals, sets.Set([ BaseLDAPEntry(dn='ou=moved,dc=example,dc=com', attributes={ 'objectClass': ['a', 'b'], 'ou': ['moved'], }), self.empty, self.oneChild, ])) return d def test_move_noChildren_newSuperior(self): d = self.empty.move('ou=moved,ou=oneChild,dc=example,dc=com') def getChildren(dummy): return self.example.children() d.addCallback(getChildren) d.addCallback(sets.Set) d.addCallback(self.assertEquals, sets.Set([ self.meta, self.oneChild, ])) def getChildren2(dummy): return self.oneChild.children() d.addCallback(getChildren2) d.addCallback(sets.Set) d.addCallback(self.assertEquals, sets.Set([ self.theChild, BaseLDAPEntry( dn='ou=moved,ou=oneChild,dc=example,dc=com', attributes={ 'objectClass': ['a', 'b'], 'ou': ['moved'], }), ])) return d def test_move_children_newSuperior(self): d = self.meta.move('ou=moved,ou=oneChild,dc=example,dc=com') def getChildren(dummy): return self.example.children() d.addCallback(getChildren) d.addCallback(sets.Set) d.addCallback(self.assertEquals, sets.Set([ self.empty, self.oneChild, ])) def getChildren2(dummy): return self.oneChild.children() d.addCallback(getChildren2) d.addCallback(sets.Set) d.addCallback(self.assertEquals, sets.Set([ self.theChild, BaseLDAPEntry(dn='ou=moved,ou=oneChild,dc=example,dc=com', attributes={ 'objectClass': ['a', 'b'], 'ou': ['moved'], }), ])) return d ldaptor-0.0.43/ldaptor/test/test_ldapsyntax.py0000755000175000017500000016367010352474314017614 0ustar janjan""" Test cases for ldaptor.protocols.ldap.ldapsyntax module. """ from twisted.trial import unittest from ldaptor import config, testutil, delta from ldaptor.protocols.ldap import ldapsyntax, ldaperrors from ldaptor.protocols import pureldap, pureber from twisted.internet import defer from twisted.python import failure from ldaptor.testutil import LDAPClientTestDriver class LDAPSyntaxBasics(unittest.TestCase): def testCreation(self): """Creating an LDAP object should succeed.""" client = LDAPClientTestDriver() o=ldapsyntax.LDAPEntry(client=client, dn='cn=foo,dc=example,dc=com', attributes={ 'objectClass': ['a', 'b'], 'aValue': ['a'], 'bValue': ['b'], }) self.failUnlessEqual(str(o.dn), 'cn=foo,dc=example,dc=com') self.failUnlessEqual(o['objectClass'], ['a', 'b']) self.failUnlessEqual(o['aValue'], ['a']) self.failUnlessEqual(o['bValue'], ['b']) client.assertNothingSent() def testKeys(self): """Iterating over the keys of an LDAP object gives expected results.""" client = LDAPClientTestDriver() o=ldapsyntax.LDAPEntry(client=client, dn='cn=foo,dc=example,dc=com', attributes={ 'objectClass': ['a', 'b'], 'aValue': ['a'], 'bValue': ['b'], }) seen={} for k in o.keys(): assert not seen.has_key(k) seen[k]=1 assert seen == {'objectClass': 1, 'aValue': 1, 'bValue': 1, } def testItems(self): """Iterating over the items of an LDAP object gives expected results.""" client = LDAPClientTestDriver() o=ldapsyntax.LDAPEntry(client=client, dn='cn=foo,dc=example,dc=com', attributes={ 'objectClass': ['a', 'b'], 'aValue': ['a'], 'bValue': ['b'], }) seen={} for k,vs in o.items(): assert not seen.has_key(k) seen[k]=vs assert seen == {'objectClass': ['a', 'b'], 'aValue': ['a'], 'bValue': ['b'], } def testIn(self): """Key in object gives expected results.""" client=LDAPClientTestDriver() o=ldapsyntax.LDAPEntry(client=client, dn='cn=foo,dc=example,dc=com', attributes={ 'objectClass': ['a', 'b'], 'aValue': ['a'], 'bValue': ['b'], }) assert 'objectClass' in o assert 'aValue' in o assert 'bValue' in o assert 'foo' not in o assert '' not in o assert None not in o assert 'a' in o['objectClass'] assert 'b' in o['objectClass'] assert 'foo' not in o['objectClass'] assert '' not in o['objectClass'] assert None not in o['objectClass'] assert 'a' in o['aValue'] assert 'foo' not in o['aValue'] assert '' not in o['aValue'] assert None not in o['aValue'] class LDAPSyntaxAttributes(unittest.TestCase): def testAttributeSetting(self): client=LDAPClientTestDriver() o=ldapsyntax.LDAPEntry(client=client, dn='cn=foo,dc=example,dc=com', attributes={ 'objectClass': ['a', 'b'], 'aValue': ['a'], 'bValue': ['b'], }) o['aValue']=['foo', 'bar'] self.failUnlessEqual(o['aValue'], ['foo', 'bar']) o['aValue']=['quux'] self.failUnlessEqual(o['aValue'], ['quux']) self.failUnlessEqual(o['bValue'], ['b']) o['cValue']=['thud'] self.failUnlessEqual(o['aValue'], ['quux']) self.failUnlessEqual(o['bValue'], ['b']) self.failUnlessEqual(o['cValue'], ['thud']) def testAttributeDelete(self): client=LDAPClientTestDriver() o=ldapsyntax.LDAPEntry(client=client, dn='cn=foo,dc=example,dc=com', attributes={ 'objectClass': ['a', 'b'], 'aValue': ['a'], 'bValue': ['b'], }) o['aValue']=['quux'] del o['aValue'] del o['bValue'] self.failIf(o.has_key('aValue')) self.failIf(o.has_key('bValue')) def testAttributeAdd(self): client=LDAPClientTestDriver() o=ldapsyntax.LDAPEntry(client=client, dn='cn=foo,dc=example,dc=com', attributes={ 'objectClass': ['a', 'b'], 'aValue': ['a'], 'bValue': ['b'], }) o['aValue'].add('foo') self.failUnlessEqual(o['aValue'], ['a', 'foo']) def testAttributeItemDelete(self): client=LDAPClientTestDriver() o=ldapsyntax.LDAPEntry(client=client, dn='cn=foo,dc=example,dc=com', attributes={ 'objectClass': ['a', 'b'], 'aValue': ['a', 'b', 'c'], 'bValue': ['b'], }) o['aValue'].remove('b') self.failUnlessEqual(o['aValue'], ['a', 'c']) def testUndo(self): """Undo should forget the modifications.""" client=LDAPClientTestDriver() o=ldapsyntax.LDAPEntry(client=client, dn='cn=foo,dc=example,dc=com', attributes={ 'objectClass': ['a', 'b'], 'aValue': ['a'], 'bValue': ['b'], 'cValue': ['c'], }) o['aValue']=['foo', 'bar'] o['aValue']=['quux'] del o['cValue'] o.undo() self.failUnlessEqual(o['aValue'], ['a']) self.failUnlessEqual(o['bValue'], ['b']) self.failUnlessEqual(o['cValue'], ['c']) def testUndoJournaling(self): """Journaling should still work after undo.""" client=LDAPClientTestDriver( [ pureldap.LDAPModifyResponse(resultCode=0, matchedDN='', errorMessage=''), ]) o=ldapsyntax.LDAPEntry(client=client, dn='cn=foo,dc=example,dc=com', attributes={ 'objectClass': ['a', 'b'], 'aValue': ['a'], 'bValue': ['b'], 'cValue': ['c'], }) o['aValue']=['foo', 'bar'] o['aValue']=['quux'] del o['cValue'] o.undo() o['aValue'].update(['newValue', 'anotherNewValue']) d=o.commit() def cb(dummy): self.failUnlessEqual(o['aValue'], ['a', 'newValue', 'anotherNewValue']) self.failUnlessEqual(o['bValue'], ['b']) self.failUnlessEqual(o['cValue'], ['c']) client.assertSent(delta.ModifyOp('cn=foo,dc=example,dc=com', [ delta.Add('aValue', ['newValue', 'anotherNewValue']), ]).asLDAP()) d.addCallback(cb) return d def testUndoAfterCommit(self): """Undo should not undo things that have been commited.""" client=LDAPClientTestDriver( [ pureldap.LDAPModifyResponse(resultCode=0, matchedDN='', errorMessage=''), ]) o=ldapsyntax.LDAPEntry( client=client, dn='cn=foo,dc=example,dc=com', attributes={ 'objectClass': ['a', 'b'], 'aValue': ['a'], 'bValue': ['b'], 'cValue': ['c'], }) o['aValue']=['foo', 'bar'] o['bValue']=['quux'] del o['cValue'] d=o.commit() def cb(dummy): o.undo() self.failUnlessEqual(o['aValue'], ['foo', 'bar']) self.failUnlessEqual(o['bValue'], ['quux']) self.failIf(o.has_key('cValue')) d.addCallback(cb) return d class LDAPSyntaxAttributesModificationOnWire(unittest.TestCase): def testAdd(self): """Modify & commit should write the right data to the server.""" client = LDAPClientTestDriver( [ pureldap.LDAPModifyResponse(resultCode=0, matchedDN='', errorMessage=''), ]) o=ldapsyntax.LDAPEntry(client=client, dn='cn=foo,dc=example,dc=com', attributes={ 'objectClass': ['a', 'b'], 'aValue': ['a'], }) o['aValue'].update(['newValue', 'anotherNewValue']) d=o.commit() def cb(dummy): client.assertSent(delta.ModifyOp('cn=foo,dc=example,dc=com', [ delta.Add('aValue', ['newValue', 'anotherNewValue']), ]).asLDAP()) d.addCallback(cb) return d def testAddSeparate(self): """Modify & commit should write the right data to the server.""" client = LDAPClientTestDriver( [ pureldap.LDAPModifyResponse(resultCode=0, matchedDN='', errorMessage=''), ]) o=ldapsyntax.LDAPEntry(client=client, dn='cn=foo,dc=example,dc=com', attributes={ 'objectClass': ['a', 'b'], 'aValue': ['a'], }) o['aValue'].add('newValue') o['aValue'].add('anotherNewValue') d=o.commit() def cb(dummy): client.assertSent(delta.ModifyOp('cn=foo,dc=example,dc=com', [ delta.Add('aValue', ['newValue']), delta.Add('aValue', ['anotherNewValue']), ]).asLDAP()) d.addCallback(cb) return d def testDeleteAttribute(self): """Modify & commit should write the right data to the server.""" client = LDAPClientTestDriver( [ pureldap.LDAPModifyResponse(resultCode=0, matchedDN='', errorMessage='') ]) o=ldapsyntax.LDAPEntry(client=client, dn='cn=foo,dc=example,dc=com', attributes={ 'objectClass': ['a', 'b'], 'aValue': ['a'], }) o['aValue'].remove('a') d=o.commit() def cb(dummy): client.assertSent(delta.ModifyOp('cn=foo,dc=example,dc=com', [ delta.Delete('aValue', ['a']), ]).asLDAP()) d.addCallback(cb) return d def testDeleteAllAttribute(self): """Modify & commit should write the right data to the server.""" client = LDAPClientTestDriver( [ pureldap.LDAPModifyResponse(resultCode=0, matchedDN='', errorMessage=''), ]) o=ldapsyntax.LDAPEntry(client=client, dn='cn=foo,dc=example,dc=com', attributes={ 'objectClass': ['a', 'b'], 'aValue': ['a1', 'a2'], 'bValue': ['b1', 'b2'], }) del o['aValue'] o['bValue'].clear() d=o.commit() def cb(dummy): client.assertSent(delta.ModifyOp('cn=foo,dc=example,dc=com', [ delta.Delete('aValue'), delta.Delete('bValue'), ]).asLDAP()) d.addCallback(cb) return d def testReplaceAttributes(self): """Modify & commit should write the right data to the server.""" client = LDAPClientTestDriver( [ pureldap.LDAPModifyResponse(resultCode=0, matchedDN='', errorMessage='') ]) o=ldapsyntax.LDAPEntry(client=client, dn='cn=foo,dc=example,dc=com', attributes={ 'objectClass': ['a', 'b'], 'aValue': ['a'], }) o['aValue']=['foo', 'bar'] d=o.commit() def cb(dummy): client.assertSent(delta.ModifyOp('cn=foo,dc=example,dc=com', [ delta.Replace('aValue', ['foo', 'bar']), ]).asLDAP()) d.addCallback(cb) return d class LDAPSyntaxSearch(unittest.TestCase): def testSearch(self): """Test searches.""" client=LDAPClientTestDriver([ pureldap.LDAPSearchResultEntry( objectName='cn=foo,dc=example,dc=com', attributes=(('foo', ['a']), ('bar', ['b', 'c']), ), ), pureldap.LDAPSearchResultEntry( objectName='cn=bar,dc=example,dc=com', attributes=(('foo', ['a']), ('bar', ['d', 'e']), ), ), pureldap.LDAPSearchResultDone( resultCode=0, matchedDN='', errorMessage='') ]) o=ldapsyntax.LDAPEntry(client=client, dn='dc=example,dc=com', attributes={ 'objectClass': ['organizationalUnit'], }) d=o.search(filterText='(foo=a)', attributes=['foo', 'bar']) def cb(val): client.assertSent(pureldap.LDAPSearchRequest( baseObject='dc=example,dc=com', scope=pureldap.LDAP_SCOPE_wholeSubtree, derefAliases=pureldap.LDAP_DEREF_neverDerefAliases, sizeLimit=0, timeLimit=0, typesOnly=0, filter=pureldap.LDAPFilter_equalityMatch( attributeDesc=pureldap.LDAPAttributeDescription(value='foo'), assertionValue=pureldap.LDAPAssertionValue(value='a')), attributes=['foo', 'bar'])) self.failUnlessEqual(len(val), 2) self.failUnlessEqual(val[0], ldapsyntax.LDAPEntry( client=client, dn='cn=foo,dc=example,dc=com', attributes={ 'foo': ['a'], 'bar': ['b', 'c'], })) self.failUnlessEqual(val[1], ldapsyntax.LDAPEntry( client=client, dn='cn=bar,dc=example,dc=com', attributes={ 'foo': ['a'], 'bar': ['d', 'e'], })) d.addCallback(cb) return d def testSearch_defaultAttributes(self): """Search without explicit list of attributes returns all attributes.""" client=LDAPClientTestDriver([ pureldap.LDAPSearchResultEntry( objectName='cn=foo,dc=example,dc=com', attributes=(('foo', ['a']), ('bar', ['b', 'c']), ), ), pureldap.LDAPSearchResultEntry( objectName='cn=bar,dc=example,dc=com', attributes=(('foo', ['a']), ('bar', ['d', 'e']), ), ), pureldap.LDAPSearchResultDone( resultCode=0, matchedDN='', errorMessage='') ]) o=ldapsyntax.LDAPEntry(client=client, dn='dc=example,dc=com', attributes={ 'objectClass': ['organizationalUnit'], }) d=o.search(filterText='(foo=a)') def cb(val): client.assertSent(pureldap.LDAPSearchRequest( baseObject='dc=example,dc=com', scope=pureldap.LDAP_SCOPE_wholeSubtree, derefAliases=pureldap.LDAP_DEREF_neverDerefAliases, sizeLimit=0, timeLimit=0, typesOnly=0, filter=pureldap.LDAPFilter_equalityMatch( attributeDesc=pureldap.LDAPAttributeDescription(value='foo'), assertionValue=pureldap.LDAPAssertionValue(value='a')), attributes=[])) self.failUnlessEqual(len(val), 2) self.failUnlessEqual(val[0], ldapsyntax.LDAPEntry( client=client, dn='cn=foo,dc=example,dc=com', attributes={ 'foo': ['a'], 'bar': ['b', 'c'], })) self.failUnless(val[0].complete) self.failUnlessEqual(val[1], ldapsyntax.LDAPEntry( client=client, dn='cn=bar,dc=example,dc=com', attributes={ 'foo': ['a'], 'bar': ['d', 'e'], })) self.failUnless(val[1].complete) d.addCallback(cb) return d def testSearch_noAttributes(self): """Search with attributes=None returns no attributes.""" client=LDAPClientTestDriver([ pureldap.LDAPSearchResultEntry('cn=foo,dc=example,dc=com', attributes=()), pureldap.LDAPSearchResultEntry('cn=bar,dc=example,dc=com', attributes=()), pureldap.LDAPSearchResultDone( resultCode=0, matchedDN='', errorMessage='') ]) o=ldapsyntax.LDAPEntry(client=client, dn='dc=example,dc=com', attributes={ 'objectClass': ['organizationalUnit'], }) d=o.search(filterText='(foo=a)', attributes=None) def cb(val): client.assertSent(pureldap.LDAPSearchRequest( baseObject='dc=example,dc=com', scope=pureldap.LDAP_SCOPE_wholeSubtree, derefAliases=pureldap.LDAP_DEREF_neverDerefAliases, sizeLimit=0, timeLimit=0, typesOnly=0, filter=pureldap.LDAPFilter_equalityMatch( attributeDesc=pureldap.LDAPAttributeDescription(value='foo'), assertionValue=pureldap.LDAPAssertionValue(value='a')), attributes=['1.1'])) self.failUnlessEqual(len(val), 2) self.failUnlessEqual(val[0], ldapsyntax.LDAPEntry( client=client, dn='cn=foo,dc=example,dc=com')) self.failIf(val[0].complete) self.failUnlessEqual(val[1], ldapsyntax.LDAPEntry( client=client, dn='cn=bar,dc=example,dc=com')) self.failIf(val[1].complete) d.addCallback(cb) return d def testSearch_ImmediateProcessing(self): """Test searches with the immediate processing feature.""" client=LDAPClientTestDriver([ pureldap.LDAPSearchResultEntry( objectName='cn=foo,dc=example,dc=com', attributes=(('bar', ['b', 'c']), ), ), pureldap.LDAPSearchResultEntry( objectName='cn=bar,dc=example,dc=com', attributes=(('bar', ['b', 'c']), ), ), pureldap.LDAPSearchResultDone( resultCode=0, matchedDN='', errorMessage='') ]) o=ldapsyntax.LDAPEntry(client=client, dn='dc=example,dc=com', attributes={ 'objectClass': ['organizationalUnit'], }) seen=[] def process(o): seen.append(o) d=o.search(filterText='(foo=a)', attributes=['bar'], callback=process) def cb(val): self.assertEquals(val, None) client.assertSent(pureldap.LDAPSearchRequest( baseObject='dc=example,dc=com', scope=pureldap.LDAP_SCOPE_wholeSubtree, derefAliases=pureldap.LDAP_DEREF_neverDerefAliases, sizeLimit=0, timeLimit=0, typesOnly=0, filter=pureldap.LDAPFilter_equalityMatch( attributeDesc=pureldap.LDAPAttributeDescription(value='foo'), assertionValue=pureldap.LDAPAssertionValue(value='a')), attributes=['bar'])) self.failUnlessEqual(seen, [ ldapsyntax.LDAPEntry( client=client, dn='cn=foo,dc=example,dc=com', attributes={ 'bar': ['b', 'c'], }), ldapsyntax.LDAPEntry( client=client, dn='cn=bar,dc=example,dc=com', attributes={ 'bar': ['b', 'c'], })]) d.addCallback(cb) return d def testSearch_fail(self): client=LDAPClientTestDriver([ pureldap.LDAPSearchResultDone( resultCode=ldaperrors.LDAPBusy.resultCode, matchedDN='', errorMessage='Go away') ]) o=ldapsyntax.LDAPEntry(client=client, dn='dc=example,dc=com') d=o.search(filterText='(foo=a)') def eb(fail): fail.trap(ldaperrors.LDAPBusy) self.assertEquals(fail.value.message, 'Go away') client.assertSent(pureldap.LDAPSearchRequest( baseObject='dc=example,dc=com', scope=pureldap.LDAP_SCOPE_wholeSubtree, derefAliases=pureldap.LDAP_DEREF_neverDerefAliases, sizeLimit=0, timeLimit=0, typesOnly=0, filter=pureldap.LDAPFilter_equalityMatch( attributeDesc=pureldap.LDAPAttributeDescription(value='foo'), assertionValue=pureldap.LDAPAssertionValue(value='a')), )) d.addCallbacks(testutil.mustRaise, eb) return d class LDAPSyntaxDNs(unittest.TestCase): def testDNKeyExistenceSuccess(self): client = LDAPClientTestDriver() ldapsyntax.LDAPEntry(client=client, dn='cn=foo,dc=example,dc=com', attributes={ 'cn': ['foo'], }) def TODOtestDNKeyExistenceFailure(self): client = LDAPClientTestDriver() self.failUnlessRaises(ldapsyntax.DNNotPresentError, ldapsyntax.LDAPEntry, client=client, dn='cn=foo,dc=example,dc=com', attributes={ 'foo': ['bar'], }) class LDAPSyntaxLDIF(unittest.TestCase): def testLDIFConversion(self): client = LDAPClientTestDriver() o=ldapsyntax.LDAPEntry(client=client, dn='cn=foo,dc=example,dc=com', attributes={ 'objectClass': ['a', 'b'], 'aValue': ['a', 'b'], 'bValue': ['c'], }) self.failUnlessEqual(str(o), '\n'.join(( "dn: cn=foo,dc=example,dc=com", "objectClass: a", "objectClass: b", "aValue: a", "aValue: b", "bValue: c", "\n"))) class LDAPSyntaxDelete(unittest.TestCase): def testDeleteInvalidates(self): """Deleting an LDAPEntry invalidates it.""" client = LDAPClientTestDriver( [pureldap.LDAPDelResponse(resultCode=0, matchedDN='', errorMessage=''), ]) o=ldapsyntax.LDAPEntry(client=client, dn='cn=foo,dc=example,dc=com', attributes={ 'objectClass': ['a'], }) d=o.delete() def cb(dummy): self.failUnlessRaises( ldapsyntax.ObjectDeletedError, o.search, filterText='(foo=a)') self.failUnlessRaises( ldapsyntax.ObjectDeletedError, o.get, 'objectClass') d.addCallback(cb) return d def testDeleteOnWire(self): """LDAPEntry.delete should write the right data to the server.""" client = LDAPClientTestDriver( [pureldap.LDAPDelResponse(resultCode=0, matchedDN='', errorMessage=''), ]) o=ldapsyntax.LDAPEntry(client=client, dn='cn=foo,dc=example,dc=com', attributes={ 'objectClass': ['a'], }) d=o.delete() def cb(dummy): client.assertSent(pureldap.LDAPDelRequest( entry='cn=foo,dc=example,dc=com', )) d.addCallback(cb) return d def testErrorHandling(self): """LDAPEntry.delete should pass LDAP errors to it's deferred.""" client = LDAPClientTestDriver( [pureldap.LDAPDelResponse(resultCode=ldaperrors.LDAPBusy.resultCode, matchedDN='', errorMessage='Go away'), ]) o=ldapsyntax.LDAPEntry(client=client, dn='cn=foo,dc=example,dc=com', attributes={ 'objectClass': ['a'], }) d=o.delete() def eb(fail): fail.trap(ldaperrors.LDAPBusy) self.assertEquals(fail.value.message, 'Go away') client.assertSent(pureldap.LDAPDelRequest( entry='cn=foo,dc=example,dc=com', )) d.addCallbacks(testutil.mustRaise, eb) return d def testErrorHandling_extended(self): """LDAPEntry.delete should pass even non-LDAPDelResponse errors to it's deferred.""" client = LDAPClientTestDriver( [pureldap.LDAPExtendedResponse(resultCode=ldaperrors.LDAPProtocolError.resultCode, responseName='1.3.6.1.4.1.1466.20036', errorMessage='Unknown request') ]) o=ldapsyntax.LDAPEntry(client=client, dn='cn=foo,dc=example,dc=com', attributes={ 'objectClass': ['a'], }) d=o.delete() def eb(fail): fail.trap(ldaperrors.LDAPProtocolError) self.assertEquals(fail.value.message, 'Unknown request') client.assertSent(pureldap.LDAPDelRequest( entry='cn=foo,dc=example,dc=com', )) d.addCallbacks(testutil.mustRaise, eb) return d class LDAPSyntaxAddChild(unittest.TestCase): def testAddChildOnWire(self): """LDAPEntry.addChild should write the right data to the server.""" client = LDAPClientTestDriver( [pureldap.LDAPAddResponse(resultCode=0, matchedDN='', errorMessage=''), ]) o=ldapsyntax.LDAPEntry(client=client, dn='ou=things,dc=example,dc=com', attributes={ 'objectClass': ['organizationalUnit'], 'ou': ['things'], }) d=o.addChild( rdn='givenName=Firstname+surname=Lastname', attributes={'objectClass': ['person', 'otherStuff'], 'givenName': ['Firstname'], 'surname': ['Lastname'], }) def cb(dummy): client.assertSent(pureldap.LDAPAddRequest( entry='givenName=Firstname+surname=Lastname,ou=things,dc=example,dc=com', attributes=[ (pureldap.LDAPAttributeDescription('objectClass'), pureber.BERSet([pureldap.LDAPAttributeValue('person'), pureldap.LDAPAttributeValue('otherStuff'), ])), (pureldap.LDAPAttributeDescription('givenName'), pureber.BERSet([pureldap.LDAPAttributeValue('Firstname')])), (pureldap.LDAPAttributeDescription('surname'), pureber.BERSet([pureldap.LDAPAttributeValue('Lastname')])), ], )) d.addCallback(cb) return d class LDAPSyntaxContainingNamingContext(unittest.TestCase): def testNamingContext(self): """LDAPEntry.namingContext returns the naming context that contains this object (via a Deferred).""" client=LDAPClientTestDriver( [ pureldap.LDAPSearchResultEntry( objectName='', attributes=[('namingContexts', ('dc=foo,dc=example', 'dc=example,dc=com', 'dc=bar,dc=example', ))]), pureldap.LDAPSearchResultDone(resultCode=0, matchedDN='', errorMessage='') ]) o=ldapsyntax.LDAPEntry(client=client, dn='cn=foo,ou=bar,dc=example,dc=com', attributes={ 'objectClass': ['a'], }) d=o.namingContext() def cb(p): assert isinstance(p, ldapsyntax.LDAPEntry) assert p.client == o.client assert str(p.dn) == 'dc=example,dc=com' client.assertSent(pureldap.LDAPSearchRequest( baseObject='', scope=pureldap.LDAP_SCOPE_baseObject, filter=pureldap.LDAPFilter_present('objectClass'), attributes=['namingContexts'], )) d.addCallback(cb) return d class LDAPSyntaxPasswords(unittest.TestCase): def setUp(self): cfg = config.loadConfig() cfg.set('samba', 'use-lmhash', 'no') def testPasswordSetting_ExtendedOperation(self): """LDAPEntry.setPassword_ExtendedOperation(newPasswd=...) changes the password.""" client = LDAPClientTestDriver( [pureldap.LDAPExtendedResponse(resultCode=0, matchedDN='', errorMessage='')], ) o=ldapsyntax.LDAPEntry(client=client, dn='cn=foo,dc=example,dc=com') d=o.setPassword_ExtendedOperation(newPasswd='new') def cb(dummy): client.assertSent(pureldap.LDAPPasswordModifyRequest( userIdentity='cn=foo,dc=example,dc=com', newPasswd='new'), ) d.addCallback(cb) return d def testPasswordSetting_Samba_sambaAccount(self): """LDAPEntry.setPassword_Samba(newPasswd=..., style='sambaAccount') changes the password.""" client = LDAPClientTestDriver( [pureldap.LDAPModifyResponse(resultCode=0, matchedDN='', errorMessage='')], ) o=ldapsyntax.LDAPEntry(client=client, dn='cn=foo,dc=example,dc=com') d=o.setPassword_Samba(newPasswd='new', style='sambaAccount') def cb(dummy): client.assertSent(delta.ModifyOp('cn=foo,dc=example,dc=com', [ delta.Replace('ntPassword', ['89963F5042E5041A59C249282387A622']), delta.Replace('lmPassword', ['XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX']), ]).asLDAP()) d.addCallback(cb) return d def testPasswordSetting_Samba_sambaSamAccount(self): """LDAPEntry.setPassword_Samba(newPasswd=..., style='sambaSamAccount') changes the password.""" client = LDAPClientTestDriver( [pureldap.LDAPModifyResponse(resultCode=0, matchedDN='', errorMessage='')], ) o=ldapsyntax.LDAPEntry(client=client, dn='cn=foo,dc=example,dc=com') d=o.setPassword_Samba(newPasswd='new', style='sambaSamAccount') def cb(dummy): client.assertSent(delta.ModifyOp('cn=foo,dc=example,dc=com', [ delta.Replace('sambaNTPassword', ['89963F5042E5041A59C249282387A622']), delta.Replace('sambaLMPassword', ['XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX']), ]).asLDAP()) d.addCallback(cb) return d def testPasswordSetting_Samba_defaultStyle(self): """LDAPEntry.setPassword_Samba(newPasswd=...) changes the password.""" client = LDAPClientTestDriver( [pureldap.LDAPModifyResponse(resultCode=0, matchedDN='', errorMessage='')], ) o=ldapsyntax.LDAPEntry(client=client, dn='cn=foo,dc=example,dc=com') d=o.setPassword_Samba(newPasswd='new') def cb(dummy): client.assertSent(delta.ModifyOp('cn=foo,dc=example,dc=com', [ delta.Replace('sambaNTPassword', ['89963F5042E5041A59C249282387A622']), delta.Replace('sambaLMPassword', ['XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX']), ]).asLDAP()) d.addCallback(cb) return d def testPasswordSetting_Samba_badStyle(self): """LDAPEntry.setPassword_Samba(..., style='foo') fails.""" client = LDAPClientTestDriver( [pureldap.LDAPModifyResponse(resultCode=0, matchedDN='', errorMessage='')], ) o=ldapsyntax.LDAPEntry(client=client, dn='cn=foo,dc=example,dc=com') d=defer.maybeDeferred(o.setPassword_Samba, newPasswd='new', style='foo') def eb(fail): fail.trap(RuntimeError) self.assertEquals(fail.getErrorMessage(), "Unknown samba password style 'foo'") client.assertNothingSent() d.addCallbacks(testutil.mustRaise, eb) return d def testPasswordSettingAll_noSamba(self): """LDAPEntry.setPassword(newPasswd=...) changes the password.""" client = LDAPClientTestDriver( [pureldap.LDAPExtendedResponse(resultCode=0, matchedDN='', errorMessage='')], ) o=ldapsyntax.LDAPEntry(client=client, dn='cn=foo,dc=example,dc=com', attributes={ 'objectClass': ['foo'], }, complete=1) d=o.setPassword(newPasswd='new') def cb(dummy): client.assertSent(pureldap.LDAPPasswordModifyRequest( userIdentity='cn=foo,dc=example,dc=com', newPasswd='new'), ) d.addCallback(cb) return d def testPasswordSettingAll_hasSamba(self): """LDAPEntry.setPassword(newPasswd=...) changes the password.""" client = LDAPClientTestDriver( [pureldap.LDAPExtendedResponse(resultCode=0, matchedDN='', errorMessage='')], [pureldap.LDAPModifyResponse(resultCode=0, matchedDN='', errorMessage='')], ) o=ldapsyntax.LDAPEntry(client=client, dn='cn=foo,dc=example,dc=com', attributes={ 'objectClass': ['foo', 'sambaAccount'], }, complete=1) d=o.setPassword(newPasswd='new') def cb(dummy): client.assertSent(pureldap.LDAPPasswordModifyRequest( userIdentity='cn=foo,dc=example,dc=com', newPasswd='new'), delta.ModifyOp('cn=foo,dc=example,dc=com', [ delta.Replace('ntPassword', ['89963F5042E5041A59C249282387A622']), delta.Replace('lmPassword', ['XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX']), ]).asLDAP()) d.addCallback(cb) return d def testPasswordSettingAll_hasSambaSam(self): """LDAPEntry.setPassword(newPasswd=...) changes the password.""" client = LDAPClientTestDriver( [pureldap.LDAPExtendedResponse(resultCode=0, matchedDN='', errorMessage='')], [pureldap.LDAPModifyResponse(resultCode=0, matchedDN='', errorMessage='')], ) o=ldapsyntax.LDAPEntry(client=client, dn='cn=foo,dc=example,dc=com', attributes={ 'objectClass': ['foo', 'sambaSamAccount'], }, complete=1) d=o.setPassword(newPasswd='new') def cb(dummy): client.assertSent(pureldap.LDAPPasswordModifyRequest( userIdentity='cn=foo,dc=example,dc=com', newPasswd='new'), delta.ModifyOp('cn=foo,dc=example,dc=com', [ delta.Replace('sambaNTPassword', ['89963F5042E5041A59C249282387A622']), delta.Replace('sambaLMPassword', ['XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX']), ]).asLDAP()) d.addCallback(cb) return d def testPasswordSettingAll_hasSamba_differentCase(self): """LDAPEntry.setPassword(newPasswd=...) changes the password.""" client = LDAPClientTestDriver( [pureldap.LDAPExtendedResponse(resultCode=0, matchedDN='', errorMessage='')], [pureldap.LDAPModifyResponse(resultCode=0, matchedDN='', errorMessage='')], ) o=ldapsyntax.LDAPEntry(client=client, dn='cn=foo,dc=example,dc=com', attributes={ 'objectClass': ['foo', 'saMBaAccOuNT'], }, complete=1) d=o.setPassword(newPasswd='new') def cb(dummy): client.assertSent(pureldap.LDAPPasswordModifyRequest( userIdentity='cn=foo,dc=example,dc=com', newPasswd='new'), delta.ModifyOp('cn=foo,dc=example,dc=com', [ delta.Replace('ntPassword', ['89963F5042E5041A59C249282387A622']), delta.Replace('lmPassword', ['XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX']), ]).asLDAP()) d.addCallback(cb) return d def testPasswordSettingAll_hasSambaSam_differentCase(self): """LDAPEntry.setPassword(newPasswd=...) changes the password.""" client = LDAPClientTestDriver( [pureldap.LDAPExtendedResponse(resultCode=0, matchedDN='', errorMessage='')], [pureldap.LDAPModifyResponse(resultCode=0, matchedDN='', errorMessage='')], ) o=ldapsyntax.LDAPEntry(client=client, dn='cn=foo,dc=example,dc=com', attributes={ 'objectClass': ['foo', 'sAmbASAmaccoUnt'], }, complete=1) d=o.setPassword(newPasswd='new') def cb(dummy): client.assertSent(pureldap.LDAPPasswordModifyRequest( userIdentity='cn=foo,dc=example,dc=com', newPasswd='new'), delta.ModifyOp('cn=foo,dc=example,dc=com', [ delta.Replace('sambaNTPassword', ['89963F5042E5041A59C249282387A622']), delta.Replace('sambaLMPassword', ['XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX']), ]).asLDAP()) d.addCallback(cb) return d def testPasswordSettingAll_maybeSamba_WillFind(self): """LDAPEntry.setPassword(newPasswd=...) changes the password.""" client = LDAPClientTestDriver( [pureldap.LDAPExtendedResponse(resultCode=0, matchedDN='', errorMessage='')], [ pureldap.LDAPSearchResultEntry(objectName='', attributes=[('objectClass', ('foo', 'sambaAccount', 'bar'))]), pureldap.LDAPSearchResultDone(resultCode=0, matchedDN='', errorMessage=''), ], [pureldap.LDAPModifyResponse(resultCode=0, matchedDN='', errorMessage='')], ) o=ldapsyntax.LDAPEntry(client=client, dn='cn=foo,dc=example,dc=com') d=o.setPassword(newPasswd='new') def cb(dummy): client.assertSent( pureldap.LDAPPasswordModifyRequest(userIdentity='cn=foo,dc=example,dc=com', newPasswd='new'), pureldap.LDAPSearchRequest(baseObject='cn=foo,dc=example,dc=com', scope=pureldap.LDAP_SCOPE_baseObject, derefAliases=pureldap.LDAP_DEREF_neverDerefAliases, sizeLimit=0, timeLimit=0, typesOnly=0, filter=pureldap.LDAPFilterMatchAll, attributes=('objectClass',)), delta.ModifyOp('cn=foo,dc=example,dc=com', [ delta.Replace('ntPassword', ['89963F5042E5041A59C249282387A622']), delta.Replace('lmPassword', ['XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX']), ]).asLDAP(), ) d.addCallback(cb) return d def testPasswordSettingAll_maybeSamba_WillNotFind(self): """LDAPEntry.setPassword(newPasswd=...) changes the password.""" client = LDAPClientTestDriver( [pureldap.LDAPExtendedResponse(resultCode=0, matchedDN='', errorMessage='')], [pureldap.LDAPSearchResultEntry(objectName='', attributes=[('objectClass', ('foo', 'bar'))]), pureldap.LDAPSearchResultDone(resultCode=0, matchedDN='', errorMessage=''), ], [pureldap.LDAPModifyResponse(resultCode=0, matchedDN='', errorMessage='')], ) o=ldapsyntax.LDAPEntry(client=client, dn='cn=foo,dc=example,dc=com') d=o.setPassword(newPasswd='new') def cb(dummy): client.assertSent( pureldap.LDAPPasswordModifyRequest(userIdentity='cn=foo,dc=example,dc=com', newPasswd='new'), pureldap.LDAPSearchRequest(baseObject='cn=foo,dc=example,dc=com', scope=pureldap.LDAP_SCOPE_baseObject, derefAliases=pureldap.LDAP_DEREF_neverDerefAliases, sizeLimit=0, timeLimit=0, typesOnly=0, filter=pureldap.LDAPFilterMatchAll, attributes=('objectClass',)), ) d.addCallback(cb) return d def testPasswordSettingAll_maybeSamba_WillNotFindAnything(self): """LDAPEntry.setPassword(newPasswd=...) changes the password.""" client = LDAPClientTestDriver( [pureldap.LDAPExtendedResponse(resultCode=0, matchedDN='', errorMessage='')], [ pureldap.LDAPSearchResultDone(resultCode=0, matchedDN='', errorMessage=''), ], [pureldap.LDAPModifyResponse(resultCode=0, matchedDN='', errorMessage='')], ) o=ldapsyntax.LDAPEntry(client=client, dn='cn=foo,dc=example,dc=com') d=o.setPassword(newPasswd='new') def checkError(fail): fail.trap(ldapsyntax.PasswordSetAggregateError) l=fail.value.errors assert len(l)==1 assert len(l[0])==2 assert l[0][0]=='Samba' assert isinstance(l[0][1], failure.Failure) l[0][1].trap(ldapsyntax.DNNotPresentError) return 'This test run should succeed' def chainMustErrback(dummy): raise 'Should never get here' d.addCallbacks(callback=chainMustErrback, errback=checkError) d.addCallback(self.assertEquals, 'This test run should succeed') def cb(dummy): client.assertSent( pureldap.LDAPPasswordModifyRequest(userIdentity='cn=foo,dc=example,dc=com', newPasswd='new'), pureldap.LDAPSearchRequest(baseObject='cn=foo,dc=example,dc=com', scope=pureldap.LDAP_SCOPE_baseObject, derefAliases=pureldap.LDAP_DEREF_neverDerefAliases, sizeLimit=0, timeLimit=0, typesOnly=0, filter=pureldap.LDAPFilterMatchAll, attributes=('objectClass',)), ) d.addCallback(cb) return d def testPasswordSetting_abortsOnFirstError(self): """LDAPEntry.setPassword() aborts on first error (does not parallelize, as it used to).""" client = LDAPClientTestDriver( [pureldap.LDAPExtendedResponse(resultCode=ldaperrors.LDAPInsufficientAccessRights.resultCode, matchedDN='', errorMessage='')], ) o=ldapsyntax.LDAPEntry(client=client, dn='cn=foo,dc=example,dc=com', attributes={ 'objectClass': ['foo', 'sambaAccount'], }, complete=1) d=o.setPassword(newPasswd='new') def eb(fail): fail.trap(ldapsyntax.PasswordSetAggregateError) l=fail.value.errors assert len(l)==2 assert len(l[0])==2 self.assertEquals(l[0][0], 'ExtendedOperation') assert isinstance(l[0][1], failure.Failure) l[0][1].trap(ldaperrors.LDAPInsufficientAccessRights) assert len(l[1])==2 self.assertEquals(l[1][0], 'Samba') assert isinstance(l[1][1], failure.Failure) l[1][1].trap(ldapsyntax.PasswordSetAborted) client.assertSent(pureldap.LDAPPasswordModifyRequest( userIdentity='cn=foo,dc=example,dc=com', newPasswd='new'), ) d.addCallbacks(testutil.mustRaise, eb) return d class LDAPSyntaxFetch(unittest.TestCase): def testFetch_WithDirtyJournal(self): """Trying to fetch attributes with a dirty journal fails.""" client = LDAPClientTestDriver() o=ldapsyntax.LDAPEntry(client=client, dn='cn=foo,dc=example,dc=com') o['x']=['foo'] self.failUnlessRaises( ldapsyntax.ObjectDirtyError, o.fetch) def testFetch_Empty(self): """Fetching attributes for a newly-created object works.""" client = LDAPClientTestDriver( [ pureldap.LDAPSearchResultEntry(objectName='cn=foo,dc=example,dc=com', attributes=( ('foo', ['a']), ('bar', ['b', 'c']), )), pureldap.LDAPSearchResultDone(resultCode=0, matchedDN='', errorMessage=''), ]) o=ldapsyntax.LDAPEntry(client=client, dn='cn=foo,dc=example,dc=com') d=o.fetch() def cb(dummy): client.assertSent(pureldap.LDAPSearchRequest( baseObject='cn=foo,dc=example,dc=com', scope=pureldap.LDAP_SCOPE_baseObject, )) has=o.keys() has.sort() want=['foo', 'bar'] want.sort() self.assertEquals(has, want) self.assertEquals(o['foo'], ['a']) self.assertEquals(o['bar'], ['b', 'c']) d.addCallback(cb) return d def testFetch_Prefilled(self): """Fetching attributes for a (partially) known object overwrites the old attributes.""" client = LDAPClientTestDriver( [ pureldap.LDAPSearchResultEntry(objectName='cn=foo,dc=example,dc=com', attributes=( ('foo', ['a']), ('bar', ['b', 'c']), )), pureldap.LDAPSearchResultDone(resultCode=0, matchedDN='', errorMessage=''), ]) o=ldapsyntax.LDAPEntry(client=client, dn='cn=foo,dc=example,dc=com', attributes={ 'foo': ['x'], 'quux': ['baz', 'xyzzy'] }) d=o.fetch() def cb(dummy): client.assertSent(pureldap.LDAPSearchRequest( baseObject='cn=foo,dc=example,dc=com', scope=pureldap.LDAP_SCOPE_baseObject, )) has=o.keys() has.sort() want=['foo', 'bar'] want.sort() self.assertEquals(has, want) self.assertEquals(o['foo'], ['a']) self.assertEquals(o['bar'], ['b', 'c']) d.addCallback(cb) return d def testFetch_Partial(self): """Fetching only some of the attributes does not overwrite existing values of different attribute types.""" client = LDAPClientTestDriver( [ pureldap.LDAPSearchResultEntry(objectName='cn=foo,dc=example,dc=com', attributes=( ('foo', ['a']), ('bar', ['b', 'c']), )), pureldap.LDAPSearchResultDone(resultCode=0, matchedDN='', errorMessage=''), ]) o=ldapsyntax.LDAPEntry(client=client, dn='cn=foo,dc=example,dc=com', attributes={ 'foo': ['x'], 'quux': ['baz', 'xyzzy'] }) d=o.fetch('foo', 'bar', 'thud') def cb(dummy): client.assertSent(pureldap.LDAPSearchRequest( baseObject='cn=foo,dc=example,dc=com', scope=pureldap.LDAP_SCOPE_baseObject, attributes=('foo', 'bar', 'thud'), )) has=o.keys() has.sort() want=['foo', 'bar', 'quux'] want.sort() self.assertEquals(has, want) self.assertEquals(o['foo'], ['a']) self.assertEquals(o['bar'], ['b', 'c']) self.assertEquals(o['quux'], ['baz', 'xyzzy']) d.addCallback(cb) return d def testCommitAndFetch(self): """Fetching after a commit works.""" client = LDAPClientTestDriver( [ pureldap.LDAPModifyResponse(resultCode=0, matchedDN='', errorMessage='') ], [ pureldap.LDAPSearchResultEntry('cn=foo,dc=example,dc=com', [('aValue', ['foo', 'bar'])], ), pureldap.LDAPSearchResultDone(resultCode=0), ]) o=ldapsyntax.LDAPEntry(client=client, dn='cn=foo,dc=example,dc=com', attributes={ 'objectClass': ['a', 'b'], 'aValue': ['a'], }) o['aValue']=['foo', 'bar'] d=o.commit() d.addCallback(self.assertIdentical, o) d.addCallback(lambda _: o.fetch('aValue')) d.addCallback(self.assertIdentical, o) def cb(dummy): client.assertSent(delta.ModifyOp('cn=foo,dc=example,dc=com', [ delta.Replace('aValue', ['foo', 'bar']), ]).asLDAP(), pureldap.LDAPSearchRequest( baseObject='cn=foo,dc=example,dc=com', scope=pureldap.LDAP_SCOPE_baseObject, attributes=['aValue'], )) d.addCallback(cb) return d class LDAPSyntaxRDNHandling(unittest.TestCase): def testRemovingRDNFails(self): """Removing RDN fails with CannotRemoveRDNError.""" o=ldapsyntax.LDAPEntry(client=None, dn='cn=foo,dc=example,dc=com', attributes={ 'objectClass': ['someObjectClass'], 'cn': ['foo', 'bar', 'baz'], 'a': ['aValue'], }) o['cn'].remove('bar') del o['a'] self.assertRaises(ldapsyntax.CannotRemoveRDNError, o['cn'].remove, 'foo') def f(): del o['cn'] self.assertRaises(ldapsyntax.CannotRemoveRDNError, f) def f(): o['cn']=['thud'] self.assertRaises(ldapsyntax.CannotRemoveRDNError, f) # TODO maybe this should be ok, it preserves the RDN. # For now, disallow it. def f(): o['cn']=['foo'] self.assertRaises(ldapsyntax.CannotRemoveRDNError, f) class LDAPSyntaxMove(unittest.TestCase): def test_move(self): client = LDAPClientTestDriver( [ pureldap.LDAPModifyDNResponse(resultCode=0, matchedDN='', errorMessage=''), ]) o=ldapsyntax.LDAPEntry(client=client, dn='cn=foo,dc=example,dc=com', attributes={ 'objectClass': ['a', 'b'], 'cn': ['foo'], 'aValue': ['a'], }) d = o.move('cn=bar,ou=somewhere,dc=example,dc=com') def cb(dummy): client.assertSent(pureldap.LDAPModifyDNRequest( entry='cn=foo,dc=example,dc=com', newrdn='cn=bar', deleteoldrdn=1, newSuperior='ou=somewhere,dc=example,dc=com', )) self.assertEquals(o.dn, 'cn=bar,ou=somewhere,dc=example,dc=com') d.addCallback(cb) return d class Bind(unittest.TestCase): def test_ok(self): client = LDAPClientTestDriver( [ pureldap.LDAPBindResponse(resultCode=0, matchedDN=''), ]) o=ldapsyntax.LDAPEntry(client=client, dn='cn=foo,dc=example,dc=com') d = defer.maybeDeferred(o.bind, 's3krit') d.addCallback(self.assertIdentical, o) def cb(dummy): client.assertSent(pureldap.LDAPBindRequest( dn='cn=foo,dc=example,dc=com', auth='s3krit')) d.addCallback(cb) return d def test_fail(self): client = LDAPClientTestDriver( [ pureldap.LDAPBindResponse( resultCode=ldaperrors.LDAPInvalidCredentials.resultCode, matchedDN=''), ]) o=ldapsyntax.LDAPEntry(client=client, dn='cn=foo,dc=example,dc=com') d = defer.maybeDeferred(o.bind, 's3krit') def eb(fail): fail.trap(ldaperrors.LDAPInvalidCredentials) d.addCallbacks(testutil.mustRaise, eb) return d ldaptor-0.0.43/ldaptor/test/mockweb.py0000644000175000017500000000525110344407651016001 0ustar janjan"""I mock the web.""" from nevow import url from twisted.internet import address from twisted.web import client from nevow import testutil from ldaptor.test import util class FakeChannel(testutil.FakeChannel): def requestDone(self, request): self.transport.loseConnection() class MyHTTPPageGetter(client.HTTPPageGetter): def handleStatus_301(self): if not self.followRedirect: client.HTTPPageGetter.handleStatus_301(self) return l = self.headers.get('location') if not l: self.handleStatusDefault() url = l[0] self.factory.setURL(url) _getPage_connect(clientFactory=self.factory, serverAddress=address.IPv4Address( 'TCP', self.factory.host, self.factory.port), clientAddress=None) self.quietLoss = 1 self.transport.loseConnection() class HTTPClientFactory_noCookies(client.HTTPClientFactory): def gotHeaders(self, headers): client.HTTPClientFactory.gotHeaders(self, headers) self.cookies.clear() def _getPage_connect(clientFactory, serverAddress, clientAddress): clientProto = clientFactory.buildProtocol(serverAddress) serverProto = clientFactory.site.buildProtocol(clientAddress) pump = util.returnConnected(serverProto, clientProto, serverAddress=serverAddress, clientAddress=clientAddress) def getPage(site, u, extraInfo=False, factoryClass=client.HTTPClientFactory, *a, **kw): u = url.URL.fromString(str(u)) clientFactory = factoryClass(str(u), *a, **kw) clientFactory.protocol = MyHTTPPageGetter clientFactory.site = site if ':' in u.netloc: host, port = u.netloc.split(':', 1) else: host, port = u.netloc, 80 serverAddress = address.IPv4Address('TCP', host, port) clientAddress = address.IPv4Address('TCP', 'localhost', 1024) _getPage_connect(clientFactory, serverAddress, clientAddress) if extraInfo: def _cb(page): return {'status': clientFactory.status, 'version': clientFactory.version, 'message': clientFactory.message, 'headers': clientFactory.headers, 'page': page, 'url': url.URL.fromString(clientFactory.url), } clientFactory.deferred.addCallback(_cb) return clientFactory.deferred def getPage_noCookies(*a, **kw): defaults = { 'factoryClass': HTTPClientFactory_noCookies, } defaults.update(kw) return getPage(*a, **defaults) ldaptor-0.0.43/ldaptor/test/test_autofill.py0000644000175000017500000000313410345261504017222 0ustar janjan""" Test cases for ldaptor.protocols.ldap.autofill module. """ from twisted.trial import unittest from ldaptor.protocols.ldap import ldapsyntax from ldaptor.testutil import LDAPClientTestDriver class Autofill_sum: #TODO baseclass def __init__(self, resultAttr, sumAttrs): self.resultAttr = resultAttr self.sumAttrs = sumAttrs def start(self, ldapObject): pass def notify(self, ldapObject, attributeType): if attributeType not in self.sumAttrs: return sum = 0 for sumAttr in self.sumAttrs: if sumAttr not in ldapObject: continue for val in ldapObject[sumAttr]: val = int(val) sum += val sum = str(sum) ldapObject[self.resultAttr] = [sum] class LDAPAutoFill_Simple(unittest.TestCase): def testSimpleSum(self): """A simple autofiller that calculates sums of attributes should work..""" client = LDAPClientTestDriver() o=ldapsyntax.LDAPEntryWithAutoFill(client=client, dn='cn=foo,dc=example,dc=com', attributes={ 'objectClass': ['some', 'other'], }) d = o.addAutofiller(Autofill_sum(resultAttr='sum', sumAttrs=['a', 'b'])) def cb(dummy): client.assertNothingSent() o['a'] = ['1'] o['b'] = ['2', '3'] self.failUnless('sum' in o) self.failUnlessEqual(o['sum'], ['6']) d.addCallback(cb) return d ldaptor-0.0.43/ldaptor/test/test_ldapclient.py0000644000175000017500000000210310345261504017515 0ustar janjan""" Test cases for ldaptor.protocols.ldap.ldapsyntax module. """ from twisted.trial import unittest from twisted.test import proto_helpers from twisted.internet import defer from ldaptor.protocols.ldap import ldapclient from ldaptor import testutil class SillyMessage(object): needs_answer = True def __init__(self, value): self.value = value def __str__(self): return self.value class SillyError(Exception): def __str__(self): 'Exception for test purposes.' class ConnectionLost(unittest.TestCase): def test_simple(self): c = ldapclient.LDAPClient() c.makeConnection(proto_helpers.StringTransport()) d1 = c.send(SillyMessage('foo')) d2 = c.send(SillyMessage('bar')) c.connectionLost(SillyError()) def eb(fail): fail.trap(SillyError) d1.addCallbacks(testutil.mustRaise, eb) d2.addCallbacks(testutil.mustRaise, eb) d = defer.DeferredList([d1, d2]) return defer.DeferredList([d1, d2], fireOnOneErrback=True) ldaptor-0.0.43/ldaptor/test/test_autofill_posix.py0000644000175000017500000001653710345261504020457 0ustar janjan""" Test cases for ldaptor.protocols.ldap.autofill.posixAccount module. """ from twisted.trial import unittest from ldaptor.protocols.ldap import ldapsyntax, autofill from ldaptor.protocols import pureldap from ldaptor.protocols.ldap.autofill import posixAccount from ldaptor.testutil import LDAPClientTestDriver class LDAPAutoFill_Posix(unittest.TestCase): def testMustHaveObjectClass(self): """Test that Autofill_posix fails unless object is a posixAccount.""" client = LDAPClientTestDriver() o=ldapsyntax.LDAPEntryWithAutoFill(client=client, dn='cn=foo,dc=example,dc=com', attributes={ 'objectClass': ['something', 'other'], }) autoFiller = posixAccount.Autofill_posix(baseDN='dc=example,dc=com') d = o.addAutofiller(autoFiller) def _cbMustRaise(_): raise unittest.FailTest('Should have raised an exception') def _eb(fail): client.assertNothingSent() fail.trap(autofill.ObjectMissingObjectClassException) return None d.addCallbacks(_cbMustRaise, _eb) return d def testDefaultSetting(self): """Test that fields get their default values.""" client = LDAPClientTestDriver( # uid==1000 -> free [ pureldap.LDAPSearchResultDone(resultCode=0, matchedDN='', errorMessage=''),], # gid==1000 -> taken [ pureldap.LDAPSearchResultEntry(objectName='', attributes=[('objectClass', ('foo', 'posixAccount', 'bar'))]), pureldap.LDAPSearchResultDone(resultCode=0, matchedDN='', errorMessage=''),], # gid==1500 -> free [ pureldap.LDAPSearchResultDone(resultCode=0, matchedDN='', errorMessage=''),], # gid==1250 -> free [ pureldap.LDAPSearchResultDone(resultCode=0, matchedDN='', errorMessage=''),], # gid==1125 -> free [ pureldap.LDAPSearchResultDone(resultCode=0, matchedDN='', errorMessage=''),], # gid==1062 -> free [ pureldap.LDAPSearchResultDone(resultCode=0, matchedDN='', errorMessage=''),], # gid==1031 -> free [ pureldap.LDAPSearchResultEntry(objectName='', attributes=[('objectClass', ('foo', 'posixAccount', 'bar'))]), pureldap.LDAPSearchResultDone(resultCode=0, matchedDN='', errorMessage=''),], # gid==1046 -> free [ pureldap.LDAPSearchResultDone(resultCode=0, matchedDN='', errorMessage=''),], # gid==1038 -> taken [ pureldap.LDAPSearchResultEntry(objectName='', attributes=[('objectClass', ('foo', 'posixAccount', 'bar'))]), pureldap.LDAPSearchResultDone(resultCode=0, matchedDN='', errorMessage=''),], # gid==1042 -> free [ pureldap.LDAPSearchResultDone(resultCode=0, matchedDN='', errorMessage=''),], # gid==1040 -> taken [ pureldap.LDAPSearchResultEntry(objectName='', attributes=[('objectClass', ('foo', 'posixAccount', 'bar'))]), pureldap.LDAPSearchResultDone(resultCode=0, matchedDN='', errorMessage=''),], # gid==1041 -> taken [ pureldap.LDAPSearchResultEntry(objectName='', attributes=[('objectClass', ('foo', 'posixAccount', 'bar'))]), pureldap.LDAPSearchResultDone(resultCode=0, matchedDN='', errorMessage=''),], ) o=ldapsyntax.LDAPEntryWithAutoFill(client=client, dn='cn=foo,dc=example,dc=com', attributes={ 'objectClass': ['posixAccount', 'other'], }) d = o.addAutofiller(posixAccount.Autofill_posix(baseDN='dc=example,dc=com')) d.addCallback(self._cb_testDefaultSetting, client, o) return d def _cb_testDefaultSetting(self, val, client, o): client.assertSent( *[ pureldap.LDAPSearchRequest( baseObject='dc=example,dc=com', scope=2, derefAliases=0, sizeLimit=1, timeLimit=0, typesOnly=0, filter=pureldap.LDAPFilter_equalityMatch(attributeDesc=pureldap.LDAPAttributeDescription(value='uidNumber'), assertionValue=pureldap.LDAPAssertionValue(value='1000')), attributes=()), ] + [ pureldap.LDAPSearchRequest( baseObject='dc=example,dc=com', scope=2, derefAliases=0, sizeLimit=1, timeLimit=0, typesOnly=0, filter=pureldap.LDAPFilter_equalityMatch(attributeDesc=pureldap.LDAPAttributeDescription(value='gidNumber'), assertionValue=pureldap.LDAPAssertionValue(value=str(x))), attributes=()) for x in (1000, 1500, 1250, 1125, 1062, 1031, 1046, 1038, 1042, 1040, 1041)]) self.failUnless('loginShell' in o) self.failUnlessEqual(o['loginShell'], ['/bin/sh']) self.failUnless('uidNumber' in o) self.failUnlessEqual(o['uidNumber'], ['1000']) self.failUnless('gidNumber' in o) self.failUnlessEqual(o['gidNumber'], ['1042']) ldaptor-0.0.43/ldaptor/test/test_ldifprotocol.py0000644000175000017500000003320110344535643020110 0ustar janjan""" Test cases for ldaptor.protocols.ldap.ldif module. """ from twisted.trial import unittest import sets from ldaptor.protocols.ldap import ldifprotocol, distinguishedname class LDIFDriver(ldifprotocol.LDIF): def __init__(self): self.listOfCompleted = [] def gotEntry(self, obj): self.listOfCompleted.append(obj) class TestLDIFParsing(unittest.TestCase): def testFromLDIF(self): proto = LDIFDriver() for line in ( "dn: cn=foo,dc=example,dc=com", "objectClass: a", "objectClass: b", "aValue: a", "aValue: b", "bValue: c", "", "dn: cn=bar,dc=example,dc=com", "objectClass: c", "aValue:: IEZPTyE=", "aValue: b", "bValue: C", "", ): proto.lineReceived(line) self.failUnlessEqual(len(proto.listOfCompleted), 2) o = proto.listOfCompleted.pop(0) self.failUnlessEqual(str(o.dn), 'cn=foo,dc=example,dc=com') self.failUnlessEqual(o['objectClass'], ['a', 'b']) self.failUnlessEqual(o['aValue'], ['a', 'b']) self.failUnlessEqual(o['bValue'], ['c']) o = proto.listOfCompleted.pop(0) self.failUnlessEqual(str(o.dn), 'cn=bar,dc=example,dc=com') self.failUnlessEqual(o['objectClass'], ['c']) self.failUnlessEqual(o['aValue'], [' FOO!', 'b']) self.failUnlessEqual(o['bValue'], ['C']) self.failUnlessEqual(proto.listOfCompleted, []) def testSplitLines(self): proto = LDIFDriver() for line in ( "dn: cn=foo,dc=ex", " ample,dc=com", "objectClass: a", "ob", " jectClass: b", "", ): proto.lineReceived(line) self.failUnlessEqual(len(proto.listOfCompleted), 1) o = proto.listOfCompleted.pop(0) self.failUnlessEqual(str(o.dn), 'cn=foo,dc=example,dc=com') self.failUnlessEqual(o['objectClass'], ['a', 'b']) self.failUnlessEqual(proto.listOfCompleted, []) def testCaseInsensitiveAttributeTypes(self): proto = LDIFDriver() proto.dataReceived("""\ dn: cn=foo,dc=example,dc=com objectClass: a obJeCtClass: b cn: foo avalue: a aValUe: b """) self.failUnlessEqual(len(proto.listOfCompleted), 1) o = proto.listOfCompleted.pop(0) self.failUnlessEqual(str(o.dn), 'cn=foo,dc=example,dc=com') self.failUnlessEqual(o['objectClass'], ['a', 'b']) self.failUnlessEqual(o['CN'], ['foo']) self.failUnlessEqual(o['aValue'], ['a', 'b']) self.failUnlessEqual(proto.listOfCompleted, []) def testVersion1(self): proto = LDIFDriver() proto.dataReceived("""\ version: 1 dn: cn=foo,dc=example,dc=com objectClass: a objectClass: b aValue: a aValue: b bValue: c """) self.failUnlessEqual(len(proto.listOfCompleted), 1) o = proto.listOfCompleted.pop(0) self.failUnlessEqual(str(o.dn), 'cn=foo,dc=example,dc=com') self.failUnlessEqual(o['objectClass'], ['a', 'b']) self.failUnlessEqual(o['aValue'], ['a', 'b']) self.failUnlessEqual(o['bValue'], ['c']) def testVersionInvalid(self): proto = LDIFDriver() self.assertRaises(ldifprotocol.LDIFVersionNotANumberError, proto.dataReceived, """\ version: junk dn: cn=foo,dc=example,dc=com objectClass: a objectClass: b aValue: a aValue: b bValue: c """) def testVersion2(self): proto = LDIFDriver() self.assertRaises(ldifprotocol.LDIFUnsupportedVersionError, proto.dataReceived, """\ version: 2 dn: cn=foo,dc=example,dc=com objectClass: a objectClass: b aValue: a aValue: b bValue: c """) def testNoSpaces(self): proto = LDIFDriver() proto.dataReceived("""\ dn:cn=foo,dc=example,dc=com objectClass:a obJeCtClass:b cn:foo avalue:a aValUe:b """) self.failUnlessEqual(len(proto.listOfCompleted), 1) o = proto.listOfCompleted.pop(0) self.failUnlessEqual(str(o.dn), 'cn=foo,dc=example,dc=com') self.failUnlessEqual(o['objectClass'], ['a', 'b']) self.failUnlessEqual(o['CN'], ['foo']) self.failUnlessEqual(o['aValue'], ['a', 'b']) self.failUnlessEqual(proto.listOfCompleted, []) def testTruncatedFailure(self): proto = LDIFDriver() proto.dataReceived("""\ version: 1 dn: cn=foo,dc=example,dc=com objectClass: a objectClass: b aValue: a aValue: b bValue: c """) self.failUnlessEqual(len(proto.listOfCompleted), 0) self.assertRaises(ldifprotocol.LDIFTruncatedError, proto.connectionLost) class RFC2849_Examples(unittest.TestCase): examples = [ ( """Example 1: An simple LDAP file with two entries""", """\ version: 1 dn: cn=Barbara Jensen, ou=Product Development, dc=airius, dc=com objectclass: top objectclass: person objectclass: organizationalPerson cn: Barbara Jensen cn: Barbara J Jensen cn: Babs Jensen sn: Jensen uid: bjensen telephonenumber: +1 408 555 1212 description: A big sailing fan. dn: cn=Bjorn Jensen, ou=Accounting, dc=airius, dc=com objectclass: top objectclass: person objectclass: organizationalPerson cn: Bjorn Jensen sn: Jensen telephonenumber: +1 408 555 1212 """, [ ( 'cn=Barbara Jensen,ou=Product Development,dc=airius,dc=com', { 'objectClass': ['top', 'person', 'organizationalPerson'], 'cn': ['Barbara Jensen', 'Barbara J Jensen', 'Babs Jensen'], 'sn': ['Jensen'], 'uid': ['bjensen'], 'telephonenumber': ['+1 408 555 1212'], 'description': ['A big sailing fan.'], }), ( 'cn=Bjorn Jensen,ou=Accounting,dc=airius,dc=com', { 'objectClass': ['top', 'person', 'organizationalPerson'], 'cn': ['Bjorn Jensen'], 'sn': ['Jensen'], 'telephonenumber': ['+1 408 555 1212'], }), ]), ( """Example 2: A file containing an entry with a folded attribute value""", """\ version: 1 dn:cn=Barbara Jensen, ou=Product Development, dc=airius, dc=com objectclass:top objectclass:person objectclass:organizationalPerson cn:Barbara Jensen cn:Barbara J Jensen cn:Babs Jensen sn:Jensen uid:bjensen telephonenumber:+1 408 555 1212 description:Babs is a big sailing fan, and travels extensively in sea rch of perfect sailing conditions. title:Product Manager, Rod and Reel Division """, [ ( 'cn=Barbara Jensen, ou=Product Development, dc=airius, dc=com', { 'objectclass': ['top', 'person', 'organizationalPerson'], 'cn': ['Barbara Jensen', 'Barbara J Jensen', 'Babs Jensen'], 'sn': ['Jensen'], 'uid': ['bjensen'], 'telephonenumber': ['+1 408 555 1212'], 'description': ['Babs is a big sailing fan, and travels extensively in search of perfect sailing conditions.'], 'title': ['Product Manager, Rod and Reel Division'], }), ]), ( """Example 3: A file containing a base-64-encoded value""", """\ version: 1 dn: cn=Gern Jensen, ou=Product Testing, dc=airius, dc=com objectclass: top objectclass: person objectclass: organizationalPerson cn: Gern Jensen cn: Gern O Jensen sn: Jensen uid: gernj telephonenumber: +1 408 555 1212 description:: V2hhdCBhIGNhcmVmdWwgcmVhZGVyIHlvdSBhcmUhICBUaGlzIHZhbHVlIGlzIGJhc2UtNjQtZW5jb2RlZCBiZWNhdXNlIGl0IGhhcyBhIGNvbnRyb2wgY2hhcmFjdGVyIGluIGl0IChhIENSKS4NICBCeSB0aGUgd2F5LCB5b3Ugc2hvdWxkIHJlYWxseSBnZXQgb3V0IG1vcmUu """, [ ( 'cn=Gern Jensen, ou=Product Testing, dc=airius, dc=com', { 'objectclass': ['top', 'person', 'organizationalPerson'], 'cn': ['Gern Jensen', 'Gern O Jensen'], 'sn': ['Jensen'], 'uid': ['gernj'], 'telephonenumber': ['+1 408 555 1212'], 'description': ['What a careful reader you are! This value is base-64-encoded because it has a control character in it (a CR).\r By the way, you should really get out more.'], }), ]), ] def testExamples(self): for name, data, expected in self.examples: proto = LDIFDriver() proto.dataReceived(data) self.failUnlessEqual(len(proto.listOfCompleted), len(expected)) for dn, attr in expected: o = proto.listOfCompleted.pop(0) self.failUnlessEqual(o.dn, distinguishedname.DistinguishedName(dn)) got = sets.Set([x.lower() for x in o.keys()]) want = sets.Set([x.lower() for x in attr.keys()]) self.failUnlessEqual(got, want) for k, v in attr.items(): self.failUnlessEqual(o[k], v) self.failUnlessEqual(proto.listOfCompleted, []) """ TODO more tests from RFC2849: Example 4: A file containing an entries with UTF-8-encoded attribute values, including language tags. Comments indicate the contents of UTF-8-encoded attributes and distinguished names. version: 1 dn:: b3U95Za25qWt6YOoLG89QWlyaXVz # dn:: ou=,o=Airius objectclass: top objectclass: organizationalUnit ou:: 5Za25qWt6YOo # ou:: ou;lang-ja:: 5Za25qWt6YOo # ou;lang-ja:: ou;lang-ja;phonetic:: 44GI44GE44GO44KH44GG44G2 # ou;lang-ja:: ou;lang-en: Sales description: Japanese office dn:: dWlkPXJvZ2FzYXdhcmEsb3U95Za25qWt6YOoLG89QWlyaXVz # dn:: uid=,ou=,o=Airius userpassword: {SHA}O3HSv1MusyL4kTjP+HKI5uxuNoM= objectclass: top objectclass: person objectclass: organizationalPerson objectclass: inetOrgPerson uid: rogasawara mail: rogasawara@airius.co.jp givenname;lang-ja:: 44Ot44OJ44OL44O8 # givenname;lang-ja:: sn;lang-ja:: 5bCP56yg5Y6f # sn;lang-ja:: cn;lang-ja:: 5bCP56yg5Y6fIOODreODieODi+ODvA== # cn;lang-ja:: title;lang-ja:: 5Za25qWt6YOoIOmDqOmVtw== # title;lang-ja:: preferredlanguage: ja givenname:: 44Ot44OJ44OL44O8 # givenname:: sn:: 5bCP56yg5Y6f # sn:: cn:: 5bCP56yg5Y6fIOODreODieODi+ODvA== # cn:: title:: 5Za25qWt6YOoIOmDqOmVtw== # title:: givenname;lang-ja;phonetic:: 44KN44Gp44Gr44O8 # givenname;lang-ja;phonetic:: sn;lang-ja;phonetic:: 44GK44GM44GV44KP44KJ # sn;lang-ja;phonetic:: cn;lang-ja;phonetic:: 44GK44GM44GV44KP44KJIOOCjeOBqeOBq+ODvA== # cn;lang-ja;phonetic:: title;lang-ja;phonetic:: 44GI44GE44GO44KH44GG44G2IOOBtuOBoeOCh+OBhg== # title;lang-ja;phonetic:: # givenname;lang-en: Rodney sn;lang-en: Ogasawara cn;lang-en: Rodney Ogasawara title;lang-en: Sales, Director """ """ Example 5: A file containing a reference to an external file version: 1 dn: cn=Horatio Jensen, ou=Product Testing, dc=airius, dc=com objectclass: top objectclass: person objectclass: organizationalPerson cn: Horatio Jensen cn: Horatio N Jensen sn: Jensen uid: hjensen telephonenumber: +1 408 555 1212 jpegphoto:< file:///usr/local/directory/photos/hjensen.jpg """ """ Example 6: A file containing a series of change records and comments version: 1 # Add a new entry dn: cn=Fiona Jensen, ou=Marketing, dc=airius, dc=com changetype: add objectclass: top objectclass: person objectclass: organizationalPerson cn: Fiona Jensen sn: Jensen uid: fiona telephonenumber: +1 408 555 1212 jpegphoto:< file:///usr/local/directory/photos/fiona.jpg # Delete an existing entry dn: cn=Robert Jensen, ou=Marketing, dc=airius, dc=com changetype: delete # Modify an entry's relative distinguished name dn: cn=Paul Jensen, ou=Product Development, dc=airius, dc=com changetype: modrdn newrdn: cn=Paula Jensen deleteoldrdn: 1 # Rename an entry and move all of its children to a new location in # the directory tree (only implemented by LDAPv3 servers). dn: ou=PD Accountants, ou=Product Development, dc=airius, dc=com changetype: modrdn newrdn: ou=Product Development Accountants deleteoldrdn: 0 newsuperior: ou=Accounting, dc=airius, dc=com # Modify an entry: add an additional value to the postaladdress # attribute, completely delete the description attribute, replace # the telephonenumber attribute with two values, and delete a specific # value from the facsimiletelephonenumber attribute dn: cn=Paula Jensen, ou=Product Development, dc=airius, dc=com changetype: modify add: postaladdress postaladdress: 123 Anystreet $ Sunnyvale, CA $ 94086 - delete: description - replace: telephonenumber telephonenumber: +1 408 555 1234 telephonenumber: +1 408 555 5678 - delete: facsimiletelephonenumber facsimiletelephonenumber: +1 408 555 9876 - # Modify an entry: replace the postaladdress attribute with an empty # set of values (which will cause the attribute to be removed), and # delete the entire description attribute. Note that the first will # always succeed, while the second will only succeed if at least # one value for the description attribute is present. dn: cn=Ingrid Jensen, ou=Product Support, dc=airius, dc=com changetype: modify replace: postaladdress - delete: description - """ """ Example 7: An LDIF file containing a change record with a control version: 1 # Delete an entry. The operation will attach the LDAPv3 # Tree Delete Control defined in [9]. The criticality # field is "true" and the controlValue field is # absent, as required by [9]. dn: ou=Product Development, dc=airius, dc=com control: 1.2.840.113556.1.4.805 true changetype: delete """ ldaptor-0.0.43/ldaptor/test/test_dns.py0000644000175000017500000000546310160267335016201 0ustar janjan""" Test cases for ldaptor.dns """ from twisted.trial import unittest from ldaptor import dns class NetmaskToNumbits(unittest.TestCase): def test_classA(self): self.assertEquals(dns.netmaskToNumbits('255.0.0.0'), 8) def test_classB(self): self.assertEquals(dns.netmaskToNumbits('255.255.0.0'), 16) def test_classC(self): self.assertEquals(dns.netmaskToNumbits('255.255.255.0'), 24) def test_host(self): self.assertEquals(dns.netmaskToNumbits('255.255.255.255'), 32) def test_numbits(self): for i in range(0, 33): self.assertEquals(dns.netmaskToNumbits(str(i)), i) def test_CIDR(self): for i in range(0, 33): mask = dns.ntoa(dns.aton(i)) self.assertEquals(dns.netmaskToNumbits(mask), i) class PtrSoaName(unittest.TestCase): def test_classA(self): self.assertEquals(dns.ptrSoaName('1.2.3.4', '255.0.0.0'), '1.in-addr.arpa.') self.assertEquals(dns.ptrSoaName('1.2.3.4', '8'), '1.in-addr.arpa.') def test_classB(self): self.assertEquals(dns.ptrSoaName('1.2.3.4', '255.255.0.0'), '2.1.in-addr.arpa.') self.assertEquals(dns.ptrSoaName('1.2.3.4', '16'), '2.1.in-addr.arpa.') def test_classC(self): self.assertEquals(dns.ptrSoaName('1.2.3.4', '255.255.255.0'), '3.2.1.in-addr.arpa.') self.assertEquals(dns.ptrSoaName('1.2.3.4', '24'), '3.2.1.in-addr.arpa.') def test_CIDR_9(self): self.assertEquals(dns.ptrSoaName('1.2.3.4', '9'), '0/9.1.in-addr.arpa.') self.assertEquals(dns.ptrSoaName('1.200.3.4', '9'), '128/9.1.in-addr.arpa.') def test_CIDR_12(self): self.assertEquals(dns.ptrSoaName('1.2.3.4', '12'), '0/12.1.in-addr.arpa.') self.assertEquals(dns.ptrSoaName('1.200.3.4', '12'), '192/12.1.in-addr.arpa.') def test_CIDR_13(self): self.assertEquals(dns.ptrSoaName('1.2.3.4', '13'), '0/13.1.in-addr.arpa.') self.assertEquals(dns.ptrSoaName('1.200.3.4', '13'), '200/13.1.in-addr.arpa.') def test_CIDR_15(self): self.assertEquals(dns.ptrSoaName('1.2.3.4', '15'), '2/15.1.in-addr.arpa.') self.assertEquals(dns.ptrSoaName('1.200.3.4', '15'), '200/15.1.in-addr.arpa.') def test_CIDR_29(self): self.assertEquals(dns.ptrSoaName('1.2.3.4', '29'), '0/29.3.2.1.in-addr.arpa.') def test_CIDR_30(self): self.assertEquals(dns.ptrSoaName('1.2.3.4', '30'), '4/30.3.2.1.in-addr.arpa.') ldaptor-0.0.43/ldaptor/test/test_ldapfilter.py0000644000175000017500000005371110344535643017546 0ustar janjan""" Test cases for ldaptor.protocols.ldap.ldapfilter module. """ from twisted.trial import unittest from ldaptor.protocols import pureldap from ldaptor import ldapfilter import types def s(*l): """Join all members of list to a string. Integer members are chr()ed""" r='' for e in l: if isinstance(e, types.IntType): e=chr(e) r=r+str(e) return r def l(s): """Split a string to ord's of chars.""" return map(lambda x: ord(x), s) class RFC2254Examples(unittest.TestCase): def test_cn(self): text = '(cn=Babs Jensen)' filt = pureldap.LDAPFilter_equalityMatch( attributeDesc=pureldap.LDAPAttributeDescription(value='cn'), assertionValue=pureldap.LDAPAssertionValue(value='Babs Jensen')) self.assertEquals(ldapfilter.parseFilter(text), filt) self.assertEquals(filt.asText(), text) def test_not_cn(self): text = '(!(cn=Tim Howes))' filt = pureldap.LDAPFilter_not( pureldap.LDAPFilter_equalityMatch( attributeDesc=pureldap.LDAPAttributeDescription(value='cn'), assertionValue=pureldap.LDAPAssertionValue(value='Tim Howes'))) self.assertEquals(ldapfilter.parseFilter(text), filt) self.assertEquals(filt.asText(), text) def test_and_or(self): text = '(&(objectClass=Person)(|(sn=Jensen)(cn=Babs J*)))' filt = pureldap.LDAPFilter_and( [ pureldap.LDAPFilter_equalityMatch( attributeDesc=pureldap.LDAPAttributeDescription(value='objectClass'), assertionValue=pureldap.LDAPAssertionValue(value='Person')), pureldap.LDAPFilter_or([ pureldap.LDAPFilter_equalityMatch( attributeDesc=pureldap.LDAPAttributeDescription(value='sn'), assertionValue=pureldap.LDAPAssertionValue(value='Jensen')), pureldap.LDAPFilter_substrings( type='cn', substrings=[ pureldap.LDAPFilter_substrings_initial(value='Babs J') ]) ]), ]) self.assertEquals(ldapfilter.parseFilter(text), filt) self.assertEquals(filt.asText(), text) def test_substrings(self): text = '(o=univ*of*mich*)' filt = pureldap.LDAPFilter_substrings( type='o', substrings=[ pureldap.LDAPFilter_substrings_initial(value='univ'), pureldap.LDAPFilter_substrings_any(value='of'), pureldap.LDAPFilter_substrings_any(value='mich'), ]) self.assertEquals(ldapfilter.parseFilter(text), filt) self.assertEquals(filt.asText(), text) def test_extensible_1(self): text = '(cn:1.2.3.4.5:=Fred Flintstone)' self.assertEquals(ldapfilter.parseFilter(text), pureldap.LDAPFilter_extensibleMatch( type='cn', dnAttributes=False, matchingRule='1.2.3.4.5', matchValue='Fred Flintstone', )) def test_extensible_2(self): text = '(sn:dn:2.4.6.8.10:=Barney Rubble)' self.assertEquals(ldapfilter.parseFilter(text), pureldap.LDAPFilter_extensibleMatch( type='sn', dnAttributes=True, matchingRule='2.4.6.8.10', matchValue='Barney Rubble', )) def test_extensible_3(self): text = '(o:dn:=Ace Industry)' self.assertEquals(ldapfilter.parseFilter(text), pureldap.LDAPFilter_extensibleMatch( type='o', dnAttributes=True, matchingRule=None, matchValue='Ace Industry', )) def test_extensible_4(self): text = '(:dn:2.4.6.8.10:=Dino)' self.assertEquals(ldapfilter.parseFilter(text), pureldap.LDAPFilter_extensibleMatch( type=None, dnAttributes=True, matchingRule='2.4.6.8.10', matchValue='Dino', )) def test_escape_parens(self): text = r'(o=Parens R Us \28for all your parenthetical needs\29)' filt = pureldap.LDAPFilter_equalityMatch( attributeDesc=pureldap.LDAPAttributeDescription(value='o'), assertionValue=pureldap.LDAPAssertionValue(value='Parens R Us (for all your parenthetical needs)')) self.assertEquals(ldapfilter.parseFilter(text), filt) self.assertEquals(filt.asText(), text) def test_escape_asterisk(self): text = r'(cn=*\2A*)' filt = pureldap.LDAPFilter_substrings( type='cn', substrings=[ pureldap.LDAPFilter_substrings_any(value='*'), ]) self.assertEquals(ldapfilter.parseFilter(text), filt) self.assertEquals(filt.asText(), text.lower()) def test_escape_backslash(self): text = r'(filename=C:\5cMyFile)' filt = pureldap.LDAPFilter_equalityMatch( attributeDesc=pureldap.LDAPAttributeDescription(value='filename'), assertionValue=pureldap.LDAPAssertionValue(value=r'C:\MyFile')) self.assertEquals(ldapfilter.parseFilter(text), filt) self.assertEquals(filt.asText(), text) def test_escape_binary(self): text = r'(bin=\00\00\00\04)' filt = pureldap.LDAPFilter_equalityMatch( attributeDesc=pureldap.LDAPAttributeDescription(value='bin'), assertionValue=pureldap.LDAPAssertionValue(value='\00\00\00\04')) self.assertEquals(ldapfilter.parseFilter(text), filt) def test_escape_utf8(self): text = r'(sn=Lu\c4\8di\c4\87)' filt = pureldap.LDAPFilter_equalityMatch( attributeDesc=pureldap.LDAPAttributeDescription(value='sn'), assertionValue=pureldap.LDAPAssertionValue(value='Lu\xc4\x8di\xc4\x87')) self.assertEquals(ldapfilter.parseFilter(text), filt) #self.assertEquals(filt.asText(), text) class TestValid(unittest.TestCase): def test_item_present(self): text = r'(cn=*)' filt = pureldap.LDAPFilter_present(value='cn') self.assertEquals(ldapfilter.parseFilter(text), filt) self.assertEquals(filt.asText(), text) def test_item_simple(self): text = r'(cn=foo)' filt = pureldap.LDAPFilter_equalityMatch( attributeDesc=pureldap.LDAPAttributeDescription(value='cn'), assertionValue=pureldap.LDAPAssertionValue(value='foo')) self.assertEquals(ldapfilter.parseFilter(text), filt) self.assertEquals(filt.asText(), text) def test_item_substring_init(self): text = r'(cn=foo*)' filt = pureldap.LDAPFilter_substrings( type='cn', substrings=[pureldap.LDAPFilter_substrings_initial('foo'), ]) self.assertEquals(ldapfilter.parseFilter(text), filt) self.assertEquals(filt.asText(), text) def test_item_substring_final(self): text = r'(cn=*foo)' filt = pureldap.LDAPFilter_substrings( type='cn', substrings=[pureldap.LDAPFilter_substrings_final('foo'), ]) self.assertEquals(ldapfilter.parseFilter(text), filt) self.assertEquals(filt.asText(), text) def test_item_substring_any(self): text = r'(cn=*foo*)' filt = pureldap.LDAPFilter_substrings( type='cn', substrings=[pureldap.LDAPFilter_substrings_any('foo'), ]) self.assertEquals(ldapfilter.parseFilter(text), filt) self.assertEquals(filt.asText(), text) def test_item_substring_aa(self): text = r'(cn=*foo*bar*)' filt = pureldap.LDAPFilter_substrings( type='cn', substrings=[pureldap.LDAPFilter_substrings_any('foo'), pureldap.LDAPFilter_substrings_any('bar'), ]) self.assertEquals(ldapfilter.parseFilter(text), filt) self.assertEquals(filt.asText(), text) def test_item_substring_ia(self): text = r'(cn=foo*bar*)' filt = pureldap.LDAPFilter_substrings( type='cn', substrings=[pureldap.LDAPFilter_substrings_initial('foo'), pureldap.LDAPFilter_substrings_any('bar'), ]) self.assertEquals(ldapfilter.parseFilter(text), filt) self.assertEquals(filt.asText(), text) def test_item_substring_iaa(self): text = r'(cn=foo*bar*baz*)' filt = pureldap.LDAPFilter_substrings( type='cn', substrings=[pureldap.LDAPFilter_substrings_initial('foo'), pureldap.LDAPFilter_substrings_any('bar'), pureldap.LDAPFilter_substrings_any('baz'), ]) self.assertEquals(ldapfilter.parseFilter(text), filt) self.assertEquals(filt.asText(), text) def test_item_substring_if(self): text = r'(cn=foo*bar)' filt = pureldap.LDAPFilter_substrings( type='cn', substrings=[pureldap.LDAPFilter_substrings_initial('foo'), pureldap.LDAPFilter_substrings_final('bar'), ]) self.assertEquals(ldapfilter.parseFilter(text), filt) self.assertEquals(filt.asText(), text) def test_item_substring_iaf(self): text = r'(cn=foo*bar*baz)' filt = pureldap.LDAPFilter_substrings( type='cn', substrings=[pureldap.LDAPFilter_substrings_initial('foo'), pureldap.LDAPFilter_substrings_any('bar'), pureldap.LDAPFilter_substrings_final('baz'), ]) self.assertEquals(ldapfilter.parseFilter(text), filt) self.assertEquals(filt.asText(), text) def test_item_substring_iaaf(self): text = r'(cn=foo*bar*baz*quux)' filt = pureldap.LDAPFilter_substrings( type='cn', substrings=[pureldap.LDAPFilter_substrings_initial('foo'), pureldap.LDAPFilter_substrings_any('bar'), pureldap.LDAPFilter_substrings_any('baz'), pureldap.LDAPFilter_substrings_final('quux'), ]) self.assertEquals(ldapfilter.parseFilter(text), filt) self.assertEquals(filt.asText(), text) def test_item_substring_af(self): text = r'(cn=*foo*bar)' filt = pureldap.LDAPFilter_substrings( type='cn', substrings=[pureldap.LDAPFilter_substrings_any('foo'), pureldap.LDAPFilter_substrings_final('bar'), ]) self.assertEquals(ldapfilter.parseFilter(text), filt) self.assertEquals(filt.asText(), text) def test_item_substring_aaf(self): text = r'(cn=*foo*bar*baz)' filt = pureldap.LDAPFilter_substrings( type='cn', substrings=[pureldap.LDAPFilter_substrings_any('foo'), pureldap.LDAPFilter_substrings_any('bar'), pureldap.LDAPFilter_substrings_final('baz'), ]) self.assertEquals(ldapfilter.parseFilter(text), filt) self.assertEquals(filt.asText(), text) def test_not_item(self): text = r'(!(cn=foo))' filt = pureldap.LDAPFilter_not( pureldap.LDAPFilter_equalityMatch( attributeDesc=pureldap.LDAPAttributeDescription(value='cn'), assertionValue=pureldap.LDAPAssertionValue(value='foo'))) self.assertEquals(ldapfilter.parseFilter(text), filt) self.assertEquals(filt.asText(), text) def test_or_item(self): text = r'(|(cn=foo)(cn=bar))' filt = pureldap.LDAPFilter_or([ pureldap.LDAPFilter_equalityMatch( attributeDesc=pureldap.LDAPAttributeDescription(value='cn'), assertionValue=pureldap.LDAPAssertionValue(value='foo')), pureldap.LDAPFilter_equalityMatch( attributeDesc=pureldap.LDAPAttributeDescription(value='cn'), assertionValue=pureldap.LDAPAssertionValue(value='bar')), ]) self.assertEquals(ldapfilter.parseFilter(text), filt) self.assertEquals(filt.asText(), text) def test_and_item(self): text = r'(&(cn=foo)(cn=bar))' filt = pureldap.LDAPFilter_and([ pureldap.LDAPFilter_equalityMatch( attributeDesc=pureldap.LDAPAttributeDescription(value='cn'), assertionValue=pureldap.LDAPAssertionValue(value='foo')), pureldap.LDAPFilter_equalityMatch( attributeDesc=pureldap.LDAPAttributeDescription(value='cn'), assertionValue=pureldap.LDAPAssertionValue(value='bar')), ]) self.assertEquals(ldapfilter.parseFilter(text), filt) self.assertEquals(filt.asText(), text) def test_andornot(self): text = r'(&(!(|(cn=foo)(cn=bar)))(sn=a*b*c*d))' filt = pureldap.LDAPFilter_and([ pureldap.LDAPFilter_not( pureldap.LDAPFilter_or([ pureldap.LDAPFilter_equalityMatch( attributeDesc=pureldap.LDAPAttributeDescription(value='cn'), assertionValue=pureldap.LDAPAssertionValue(value='foo')), pureldap.LDAPFilter_equalityMatch( attributeDesc=pureldap.LDAPAttributeDescription(value='cn'), assertionValue=pureldap.LDAPAssertionValue(value='bar')), ])), pureldap.LDAPFilter_substrings( type='sn', substrings=[pureldap.LDAPFilter_substrings_initial('a'), pureldap.LDAPFilter_substrings_any('b'), pureldap.LDAPFilter_substrings_any('c'), pureldap.LDAPFilter_substrings_final('d'), ])]) self.assertEquals(ldapfilter.parseFilter(text), filt) self.assertEquals(filt.asText(), text) def test_whitespace_beforeCloseParen(self): text = r'(cn=foo )' filt = pureldap.LDAPFilter_equalityMatch( attributeDesc=pureldap.LDAPAttributeDescription(value='cn'), assertionValue=pureldap.LDAPAssertionValue(value='foo ')) self.assertEquals(ldapfilter.parseFilter(text), filt) self.assertEquals(filt.asText(), text) def test_whitespace_afterEq(self): text = r'(cn= foo)' filt = pureldap.LDAPFilter_equalityMatch( attributeDesc=pureldap.LDAPAttributeDescription(value='cn'), assertionValue=pureldap.LDAPAssertionValue(value=' foo')) self.assertEquals(ldapfilter.parseFilter(text), filt) self.assertEquals(filt.asText(), text) class TestInvalid(unittest.TestCase): def test_closeParen_1(self): self.assertRaises(ldapfilter.InvalidLDAPFilter, ldapfilter.parseFilter, '(&(|(mail=)@*)(uid=)))(mail=*))') def test_closeParen_2(self): self.assertRaises(ldapfilter.InvalidLDAPFilter, ldapfilter.parseFilter, '(|(mail=)@*)(uid=)))') def test_closeParen_3(self): self.assertRaises(ldapfilter.InvalidLDAPFilter, ldapfilter.parseFilter, '(mail=)@*)') def test_closeParen_4(self): self.assertRaises(ldapfilter.InvalidLDAPFilter, ldapfilter.parseFilter, '(uid=))') def test_openParen_1(self): self.assertRaises(ldapfilter.InvalidLDAPFilter, ldapfilter.parseFilter, '(&(|(mail=(@*)(uid=())(mail=*))') def test_openParen_2(self): self.assertRaises(ldapfilter.InvalidLDAPFilter, ldapfilter.parseFilter, '(|(mail=(@*)(uid=())') def test_openParen_3(self): self.assertRaises(ldapfilter.InvalidLDAPFilter, ldapfilter.parseFilter, '(mail=(@*)') def test_openParen_4(self): self.assertRaises(ldapfilter.InvalidLDAPFilter, ldapfilter.parseFilter, '(uid=()') def test_whitespace_leading(self): self.assertRaises(ldapfilter.InvalidLDAPFilter, ldapfilter.parseFilter, r' (cn=foo)') def test_whitespace_trailing(self): self.assertRaises(ldapfilter.InvalidLDAPFilter, ldapfilter.parseFilter, r'(cn=foo) ') def test_whitespace_afterOpenParen(self): self.assertRaises(ldapfilter.InvalidLDAPFilter, ldapfilter.parseFilter, r'( cn=foo)') def test_whitespace_beforeEq(self): self.assertRaises(ldapfilter.InvalidLDAPFilter, ldapfilter.parseFilter, r'(cn =foo)') class TestMaybeSubstring(unittest.TestCase): def test_item_present(self): text = r'*' filt = pureldap.LDAPFilter_present(value='cn') self.assertEquals(ldapfilter.parseMaybeSubstring('cn', text), filt) def test_item_simple(self): text = r'foo' filt = pureldap.LDAPFilter_equalityMatch( attributeDesc=pureldap.LDAPAttributeDescription(value='cn'), assertionValue=pureldap.LDAPAssertionValue(value='foo')) self.assertEquals(ldapfilter.parseMaybeSubstring('cn', text), filt) def test_item_substring_init(self): text = r'foo*' filt = pureldap.LDAPFilter_substrings( type='cn', substrings=[pureldap.LDAPFilter_substrings_initial('foo'), ]) self.assertEquals(ldapfilter.parseMaybeSubstring('cn', text), filt) def test_item_substring_final(self): text = r'*foo' filt = pureldap.LDAPFilter_substrings( type='cn', substrings=[pureldap.LDAPFilter_substrings_final('foo'), ]) self.assertEquals(ldapfilter.parseMaybeSubstring('cn', text), filt) def test_item_substring_any(self): text = r'*foo*' filt = pureldap.LDAPFilter_substrings( type='cn', substrings=[pureldap.LDAPFilter_substrings_any('foo'), ]) self.assertEquals(ldapfilter.parseMaybeSubstring('cn', text), filt) def test_item_substring_aa(self): text = r'*foo*bar*' filt = pureldap.LDAPFilter_substrings( type='cn', substrings=[pureldap.LDAPFilter_substrings_any('foo'), pureldap.LDAPFilter_substrings_any('bar'), ]) self.assertEquals(ldapfilter.parseMaybeSubstring('cn', text), filt) def test_item_substring_ia(self): text = r'foo*bar*' filt = pureldap.LDAPFilter_substrings( type='cn', substrings=[pureldap.LDAPFilter_substrings_initial('foo'), pureldap.LDAPFilter_substrings_any('bar'), ]) self.assertEquals(ldapfilter.parseMaybeSubstring('cn', text), filt) def test_item_substring_iaa(self): text = r'foo*bar*baz*' filt = pureldap.LDAPFilter_substrings( type='cn', substrings=[pureldap.LDAPFilter_substrings_initial('foo'), pureldap.LDAPFilter_substrings_any('bar'), pureldap.LDAPFilter_substrings_any('baz'), ]) self.assertEquals(ldapfilter.parseMaybeSubstring('cn', text), filt) def test_item_substring_if(self): text = r'foo*bar' filt = pureldap.LDAPFilter_substrings( type='cn', substrings=[pureldap.LDAPFilter_substrings_initial('foo'), pureldap.LDAPFilter_substrings_final('bar'), ]) self.assertEquals(ldapfilter.parseMaybeSubstring('cn', text), filt) def test_item_substring_iaf(self): text = r'foo*bar*baz' filt = pureldap.LDAPFilter_substrings( type='cn', substrings=[pureldap.LDAPFilter_substrings_initial('foo'), pureldap.LDAPFilter_substrings_any('bar'), pureldap.LDAPFilter_substrings_final('baz'), ]) self.assertEquals(ldapfilter.parseMaybeSubstring('cn', text), filt) def test_item_substring_iaaf(self): text = r'foo*bar*baz*quux' filt = pureldap.LDAPFilter_substrings( type='cn', substrings=[pureldap.LDAPFilter_substrings_initial('foo'), pureldap.LDAPFilter_substrings_any('bar'), pureldap.LDAPFilter_substrings_any('baz'), pureldap.LDAPFilter_substrings_final('quux'), ]) self.assertEquals(ldapfilter.parseMaybeSubstring('cn', text), filt) def test_item_substring_af(self): text = r'*foo*bar' filt = pureldap.LDAPFilter_substrings( type='cn', substrings=[pureldap.LDAPFilter_substrings_any('foo'), pureldap.LDAPFilter_substrings_final('bar'), ]) self.assertEquals(ldapfilter.parseMaybeSubstring('cn', text), filt) def test_item_substring_aaf(self): text = r'*foo*bar*baz' filt = pureldap.LDAPFilter_substrings( type='cn', substrings=[pureldap.LDAPFilter_substrings_any('foo'), pureldap.LDAPFilter_substrings_any('bar'), pureldap.LDAPFilter_substrings_final('baz'), ]) self.assertEquals(ldapfilter.parseMaybeSubstring('cn', text), filt) def test_escape_simple(self): text = r'f\2aoo(bar' filt = pureldap.LDAPFilter_equalityMatch( attributeDesc=pureldap.LDAPAttributeDescription(value='cn'), assertionValue=pureldap.LDAPAssertionValue(value='f*oo(bar')) self.assertEquals(ldapfilter.parseMaybeSubstring('cn', text), filt) class TestWhitespace(unittest.TestCase): def test_escape(self): self.assertRaises(ldapfilter.InvalidLDAPFilter, ldapfilter.parseFilter, r'(cn=\ 61)') ldaptor-0.0.43/ldaptor/test/test_entry_diff.py0000644000175000017500000001450610344407651017545 0ustar janjan""" Test cases for ldaptor.diff """ from twisted.trial import unittest from ldaptor import delta, entry class TestDiffEntry(unittest.TestCase): def testEqual(self): a = entry.BaseLDAPEntry(dn='dc=foo', attributes={ 'foo': ['bar'], }) b = entry.BaseLDAPEntry(dn='dc=foo', attributes={ 'foo': ['bar'], }) result = a.diff(b) self.assertEquals(result, None) def testAdd_New_OneType_OneValue(self): a = entry.BaseLDAPEntry(dn='dc=foo', attributes={ 'foo': ['bar'], }) b = entry.BaseLDAPEntry(dn='dc=foo', attributes={ 'foo': ['bar'], 'baz': ['quux'], }) result = a.diff(b) self.assertEquals(result, delta.ModifyOp('dc=foo', [ delta.Add('baz', ['quux']), ])) def testAdd_New_OneType_ManyValues(self): a = entry.BaseLDAPEntry(dn='dc=foo', attributes={ 'foo': ['bar'], }) b = entry.BaseLDAPEntry(dn='dc=foo', attributes={ 'foo': ['bar'], 'baz': ['quux', 'thud', 'foo'], }) result = a.diff(b) self.assertEquals(result, delta.ModifyOp('dc=foo', [ delta.Add('baz', ['quux', 'thud', 'foo']), ])) def testAdd_New_ManyTypes(self): a = entry.BaseLDAPEntry(dn='dc=foo', attributes={ 'foo': ['bar'], }) b = entry.BaseLDAPEntry(dn='dc=foo', attributes={ 'foo': ['bar'], 'baz': ['quux'], 'bang': ['thud'], }) result = a.diff(b) self.assertEquals(result, delta.ModifyOp('dc=foo', [ delta.Add('bang', ['thud']), delta.Add('baz', ['quux']), ])) def testAdd_Existing_OneType_OneValue(self): a = entry.BaseLDAPEntry(dn='dc=foo', attributes={ 'foo': ['bar'], }) b = entry.BaseLDAPEntry(dn='dc=foo', attributes={ 'foo': ['bar', 'quux'], }) result = a.diff(b) self.assertEquals(result, delta.ModifyOp('dc=foo', [ delta.Add('foo', ['quux']), ])) def testAdd_Existing_OneType_ManyValues(self): a = entry.BaseLDAPEntry(dn='dc=foo', attributes={ 'foo': ['bar'], }) b = entry.BaseLDAPEntry(dn='dc=foo', attributes={ 'foo': ['bar', 'quux', 'thud', 'foo'], }) result = a.diff(b) self.assertEquals(result, delta.ModifyOp('dc=foo', [ delta.Add('foo', ['quux', 'thud', 'foo']), ])) def testAdd_NewAndExisting_ManyTypes(self): a = entry.BaseLDAPEntry(dn='dc=foo', attributes={ 'foo': ['bar'], 'baz': ['quux'], }) b = entry.BaseLDAPEntry(dn='dc=foo', attributes={ 'foo': ['bar', 'thud', 'bang'], 'baz': ['quux', 'bar', 'stump'], 'bang': ['thud', 'barble'], }) result = a.diff(b) self.assertEquals(result, delta.ModifyOp('dc=foo', [ delta.Add('bang', ['thud', 'barble']), delta.Add('baz', ['bar', 'stump']), delta.Add('foo', ['thud', 'bang']), ])) def testDelete_All_OneType(self): a = entry.BaseLDAPEntry(dn='dc=foo', attributes={ 'foo': ['bar'], 'baz': ['quux', 'thud'], }) b = entry.BaseLDAPEntry(dn='dc=foo', attributes={ 'foo': ['bar'], }) result = a.diff(b) self.assertEquals(result, delta.ModifyOp('dc=foo', [ delta.Delete('baz', ['quux', 'thud']), ])) def testDelete_Some_OneType(self): a = entry.BaseLDAPEntry(dn='dc=foo', attributes={ 'foo': ['bar'], 'baz': ['quux', 'thud'], }) b = entry.BaseLDAPEntry(dn='dc=foo', attributes={ 'foo': ['bar'], 'baz': ['thud'], }) result = a.diff(b) self.assertEquals(result, delta.ModifyOp('dc=foo', [ delta.Delete('baz', ['quux']), ])) def testComplex(self): a = entry.BaseLDAPEntry(dn='cn=Paula Jensen,ou=Product Development,dc=airius,dc=com', attributes={ 'description': ['Something'], 'telephonenumber': ['+123 456'], 'facsimiletelephonenumber': ['+1 408 555 9876'], }) b = entry.BaseLDAPEntry(dn='cn=Paula Jensen,ou=Product Development,dc=airius,dc=com', attributes={ 'postalAddress': ['123 Anystreet $ Sunnyvale, CA $ 94086'], 'telephonenumber': ['+1 408 555 1234', '+1 408 555 5678'], }) result = a.diff(b) self.assertEquals(result, delta.ModifyOp('cn=Paula Jensen,ou=Product Development,dc=airius,dc=com', [ delta.Add('postalAddress', ['123 Anystreet $ Sunnyvale, CA $ 94086']), delta.Delete('description', ['Something']), delta.Delete('facsimiletelephonenumber', ['+1 408 555 9876']), delta.Add('telephonenumber', ['+1 408 555 1234', '+1 408 555 5678']), delta.Delete('telephonenumber', ['+123 456']), ])) ldaptor-0.0.43/ldaptor/test/__init__.py0000644000175000017500000000003607712743107016111 0ustar janjan"""Unit tests for Ldaptor.""" ldaptor-0.0.43/ldaptor/test/util.py0000644000175000017500000001051710344535643015333 0ustar janjanfrom twisted.python import failure from twisted.internet import reactor, protocol, address, error from twisted.test import testutils from twisted.trial import unittest from StringIO import StringIO class FakeTransport(protocol.FileWrapper): disconnecting = False disconnect_done = False def __init__(self, addr, peerAddr): self.data = StringIO() protocol.FileWrapper.__init__(self, self.data) self.addr = addr self.peerAddr = peerAddr def getHost(self): return self.addr def getPeer(self): return self.peerAddr def loseConnection(self): self.disconnecting = True class FasterIOPump(testutils.IOPump): def pump(self): """Move data back and forth. Returns whether any data was moved. """ self.clientIO.seek(0) self.serverIO.seek(0) cData = self.clientIO.read() sData = self.serverIO.read() self.clientIO.seek(0) self.serverIO.seek(0) self.clientIO.truncate() self.serverIO.truncate() self.server.dataReceived(cData) self.client.dataReceived(sData) if cData or sData: return 1 else: return 0 class IOPump(FasterIOPump): active = [] def __init__(self, client, server, clientTransport, serverTransport): self.clientTransport = clientTransport self.serverTransport = serverTransport testutils.IOPump.__init__(self, client=client, server=server, clientIO=clientTransport.data, serverIO=serverTransport.data) self.active.append(self) def pump(self): FasterIOPump.pump(self) if (self.clientTransport.disconnecting and not self.clientTransport.data.getvalue() and not self.clientTransport.disconnect_done): self.server.connectionLost(error.ConnectionDone) self.clientTransport.disconnect_done = True if (self.serverTransport.disconnecting and not self.serverTransport.data.getvalue() and not self.serverTransport.disconnect_done): self.client.connectionLost(error.ConnectionDone) self.serverTransport.disconnect_done = True if (self.clientTransport.disconnect_done and self.serverTransport.disconnect_done): self.active.remove(self) def __repr__(self): return '<%s client=%r/%r server=%r/%r>' % ( self.__class__.__name__, self.client, self.clientIO.getvalue(), self.server, self.serverIO.getvalue(), ) def returnConnected(server, client, clientAddress=None, serverAddress=None): """Take two Protocol instances and connect them. """ if serverAddress is None: serverAddress = address.IPv4Address('TCP', 'localhost', 1) if clientAddress is None: clientAddress = address.IPv4Address('TCP', 'localhost', 2) clientTransport = FakeTransport(clientAddress, serverAddress) client.makeConnection(clientTransport) serverTransport = FakeTransport(serverAddress, clientAddress) server.makeConnection(serverTransport) pump = IOPump(client, server, clientTransport, serverTransport) # Challenge-response authentication: pump.flush() # Uh... pump.flush() return pump def _append(result, lst): lst.append(result) def _getDeferredResult(d, timeout=None): if timeout is not None: d.setTimeout(timeout) resultSet = [] d.addBoth(_append, resultSet) while not resultSet: for pump in IOPump.active: pump.pump() reactor.iterate() return resultSet[0] def pumpingDeferredResult(d, timeout=None): result = _getDeferredResult(d, timeout) if isinstance(result, failure.Failure): if result.tb: raise result.value.__class__, result.value, result.tb raise result.value else: return result def pumpingDeferredError(d, timeout=None): result = _getDeferredResult(d, timeout) if isinstance(result, failure.Failure): return result else: raise unittest.FailTest, "Deferred did not fail: %r" % (result,) ldaptor-0.0.43/ldaptor/test/test_svcbindproxy.py0000644000175000017500000006261710344535643020157 0ustar janjan""" Test cases for ldaptor.protocols.ldap.svcbindproxy module. """ from twisted.trial import unittest from twisted.internet import reactor from ldaptor.protocols.ldap import svcbindproxy, ldaperrors from ldaptor.protocols import pureldap, pureber from ldaptor import ldapfilter, testutil 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))) ldaptor-0.0.43/ldaptor/test/test_schema.py0000644000175000017500000005345010104536552016652 0ustar janjan""" Test cases for ldaptor.schema module. """ from twisted.trial import unittest from ldaptor import schema OBJECTCLASSES = { 'organization': """( 2.5.6.4 NAME 'organization' DESC 'RFC2256: an organization' SUP top STRUCTURAL MUST o MAY ( userPassword $ searchGuide $ seeAlso $ businessCategory $ x121Address $ registeredAddress $ destinationIndicator $ preferredDeliveryMethod $ telexNumber $ teletexTerminalIdentifier $ telephoneNumber $ internationaliSDNNumber $ facsimileTelephoneNumber $ street $ postOfficeBox $ postalCode $ postalAddress $ physicalDeliveryOfficeName $ st $ l $ description ) )""", 'organizationalUnit': """( 2.5.6.5 NAME 'organizationalUnit' DESC 'RFC2256: an organizational unit' SUP top STRUCTURAL MUST ou MAY ( userPassword $ searchGuide $ seeAlso $ businessCategory $ x121Address $ registeredAddress $ destinationIndicator $ preferredDeliveryMethod $ telexNumber $ teletexTerminalIdentifier $ telephoneNumber $ internationaliSDNNumber $ facsimileTelephoneNumber $ street $ postOfficeBox $ postalCode $ postalAddress $ physicalDeliveryOfficeName $ st $ l $ description ) )""", 'country': """( 2.5.6.2 NAME 'country' DESC 'RFC2256: a country' SUP top STRUCTURAL MUST c MAY ( searchGuide $ description ) )""", } class AttributeType_KnownValues(unittest.TestCase): knownValues = [ ("""( 2.5.4.4 NAME ( 'sn' 'surname' ) DESC 'RFC2256: last (family) name(s) for which the entity is known by' SUP name )""", { 'oid': '2.5.4.4', 'name': ('sn', 'surname',), 'desc': 'RFC2256: last (family) name(s) for which the entity is known by', 'sup': 'name', }), ("""( 2.5.4.2 NAME 'knowledgeInformation' DESC 'RFC2256: knowledge information' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{32768} )""", { 'oid': '2.5.4.2', 'name': ('knowledgeInformation',), 'desc': 'RFC2256: knowledge information', 'equality': 'caseIgnoreMatch', 'syntax': '1.3.6.1.4.1.1466.115.121.1.15{32768}', }), ("""( 2.5.4.5 NAME 'serialNumber' DESC 'RFC2256: serial number of the entity' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.44{64} )""", { 'oid': '2.5.4.5', 'name': ('serialNumber',), 'desc': 'RFC2256: serial number of the entity', 'equality': 'caseIgnoreMatch', 'substr': 'caseIgnoreSubstringsMatch', 'syntax': '1.3.6.1.4.1.1466.115.121.1.44{64}', }), ("""( 2.5.4.6 NAME ( 'c' 'countryName' ) DESC 'RFC2256: ISO-3166 country 2-letter code' SUP name SINGLE-VALUE )""", { 'oid': '2.5.4.6', 'name': ('c', 'countryName',), 'desc': 'RFC2256: ISO-3166 country 2-letter code', 'sup': 'name', 'single_value': 1, }), ("""( 1.2.840.113549.1.9.1 NAME ( 'email' 'emailAddress' 'pkcs9email' ) DESC 'RFC2459: legacy attribute for email addresses in DNs' EQUALITY caseIgnoreIA5Match SUBSTR caseIgnoreIA5SubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{128} )""", { 'oid': '1.2.840.113549.1.9.1', 'name': ('email', 'emailAddress', 'pkcs9email', ), 'desc': 'RFC2459: legacy attribute for email addresses in DNs', 'equality': 'caseIgnoreIA5Match', 'substr': 'caseIgnoreIA5SubstringsMatch', 'syntax': '1.3.6.1.4.1.1466.115.121.1.26{128}', }), ] def testParse(self): defaults = { 'name': None, 'desc': None, 'obsolete': 0, 'sup': [], 'equality': None, 'ordering': None, 'substr': None, 'syntax': None, 'single_value': 0, 'collective': 0, 'no_user_modification': 0, 'usage': None, } for text, expected in self.knownValues: a=schema.AttributeTypeDescription(text) self.failIfEqual(a.oid, None) for key, want in expected.items(): if key in defaults: del defaults[key] got = getattr(a, key) self.assertEquals(got, want) for key, want in defaults.items(): got = getattr(a, key) self.assertEquals(got, want) def testStringification(self): for want, values in self.knownValues: a=schema.AttributeTypeDescription(None) for key, val in values.items(): setattr(a, key, val) want = ' '.join(want.split(None)) got = ' '.join(str(a).split(None)) self.assertEquals(got, want) class ObjectClass_KnownValues(unittest.TestCase): knownValues = [ (OBJECTCLASSES['organization'], { 'oid': '2.5.6.4', 'name': ('organization',), 'desc': 'RFC2256: an organization', 'sup': ['top'], 'type': 'STRUCTURAL', 'must': ['o'], 'may': ['userPassword', 'searchGuide', 'seeAlso', 'businessCategory', 'x121Address', 'registeredAddress', 'destinationIndicator', 'preferredDeliveryMethod', 'telexNumber', 'teletexTerminalIdentifier', 'telephoneNumber', 'internationaliSDNNumber', 'facsimileTelephoneNumber', 'street', 'postOfficeBox', 'postalCode', 'postalAddress', 'physicalDeliveryOfficeName', 'st', 'l', 'description'], }), (OBJECTCLASSES['organizationalUnit'], { 'oid': '2.5.6.5', 'name': ('organizationalUnit',), 'desc': 'RFC2256: an organizational unit', 'sup': ['top'], 'type': 'STRUCTURAL', 'must': ['ou'], 'may': [ 'userPassword', 'searchGuide', 'seeAlso', 'businessCategory', 'x121Address', 'registeredAddress', 'destinationIndicator', 'preferredDeliveryMethod', 'telexNumber', 'teletexTerminalIdentifier', 'telephoneNumber', 'internationaliSDNNumber', 'facsimileTelephoneNumber', 'street', 'postOfficeBox', 'postalCode', 'postalAddress', 'physicalDeliveryOfficeName', 'st', 'l', 'description', ], }), ] def testParse(self): defaults = { 'name': None, 'desc': None, 'obsolete': 0, 'sup': None, 'type': 'STRUCTURAL', 'must': [], 'may': [], } for text, expected in self.knownValues: a=schema.ObjectClassDescription(text) self.failIfEqual(a.oid, None) for key, want in expected.items(): if key in defaults: del defaults[key] got = getattr(a, key) self.assertEquals(got, want) for key, want in defaults.items(): got = getattr(a, key) self.assertEquals(got, want) def testStringification(self): for want, values in self.knownValues: a=schema.ObjectClassDescription(None) for key, val in values.items(): setattr(a, key, val) want = ' '.join(want.split(None)) got = ' '.join(str(a).split(None)) self.assertEquals(got, want) class TestComparison(unittest.TestCase): ORDER = [ 'country', 'organization', 'organizationalUnit', ] def setUp(self): data = {} for oc,text in OBJECTCLASSES.items(): data[oc] = schema.ObjectClassDescription(text) self.data = data def test_eq(self): for k1 in self.data: for k2 in self.data: if k1 == k2: self.failUnless(self.data[k1] == self.data[k2]) else: self.failIf(self.data[k1] == self.data[k2]) def test_ne(self): for k1 in self.data: for k2 in self.data: if k1 == k2: self.failIf(self.data[k1] != self.data[k2]) else: self.failUnless(self.data[k1] != self.data[k2]) def test_order(self): for i,base in enumerate(self.ORDER): self.failUnless(self.data[base] <= self.data[base]) self.failUnless(self.data[base] >= self.data[base]) self.failIf(self.data[base] < self.data[base]) self.failIf(self.data[base] > self.data[base]) for lower in self.ORDER[:i]: self.failUnless(self.data[lower] < self.data[base]) self.failUnless(self.data[lower] <= self.data[base]) self.failIf(self.data[base] < self.data[lower]) self.failIf(self.data[base] <= self.data[lower]) for higher in self.ORDER[i+1:]: self.failUnless(self.data[higher] > self.data[base]) self.failUnless(self.data[higher] >= self.data[base]) self.failIf(self.data[base] > self.data[higher]) self.failIf(self.data[base] >= self.data[higher]) """ attributetype ( 2.5.4.7 NAME ( 'l' 'localityName' ) DESC 'RFC2256: locality which this object resides in' SUP name ) attributetype ( 2.5.4.8 NAME ( 'st' 'stateOrProvinceName' ) DESC 'RFC2256: state or province which this object resides in' SUP name ) attributetype ( 2.5.4.9 NAME ( 'street' 'streetAddress' ) DESC 'RFC2256: street address of this object' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{128} ) attributetype ( 2.5.4.10 NAME ( 'o' 'organizationName' ) DESC 'RFC2256: organization this object belongs to' SUP name ) attributetype ( 2.5.4.11 NAME ( 'ou' 'organizationalUnitName' ) DESC 'RFC2256: organizational unit this object belongs to' SUP name ) attributetype ( 2.5.4.12 NAME 'title' DESC 'RFC2256: title associated with the entity' SUP name ) attributetype ( 2.5.4.13 NAME 'description' DESC 'RFC2256: descriptive information' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{1024} ) attributetype ( 2.5.4.14 NAME 'searchGuide' DESC 'RFC2256: search guide, obsoleted by enhancedSearchGuide' SYNTAX 1.3.6.1.4.1.1466.115.121.1.25 ) attributetype ( 2.5.4.15 NAME 'businessCategory' DESC 'RFC2256: business category' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{128} ) attributetype ( 2.5.4.16 NAME 'postalAddress' DESC 'RFC2256: postal address' EQUALITY caseIgnoreListMatch SUBSTR caseIgnoreListSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.41 ) attributetype ( 2.5.4.17 NAME 'postalCode' DESC 'RFC2256: postal code' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{40} ) attributetype ( 2.5.4.18 NAME 'postOfficeBox' DESC 'RFC2256: Post Office Box' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{40} ) attributetype ( 2.5.4.19 NAME 'physicalDeliveryOfficeName' DESC 'RFC2256: Physical Delivery Office Name' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{128} ) attributetype ( 2.5.4.20 NAME 'telephoneNumber' DESC 'RFC2256: Telephone Number' EQUALITY telephoneNumberMatch SUBSTR telephoneNumberSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.50{32} ) attributetype ( 2.5.4.21 NAME 'telexNumber' DESC 'RFC2256: Telex Number' SYNTAX 1.3.6.1.4.1.1466.115.121.1.52 ) attributetype ( 2.5.4.22 NAME 'teletexTerminalIdentifier' DESC 'RFC2256: Teletex Terminal Identifier' SYNTAX 1.3.6.1.4.1.1466.115.121.1.51 ) attributetype ( 2.5.4.23 NAME ( 'facsimileTelephoneNumber' 'fax' ) DESC 'RFC2256: Facsimile (Fax) Telephone Number' SYNTAX 1.3.6.1.4.1.1466.115.121.1.22 ) attributetype ( 2.5.4.24 NAME 'x121Address' DESC 'RFC2256: X.121 Address' EQUALITY numericStringMatch SUBSTR numericStringSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.36{15} ) attributetype ( 2.5.4.25 NAME 'internationaliSDNNumber' DESC 'RFC2256: international ISDN number' EQUALITY numericStringMatch SUBSTR numericStringSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.36{16} ) attributetype ( 2.5.4.26 NAME 'registeredAddress' DESC 'RFC2256: registered postal address' SUP postalAddress SYNTAX 1.3.6.1.4.1.1466.115.121.1.41 ) attributetype ( 2.5.4.27 NAME 'destinationIndicator' DESC 'RFC2256: destination indicator' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.44{128} ) attributetype ( 2.5.4.28 NAME 'preferredDeliveryMethod' DESC 'RFC2256: preferred delivery method' SYNTAX 1.3.6.1.4.1.1466.115.121.1.14 SINGLE-VALUE ) attributetype ( 2.5.4.29 NAME 'presentationAddress' DESC 'RFC2256: presentation address' EQUALITY presentationAddressMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.43 SINGLE-VALUE ) attributetype ( 2.5.4.30 NAME 'supportedApplicationContext' DESC 'RFC2256: supported application context' EQUALITY objectIdentifierMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 ) attributetype ( 2.5.4.31 NAME 'member' DESC 'RFC2256: member of a group' SUP distinguishedName ) attributetype ( 2.5.4.32 NAME 'owner' DESC 'RFC2256: owner (of the object)' SUP distinguishedName ) attributetype ( 2.5.4.33 NAME 'roleOccupant' DESC 'RFC2256: occupant of role' SUP distinguishedName ) attributetype ( 2.5.4.34 NAME 'seeAlso' DESC 'RFC2256: DN of related object' SUP distinguishedName ) attributetype ( 2.5.4.36 NAME 'userCertificate' DESC 'RFC2256: X.509 user certificate, use ;binary' SYNTAX 1.3.6.1.4.1.1466.115.121.1.8 ) attributetype ( 2.5.4.37 NAME 'cACertificate' DESC 'RFC2256: X.509 CA certificate, use ;binary' SYNTAX 1.3.6.1.4.1.1466.115.121.1.8 ) attributetype ( 2.5.4.38 NAME 'authorityRevocationList' DESC 'RFC2256: X.509 authority revocation list, use ;binary' SYNTAX 1.3.6.1.4.1.1466.115.121.1.9 ) attributetype ( 2.5.4.39 NAME 'certificateRevocationList' DESC 'RFC2256: X.509 certificate revocation list, use ;binary' SYNTAX 1.3.6.1.4.1.1466.115.121.1.9 ) attributetype ( 2.5.4.40 NAME 'crossCertificatePair' DESC 'RFC2256: X.509 cross certificate pair, use ;binary' SYNTAX 1.3.6.1.4.1.1466.115.121.1.10 ) attributetype ( 2.5.4.42 NAME ( 'givenName' 'gn' ) DESC 'RFC2256: first name(s) for which the entity is known by' SUP name ) attributetype ( 2.5.4.43 NAME 'initials' DESC 'RFC2256: initials of some or all of names, but not the surname(s).' SUP name ) attributetype ( 2.5.4.44 NAME 'generationQualifier' DESC 'RFC2256: name qualifier indicating a generation' SUP name ) attributetype ( 2.5.4.45 NAME 'x500UniqueIdentifier' DESC 'RFC2256: X.500 unique identifier' EQUALITY bitStringMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.6 ) attributetype ( 2.5.4.46 NAME 'dnQualifier' DESC 'RFC2256: DN qualifier' EQUALITY caseIgnoreMatch ORDERING caseIgnoreOrderingMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.44 ) attributetype ( 2.5.4.47 NAME 'enhancedSearchGuide' DESC 'RFC2256: enhanced search guide' SYNTAX 1.3.6.1.4.1.1466.115.121.1.21 ) attributetype ( 2.5.4.48 NAME 'protocolInformation' DESC 'RFC2256: protocol information' EQUALITY protocolInformationMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.42 ) attributetype ( 2.5.4.50 NAME 'uniqueMember' DESC 'RFC2256: unique member of a group' EQUALITY uniqueMemberMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.34 ) attributetype ( 2.5.4.51 NAME 'houseIdentifier' DESC 'RFC2256: house identifier' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{32768} ) attributetype ( 2.5.4.52 NAME 'supportedAlgorithms' DESC 'RFC2256: supported algorithms' SYNTAX 1.3.6.1.4.1.1466.115.121.1.49 ) attributetype ( 2.5.4.53 NAME 'deltaRevocationList' DESC 'RFC2256: delta revocation list; use ;binary' SYNTAX 1.3.6.1.4.1.1466.115.121.1.9 ) attributetype ( 2.5.4.54 NAME 'dmdName' DESC 'RFC2256: name of DMD' SUP name ) objectclass ( 2.5.6.3 NAME 'locality' DESC 'RFC2256: a locality' SUP top STRUCTURAL MAY ( street $ seeAlso $ searchGuide $ st $ l $ description ) ) objectclass ( 2.5.6.6 NAME 'person' DESC 'RFC2256: a person' SUP top STRUCTURAL MUST ( sn $ cn ) MAY ( userPassword $ telephoneNumber $ seeAlso $ description ) ) objectclass ( 2.5.6.7 NAME 'organizationalPerson' DESC 'RFC2256: an organizational person' SUP person STRUCTURAL MAY ( title $ x121Address $ registeredAddress $ destinationIndicator $ preferredDeliveryMethod $ telexNumber $ teletexTerminalIdentifier $ telephoneNumber $ internationaliSDNNumber $ facsimileTelephoneNumber $ street $ postOfficeBox $ postalCode $ postalAddress $ physicalDeliveryOfficeName $ ou $ st $ l ) ) objectclass ( 2.5.6.8 NAME 'organizationalRole' DESC 'RFC2256: an organizational role' SUP top STRUCTURAL MUST cn MAY ( x121Address $ registeredAddress $ destinationIndicator $ preferredDeliveryMethod $ telexNumber $ teletexTerminalIdentifier $ telephoneNumber $ internationaliSDNNumber $ facsimileTelephoneNumber $ seeAlso $ roleOccupant $ preferredDeliveryMethod $ street $ postOfficeBox $ postalCode $ postalAddress $ physicalDeliveryOfficeName $ ou $ st $ l $ description ) ) objectclass ( 2.5.6.9 NAME 'groupOfNames' DESC 'RFC2256: a group of names (DNs)' SUP top STRUCTURAL MUST ( member $ cn ) MAY ( businessCategory $ seeAlso $ owner $ ou $ o $ description ) ) objectclass ( 2.5.6.10 NAME 'residentialPerson' DESC 'RFC2256: an residential person' SUP person STRUCTURAL MUST l MAY ( businessCategory $ x121Address $ registeredAddress $ destinationIndicator $ preferredDeliveryMethod $ telexNumber $ teletexTerminalIdentifier $ telephoneNumber $ internationaliSDNNumber $ facsimileTelephoneNumber $ preferredDeliveryMethod $ street $ postOfficeBox $ postalCode $ postalAddress $ physicalDeliveryOfficeName $ st $ l ) ) objectclass ( 2.5.6.11 NAME 'applicationProcess' DESC 'RFC2256: an application process' SUP top STRUCTURAL MUST cn MAY ( seeAlso $ ou $ l $ description ) ) objectclass ( 2.5.6.12 NAME 'applicationEntity' DESC 'RFC2256: an application entity' SUP top STRUCTURAL MUST ( presentationAddress $ cn ) MAY ( supportedApplicationContext $ seeAlso $ ou $ o $ l $ description ) ) objectclass ( 2.5.6.13 NAME 'dSA' DESC 'RFC2256: a directory system agent (a server)' SUP applicationEntity STRUCTURAL MAY knowledgeInformation ) objectclass ( 2.5.6.14 NAME 'device' DESC 'RFC2256: a device' SUP top STRUCTURAL MUST cn MAY ( serialNumber $ seeAlso $ owner $ ou $ o $ l $ description ) ) objectclass ( 2.5.6.15 NAME 'strongAuthenticationUser' DESC 'RFC2256: a strong authentication user' SUP top AUXILIARY MUST userCertificate ) objectclass ( 2.5.6.16 NAME 'certificationAuthority' DESC 'RFC2256: a certificate authority' SUP top AUXILIARY MUST ( authorityRevocationList $ certificateRevocationList $ cACertificate ) MAY crossCertificatePair ) objectclass ( 2.5.6.17 NAME 'groupOfUniqueNames' DESC 'RFC2256: a group of unique names (DN and Unique Identifier)' SUP top STRUCTURAL MUST ( uniqueMember $ cn ) MAY ( businessCategory $ seeAlso $ owner $ ou $ o $ description ) ) objectclass ( 2.5.6.18 NAME 'userSecurityInformation' DESC 'RFC2256: a user security information' SUP top AUXILIARY MAY ( supportedAlgorithms ) ) objectclass ( 2.5.6.16.2 NAME 'certificationAuthority-V2' SUP certificationAuthority AUXILIARY MAY ( deltaRevocationList ) ) objectclass ( 2.5.6.19 NAME 'cRLDistributionPoint' SUP top STRUCTURAL MUST ( cn ) MAY ( certificateRevocationList $ authorityRevocationList $ deltaRevocationList ) ) objectclass ( 2.5.6.20 NAME 'dmd' SUP top STRUCTURAL MUST ( dmdName ) MAY ( userPassword $ searchGuide $ seeAlso $ businessCategory $ x121Address $ registeredAddress $ destinationIndicator $ preferredDeliveryMethod $ telexNumber $ teletexTerminalIdentifier $ telephoneNumber $ internationaliSDNNumber $ facsimileTelephoneNumber $ street $ postOfficeBox $ postalCode $ postalAddress $ physicalDeliveryOfficeName $ st $ l $ description ) ) objectclass ( 2.5.6.21 NAME 'pkiUser' DESC 'RFC2587: a PKI user' SUP top AUXILIARY MAY userCertificate ) objectclass ( 2.5.6.22 NAME 'pkiCA' DESC 'RFC2587: PKI certificate authority' SUP top AUXILIARY MAY ( authorityRevocationList $ certificateRevocationList $ cACertificate $ crossCertificatePair ) ) objectclass ( 2.5.6.23 NAME 'deltaCRL' DESC 'RFC2587: PKI user' SUP top AUXILIARY MAY deltaRevocationList ) attributetype ( 1.3.6.1.4.1.250.1.57 NAME 'labeledURI' DESC 'RFC2079: Uniform Resource Identifier with optional label' EQUALITY caseExactMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 ) objectclass ( 1.3.6.1.4.1.250.3.15 NAME 'labeledURIObject' DESC 'RFC2079: object that contains the URI attribute type' MAY ( labeledURI ) SUP top AUXILIARY ) attributetype ( 0.9.2342.19200300.100.1.1 NAME ( 'uid' 'userid' ) DESC 'RFC1274: user identifier' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} ) attributetype ( 0.9.2342.19200300.100.1.3 NAME ( 'mail' 'rfc822Mailbox' ) DESC 'RFC1274: RFC822 Mailbox' EQUALITY caseIgnoreIA5Match SUBSTR caseIgnoreIA5SubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{256} ) objectclass ( 0.9.2342.19200300.100.4.19 NAME 'simpleSecurityObject' DESC 'RFC1274: simple security object' SUP top AUXILIARY MUST userPassword ) attributetype ( 0.9.2342.19200300.100.1.25 NAME ( 'dc' 'domainComponent' ) DESC 'RFC1274/2247: domain component' EQUALITY caseIgnoreIA5Match SUBSTR caseIgnoreIA5SubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE ) objectclass ( 1.3.6.1.4.1.1466.344 NAME 'dcObject' DESC 'RFC2247: domain component object' SUP top AUXILIARY MUST dc ) objectclass ( 1.3.6.1.1.3.1 NAME 'uidObject' DESC 'RFC2377: uid object' SUP top AUXILIARY MUST uid ) attributetype ( 0.9.2342.19200300.100.1.37 NAME 'associatedDomain' DESC 'RFC1274: domain associated with object' EQUALITY caseIgnoreIA5Match SUBSTR caseIgnoreIA5SubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 ) """ ldaptor-0.0.43/ldaptor/test/web/0000755000175000017500000000000010403234277014550 5ustar janjanldaptor-0.0.43/ldaptor/test/web/edit-simple.twill0000644000175000017500000000457510352514563020056 0ustar janjan# textareas reshown on forms with error get spurious \n prefixes and # suffixes with tidy config do_run_tidy 0 formvalue go baseDN ou=People,dc=example,dc=com submit code 200 title "Ldaptor Search Page" formvalue search search_Name jack submit code 200 title "Ldaptor Search Page" find "

1 entries matched\.

" follow /ou%3DPeople%2Cdc%3Dexample%2Cdc%3Dcom/edit/uid%3Djack%2Cou%3DPeople%2Cdc%3Dexample%2Cdc%3Dcom$ code 200 title "Login" formvalue login name jack formvalue login password secret submit code 200 title "Ldaptor Edit Page" notfind Exception notfind traceback find '\sNo error for error key: edit\s' find '' formvalue edit edit_cn 'Jack R. Black' submit code 200 title "Ldaptor Edit Page" notfind Exception notfind traceback find '\s*Edited uid=jack,ou=People,dc=example,dc=com successfully\. \[[^]]+\]

  • changing cn
    • remove \'Jack Black\'
    • add \'Jack R\. Black\'

\s*
' find '\sNo error for error key: edit\s' find '' follow Search code 200 title "Ldaptor Search Page" formvalue search search_Name 'Jack Black' submit code 200 title "Ldaptor Search Page" find '

0 entries matched\.

' formvalue search search_Name 'Jack R. Black' submit code 200 title "Ldaptor Search Page" find '

1 entries matched\.

' follow edit code 200 title "Ldaptor Edit Page" # put it back like it used to be formvalue edit edit_cn 'Jack Black' submit code 200 title "Ldaptor Edit Page" notfind Exception notfind traceback find '\s*Edited uid=jack,ou=People,dc=example,dc=com successfully\. \[[^]]+\]

  • changing cn
    • remove \'Jack R\. Black\'
    • add \'Jack Black\'

\s*
' find '\sNo error for error key: edit\s' find '' ldaptor-0.0.43/ldaptor/test/web/frontpage.twill0000644000175000017500000000040110403043506017576 0ustar janjantitle "Ldaptor Web Interface" formvalue go baseDN dc=example,dc=com submit code 200 title "Ldaptor Search Page" #TODO if possible, get rid of %3D and use just = instead url '/dc%3Dexample%2Cdc%3Dcom/search/$' find dc=example,dc=com notfind LDAPNoSuchObject ldaptor-0.0.43/ldaptor/test/web/search.twill0000644000175000017500000000207710350214673017077 0ustar janjanformvalue go baseDN dc=example,dc=com submit code 200 title "Ldaptor Search Page" formvalue search searchfilter '(objectClass=dcObject)' find '' formvalue search scope 0 submit code 200 find '

1 entries matched\.

' find '

Used filter \(objectClass=dcObject\)

' formvalue search searchfilter '' formvalue search search_Name 'j*' find '' formvalue search scope 2 submit code 200 find '

2 entries matched\.

' find '

Used filter \(\|\(cn=j\*\)\(uid=j\*\)\)

' ldaptor-0.0.43/ldaptor/test/web/add-delete.twill0000644000175000017500000000206710350243416017616 0ustar janjanformvalue go baseDN ou=People,dc=example,dc=com submit code 200 follow "add new entry" code 200 title "Login" formvalue login name jack formvalue login password secret submit code 200 title "Ldaptor Add Page" formvalue add structuralObjectClass account submit code 200 title "Ldaptor Add Page" formvalue add add_uid justfortest submit code 200 find 'Added uid=justfortest,ou=People,dc=example,dc=com successfully\.' follow Search code 200 title "Ldaptor Search Page" formvalue search search_Name justfortest submit code 200 find "

1 entries matched\.

" follow /ou%3DPeople%2Cdc%3Dexample%2Cdc%3Dcom/delete/uid%3Djustfortest%2Cou%3DPeople%2Cdc%3Dexample%2Cdc%3Dcom$ code 200 title "Ldaptor Delete Page" find "

Remove uid=justfortest,ou=People,dc=example,dc=com\?

" formvalue delete delete dummy submit code 200 title "Ldaptor Search Page" find '

Deleted uid=justfortest,ou=People,dc=example,dc=com\.

' formvalue search search_Name justfortest submit code 200 find "

0 entries matched\.

" ldaptor-0.0.43/ldaptor/test/web/edit-missingMust.twill0000644000175000017500000000335310352532370021074 0ustar janjan# textareas reshown on forms with error get spurious \n prefixes and # suffixes with tidy config do_run_tidy 0 formvalue go baseDN ou=People,dc=example,dc=com submit code 200 title "Ldaptor Search Page" formvalue search search_Name missing-must-fields submit code 200 title "Ldaptor Search Page" find "

1 entries matched\.

" follow /ou%3DPeople%2Cdc%3Dexample%2Cdc%3Dcom/edit/cn%3Dmissing-must-fields%2Cou%3DPeople%2Cdc%3Dexample%2Cdc%3Dcom$ code 200 title "Login" formvalue login name jack formvalue login password secret submit code 200 title "Ldaptor Edit Page" notfind Exception notfind traceback find '' notfind '' formvalue edit edit dummy submit code 200 title "Ldaptor Edit Page" notfind 'No changes!' # no status message at all find '\s*\s*' # sn field gives error find '
Error:
' find '
Please enter a string.
' formvalue edit edit_sn Thelastname submit code 200 title "Ldaptor Edit Page" notfind 'No changes!' find '\s*Edited cn=missing-must-fields,ou=People,dc=example,dc=com successfully. \[[^]]+\]

  • changing sn
    • add \'Thelastname\'

\s*
' ldaptor-0.0.43/ldaptor/test/web/add-move.twill0000644000175000017500000000306310350243416017317 0ustar janjanformvalue go baseDN ou=People,dc=example,dc=com submit code 200 follow "add new entry" code 200 title "Login" formvalue login name jack formvalue login password secret submit code 200 title "Ldaptor Add Page" formvalue add structuralObjectClass account submit code 200 title "Ldaptor Add Page" formvalue add add_uid justfortest submit code 200 find 'Added uid=justfortest,ou=People,dc=example,dc=com successfully\.' follow Search code 200 title "Ldaptor Search Page" formvalue search search_Name justfortest submit code 200 find "

1 entries matched\.

" follow /ou%3DPeople%2Cdc%3Dexample%2Cdc%3Dcom/move/uid%3Djustfortest%2Cou%3DPeople%2Cdc%3Dexample%2Cdc%3Dcom$ code 200 title "Ldaptor Search Page" find '
' find 'uid=justfortest,ou=People,dc=example,dc=com' follow ^\.\./\.\./dc=example,dc=com$ code 200 formvalue move move dummy submit code 200 title "Ldaptor Search Page" find "

Moved uid=justfortest,ou=People,dc=example,dc=com to uid=justfortest,dc=example,dc=com\.

" formvalue search search_Name justfortest submit code 200 find '

1 entries matched\.

' follow /dc%3Dexample%2Cdc%3Dcom/delete/uid%3Djustfortest%2Cdc%3Dexample%2Cdc%3Dcom$ code 200 title "Ldaptor Delete Page" find "

Remove uid=justfortest,dc=example,dc=com\?

" formvalue delete delete dummy submit code 200 title "Ldaptor Search Page" find '

Deleted uid=justfortest,dc=example,dc=com\.

' formvalue search search_Name justfortest submit code 200 find "

0 entries matched\.

" ldaptor-0.0.43/ldaptor/test/web/add-fail-missingRequiredFields.twill0000644000175000017500000000127710350215020023556 0ustar janjanformvalue go baseDN ou=People,dc=example,dc=com submit code 200 follow "add new entry" code 200 title "Login" formvalue login name jack formvalue login password secret submit code 200 title "Ldaptor Add Page" formvalue add structuralObjectClass account formvalue add auxiliary_posixAccount True submit code 200 title "Ldaptor Add Page" # required fields for posixAccount are enforced formvalue add add_uid justfortest submit code 200 find '
Error:
' find '
Please enter a string\.
' ldaptor-0.0.43/ldaptor/test/web/delete-fail-nodn.twill0000644000175000017500000000040510350243416020727 0ustar janjango dc=example,dc=com/delete code 200 title "Login" formvalue login name jack formvalue login password secret submit code 200 title 'Ldaptor Delete Page' find 'Missing DN to delete\.' follow 'search page' code 200 title 'Ldaptor Search Page' ldaptor-0.0.43/ldaptor/test/web/frontpage-baddn.twill0000644000175000017500000000117310350243416020656 0ustar janjantitle "Ldaptor Web Interface" formvalue go baseDN junk submit code 200 title "Ldaptor Web Interface" find '
\'junk\' is not a valid LDAP DN: Invalid relative distinguished name \'junk\'\.
' go 'junk' code 200 title "Ldaptor Web Interface" find '' find 'No error for error key: go' find 'No error for error key: go\.baseDN' url ^http://localhost:\d+/$ ldaptor-0.0.43/ldaptor/test/web/add-delete-lookingAtRemovedEntry.twill0000644000175000017500000000242210352503422024100 0ustar janjanformvalue go baseDN ou=People,dc=example,dc=com submit code 200 follow "add new entry" code 200 title "Login" formvalue login name jack formvalue login password secret submit code 200 title "Ldaptor.Add.Page" formvalue add structuralObjectClass account submit code 200 title "Ldaptor Add Page" formvalue add add_uid justfortest submit code 200 find 'Added uid=justfortest,ou=People,dc=example,dc=com successfully\.' follow Search code 200 title "Ldaptor Search Page" formvalue search search_Name justfortest submit code 200 find "

1 entries matched\.

" follow /uid%3Djustfortest%2Cou%3DPeople%2Cdc%3Dexample%2Cdc%3Dcom/search/$ code 200 title "Ldaptor Search Page" follow /uid%3Djustfortest%2Cou%3DPeople%2Cdc%3Dexample%2Cdc%3Dcom/delete/uid%3Djustfortest%2Cou%3DPeople%2Cdc%3Dexample%2Cdc%3Dcom$ code 200 title "Ldaptor Delete Page" find "

Remove uid=justfortest,ou=People,dc=example,dc=com\?

" formvalue delete delete dummy submit code 200 title "Ldaptor Search Page" find '

Deleted uid=justfortest,ou=People,dc=example,dc=com\.

' url /ou%3DPeople%2Cdc%3Dexample%2Cdc%3Dcom/search/(\?_nevow_carryover_=\d+(\.\d+)*)?$ formvalue search search_Name justfortest submit code 200 find "

0 entries matched\.

" ldaptor-0.0.43/ldaptor/test/web/search-all.twill0000644000175000017500000000035110370122041017623 0ustar janjanformvalue go baseDN dc=example,dc=com submit code 200 title "Ldaptor Search Page" formvalue search search dummy submit code 200 find '

5 entries matched\.

' find '

Used filter \(objectClass=\*\)

' ldaptor-0.0.43/ldaptor/test/web/delete-fail-ldaperror.twill0000644000175000017500000000103110350256624021764 0ustar janjango dc=example,dc=com/delete/dc=example,dc=com code 200 title "Login" formvalue login name jack formvalue login password secret submit code 200 # Make sure the authentication worked title "Ldaptor Delete Page" find "

Remove dc=example,dc=com\?

" go cn=does-not-exist,dc=example,dc=com code 200 title "Ldaptor Delete Page" find '

Remove cn=does-not-exist,dc=example,dc=com\?

' find '
An error occurred: noSuchObject
' notfind Exception notfind traceback ldaptor-0.0.43/ldaptor/test/web/add-fail-missingRequiredFields-retryWorks.twill0000644000175000017500000000455010350243416025756 0ustar janjan# textareas reshown on forms with error get spurious \n prefixes and # suffixes with tidy config do_run_tidy 0 formvalue go baseDN ou=People,dc=example,dc=com submit code 200 follow "add new entry" code 200 title "Login" formvalue login name jack formvalue login password secret submit code 200 title "Ldaptor Add Page" formvalue add structuralObjectClass account formvalue add auxiliary_posixAccount True submit code 200 title "Ldaptor Add Page" # required fields for posixAccount are enforced formvalue add add_uid justfortest submit code 200 find '
Error:
' find '
Please enter a string\.
' find '
Please enter a string\.
' find '' find 'No error for error key: add\.add_uid' # we purposefully do not re-set add_uid here, as # letting it preserve the old form value triggers # a bug where the value was prefixed and suffixed # with a newline. still unclear whether the bug is # in twill or in ldaptor formvalue add add_cn 'Just For Test' formvalue add add_homeDirectory /home/testusers/justfortest submit code 200 notfind '
Please enter a string\.
' find 'Added uid=justfortest,ou=People,dc=example,dc=com successfully\.' follow Search code 200 title "Ldaptor Search Page" formvalue search search_Name justfortest submit code 200 find "

1 entries matched\.

" follow /ou%3DPeople%2Cdc%3Dexample%2Cdc%3Dcom/delete/uid%3Djustfortest%2Cou%3DPeople%2Cdc%3Dexample%2Cdc%3Dcom$ code 200 title "Ldaptor Delete Page" find "

Remove uid=justfortest,ou=People,dc=example,dc=com\?

" formvalue delete delete dummy submit code 200 title "Ldaptor Search Page" find '

Deleted uid=justfortest,ou=People,dc=example,dc=com\.

' formvalue search search_Name justfortest submit code 200 find "

0 entries matched\.

" ldaptor-0.0.43/ldaptor/test/web/delete-nonleaf.twill0000644000175000017500000000111110350214673020500 0ustar janjanformvalue go baseDN ou=People,dc=example,dc=com submit code 200 follow /ou%3DPeople%2Cdc%3Dexample%2Cdc%3Dcom/delete/ou%3DPeople%2Cdc%3Dexample%2Cdc%3Dcom$ code 200 title "Login" formvalue login name jack formvalue login password secret submit code 200 title "Ldaptor Delete Page" find "

Remove ou=People,dc=example,dc=com\?

" formvalue delete delete dummy submit code 200 # we do not want to get redirected away on error title "Ldaptor Delete Page" find '
Failed: notAllowedOnNonLeaf: Cannot remove entry with children: ou=People,dc=example,dc=com\.
' ldaptor-0.0.43/ldaptor/test/web/password-simple.twill0000644000175000017500000000157710365664731021001 0ustar janjan# textareas reshown on forms with error get spurious \n prefixes and # suffixes with tidy config do_run_tidy 0 formvalue go baseDN ou=People,dc=example,dc=com submit code 200 title "Ldaptor Search Page" formvalue search search_Name "jack" submit code 200 title "Ldaptor Search Page" find "

1 entries matched\.

" follow '/ou%3DPeople%2Cdc%3Dexample%2Cdc%3Dcom/change_password/uid%3Djack%2Cou%3DPeople%2Cdc%3Dexample%2Cdc%3Dcom$' code 200 title "Login" formvalue login name jack formvalue login password secret submit code 200 title "Ldaptor Password Change Page" find '

\s*About to set password for\s+uid=jack,ou=People,dc=example,dc=com.\s*

' formvalue setPassword newPassword hushhush formvalue setPassword again hushhush submit code 200 title "Ldaptor Password Change Page" notfind 'Failed' find '\s*Password set\.\s*' ldaptor-0.0.43/ldaptor/test/web/edit-textarea-emptiness.twill0000644000175000017500000000233610352530664022400 0ustar janjan### empty textareas don't suddenly get "" as their value # textareas reshown on forms with error get spurious \n prefixes and # suffixes with tidy config do_run_tidy 0 formvalue go baseDN ou=People,dc=example,dc=com submit code 200 title "Ldaptor Search Page" formvalue search search_Name 'John Smith' submit code 200 title "Ldaptor Search Page" find "

1 entries matched\.

" follow /ou%3DPeople%2Cdc%3Dexample%2Cdc%3Dcom/edit/cn%3DJohn%20Smith%2Cou%3DPeople%2Cdc%3Dexample%2Cdc%3Dcom$ code 200 title "Login" formvalue login name jack formvalue login password secret submit code 200 title "Ldaptor Edit Page" notfind Exception notfind traceback formvalue edit edit dummy submit code 200 title "Ldaptor Edit Page" notfind '\s*Edited cn=John Smith,ou=People,dc=example,dc=com successfully. \[[^]]+\]

  • changing description
    • add \'\'

\s*
' find '\s*Edited cn=John Smith,ou=People,dc=example,dc=com successfully. \[[^]]+\]

No changes!

\s*
' ldaptor-0.0.43/ldaptor/test/web/search-broken-filter.twill0000644000175000017500000000042610350214673021634 0ustar janjanformvalue go baseDN dc=example,dc=com submit code 200 title "Ldaptor Search Page" formvalue search searchfilter broke submit code 200 notfind Exception notfind InvalidLDAPFilter find '
Invalid LDAP filter: Expected "\(" at point 0 in \'broke\'
' ldaptor-0.0.43/ldaptor/test/web/password-simple-others.twill0000644000175000017500000000173210370122117022253 0ustar janjan# textareas reshown on forms with error get spurious \n prefixes and # suffixes with tidy config do_run_tidy 0 formvalue go baseDN ou=People,dc=example,dc=com submit code 200 title "Ldaptor Search Page" formvalue search search_Name "John Smith" submit code 200 title "Ldaptor Search Page" find "

1 entries matched\.

" follow '/ou%3DPeople%2Cdc%3Dexample%2Cdc%3Dcom/change_password/cn%3DJohn%20Smith%2Cou%3DPeople%2Cdc%3Dexample%2Cdc%3Dcom$' code 200 title "Login" formvalue login name jack formvalue login password secret submit code 200 title "Ldaptor Password Change Page" find '

\s*About to set password for\s+cn=John Smith,ou=People,dc=example,dc=com.\s*

' formvalue setPassword newPassword hushhush formvalue setPassword again hushhush submit code 200 title "Ldaptor Password Change Page" find 'Failed: Some of the password plugins failed: ExtendedOperation failed with insufficientAccessRights; Samba failed with Aborted.' ldaptor-0.0.43/ldaptor/test/web/delete-fail-baddn.twill0000644000175000017500000000044010350256624021045 0ustar janjango dc=example,dc=com/delete/dc=example,dc=com code 200 title "Login" formvalue login name jack formvalue login password secret submit code 200 # Make sure the authentication worked title "Ldaptor Delete Page" find "

Remove dc=example,dc=com\?

" go invaliddn code 404 ldaptor-0.0.43/ldaptor/test/web/add-posixaccount.twill0000644000175000017500000000270110350243416021066 0ustar janjanformvalue go baseDN ou=People,dc=example,dc=com submit code 200 follow "add new entry" code 200 title "Login" formvalue login name jack formvalue login password secret submit code 200 title "Ldaptor Add Page" formvalue add structuralObjectClass account formvalue add auxiliary_posixAccount True submit code 200 title "Ldaptor Add Page" formvalue add add_uid justfortest formvalue add add_homeDirectory /home/testusers/justfortest submit code 200 find 'Added uid=justfortest,ou=People,dc=example,dc=com successfully\.' follow Search code 200 title "Ldaptor Search Page" formvalue search search_Name justfortest submit code 200 find "

1 entries matched\.

" find "
  • \s*gidNumber:\s*
    • 1000
  • " find "
  • \s*uidNumber:\s*
    • 1000
  • " find "
  • \s*uid:\s*
    • justfortest
  • " find "
  • \s*homeDirectory:\s*
    • /home/testusers/justfortest
  • " follow /ou%3DPeople%2Cdc%3Dexample%2Cdc%3Dcom/delete/uid%3Djustfortest%2Cou%3DPeople%2Cdc%3Dexample%2Cdc%3Dcom$ code 200 title "Ldaptor Delete Page" find "

    Remove uid=justfortest,ou=People,dc=example,dc=com\?

    " formvalue delete delete dummy submit code 200 title "Ldaptor Search Page" find '

    Deleted uid=justfortest,ou=People,dc=example,dc=com\.

    ' formvalue search search_Name justfortest submit code 200 find "

    0 entries matched\.

    " ldaptor-0.0.43/ldaptor/test/web/move-cancel.twill0000644000175000017500000000202510352503422020007 0ustar janjanformvalue go baseDN ou=People,dc=example,dc=com submit code 200 title "Ldaptor Search Page" notfind '
    ' formvalue search search_Name "john smith" submit code 200 title "Ldaptor Search Page" find '

    1 entries matched\.

    ' follow /ou%3DPeople%2Cdc%3Dexample%2Cdc%3Dcom/move/cn%3DJohn%20Smith%2Cou%3DPeople%2Cdc%3Dexample%2Cdc%3Dcom$ code 200 title "Login" formvalue login name jack formvalue login password secret submit code 200 title "Ldaptor Search Page" find '
    \s*
    1. \s*cn=John Smith,ou=People,dc=example,dc=com\s' formvalue cancel cancel dummy submit code 200 title "Ldaptor Search Page" find '

      Cancelled move of cn=John Smith,ou=People,dc=example,dc=com

      ' notfind '
      ' formvalue search search_Name "john smith" submit code 200 title "Ldaptor Search Page" find '

      1 entries matched\.

      ' find 'cn=John Smith,ou=People,dc=example,dc=com\s' ldaptor-0.0.43/ldaptor/test/test_pureldap.py0000644000175000017500000005310010365653714017227 0ustar janjan# Ldaptor -- TODO # Copyright (C) 2001 Matthew W. Lefkowitz # # This library is free software; you can redistribute it and/or # modify it under the terms of version 2.1 of the GNU Lesser General Public # License as published by the Free Software Foundation. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA """ Test cases for ldaptor.protocols.pureldap module. """ from twisted.trial import unittest from ldaptor.protocols import pureldap, pureber import types def s(*l): """Join all members of list to a string. Integer members are chr()ed""" r='' for e in l: if isinstance(e, types.IntType): e=chr(e) r=r+str(e) return r def l(s): """Split a string to ord's of chars.""" return map(lambda x: ord(x), s) 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, '')) class TestEquality(unittest.TestCase): valuesToTest=( (pureldap.LDAPFilter_equalityMatch, [ pureldap.LDAPAttributeDescription(value='cn'), pureldap.LDAPAssertionValue(value='foo'), ]), (pureldap.LDAPFilter_equalityMatch, [ pureldap.LDAPAttributeDescription(value='cn'), pureldap.LDAPAssertionValue(value='bar'), ]), (pureber.BERInteger, [0]), ) def testEquality(self): """LDAP objects equal LDAP objects with same type and content""" for class_, args in self.valuesToTest: x=class_(*args) y=class_(*args) self.assertEquals(x, x) self.assertEquals(x, y) def testInEquality(self): """LDAP objects do not equal LDAP objects with different type or content""" for i in xrange(len(self.valuesToTest)): for j in xrange(len(self.valuesToTest)): if i!=j: i_class, i_args = self.valuesToTest[i] j_class, j_args = self.valuesToTest[j] x=i_class(*i_args) y=j_class(*j_args) self.assertNotEquals(x, y) class Substrings(unittest.TestCase): 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) ldaptor-0.0.43/ldaptor/test/test_webui.py0000644000175000017500000004304510402650176016524 0ustar janjanfrom twisted.trial import unittest import urllib, string from twisted.internet import address, protocol, defer from twisted.python import components from twisted.web import microdom from nevow import appserver, tags from webut.skin import skin from ldaptor import inmemory, interfaces, config from ldaptor.protocols.ldap import ldapserver from ldaptor.apps.webui import main, defskin from ldaptor.test import mockweb, util def getTextContents(node): s=u'' for text in node.childNodes: assert (isinstance(text, microdom.Text) or isinstance(text, microdom.EntityReference)) if isinstance(text, microdom.Text): s += text.toxml() elif isinstance(text, microdom.EntityReference): if text.eref.startswith('#x'): n = int(text.eref[len('#x'):], 16) s += unichr(n) else: s += text.toxml() else: raise RuntimeError, 'TODO' return s class MockLDAPConfig(config.LDAPConfig): def _loadServiceLocationOverrides(self): return {} class SiteMixin: def setUp(self): db = inmemory.ReadOnlyInMemoryLDAPEntry('') db.addChild('cn=schema', {'objectClass': ['TODO'], 'cn': ['schema'], 'attributeTypes': [ """( 0.9.2342.19200300.100.1.25 NAME ( 'dc' 'domainComponent' ) DESC 'RFC1274/2247: domain component' EQUALITY caseIgnoreIA5Match SUBSTR caseIgnoreIA5SubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )""", """( 2.5.4.0 NAME 'objectClass' DESC 'RFC2256: object classes of the entity' EQUALITY objectIdentifierMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 )""", """( 2.5.4.4 NAME ( 'sn' 'surname' ) DESC 'RFC2256: last (family) name(s) for which the entity is known by' SUP name )""", """( 2.5.4.3 NAME ( 'cn' 'commonName' ) DESC 'RFC2256: common name(s) for which the entity is known by' SUP name )""", """( 2.5.4.35 NAME 'userPassword' DESC 'RFC2256/2307: password of user' EQUALITY octetStringMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.40{128} )""", """( 2.5.4.20 NAME 'telephoneNumber' DESC 'RFC2256: Telephone Number' EQUALITY telephoneNumberMatch SUBSTR telephoneNumberSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.50{32} )""", """( 2.5.4.34 NAME 'seeAlso' DESC 'RFC2256: DN of related object' SUP distinguishedName )""", """( 2.5.4.13 NAME 'description' DESC 'RFC2256: descriptive information' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{1024} )""", ], 'objectClasses': [ """( 2.5.6.0 NAME 'top' DESC 'top of the superclass chain' ABSTRACT MUST objectClass )""", """( 1.3.6.1.4.1.1466.344 NAME 'dcObject' DESC 'RFC2247: domain component object' SUP top AUXILIARY MUST dc )""", """( 2.5.6.6 NAME 'person' DESC 'RFC2256: a person' SUP top STRUCTURAL MUST ( sn $ cn ) MAY ( userPassword $ telephoneNumber $ seeAlso $ description ) )""", ], }) self.com = db.addChild('dc=com', {}) self.example = self.com.addChild('dc=example', {'objectClass': ['dcObject'], 'dc': ['example'], 'subschemaSubentry': ['cn=schema'], }) self.foo = self.example.addChild('uid=foo', {'objectClass': ['person'], 'uid': ['foo'], 'cn': ['Foo Bar'], 'sn': ['Bar'], 'userPassword': ['{SSHA}1feEJLgP7OB5mUKU/fYJzBoAGlOrze8='], # "foo" 'subschemaSubentry': ['cn=schema'], }) class LDAPServerFactory(protocol.ServerFactory): protocol = ldapserver.LDAPServer def __init__(self, root): self.root = root components.registerAdapter(lambda x: x.root, LDAPServerFactory, interfaces.IConnectedLDAPEntry) serverFactory = LDAPServerFactory(db) def _doConnect(factory): factory.doStart() client = factory.buildProtocol(address.IPv4Address('TCP', 'localhost', '389')) server = serverFactory.buildProtocol(address.IPv4Address('TCP', 'localhost', '1024')) util.returnConnected(server, client) cfg = MockLDAPConfig(baseDN='dc=example,dc=com', serviceLocationOverrides={'': _doConnect}, identityBaseDN='dc=example,dc=com',) class TestSkin(defskin.DefaultSkin): def render_head(self, ctx, data): d = defer.maybeDeferred(super(TestSkin, self).render_head, ctx, data) def cb(stan): return stan[tags.comment['kilroy was here']] d.addCallback(cb) return d self.root = main.getResource(cfg, skinFactory=TestSkin) self.site = appserver.NevowSite(self.root) self.site.startFactory() def tearDown(self): assert isinstance(self.root, skin.Skinner) for name, sess in self.root.resource.sessions.items(): sess.expire() self.site.stopFactory() def getPage(self, url, cookies, *a, **kw): parse = kw.pop('parse', True) if cookies: getter = mockweb.getPage else: getter = mockweb.getPage_noCookies kw['extraInfo'] = True d = getter(self.site, url, *a, **kw) data = util.pumpingDeferredResult(d) if parse: tree = microdom.parseString(data['page'], beExtremelyLenient=True) assert 'tree' not in data data['tree'] = tree title = data['tree'].getElementsByTagName('title')[0] assert 'title' not in data data['title'] = getTextContents(title) return data class TestCSS(SiteMixin, unittest.TestCase): urls = [ 'http://localhost/', 'http://localhost/dc=example,dc=com/', 'http://localhost/dc=example,dc=com/search', 'http://localhost/dc=example,dc=com/search/', 'http://localhost/dc=example,dc=com/edit/', # to test login ] def _checkResults(self, data, cookies): # while we're here, make sure the skin system seems to work self.assertIn('', data['page']) self.assertNotIn('Title Goes Here', data['page']) head = data['tree'].getElementsByTagName('head') assert len(head) == 1, \ "Expected exactly one element, got %r" % head links = head[0].getElementsByTagName('link') for link in links: if link.getAttribute('rel') == 'stylesheet': href = link.getAttribute('href') u = data['url'].clear().click(href) self.assertEquals(u.scheme, 'http') self.assertEquals(u.netloc, 'localhost') self.assertEquals(u.queryList(), []) l = u.pathList() self.failUnless(l[-1].endswith('.css'), "url %s has invalid CSS extension" % data['url']) basename = l[-1][:-len('.css')] self.failUnless(len(basename) >= 1) for c in basename: self.failUnless(c in string.ascii_lowercase, "url %s has invalid character %r in CSS reference %r" % (data['url'], c, l[-1])) cssData = self.getPage(u, cookies, followRedirect=False, parse=False) self.assertEquals(cssData['status'], '200', "CSS files must not be without a guard session") def checkPage(self, url, cookies): data = self.getPage(url, cookies) self._checkResults(data, cookies) def test_form_css(self): for u in self.urls: self.checkPage(u, cookies=True) def test_form_css_noCookies(self): for u in self.urls: self.checkPage(u, cookies=False) class TestAuthenticatedCSS(TestCSS): urls = [ 'http://localhost/dc=example,dc=com/edit', 'http://localhost/dc=example,dc=com/edit/', 'http://localhost/dc=example,dc=com/edit/uid=foo,dc=example,dc=com', 'http://localhost/dc=example,dc=com/add', 'http://localhost/dc=example,dc=com/add/', 'http://localhost/dc=example,dc=com/add/manual/dcObject', 'http://localhost/dc=example,dc=com/change_password', 'http://localhost/dc=example,dc=com/change_password/', 'http://localhost/dc=example,dc=com/change_password/uid=foo,dc=example,dc=com', 'http://localhost/dc=example,dc=com/mass_change_password', 'http://localhost/dc=example,dc=com/mass_change_password/', 'http://localhost/dc=example,dc=com/mass_change_password/(uid=foo)', 'http://localhost/dc=example,dc=com/delete', 'http://localhost/dc=example,dc=com/delete/', 'http://localhost/dc=example,dc=com/delete/uid=foo,dc=example,dc=com', 'http://localhost/dc=example,dc=com/move', 'http://localhost/dc=example,dc=com/move/', 'http://localhost/dc=example,dc=com/move/uid=foo,dc=example,dc=com', ] def checkPage(self, url, cookies): data = self.getPage(url, cookies) self.assertEquals(data['title'], 'Login') # fill form, submit forms = data['tree'].getElementsByTagName('form') self.assertEquals(len(forms), 1) form = forms[0] self.assertEquals(form.getAttribute('enctype', 'application/x-www-form-urlencoded'), 'application/x-www-form-urlencoded') data = self.getPage(data['url'].clear().click(form.getAttribute('action')), cookies, method=form.getAttribute('method', 'get').upper(), headers={'Content-Type': 'application/x-www-form-urlencoded'}, postdata='&'.join(['%s=%s' % (urllib.quote('username'), urllib.quote('foo')), '%s=%s' % (urllib.quote('password'), urllib.quote('foo')), ]), ) self._checkResults(data, cookies) class TestAuthentication(SiteMixin, unittest.TestCase): def test_ensureBind(self): self.failUnless(self.foo.bind('foo')) def checkPage(self, url, cookies): data = self.getPage(url, cookies) self.assertEquals(data['title'], 'Login') # fill form, submit forms = data['tree'].getElementsByTagName('form') self.assertEquals(len(forms), 1) form = forms[0] self.assertEquals(form.getAttribute('enctype', 'application/x-www-form-urlencoded'), 'application/x-www-form-urlencoded') data = self.getPage(data['url'].clear().click(form.getAttribute('action')), cookies, method=form.getAttribute('method', 'get').upper(), headers={'Content-Type': 'application/x-www-form-urlencoded'}, postdata='&'.join(['%s=%s' % (urllib.quote('username'), urllib.quote('foo')), '%s=%s' % (urllib.quote('password'), urllib.quote('foo')), ]), ) return data def test_edit(self): data = self.checkPage('http://localhost/dc=example,dc=com/edit/dc=example,dc=com', cookies=True) self.assertEquals(data['title'], u'Ldaptor Edit Page') def test_edit_noCookies(self): data = self.checkPage('http://localhost/dc=example,dc=com/edit/dc=example,dc=com', cookies=False) self.assertEquals(data['title'], u'Ldaptor Edit Page') def test_move(self): data = self.checkPage('http://localhost/dc=example,dc=com/move', cookies=True) self.assertEquals(data['title'], u'Ldaptor Move Page') def test_move_noCookies(self): data = self.checkPage('http://localhost/dc=example,dc=com/move', cookies=False) self.assertEquals(data['title'], u'Ldaptor Move Page') def test_add(self): data = self.checkPage('http://localhost/dc=example,dc=com/add', cookies=True) self.assertEquals(data['title'], u'Ldaptor Add Page') def test_add_noCookies(self): data = self.checkPage('http://localhost/dc=example,dc=com/add', cookies=False) self.assertEquals(data['title'], u'Ldaptor Add Page') def test_delete(self): data = self.checkPage('http://localhost/dc=example,dc=com/delete/dc=example,dc=com', cookies=True) self.assertEquals(data['title'], u'Ldaptor Delete Page') def test_delete_noCookies(self): data = self.checkPage('http://localhost/dc=example,dc=com/delete/dc=example,dc=com', cookies=False) self.assertEquals(data['title'], u'Ldaptor Delete Page') def test_mass_change_password(self): data = self.checkPage('http://localhost/dc=example,dc=com/mass_change_password', cookies=True) self.assertEquals(data['title'], u'Ldaptor Mass Password Change Page') def test_mass_change_password_noCookies(self): data = self.checkPage('http://localhost/dc=example,dc=com/mass_change_password', cookies=False) self.assertEquals(data['title'], u'Ldaptor Mass Password Change Page') def test_change_password(self): data = self.checkPage('http://localhost/dc=example,dc=com/change_password', cookies=True) self.assertEquals(data['title'], u'Ldaptor Password Change Page') def test_change_password_noCookies(self): data = self.checkPage('http://localhost/dc=example,dc=com/change_password', cookies=False) self.assertEquals(data['title'], u'Ldaptor Password Change Page') class TestDelete(SiteMixin, unittest.TestCase): def checkPage(self, url, cookies): data = self.getPage(url, cookies) self.assertEquals(data['title'], 'Login') # fill form, submit forms = data['tree'].getElementsByTagName('form') self.assertEquals(len(forms), 1) form = forms[0] self.assertEquals(form.getAttribute('enctype', 'application/x-www-form-urlencoded'), 'application/x-www-form-urlencoded') data = self.getPage(data['url'].clear().click(form.getAttribute('action')), cookies, method=form.getAttribute('method', 'get').upper(), headers={'Content-Type': 'application/x-www-form-urlencoded'}, postdata='&'.join(['%s=%s' % (urllib.quote('username'), urllib.quote('foo')), '%s=%s' % (urllib.quote('password'), urllib.quote('foo')), ]), ) return data def test_nonExisting(self): data = self.checkPage('http://localhost/dc=example,dc=com/delete/uid=bar,dc=example,dc=com', cookies=True) self.assertEquals(data['title'], u'Ldaptor Delete Page') self.failUnless('An error occurred' in data['page']) self.failUnless('noSuchObject' in data['page']) def test_existing(self): # TODO cookies don't work because there's nothing that would carry over their state data = self.checkPage('http://localhost/dc=example,dc=com/delete/uid=foo,dc=example,dc=com', cookies=False) self.assertEquals(data['title'], u'Ldaptor Delete Page') self.failUnless('

      Remove uid=foo,dc=example,dc=com?

      ' in data['page']) # fill form, submit forms = data['tree'].getElementsByTagName('form') self.assertEquals(len(forms), 1) form = forms[0] # TODO support multipart/form-data, that's what the form tells us to use ## self.assertEquals(form.getAttribute('enctype', 'application/x-www-form-urlencoded'), ## 'application/x-www-form-urlencoded') action = data['url'].clear().click(form.getAttribute('action')) data = self.getPage(action, cookies=False, method=form.getAttribute('method', 'get').upper(), headers={'Content-Type': 'application/x-www-form-urlencoded'}, ) self.assertEquals(data['title'], 'Ldaptor Search Page') self.failUnless('Deleted uid=foo,dc=example,dc=com.' in data['page']) d = self.example.children() children = util.pumpingDeferredResult(d) self.assertEquals(children, []) ldaptor-0.0.43/ldaptor/test/test_fetchschema.py0000644000175000017500000000530210345261504017654 0ustar janjan""" Test cases for ldaptor.protocols.ldap.fetchschema module. """ from twisted.trial import unittest from ldaptor.protocols.ldap import fetchschema from ldaptor import schema from ldaptor.protocols import pureldap from ldaptor.testutil import LDAPClientTestDriver class OnWire(unittest.TestCase): cn = """( 2.5.4.3 NAME ( 'cn' 'commonName' ) DESC 'RFC2256: common name(s) for which the entity is known by' SUP name )""" dcObject = """( 1.3.6.1.4.1.1466.344 NAME 'dcObject' DESC 'RFC2247: domain component object' SUP top AUXILIARY MUST dc )""" def testSimple(self): client=LDAPClientTestDriver([ pureldap.LDAPSearchResultEntry( objectName='', attributes=(('subschemaSubentry', ['cn=Subschema']), ('bar', ['b', 'c']), ), ), pureldap.LDAPSearchResultDone( resultCode=0, matchedDN='', errorMessage='') ], [ pureldap.LDAPSearchResultEntry( objectName='cn=Subschema', attributes=(('attributeTypes', [ self.cn ]), ('objectClasses', [ self.dcObject ]), ), ), pureldap.LDAPSearchResultDone( resultCode=0, matchedDN='', errorMessage='') ], ) d=fetchschema.fetch(client, 'dc=example,dc=com') d.addCallback(self._cb_testSimple, client) return d def _cb_testSimple(self, val, client): client.assertSent(pureldap.LDAPSearchRequest( baseObject='dc=example,dc=com', scope=pureldap.LDAP_SCOPE_baseObject, derefAliases=pureldap.LDAP_DEREF_neverDerefAliases, sizeLimit=1, timeLimit=0, typesOnly=0, filter=pureldap.LDAPFilter_present('objectClass'), attributes=['subschemaSubentry']), pureldap.LDAPSearchRequest( baseObject='cn=Subschema', scope=pureldap.LDAP_SCOPE_baseObject, derefAliases=pureldap.LDAP_DEREF_neverDerefAliases, sizeLimit=1, timeLimit=0, typesOnly=0, filter=pureldap.LDAPFilter_present('objectClass'), attributes=['attributeTypes', 'objectClasses']), ) self.failUnlessEqual(len(val), 2) self.failUnlessEqual([str(x) for x in val[0]], [str(schema.AttributeTypeDescription(self.cn))]) self.failUnlessEqual([str(x) for x in val[1]], [str(schema.ObjectClassDescription(self.dcObject))]) ldaptor-0.0.43/ldaptor/test/ldif/0000755000175000017500000000000010403234303014677 5ustar janjanldaptor-0.0.43/ldaptor/test/ldif/webtests/0000755000175000017500000000000010403234304016540 5ustar janjanldaptor-0.0.43/ldaptor/test/ldif/webtests/cn=Subschema.ldif0000644000175000017500000012022410345773672021756 0ustar janjan# TODO must only be found with # ldapsearch -x -b cn=Subschema -s base '(objectClass=subschema)' attributeTypes # TODO provide createTimestamp modifyTimestamp dn: cn=Subschema objectClass: top objectClass: subschema attributeTypes: ( 2.16.840.1.113730.3.1.216 NAME 'userPKCS12' DESC 'RFC2798: p ersonal identity information, a PKCS #12 PFX' SYNTAX 1.3.6.1.4.1.1466.115.121 .1.5 ) attributeTypes: ( 2.16.840.1.113730.3.1.40 NAME 'userSMIMECertificate' DESC 'R FC2798: PKCS#7 SignedData used to support S/MIME' SYNTAX 1.3.6.1.4.1.1466.115 .121.1.5 ) attributeTypes: ( 2.16.840.1.113730.3.1.39 NAME 'preferredLanguage' DESC 'RFC2 798: preferred written or spoken language for a person' EQUALITY caseIgnoreMa tch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SIN GLE-VALUE ) attributeTypes: ( 0.9.2342.19200300.100.1.60 NAME 'jpegPhoto' DESC 'RFC2798: a JPEG image' SYNTAX 1.3.6.1.4.1.1466.115.121.1.28 ) attributeTypes: ( 2.16.840.1.113730.3.1.4 NAME 'employeeType' DESC 'RFC2798: t ype of employment for a person' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSub stringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 ) attributeTypes: ( 2.16.840.1.113730.3.1.3 NAME 'employeeNumber' DESC 'RFC2798: numerically identifies an employee within an organization' EQUALITY caseIgno reMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE ) attributeTypes: ( 2.16.840.1.113730.3.1.241 NAME 'displayName' DESC 'RFC2798: preferred name to be used when displaying entries' EQUALITY caseIgnoreMatch S UBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-V ALUE ) attributeTypes: ( 2.16.840.1.113730.3.1.2 NAME 'departmentNumber' DESC 'RFC279 8: identifies a department within an organization' EQUALITY caseIgnoreMatch S UBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 ) attributeTypes: ( 2.16.840.1.113730.3.1.1 NAME 'carLicense' DESC 'RFC2798: veh icle license or registration plate' EQUALITY caseIgnoreMatch SUBSTR caseIgnor eSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 ) attributeTypes: ( 1.3.6.1.1.1.1.27 NAME 'nisMapEntry' EQUALITY caseExactIA5Mat ch SUBSTR caseExactIA5SubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{10 24} SINGLE-VALUE ) attributeTypes: ( 1.3.6.1.1.1.1.26 NAME 'nisMapName' SUP name ) attributeTypes: ( 1.3.6.1.1.1.1.24 NAME 'bootFile' DESC 'Boot image name' EQUA LITY caseExactIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 ) attributeTypes: ( 1.3.6.1.1.1.1.23 NAME 'bootParameter' DESC 'rpc.bootparamd p arameter' SYNTAX 1.3.6.1.1.1.0.1 ) attributeTypes: ( 1.3.6.1.1.1.1.22 NAME 'macAddress' DESC 'MAC address' EQUALI TY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{128} ) attributeTypes: ( 1.3.6.1.1.1.1.21 NAME 'ipNetmaskNumber' DESC 'IP netmask' EQ UALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{128} SINGLE-VA LUE ) attributeTypes: ( 1.3.6.1.1.1.1.20 NAME 'ipNetworkNumber' DESC 'IP network' EQ UALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{128} SINGLE-VA LUE ) attributeTypes: ( 1.3.6.1.1.1.1.19 NAME 'ipHostNumber' DESC 'IP address' EQUAL ITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{128} ) attributeTypes: ( 1.3.6.1.1.1.1.18 NAME 'oncRpcNumber' EQUALITY integerMatch S YNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE ) attributeTypes: ( 1.3.6.1.1.1.1.17 NAME 'ipProtocolNumber' EQUALITY integerMat ch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE ) attributeTypes: ( 1.3.6.1.1.1.1.16 NAME 'ipServiceProtocol' SUP name ) attributeTypes: ( 1.3.6.1.1.1.1.15 NAME 'ipServicePort' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE ) attributeTypes: ( 1.3.6.1.1.1.1.14 NAME 'nisNetgroupTriple' DESC 'Netgroup tri ple' SYNTAX 1.3.6.1.1.1.0.0 ) attributeTypes: ( 1.3.6.1.1.1.1.13 NAME 'memberNisNetgroup' EQUALITY caseExact IA5Match SUBSTR caseExactIA5SubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1 .26 ) attributeTypes: ( 1.3.6.1.1.1.1.12 NAME 'memberUid' EQUALITY caseExactIA5Match SUBSTR caseExactIA5SubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 ) attributeTypes: ( 1.3.6.1.1.1.1.11 NAME 'shadowFlag' EQUALITY integerMatch SYN TAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE ) attributeTypes: ( 1.3.6.1.1.1.1.10 NAME 'shadowExpire' EQUALITY integerMatch S YNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE ) attributeTypes: ( 1.3.6.1.1.1.1.9 NAME 'shadowInactive' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE ) attributeTypes: ( 1.3.6.1.1.1.1.8 NAME 'shadowWarning' EQUALITY integerMatch S YNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE ) attributeTypes: ( 1.3.6.1.1.1.1.7 NAME 'shadowMax' EQUALITY integerMatch SYNTA X 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE ) attributeTypes: ( 1.3.6.1.1.1.1.6 NAME 'shadowMin' EQUALITY integerMatch SYNTA X 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE ) attributeTypes: ( 1.3.6.1.1.1.1.5 NAME 'shadowLastChange' EQUALITY integerMatc h SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE ) attributeTypes: ( 1.3.6.1.1.1.1.4 NAME 'loginShell' DESC 'The path to the logi n shell' EQUALITY caseExactIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SING LE-VALUE ) attributeTypes: ( 1.3.6.1.1.1.1.3 NAME 'homeDirectory' DESC 'The absolute path to the home directory' EQUALITY caseExactIA5Match SYNTAX 1.3.6.1.4.1.1466.11 5.121.1.26 SINGLE-VALUE ) attributeTypes: ( 1.3.6.1.1.1.1.2 NAME 'gecos' DESC 'The GECOS field; the comm on name' EQUALITY caseIgnoreIA5Match SUBSTR caseIgnoreIA5SubstringsMatch SYNT AX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE ) attributeTypes: ( 1.3.6.1.1.1.1.1 NAME 'gidNumber' DESC 'An integer uniquely i dentifying a group in an administrative domain' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE ) attributeTypes: ( 1.3.6.1.1.1.1.0 NAME 'uidNumber' DESC 'An integer uniquely i dentifying a user in an administrative domain' EQUALITY integerMatch SYNTAX 1 .3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE ) attributeTypes: ( 0.9.2342.19200300.100.1.56 NAME 'documentPublisher' DESC 'RF C1274: publisher of document' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubst ringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 ) attributeTypes: ( 0.9.2342.19200300.100.1.55 NAME 'audio' DESC 'RFC1274: audio (u-law)' SYNTAX 1.3.6.1.4.1.1466.115.121.1.4{25000} ) attributeTypes: ( 0.9.2342.19200300.100.1.54 NAME 'dITRedirect' DESC 'RFC1274: DIT Redirect' EQUALITY distinguishedNameMatch SYNTAX 1.3.6.1.4.1.1466.115.12 1.1.12 ) attributeTypes: ( 0.9.2342.19200300.100.1.53 NAME 'personalSignature' DESC 'RF C1274: Personal Signature (G3 fax)' SYNTAX 1.3.6.1.4.1.1466.115.121.1.23 ) attributeTypes: ( 0.9.2342.19200300.100.1.52 NAME 'subtreeMaximumQuality' DESC 'RFC1274: Subtree Maximun Quality' SYNTAX 1.3.6.1.4.1.1466.115.121.1.13 SING LE-VALUE ) attributeTypes: ( 0.9.2342.19200300.100.1.51 NAME 'subtreeMinimumQuality' DESC 'RFC1274: Subtree Mininum Quality' SYNTAX 1.3.6.1.4.1.1466.115.121.1.13 SING LE-VALUE ) attributeTypes: ( 0.9.2342.19200300.100.1.50 NAME 'singleLevelQuality' DESC 'R FC1274: Single Level Quality' SYNTAX 1.3.6.1.4.1.1466.115.121.1.13 SINGLE-VAL UE ) attributeTypes: ( 0.9.2342.19200300.100.1.49 NAME 'dSAQuality' DESC 'RFC1274: DSA Quality' SYNTAX 1.3.6.1.4.1.1466.115.121.1.19 SINGLE-VALUE ) attributeTypes: ( 0.9.2342.19200300.100.1.48 NAME 'buildingName' DESC 'RFC1274 : name of building' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} ) attributeTypes: ( 0.9.2342.19200300.100.1.47 NAME 'mailPreferenceOption' DESC 'RFC1274: mail preference option' SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 ) attributeTypes: ( 0.9.2342.19200300.100.1.46 NAME 'janetMailbox' DESC 'RFC1274 : Janet mailbox' EQUALITY caseIgnoreIA5Match SUBSTR caseIgnoreIA5SubstringsMa tch SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{256} ) attributeTypes: ( 0.9.2342.19200300.100.1.45 NAME 'organizationalStatus' DESC 'RFC1274: organizational status' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSu bstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} ) attributeTypes: ( 0.9.2342.19200300.100.1.44 NAME 'uniqueIdentifier' DESC 'RFC 1274: unique identifer' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115. 121.1.15{256} ) attributeTypes: ( 0.9.2342.19200300.100.1.43 NAME ( 'co' 'friendlyCountryName' ) DESC 'RFC1274: friendly country name' EQUALITY caseIgnoreMatch SUBSTR case IgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 ) attributeTypes: ( 0.9.2342.19200300.100.1.42 NAME ( 'pager' 'pagerTelephoneNum ber' ) DESC 'RFC1274: pager telephone number' EQUALITY telephoneNumberMatch S UBSTR telephoneNumberSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.50 ) attributeTypes: ( 0.9.2342.19200300.100.1.41 NAME ( 'mobile' 'mobileTelephoneN umber' ) DESC 'RFC1274: mobile telephone number' EQUALITY telephoneNumberMatc h SUBSTR telephoneNumberSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.50 ) attributeTypes: ( 0.9.2342.19200300.100.1.40 NAME 'personalTitle' DESC 'RFC127 4: personal title' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} ) attributeTypes: ( 0.9.2342.19200300.100.1.39 NAME 'homePostalAddress' DESC 'RF C1274: home postal address' EQUALITY caseIgnoreListMatch SUBSTR caseIgnoreLis tSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.41 ) attributeTypes: ( 0.9.2342.19200300.100.1.38 NAME 'associatedName' DESC 'RFC12 74: DN of entry associated with domain' EQUALITY distinguishedNameMatch SYNTA X 1.3.6.1.4.1.1466.115.121.1.12 ) attributeTypes: ( 0.9.2342.19200300.100.1.31 NAME 'cNAMERecord' EQUALITY caseI gnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 ) attributeTypes: ( 0.9.2342.19200300.100.1.30 NAME 'sOARecord' EQUALITY caseIgn oreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 ) attributeTypes: ( 0.9.2342.19200300.100.1.29 NAME 'nSRecord' EQUALITY caseIgno reIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 ) attributeTypes: ( 0.9.2342.19200300.100.1.28 NAME 'mXRecord' EQUALITY caseIgno reIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 ) attributeTypes: ( 0.9.2342.19200300.100.1.27 NAME 'mDRecord' EQUALITY caseIgno reIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 ) attributeTypes: ( 0.9.2342.19200300.100.1.26 NAME 'aRecord' EQUALITY caseIgnor eIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 ) attributeTypes: ( 0.9.2342.19200300.100.1.22 NAME 'otherMailbox' SYNTAX 1.3.6. 1.4.1.1466.115.121.1.39 ) attributeTypes: ( 0.9.2342.19200300.100.1.21 NAME 'secretary' DESC 'RFC1274: D N of secretary' EQUALITY distinguishedNameMatch SYNTAX 1.3.6.1.4.1.1466.115.1 21.1.12 ) attributeTypes: ( 0.9.2342.19200300.100.1.20 NAME ( 'homePhone' 'homeTelephone Number' ) DESC 'RFC1274: home telephone number' EQUALITY telephoneNumberMatch SUBSTR telephoneNumberSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.50 ) attributeTypes: ( 0.9.2342.19200300.100.1.15 NAME 'documentLocation' DESC 'RFC 1274: location of document original' EQUALITY caseIgnoreMatch SUBSTR caseIgno reSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} ) attributeTypes: ( 0.9.2342.19200300.100.1.14 NAME 'documentAuthor' DESC 'RFC12 74: DN of author of document' EQUALITY distinguishedNameMatch SYNTAX 1.3.6.1. 4.1.1466.115.121.1.12 ) attributeTypes: ( 0.9.2342.19200300.100.1.13 NAME 'documentVersion' DESC 'RFC1 274: version of document' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstring sMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} ) attributeTypes: ( 0.9.2342.19200300.100.1.12 NAME 'documentTitle' DESC 'RFC127 4: title of document' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMat ch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} ) attributeTypes: ( 0.9.2342.19200300.100.1.11 NAME 'documentIdentifier' DESC 'R FC1274: unique identifier of document' EQUALITY caseIgnoreMatch SUBSTR caseIg noreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} ) attributeTypes: ( 0.9.2342.19200300.100.1.10 NAME 'manager' DESC 'RFC1274: DN of manager' EQUALITY distinguishedNameMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1 .12 ) attributeTypes: ( 0.9.2342.19200300.100.1.9 NAME 'host' DESC 'RFC1274: host co mputer' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3. 6.1.4.1.1466.115.121.1.15{256} ) attributeTypes: ( 0.9.2342.19200300.100.1.8 NAME 'userClass' DESC 'RFC1274: ca tegory of user' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYN TAX 1.3.6.1.4.1.1466.115.121.1.15{256} ) attributeTypes: ( 0.9.2342.19200300.100.1.7 NAME 'photo' DESC 'RFC1274: photo (G3 fax)' SYNTAX 1.3.6.1.4.1.1466.115.121.1.23{25000} ) attributeTypes: ( 0.9.2342.19200300.100.1.6 NAME 'roomNumber' DESC 'RFC1274: r oom number' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} ) attributeTypes: ( 0.9.2342.19200300.100.1.5 NAME ( 'drink' 'favouriteDrink' ) DESC 'RFC1274: favorite drink' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubs tringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} ) attributeTypes: ( 0.9.2342.19200300.100.1.4 NAME 'info' DESC 'RFC1274: general information' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTA X 1.3.6.1.4.1.1466.115.121.1.15{2048} ) attributeTypes: ( 0.9.2342.19200300.100.1.2 NAME 'textEncodedORAddress' EQUALI TY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.1 15.121.1.15{256} ) attributeTypes: ( 1.2.840.113549.1.9.1 NAME ( 'email' 'emailAddress' 'pkcs9ema il' ) DESC 'RFC2459: legacy attribute for email addresses in DNs' EQUALITY ca seIgnoreIA5Match SUBSTR caseIgnoreIA5SubstringsMatch SYNTAX 1.3.6.1.4.1.1466. 115.121.1.26{128} ) attributeTypes: ( 0.9.2342.19200300.100.1.37 NAME 'associatedDomain' DESC 'RFC 1274: domain associated with object' EQUALITY caseIgnoreIA5Match SUBSTR caseI gnoreIA5SubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 ) attributeTypes: ( 0.9.2342.19200300.100.1.25 NAME ( 'dc' 'domainComponent' ) D ESC 'RFC1274/2247: domain component' EQUALITY caseIgnoreIA5Match SUBSTR caseI gnoreIA5SubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE ) attributeTypes: ( 0.9.2342.19200300.100.1.3 NAME ( 'mail' 'rfc822Mailbox' ) DE SC 'RFC1274: RFC822 Mailbox' EQUALITY caseIgnoreIA5Match SUBSTR caseIgnoreIA5 SubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{256} ) attributeTypes: ( 0.9.2342.19200300.100.1.1 NAME ( 'uid' 'userid' ) DESC 'RFC1 274: user identifier' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMat ch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} ) attributeTypes: ( 2.5.4.54 NAME 'dmdName' DESC 'RFC2256: name of DMD' SUP name ) attributeTypes: ( 2.5.4.53 NAME 'deltaRevocationList' DESC 'RFC2256: delta rev ocation list; use ;binary' SYNTAX 1.3.6.1.4.1.1466.115.121.1.9 ) attributeTypes: ( 2.5.4.52 NAME 'supportedAlgorithms' DESC 'RFC2256: supported algorithms' SYNTAX 1.3.6.1.4.1.1466.115.121.1.49 ) attributeTypes: ( 2.5.4.51 NAME 'houseIdentifier' DESC 'RFC2256: house identif ier' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1 .4.1.1466.115.121.1.15{32768} ) attributeTypes: ( 2.5.4.50 NAME 'uniqueMember' DESC 'RFC2256: unique member of a group' EQUALITY uniqueMemberMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.34 ) attributeTypes: ( 2.5.4.48 NAME 'protocolInformation' DESC 'RFC2256: protocol information' EQUALITY protocolInformationMatch SYNTAX 1.3.6.1.4.1.1466.115.12 1.1.42 ) attributeTypes: ( 2.5.4.47 NAME 'enhancedSearchGuide' DESC 'RFC2256: enhanced search guide' SYNTAX 1.3.6.1.4.1.1466.115.121.1.21 ) attributeTypes: ( 2.5.4.46 NAME 'dnQualifier' DESC 'RFC2256: DN qualifier' EQU ALITY caseIgnoreMatch ORDERING caseIgnoreOrderingMatch SUBSTR caseIgnoreSubst ringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.44 ) attributeTypes: ( 2.5.4.45 NAME 'x500UniqueIdentifier' DESC 'RFC2256: X.500 un ique identifier' EQUALITY bitStringMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.6 ) attributeTypes: ( 2.5.4.44 NAME 'generationQualifier' DESC 'RFC2256: name qual ifier indicating a generation' SUP name ) attributeTypes: ( 2.5.4.43 NAME 'initials' DESC 'RFC2256: initials of some or all of names, but not the surname(s).' SUP name ) attributeTypes: ( 2.5.4.42 NAME ( 'givenName' 'gn' ) DESC 'RFC2256: first name (s) for which the entity is known by' SUP name ) attributeTypes: ( 2.5.4.40 NAME 'crossCertificatePair' DESC 'RFC2256: X.509 cr oss certificate pair, use ;binary' SYNTAX 1.3.6.1.4.1.1466.115.121.1.10 ) attributeTypes: ( 2.5.4.39 NAME 'certificateRevocationList' DESC 'RFC2256: X.5 09 certificate revocation list, use ;binary' SYNTAX 1.3.6.1.4.1.1466.115.121. 1.9 ) attributeTypes: ( 2.5.4.38 NAME 'authorityRevocationList' DESC 'RFC2256: X.509 authority revocation list, use ;binary' SYNTAX 1.3.6.1.4.1.1466.115.121.1.9 ) attributeTypes: ( 2.5.4.37 NAME 'cACertificate' DESC 'RFC2256: X.509 CA certif icate, use ;binary' EQUALITY certificateExactMatch SYNTAX 1.3.6.1.4.1.1466.11 5.121.1.8 ) attributeTypes: ( 2.5.4.36 NAME 'userCertificate' DESC 'RFC2256: X.509 user ce rtificate, use ;binary' EQUALITY certificateExactMatch SYNTAX 1.3.6.1.4.1.146 6.115.121.1.8 ) attributeTypes: ( 2.5.4.34 NAME 'seeAlso' DESC 'RFC2256: DN of related object' SUP distinguishedName ) attributeTypes: ( 2.5.4.33 NAME 'roleOccupant' DESC 'RFC2256: occupant of role ' SUP distinguishedName ) attributeTypes: ( 2.5.4.32 NAME 'owner' DESC 'RFC2256: owner (of the object)' SUP distinguishedName ) attributeTypes: ( 2.5.4.31 NAME 'member' DESC 'RFC2256: member of a group' SUP distinguishedName ) attributeTypes: ( 2.5.4.30 NAME 'supportedApplicationContext' DESC 'RFC2256: s upported application context' EQUALITY objectIdentifierMatch SYNTAX 1.3.6.1.4 .1.1466.115.121.1.38 ) attributeTypes: ( 2.5.4.29 NAME 'presentationAddress' DESC 'RFC2256: presentat ion address' EQUALITY presentationAddressMatch SYNTAX 1.3.6.1.4.1.1466.115.12 1.1.43 SINGLE-VALUE ) attributeTypes: ( 2.5.4.28 NAME 'preferredDeliveryMethod' DESC 'RFC2256: prefe rred delivery method' SYNTAX 1.3.6.1.4.1.1466.115.121.1.14 SINGLE-VALUE ) attributeTypes: ( 2.5.4.27 NAME 'destinationIndicator' DESC 'RFC2256: destinat ion indicator' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNT AX 1.3.6.1.4.1.1466.115.121.1.44{128} ) attributeTypes: ( 2.5.4.26 NAME 'registeredAddress' DESC 'RFC2256: registered postal address' SUP postalAddress SYNTAX 1.3.6.1.4.1.1466.115.121.1.41 ) attributeTypes: ( 2.5.4.25 NAME 'internationaliSDNNumber' DESC 'RFC2256: inter national ISDN number' EQUALITY numericStringMatch SUBSTR numericStringSubstri ngsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.36{16} ) attributeTypes: ( 2.5.4.24 NAME 'x121Address' DESC 'RFC2256: X.121 Address' EQ UALITY numericStringMatch SUBSTR numericStringSubstringsMatch SYNTAX 1.3.6.1. 4.1.1466.115.121.1.36{15} ) attributeTypes: ( 2.5.4.23 NAME ( 'facsimileTelephoneNumber' 'fax' ) DESC 'RFC 2256: Facsimile (Fax) Telephone Number' SYNTAX 1.3.6.1.4.1.1466.115.121.1.22 ) attributeTypes: ( 2.5.4.22 NAME 'teletexTerminalIdentifier' DESC 'RFC2256: Tel etex Terminal Identifier' SYNTAX 1.3.6.1.4.1.1466.115.121.1.51 ) attributeTypes: ( 2.5.4.21 NAME 'telexNumber' DESC 'RFC2256: Telex Number' SYN TAX 1.3.6.1.4.1.1466.115.121.1.52 ) attributeTypes: ( 2.5.4.20 NAME 'telephoneNumber' DESC 'RFC2256: Telephone Num ber' EQUALITY telephoneNumberMatch SUBSTR telephoneNumberSubstringsMatch SYNT AX 1.3.6.1.4.1.1466.115.121.1.50{32} ) attributeTypes: ( 2.5.4.19 NAME 'physicalDeliveryOfficeName' DESC 'RFC2256: Ph ysical Delivery Office Name' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstr ingsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{128} ) attributeTypes: ( 2.5.4.18 NAME 'postOfficeBox' DESC 'RFC2256: Post Office Box ' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4. 1.1466.115.121.1.15{40} ) attributeTypes: ( 2.5.4.17 NAME 'postalCode' DESC 'RFC2256: postal code' EQUAL ITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466. 115.121.1.15{40} ) attributeTypes: ( 2.5.4.16 NAME 'postalAddress' DESC 'RFC2256: postal address' EQUALITY caseIgnoreListMatch SUBSTR caseIgnoreListSubstringsMatch SYNTAX 1.3 .6.1.4.1.1466.115.121.1.41 ) attributeTypes: ( 2.5.4.15 NAME 'businessCategory' DESC 'RFC2256: business cat egory' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6 .1.4.1.1466.115.121.1.15{128} ) attributeTypes: ( 2.5.4.14 NAME 'searchGuide' DESC 'RFC2256: search guide, obs oleted by enhancedSearchGuide' SYNTAX 1.3.6.1.4.1.1466.115.121.1.25 ) attributeTypes: ( 2.5.4.13 NAME 'description' DESC 'RFC2256: descriptive infor mation' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3. 6.1.4.1.1466.115.121.1.15{1024} ) attributeTypes: ( 2.5.4.12 NAME 'title' DESC 'RFC2256: title associated with t he entity' SUP name ) attributeTypes: ( 2.5.4.11 NAME ( 'ou' 'organizationalUnitName' ) DESC 'RFC225 6: organizational unit this object belongs to' SUP name ) attributeTypes: ( 2.5.4.10 NAME ( 'o' 'organizationName' ) DESC 'RFC2256: orga nization this object belongs to' SUP name ) attributeTypes: ( 2.5.4.9 NAME ( 'street' 'streetAddress' ) DESC 'RFC2256: str eet address of this object' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstri ngsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{128} ) attributeTypes: ( 2.5.4.8 NAME ( 'st' 'stateOrProvinceName' ) DESC 'RFC2256: s tate or province which this object resides in' SUP name ) attributeTypes: ( 2.5.4.7 NAME ( 'l' 'localityName' ) DESC 'RFC2256: locality which this object resides in' SUP name ) attributeTypes: ( 2.5.4.6 NAME ( 'c' 'countryName' ) DESC 'RFC2256: ISO-3166 c ountry 2-letter code' SUP name SINGLE-VALUE ) attributeTypes: ( 2.5.4.5 NAME 'serialNumber' DESC 'RFC2256: serial number of the entity' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.44{64} ) attributeTypes: ( 2.5.4.4 NAME ( 'sn' 'surname' ) DESC 'RFC2256: last (family) name(s) for which the entity is known by' SUP name ) attributeTypes: ( 2.5.4.2 NAME 'knowledgeInformation' DESC 'RFC2256: knowledge information' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{3 2768} ) attributeTypes: ( 1.3.6.1.4.1.250.1.57 NAME 'labeledURI' DESC 'RFC2079: Unifor m Resource Identifier with optional label' EQUALITY caseExactMatch SYNTAX 1.3 .6.1.4.1.1466.115.121.1.15 ) attributeTypes: ( 2.5.4.35 NAME 'userPassword' DESC 'RFC2256/2307: password of user' EQUALITY octetStringMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.40{128} ) attributeTypes: ( 2.5.4.3 NAME ( 'cn' 'commonName' ) DESC 'RFC2256: common nam e(s) for which the entity is known by' SUP name ) attributeTypes: ( 2.5.4.41 NAME 'name' DESC 'RFC2256: common supertype of name attributes' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{32768} ) attributeTypes: ( 2.5.4.49 NAME 'distinguishedName' DESC 'RFC2256: common supe rtype of DN attributes' EQUALITY distinguishedNameMatch SYNTAX 1.3.6.1.4.1.14 66.115.121.1.12 ) attributeTypes: ( 2.16.840.1.113730.3.1.34 NAME 'ref' DESC 'namedref: subordin ate referral URL' EQUALITY caseExactMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.1 5 USAGE distributedOperation ) attributeTypes: ( 2.5.4.1 NAME ( 'aliasedObjectName' 'aliasedEntryName' ) DESC 'RFC2256: name of aliased object' EQUALITY distinguishedNameMatch SYNTAX 1.3 .6.1.4.1.1466.115.121.1.12 SINGLE-VALUE ) attributeTypes: ( 1.3.6.1.4.1.1466.101.120.16 NAME 'ldapSyntaxes' DESC 'RFC225 2: LDAP syntaxes' EQUALITY objectIdentifierFirstComponentMatch SYNTAX 1.3.6.1 .4.1.1466.115.121.1.54 USAGE directoryOperation ) attributeTypes: ( 2.5.21.8 NAME 'matchingRuleUse' DESC 'RFC2252: matching rule uses' EQUALITY objectIdentifierFirstComponentMatch SYNTAX 1.3.6.1.4.1.1466.1 15.121.1.31 USAGE directoryOperation ) attributeTypes: ( 2.5.21.6 NAME 'objectClasses' DESC 'RFC2252: object classes' EQUALITY objectIdentifierFirstComponentMatch SYNTAX 1.3.6.1.4.1.1466.115.121 .1.37 USAGE directoryOperation ) attributeTypes: ( 2.5.21.5 NAME 'attributeTypes' DESC 'RFC2252: attribute type s' EQUALITY objectIdentifierFirstComponentMatch SYNTAX 1.3.6.1.4.1.1466.115.1 21.1.3 USAGE directoryOperation ) attributeTypes: ( 2.5.21.4 NAME 'matchingRules' DESC 'RFC2252: matching rules' EQUALITY objectIdentifierFirstComponentMatch SYNTAX 1.3.6.1.4.1.1466.115.121 .1.30 USAGE directoryOperation ) attributeTypes: ( 1.3.6.1.1.5 NAME 'vendorVersion' DESC 'RFC3045: version of i mplementation' EQUALITY caseExactMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 S INGLE-VALUE NO-USER-MODIFICATION USAGE dSAOperation ) attributeTypes: ( 1.3.6.1.1.4 NAME 'vendorName' DESC 'RFC3045: name of impleme ntation vendor' EQUALITY caseExactMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE NO-USER-MODIFICATION USAGE dSAOperation ) attributeTypes: ( 1.3.6.1.4.1.4203.1.3.5 NAME 'supportedFeatures' DESC 'featur es supported by the server' EQUALITY objectIdentifierMatch SYNTAX 1.3.6.1.4.1 .1466.115.121.1.38 USAGE dSAOperation ) attributeTypes: ( 1.3.6.1.4.1.1466.101.120.14 NAME 'supportedSASLMechanisms' D ESC 'RFC2252: supported SASL mechanisms' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 USAGE dSAOperation ) attributeTypes: ( 1.3.6.1.4.1.1466.101.120.15 NAME 'supportedLDAPVersion' DESC 'RFC2252: supported LDAP versions' SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 USAG E dSAOperation ) attributeTypes: ( 1.3.6.1.4.1.1466.101.120.7 NAME 'supportedExtension' DESC 'R FC2252: supported extended operations' SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 U SAGE dSAOperation ) attributeTypes: ( 1.3.6.1.4.1.1466.101.120.13 NAME 'supportedControl' DESC 'RF C2252: supported controls' SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 USAGE dSAOper ation ) attributeTypes: ( 1.3.6.1.4.1.1466.101.120.5 NAME 'namingContexts' DESC 'RFC22 52: naming contexts' SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 USAGE dSAOperation ) attributeTypes: ( 1.3.6.1.4.1.1466.101.120.6 NAME 'altServer' DESC 'RFC2252: a lternative servers' SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 USAGE dSAOperation ) attributeTypes: ( 2.5.18.10 NAME 'subschemaSubentry' DESC 'RFC2252: name of co ntrolling subschema entry' EQUALITY distinguishedNameMatch SYNTAX 1.3.6.1.4.1 .1466.115.121.1.12 SINGLE-VALUE NO-USER-MODIFICATION USAGE directoryOperation ) attributeTypes: ( 2.5.18.9 NAME 'hasSubordinates' DESC 'X.501: entry has child ren' EQUALITY booleanMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE N O-USER-MODIFICATION USAGE directoryOperation ) attributeTypes: ( 2.5.18.4 NAME 'modifiersName' DESC 'RFC2252: name of last mo difier' EQUALITY distinguishedNameMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 SINGLE-VALUE NO-USER-MODIFICATION USAGE directoryOperation ) attributeTypes: ( 2.5.18.3 NAME 'creatorsName' DESC 'RFC2252: name of creator' EQUALITY distinguishedNameMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 SINGLE- VALUE NO-USER-MODIFICATION USAGE directoryOperation ) attributeTypes: ( 2.5.18.2 NAME 'modifyTimestamp' DESC 'RFC2252: time which ob ject was last modified' EQUALITY generalizedTimeMatch ORDERING generalizedTim eOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 SINGLE-VALUE NO-USER-MODI FICATION USAGE directoryOperation ) attributeTypes: ( 2.5.18.1 NAME 'createTimestamp' DESC 'RFC2252: time which ob ject was created' EQUALITY generalizedTimeMatch ORDERING generalizedTimeOrder ingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 SINGLE-VALUE NO-USER-MODIFICATI ON USAGE directoryOperation ) attributeTypes: ( 2.5.21.9 NAME 'structuralObjectClass' DESC 'X.500(93): struc tural object class of entry' EQUALITY objectIdentifierMatch SYNTAX 1.3.6.1.4. 1.1466.115.121.1.38 SINGLE-VALUE NO-USER-MODIFICATION USAGE directoryOperatio n ) attributeTypes: ( 2.5.4.0 NAME 'objectClass' DESC 'RFC2256: object classes of the entity' EQUALITY objectIdentifierMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1. 38 ) objectClasses: ( 2.16.840.1.113730.3.2.2 NAME 'inetOrgPerson' DESC 'RFC2798: I nternet Organizational Person' SUP organizationalPerson STRUCTURAL MAY ( audi o $ businessCategory $ carLicense $ departmentNumber $ displayName $ employee Number $ employeeType $ givenName $ homePhone $ homePostalAddress $ initials $ jpegPhoto $ labeledURI $ mail $ manager $ mobile $ o $ pager $ photo $ room Number $ secretary $ uid $ userCertificate $ x500uniqueIdentifier $ preferred Language $ userSMIMECertificate $ userPKCS12 ) ) objectClasses: ( 1.3.6.1.1.1.2.12 NAME 'bootableDevice' DESC 'A device with bo ot parameters' SUP top AUXILIARY MAY ( bootFile $ bootParameter ) ) objectClasses: ( 1.3.6.1.1.1.2.11 NAME 'ieee802Device' DESC 'A device with a M AC address' SUP top AUXILIARY MAY macAddress ) objectClasses: ( 1.3.6.1.1.1.2.10 NAME 'nisObject' DESC 'An entry in a NIS map ' SUP top STRUCTURAL MUST ( cn $ nisMapEntry $ nisMapName ) MAY description ) objectClasses: ( 1.3.6.1.1.1.2.9 NAME 'nisMap' DESC 'A generic abstraction of a NIS map' SUP top STRUCTURAL MUST nisMapName MAY description ) objectClasses: ( 1.3.6.1.1.1.2.8 NAME 'nisNetgroup' DESC 'Abstraction of a net group' SUP top STRUCTURAL MUST cn MAY ( nisNetgroupTriple $ memberNisNetgroup $ description ) ) objectClasses: ( 1.3.6.1.1.1.2.7 NAME 'ipNetwork' DESC 'Abstraction of an IP n etwork' SUP top STRUCTURAL MUST ( cn $ ipNetworkNumber ) MAY ( ipNetmaskNumbe r $ l $ description $ manager ) ) objectClasses: ( 1.3.6.1.1.1.2.6 NAME 'ipHost' DESC 'Abstraction of a host, an IP device' SUP top AUXILIARY MUST ( cn $ ipHostNumber ) MAY ( l $ descriptio n $ manager ) ) objectClasses: ( 1.3.6.1.1.1.2.5 NAME 'oncRpc' DESC 'Abstraction of an ONC/RPC binding' SUP top STRUCTURAL MUST ( cn $ oncRpcNumber $ description ) MAY des cription ) objectClasses: ( 1.3.6.1.1.1.2.4 NAME 'ipProtocol' DESC 'Abstraction of an IP protocol' SUP top STRUCTURAL MUST ( cn $ ipProtocolNumber $ description ) MAY description ) objectClasses: ( 1.3.6.1.1.1.2.3 NAME 'ipService' DESC 'Abstraction an Interne t Protocol service' SUP top STRUCTURAL MUST ( cn $ ipServicePort $ ipServiceP rotocol ) MAY description ) objectClasses: ( 1.3.6.1.1.1.2.2 NAME 'posixGroup' DESC 'Abstraction of a grou p of accounts' SUP top STRUCTURAL MUST ( cn $ gidNumber ) MAY ( userPassword $ memberUid $ description ) ) objectClasses: ( 1.3.6.1.1.1.2.1 NAME 'shadowAccount' DESC 'Additional attribu tes for shadow passwords' SUP top AUXILIARY MUST uid MAY ( userPassword $ sha dowLastChange $ shadowMin $ shadowMax $ shadowWarning $ shadowInactive $ shad owExpire $ shadowFlag $ description ) ) objectClasses: ( 1.3.6.1.1.1.2.0 NAME 'posixAccount' DESC 'Abstraction of an a ccount with POSIX attributes' SUP top AUXILIARY MUST ( cn $ uid $ uidNumber $ gidNumber $ homeDirectory ) MAY ( userPassword $ loginShell $ gecos $ descri ption ) ) objectClasses: ( 0.9.2342.19200300.100.4.22 NAME 'qualityLabelledData' SUP top AUXILIARY MUST dsaQuality MAY ( subtreeMinimumQuality $ subtreeMaximumQualit y ) ) objectClasses: ( 0.9.2342.19200300.100.4.21 NAME 'pilotDSA' SUP dsa STRUCTURAL MAY dSAQuality ) objectClasses: ( 0.9.2342.19200300.100.4.20 NAME 'pilotOrganization' SUP ( org anization $ organizationalUnit ) STRUCTURAL MAY buildingName ) objectClasses: ( 0.9.2342.19200300.100.4.18 NAME 'friendlyCountry' SUP country STRUCTURAL MUST friendlyCountryName ) objectClasses: ( 0.9.2342.19200300.100.4.17 NAME 'domainRelatedObject' DESC 'R FC1274: an object related to an domain' SUP top AUXILIARY MUST associatedDoma in ) objectClasses: ( 0.9.2342.19200300.100.4.15 NAME 'dNSDomain' SUP domain STRUCT URAL MAY ( ARecord $ MDRecord $ MXRecord $ NSRecord $ SOARecord $ CNAMERecord ) ) objectClasses: ( 0.9.2342.19200300.100.4.14 NAME 'RFC822localPart' SUP domain STRUCTURAL MAY ( commonName $ surname $ description $ seeAlso $ telephoneNumb er $ physicalDeliveryOfficeName $ postalAddress $ postalCode $ postOfficeBox $ streetAddress $ facsimileTelephoneNumber $ internationalISDNNumber $ teleph oneNumber $ teletexTerminalIdentifier $ telexNumber $ preferredDeliveryMethod $ destinationIndicator $ registeredAddress $ x121Address ) ) objectClasses: ( 0.9.2342.19200300.100.4.13 NAME 'domain' SUP top STRUCTURAL M UST domainComponent MAY ( associatedName $ organizationName $ description $ b usinessCategory $ seeAlso $ searchGuide $ userPassword $ localityName $ state OrProvinceName $ streetAddress $ physicalDeliveryOfficeName $ postalAddress $ postalCode $ postOfficeBox $ streetAddress $ facsimileTelephoneNumber $ inte rnationalISDNNumber $ telephoneNumber $ teletexTerminalIdentifier $ telexNumb er $ preferredDeliveryMethod $ destinationIndicator $ registeredAddress $ x12 1Address ) ) objectClasses: ( 0.9.2342.19200300.100.4.9 NAME 'documentSeries' SUP top STRUC TURAL MUST commonName MAY ( description $ seeAlso $ telephonenumber $ localit yName $ organizationName $ organizationalUnitName ) ) objectClasses: ( 0.9.2342.19200300.100.4.7 NAME 'room' SUP top STRUCTURAL MUST commonName MAY ( roomNumber $ description $ seeAlso $ telephoneNumber ) ) objectClasses: ( 0.9.2342.19200300.100.4.6 NAME 'document' SUP top STRUCTURAL MUST documentIdentifier MAY ( commonName $ description $ seeAlso $ localityNa me $ organizationName $ organizationalUnitName $ documentTitle $ documentVers ion $ documentAuthor $ documentLocation $ documentPublisher ) ) objectClasses: ( 0.9.2342.19200300.100.4.5 NAME 'account' SUP top STRUCTURAL M UST userid MAY ( description $ seeAlso $ localityName $ organizationName $ or ganizationalUnitName $ host ) ) objectClasses: ( 0.9.2342.19200300.100.4.4 NAME ( 'pilotPerson' 'newPilotPerso n' ) SUP person STRUCTURAL MAY ( userid $ textEncodedORAddress $ rfc822Mailbo x $ favouriteDrink $ roomNumber $ userClass $ homeTelephoneNumber $ homePosta lAddress $ secretary $ personalTitle $ preferredDeliveryMethod $ businessCate gory $ janetMailbox $ otherMailbox $ mobileTelephoneNumber $ pagerTelephoneNu mber $ organizationalStatus $ mailPreferenceOption $ personalSignature ) ) objectClasses: ( 1.3.6.1.1.3.1 NAME 'uidObject' DESC 'RFC2377: uid object' SUP top AUXILIARY MUST uid ) objectClasses: ( 1.3.6.1.4.1.1466.344 NAME 'dcObject' DESC 'RFC2247: domain co mponent object' SUP top AUXILIARY MUST dc ) objectClasses: ( 0.9.2342.19200300.100.4.19 NAME 'simpleSecurityObject' DESC ' RFC1274: simple security object' SUP top AUXILIARY MUST userPassword ) objectClasses: ( 1.3.6.1.4.1.250.3.15 NAME 'labeledURIObject' DESC 'RFC2079: o bject that contains the URI attribute type' SUP top AUXILIARY MAY labeledURI ) objectClasses: ( 2.5.6.23 NAME 'deltaCRL' DESC 'RFC2587: PKI user' SUP top AUX ILIARY MAY deltaRevocationList ) objectClasses: ( 2.5.6.22 NAME 'pkiCA' DESC 'RFC2587: PKI certificate authorit y' SUP top AUXILIARY MAY ( authorityRevocationList $ certificateRevocationLis t $ cACertificate $ crossCertificatePair ) ) objectClasses: ( 2.5.6.21 NAME 'pkiUser' DESC 'RFC2587: a PKI user' SUP top AU XILIARY MAY userCertificate ) objectClasses: ( 2.5.6.20 NAME 'dmd' SUP top STRUCTURAL MUST dmdName MAY ( use rPassword $ searchGuide $ seeAlso $ businessCategory $ x121Address $ register edAddress $ destinationIndicator $ preferredDeliveryMethod $ telexNumber $ te letexTerminalIdentifier $ telephoneNumber $ internationaliSDNNumber $ facsimi leTelephoneNumber $ street $ postOfficeBox $ postalCode $ postalAddress $ phy sicalDeliveryOfficeName $ st $ l $ description ) ) objectClasses: ( 2.5.6.19 NAME 'cRLDistributionPoint' SUP top STRUCTURAL MUST cn MAY ( certificateRevocationList $ authorityRevocationList $ deltaRevocatio nList ) ) objectClasses: ( 2.5.6.16.2 NAME 'certificationAuthority-V2' SUP certification Authority AUXILIARY MAY deltaRevocationList ) objectClasses: ( 2.5.6.18 NAME 'userSecurityInformation' DESC 'RFC2256: a user security information' SUP top AUXILIARY MAY supportedAlgorithms ) objectClasses: ( 2.5.6.17 NAME 'groupOfUniqueNames' DESC 'RFC2256: a group of unique names (DN and Unique Identifier)' SUP top STRUCTURAL MUST ( uniqueMemb er $ cn ) MAY ( businessCategory $ seeAlso $ owner $ ou $ o $ description ) ) objectClasses: ( 2.5.6.16 NAME 'certificationAuthority' DESC 'RFC2256: a certi ficate authority' SUP top AUXILIARY MUST ( authorityRevocationList $ certific ateRevocationList $ cACertificate ) MAY crossCertificatePair ) objectClasses: ( 2.5.6.15 NAME 'strongAuthenticationUser' DESC 'RFC2256: a str ong authentication user' SUP top AUXILIARY MUST userCertificate ) objectClasses: ( 2.5.6.14 NAME 'device' DESC 'RFC2256: a device' SUP top STRUC TURAL MUST cn MAY ( serialNumber $ seeAlso $ owner $ ou $ o $ l $ description ) ) objectClasses: ( 2.5.6.13 NAME 'dSA' DESC 'RFC2256: a directory system agent ( a server)' SUP applicationEntity STRUCTURAL MAY knowledgeInformation ) objectClasses: ( 2.5.6.12 NAME 'applicationEntity' DESC 'RFC2256: an applicati on entity' SUP top STRUCTURAL MUST ( presentationAddress $ cn ) MAY ( support edApplicationContext $ seeAlso $ ou $ o $ l $ description ) ) objectClasses: ( 2.5.6.11 NAME 'applicationProcess' DESC 'RFC2256: an applicat ion process' SUP top STRUCTURAL MUST cn MAY ( seeAlso $ ou $ l $ description ) ) objectClasses: ( 2.5.6.10 NAME 'residentialPerson' DESC 'RFC2256: an residenti al person' SUP person STRUCTURAL MUST l MAY ( businessCategory $ x121Address $ registeredAddress $ destinationIndicator $ preferredDeliveryMethod $ telexN umber $ teletexTerminalIdentifier $ telephoneNumber $ internationaliSDNNumber $ facsimileTelephoneNumber $ preferredDeliveryMethod $ street $ postOfficeBo x $ postalCode $ postalAddress $ physicalDeliveryOfficeName $ st $ l ) ) objectClasses: ( 2.5.6.9 NAME 'groupOfNames' DESC 'RFC2256: a group of names ( DNs)' SUP top STRUCTURAL MUST ( member $ cn ) MAY ( businessCategory $ seeAls o $ owner $ ou $ o $ description ) ) objectClasses: ( 2.5.6.8 NAME 'organizationalRole' DESC 'RFC2256: an organizat ional role' SUP top STRUCTURAL MUST cn MAY ( x121Address $ registeredAddress $ destinationIndicator $ preferredDeliveryMethod $ telexNumber $ teletexTermi nalIdentifier $ telephoneNumber $ internationaliSDNNumber $ facsimileTelephon eNumber $ seeAlso $ roleOccupant $ preferredDeliveryMethod $ street $ postOff iceBox $ postalCode $ postalAddress $ physicalDeliveryOfficeName $ ou $ st $ l $ description ) ) objectClasses: ( 2.5.6.7 NAME 'organizationalPerson' DESC 'RFC2256: an organiz ational person' SUP person STRUCTURAL MAY ( title $ x121Address $ registeredA ddress $ destinationIndicator $ preferredDeliveryMethod $ telexNumber $ telet exTerminalIdentifier $ telephoneNumber $ internationaliSDNNumber $ facsimileT elephoneNumber $ street $ postOfficeBox $ postalCode $ postalAddress $ physic alDeliveryOfficeName $ ou $ st $ l ) ) objectClasses: ( 2.5.6.6 NAME 'person' DESC 'RFC2256: a person' SUP top STRUCT URAL MUST ( sn $ cn ) MAY ( userPassword $ telephoneNumber $ seeAlso $ descri ption ) ) objectClasses: ( 2.5.6.5 NAME 'organizationalUnit' DESC 'RFC2256: an organizat ional unit' SUP top STRUCTURAL MUST ou MAY ( userPassword $ searchGuide $ see Also $ businessCategory $ x121Address $ registeredAddress $ destinationIndica tor $ preferredDeliveryMethod $ telexNumber $ teletexTerminalIdentifier $ tel ephoneNumber $ internationaliSDNNumber $ facsimileTelephoneNumber $ street $ postOfficeBox $ postalCode $ postalAddress $ physicalDeliveryOfficeName $ st $ l $ description ) ) objectClasses: ( 2.5.6.4 NAME 'organization' DESC 'RFC2256: an organization' S UP top STRUCTURAL MUST o MAY ( userPassword $ searchGuide $ seeAlso $ busines sCategory $ x121Address $ registeredAddress $ destinationIndicator $ preferre dDeliveryMethod $ telexNumber $ teletexTerminalIdentifier $ telephoneNumber $ internationaliSDNNumber $ facsimileTelephoneNumber $ street $ postOfficeBox $ postalCode $ postalAddress $ physicalDeliveryOfficeName $ st $ l $ descript ion ) ) objectClasses: ( 2.5.6.3 NAME 'locality' DESC 'RFC2256: a locality' SUP top ST RUCTURAL MAY ( street $ seeAlso $ searchGuide $ st $ l $ description ) ) objectClasses: ( 2.5.6.2 NAME 'country' DESC 'RFC2256: a country' SUP top STRU CTURAL MUST c MAY ( searchGuide $ description ) ) objectClasses: ( 2.5.20.1 NAME 'subschema' DESC 'RFC2252: controlling subschem a (sub)entry' AUXILIARY MAY ( dITStructureRules $ nameForms $ ditContentRules $ objectClasses $ attributeTypes $ matchingRules $ matchingRuleUse ) ) objectClasses: ( 2.5.17.0 NAME 'subentry' SUP top STRUCTURAL MUST ( cn $ subtr eeSpecification ) ) objectClasses: ( 1.3.6.1.4.1.4203.1.4.1 NAME ( 'OpenLDAProotDSE' 'LDAProotDSE' ) DESC 'OpenLDAP Root DSE object' SUP top STRUCTURAL MAY cn ) objectClasses: ( 2.16.840.1.113730.3.2.6 NAME 'referral' DESC 'namedref: named subordinate referral' SUP top STRUCTURAL MUST ref ) objectClasses: ( 2.5.6.1 NAME 'alias' DESC 'RFC2256: an alias' SUP top STRUCTU RAL MUST aliasedObjectName ) objectClasses: ( 1.3.6.1.4.1.1466.101.120.111 NAME 'extensibleObject' DESC 'RF C2252: extensible object' SUP top AUXILIARY ) objectClasses: ( 2.5.6.0 NAME 'top' DESC 'top of the superclass chain' ABSTRAC T MUST objectClass ) ldaptor-0.0.43/ldaptor/test/ldif/webtests/dc=com.dir/0000755000175000017500000000000010403234304020477 5ustar janjanldaptor-0.0.43/ldaptor/test/ldif/webtests/dc=com.dir/dc=example.dir/0000755000175000017500000000000010403234304023313 5ustar janjanldaptor-0.0.43/ldaptor/test/ldif/webtests/dc=com.dir/dc=example.dir/ou=People.dir/0000755000175000017500000000000010403234304025755 5ustar janjan././@LongLink0000000000000000000000000000014500000000000011565 Lustar rootrootldaptor-0.0.43/ldaptor/test/ldif/webtests/dc=com.dir/dc=example.dir/ou=People.dir/cn=John Smith.ldifldaptor-0.0.43/ldaptor/test/ldif/webtests/dc=com.dir/dc=example.dir/ou=People.dir/cn=John Smith.ldif0000644000175000017500000000017410352525725031215 0ustar janjandn: cn=John Smith,ou=People,dc=example,dc=com cn: John Smith sn: Smith objectClass: person subschemaSubentry: cn=Subschema ldaptor-0.0.43/ldaptor/test/ldif/webtests/dc=com.dir/dc=example.dir/ou=People.dir/uid=jack.ldif0000644000175000017500000000036710352506123030336 0ustar janjandn: uid=jack,ou=People,dc=example,dc=com objectClass: posixAccount uid: jack cn: Jack Black uidNumber: 1234 gidNumber: 1234 homeDirectory: /home/jack # "secret" userPassword: {SSHA}yVLLj62rFf3kDAbzwEU0zYAVvbWrze8= subschemaSubentry: cn=Subschema ././@LongLink0000000000000000000000000000015600000000000011567 Lustar rootrootldaptor-0.0.43/ldaptor/test/ldif/webtests/dc=com.dir/dc=example.dir/ou=People.dir/cn=missing-must-fields.ldifldaptor-0.0.43/ldaptor/test/ldif/webtests/dc=com.dir/dc=example.dir/ou=People.dir/cn=missing-must-fi0000644000175000017500000000020410352532370031354 0ustar janjandn: cn=missing-must-fields,ou=People,dc=example,dc=com cn: missing-must-fields objectClass: person subschemaSubentry: cn=Subschema ldaptor-0.0.43/ldaptor/test/ldif/webtests/dc=com.dir/dc=example.dir/ou=People.ldif0000644000175000017500000000015410345773672026065 0ustar janjandn: ou=People,dc=example,dc=com objectClass: organizationalUnit ou: People subschemaSubentry: cn=Subschema ldaptor-0.0.43/ldaptor/test/ldif/webtests/dc=com.dir/dc=example.ldif0000644000175000017500000000007110345024433023401 0ustar janjandn: dc=example,dc=com objectClass: dcObject dc: example ldaptor-0.0.43/ldaptor/test/test_distinguishedname.py0000644000175000017500000002723210344535643021123 0ustar janjan""" Test cases for ldaptor.protocols.ldap.distinguishedname module. """ from twisted.trial import unittest from ldaptor.protocols.ldap import distinguishedname as dn class TestCaseWithKnownValues(unittest.TestCase): knownValues = () def testKnownValues(self): for s, l in self.knownValues: fromString = dn.DistinguishedName(s) listOfRDNs = [] for av in l: listOfAttributeTypesAndValues = [] for a,v in av: listOfAttributeTypesAndValues.append(dn.LDAPAttributeTypeAndValue(attributeType=a, value=v)) r=dn.RelativeDistinguishedName(listOfAttributeTypesAndValues) listOfRDNs.append(r) fromList = dn.DistinguishedName(listOfRDNs) self.assertEquals(fromString, fromList) fromStringToString = str(fromString) fromListToString = str(fromList) assert fromStringToString == fromListToString canon = fromStringToString # DNs equal their string representation. Note this does # not mean they equal all the possible string # representations -- just the canonical one. self.assertEquals(fromString, canon) self.assertEquals(fromList, canon) self.assertEquals(canon, fromString) self.assertEquals(canon, fromList) # DNs can be used interchangeably with their canonical # string representation as hash keys. self.assertEquals(hash(fromString), hash(canon)) self.assertEquals(hash(fromList), hash(canon)) self.assertEquals(hash(canon), hash(fromString)) self.assertEquals(hash(canon), hash(fromList)) class LDAPDistinguishedName_Escaping(TestCaseWithKnownValues): knownValues = ( ('', []), ('cn=foo', [[('cn', 'foo')]]), (r'cn=\,bar', [[('cn', r',bar')]]), (r'cn=foo\,bar', [[('cn', r'foo,bar')]]), (r'cn=foo\,', [[('cn', r'foo,')]]), (r'cn=\+bar', [[('cn', r'+bar')]]), (r'cn=foo\+bar', [[('cn', r'foo+bar')]]), (r'cn=foo\+', [[('cn', r'foo+')]]), (r'cn=\"bar', [[('cn', r'"bar')]]), (r'cn=foo\"bar', [[('cn', r'foo"bar')]]), (r'cn=foo\"', [[('cn', r'foo"')]]), (r'cn=\\bar', [[('cn', r'\bar')]]), (r'cn=foo\\bar', [[('cn', r'foo\bar')]]), (r'cn=foo\\', [[('cn', 'foo\\')]]), (r'cn=\bar', [[('cn', r'>bar')]]), (r'cn=foo\>bar', [[('cn', r'foo>bar')]]), (r'cn=foo\>', [[('cn', r'foo>')]]), (r'cn=\;bar', [[('cn', r';bar')]]), (r'cn=foo\;bar', [[('cn', r'foo;bar')]]), (r'cn=foo\;', [[('cn', r'foo;')]]), (r'cn=\#bar', [[('cn', r'#bar')]]), (r'cn=\ bar', [[('cn', r' bar')]]), (r'cn=bar\ ', [[('cn', r'bar ')]]), (r'cn=test+owner=uid\=foo\,ou\=depar' +r'tment\,dc\=example\,dc\=com,dc=ex' +r'ample,dc=com', [[('cn', r'test'), ('owner', r'uid=foo,ou=depart' +r'ment,dc=example,dc=com'), ], [('dc', r'example')], [('dc', r'com')]]), (r'cn=bar,dc=example,dc=com', [[('cn', 'bar')], [('dc', 'example')], [('dc', 'com')]]), (r'cn=bar, dc=example, dc=com', [[('cn', 'bar')], [('dc', 'example')], [('dc', 'com')]]), (r'cn=bar, dc=example,dc=com', [[('cn', 'bar')], [('dc', 'example')], [('dc', 'com')]]), ) def testOpenLDAPEqualsEscape(self): """Slapd wants = to be escaped in RDN attributeValues.""" got = dn.DistinguishedName(listOfRDNs=[ dn.RelativeDistinguishedName( attributeTypesAndValues=[ dn.LDAPAttributeTypeAndValue(attributeType='cn', value=r'test'), dn.LDAPAttributeTypeAndValue(attributeType='owner', value=r'uid=foo,ou=depart' +r'ment,dc=example,dc=com'), ]), dn.RelativeDistinguishedName('dc=example'), dn.RelativeDistinguishedName('dc=com'), ]) got = str(got) self.assertEquals(got, r'cn=test+owner=uid\=foo\,ou\=depar' +r'tment\,dc\=example\,dc\=com,dc=ex' +r'ample,dc=com') class LDAPDistinguishedName_RFC2253_Examples(TestCaseWithKnownValues): knownValues = ( ('CN=Steve Kille,O=Isode Limited,C=GB', [[('CN', 'Steve Kille')], [('O', 'Isode Limited')], [('C', 'GB')]]), ('OU=Sales+CN=J. Smith,O=Widget Inc.,C=US', [[('OU', 'Sales'), ('CN', 'J. Smith')], [('O', 'Widget Inc.')], [('C', 'US')]]), (r'CN=L. Eagle,O=Sue\, Grabbit and Runn,C=GB', [[('CN', 'L. Eagle')], [('O', 'Sue, Grabbit and Runn')], [('C', 'GB')]]), (r'CN=Before\0DAfter,O=Test,C=GB', [[('CN', 'Before\x0dAfter')], [('O', 'Test')], [('C', 'GB')]]), (r'1.3.6.1.4.1.1466.0=#04024869,O=Test,C=GB', [[('1.3.6.1.4.1.1466.0', '#04024869')], [('O', 'Test')], [('C', 'GB')]]), (u'SN=Lu\u010di\u0107'.encode('utf-8'), [[('SN', u'Lu\u010di\u0107'.encode('utf-8'))]]) ) class LDAPDistinguishedName_InitialSpaces(TestCaseWithKnownValues): knownValues = ( ('cn=foo, ou=bar, dc=quux, \ attributeThatStartsWithSpace=Value', [[('cn', 'foo')], [('ou', 'bar')], [('dc', 'quux')], [(' attributeThatStartsWithSpace', 'Value')]]), ) class LDAPDistinguishedName_DomainName(unittest.TestCase): def testNonDc(self): d=dn.DistinguishedName('cn=foo,o=bar,c=us') assert d.getDomainName() is None def testNonTrailingDc(self): d=dn.DistinguishedName('cn=foo,o=bar,dc=foo,c=us') assert d.getDomainName() is None def testSimple_ExampleCom(self): d=dn.DistinguishedName('dc=example,dc=com') assert d.getDomainName() == 'example.com' def testSimple_SubExampleCom(self): d=dn.DistinguishedName('dc=sub,dc=example,dc=com') assert d.getDomainName() == 'sub.example.com' def testSimple_HostSubExampleCom(self): d=dn.DistinguishedName('cn=host,dc=sub,dc=example,dc=com') assert d.getDomainName() == 'sub.example.com' def testInterleaved_SubHostSubExampleCom(self): d=dn.DistinguishedName('dc=sub2,cn=host,dc=sub,dc=example,dc=com') assert d.getDomainName() == 'sub.example.com' class LDAPDistinguishedName_contains(unittest.TestCase): shsec=dn.DistinguishedName('dc=sub2,cn=host,dc=sub,dc=example,dc=com') hsec=dn.DistinguishedName('cn=host,dc=sub,dc=example,dc=com') sec=dn.DistinguishedName('dc=sub,dc=example,dc=com') ec=dn.DistinguishedName('dc=example,dc=com') c=dn.DistinguishedName('dc=com') soc=dn.DistinguishedName('dc=sub,dc=other,dc=com') oc=dn.DistinguishedName('dc=other,dc=com') other=dn.DistinguishedName('o=foo,c=US') root=dn.DistinguishedName('') def test_selfContainment(self): assert self.c.contains(self.c) assert self.ec.contains(self.ec) assert self.sec.contains(self.sec) assert self.hsec.contains(self.hsec) assert self.shsec.contains(self.shsec) assert self.soc.contains(self.soc) assert self.oc.contains(self.oc) assert self.root.contains(self.root) assert self.other.contains(self.other) def test_realContainment(self): assert self.c.contains(self.ec) assert self.c.contains(self.sec) assert self.c.contains(self.hsec) assert self.c.contains(self.shsec) assert self.ec.contains(self.sec) assert self.ec.contains(self.hsec) assert self.ec.contains(self.shsec) assert self.sec.contains(self.hsec) assert self.sec.contains(self.shsec) assert self.hsec.contains(self.shsec) assert self.c.contains(self.oc) assert self.c.contains(self.soc) assert self.oc.contains(self.soc) for x in (self.shsec, self.hsec, self.sec, self.ec, self.c, self.soc, self.oc, self.other): assert self.root.contains(x) def test_nonContainment_parents(self): assert not self.shsec.contains(self.hsec) assert not self.shsec.contains(self.sec) assert not self.shsec.contains(self.ec) assert not self.shsec.contains(self.c) assert not self.hsec.contains(self.sec) assert not self.hsec.contains(self.ec) assert not self.hsec.contains(self.c) assert not self.sec.contains(self.ec) assert not self.sec.contains(self.c) assert not self.ec.contains(self.c) assert not self.soc.contains(self.oc) for x in (self.shsec, self.hsec, self.sec, self.ec, self.c, self.soc, self.oc, self.other): assert not x.contains(self.root) def test_nonContainment_nonParents(self): groups=([self.shsec, self.hsec, self.sec, self.ec], [self.soc, self.oc], [self.other]) for g1 in groups: for g2 in groups: if g1!=g2: for i1 in g1: for i2 in g2: assert not i1.contains(i2) assert not self.c.contains(self.other) assert not self.other.contains(self.c) class LDAPDistinguishedName_Malformed(unittest.TestCase): def testMalformed(self): self.assertRaises(dn.InvalidRelativeDistinguishedName, dn.DistinguishedName, 'foo') self.assertRaises(dn.InvalidRelativeDistinguishedName, dn.DistinguishedName, 'foo,dc=com') self.assertRaises(dn.InvalidRelativeDistinguishedName, dn.DistinguishedName, 'ou=something,foo') self.assertRaises(dn.InvalidRelativeDistinguishedName, dn.DistinguishedName, 'foo,foo') class LDAPDistinguishedName_Prettify(unittest.TestCase): def testPrettifySpaces(self): """str(DistinguishedName(...)) prettifies the DN by removing extra whitespace.""" d=dn.DistinguishedName('cn=foo, o=bar, c=us') assert str(d) == 'cn=foo,o=bar,c=us' class DistinguishedName_Init(unittest.TestCase): def testString(self): d=dn.DistinguishedName('dc=example,dc=com') self.assertEquals(str(d), 'dc=example,dc=com') def testDN(self): proto=dn.DistinguishedName('dc=example,dc=com') d=dn.DistinguishedName(proto) self.assertEquals(str(d), 'dc=example,dc=com') class RelativeDistinguishedName_Init(unittest.TestCase): def testString(self): rdn=dn.RelativeDistinguishedName('dc=example') self.assertEquals(str(rdn), 'dc=example') def testRDN(self): proto=dn.RelativeDistinguishedName('dc=example') rdn=dn.RelativeDistinguishedName(proto) self.assertEquals(str(rdn), 'dc=example') class DistinguishedName_Comparison(unittest.TestCase): # TODO test more carefully def testGT(self): dn1=dn.DistinguishedName('dc=example,dc=com') dn2=dn.DistinguishedName('dc=bar,dc=example,dc=com') self.failUnless(dn1 > dn2) ldaptor-0.0.43/ldaptor/test/test_attributeset.py0000644000175000017500000000703510052211034020112 0ustar janjan""" Test cases for ldaptor.attributeset """ from twisted.trial import unittest import sets from ldaptor import attributeset class TestComparison(unittest.TestCase): def testEquality_True_Set(self): a = attributeset.LDAPAttributeSet('k', ['b', 'c', 'd']) b = attributeset.LDAPAttributeSet('k', ['b', 'c', 'd']) self.assertEquals(a, b) def testEquality_True_Set_Ordering(self): a = attributeset.LDAPAttributeSet('k', ['b', 'c', 'd']) b = attributeset.LDAPAttributeSet('k', ['b', 'd', 'c']) self.assertEquals(a, b) def testEquality_True_List(self): a = attributeset.LDAPAttributeSet('k', ['b', 'c', 'd']) b = ['b', 'c', 'd'] self.assertEquals(a, b) def testEquality_True_List_Ordering(self): a = attributeset.LDAPAttributeSet('k', ['b', 'c', 'd']) b = ['b', 'd', 'c'] self.assertEquals(a, b) def testEquality_False_Value(self): a = attributeset.LDAPAttributeSet('k', ['b', 'c', 'd']) b = attributeset.LDAPAttributeSet('k', ['b', 'c', 'e']) self.assertNotEqual(a, b) def testEquality_False_Key(self): a = attributeset.LDAPAttributeSet('k', ['b', 'c', 'd']) b = attributeset.LDAPAttributeSet('l', ['b', 'c', 'd']) self.assertNotEqual(a, b) class TestSetOperations(unittest.TestCase): def testDifference(self): a = attributeset.LDAPAttributeSet('k', ['b', 'c', 'd']) b = attributeset.LDAPAttributeSet('k', ['b', 'c', 'e']) self.assertEquals(a - b, sets.Set(['d'])) def testUnion(self): a = attributeset.LDAPAttributeSet('k', ['b', 'c', 'd']) b = attributeset.LDAPAttributeSet('k', ['b', 'c', 'e']) self.assertEquals(a | b, sets.Set(['b', 'c', 'd', 'e'])) def testIntersection(self): a = attributeset.LDAPAttributeSet('k', ['b', 'c', 'd']) b = attributeset.LDAPAttributeSet('k', ['b', 'c', 'e']) self.assertEquals(a & b, sets.Set(['b', 'c'])) def testSymmetricDifference(self): a = attributeset.LDAPAttributeSet('k', ['b', 'c', 'd']) b = attributeset.LDAPAttributeSet('k', ['b', 'c', 'e']) self.assertEquals(a ^ b, sets.Set(['d', 'e'])) def testCopy(self): class Magic: pass m1 = Magic() a = attributeset.LDAPAttributeSet('k', ['b', 'c', 'd', m1]) b = a.__copy__() self.assertEquals(a, b) self.assertNotIdentical(a, b) magicFromA = [val for val in a if isinstance(val, Magic)][0] magicFromB = [val for val in b if isinstance(val, Magic)][0] self.assertEquals(magicFromA, magicFromB) self.assertIdentical(magicFromA, magicFromB) a.update('x') self.assertEquals(a, sets.Set(['b', 'c', 'd', m1, 'x'])) self.assertEquals(b, sets.Set(['b', 'c', 'd', m1])) def testDeepCopy(self): class Magic: def __eq__(self, other): return isinstance(other, self.__class__) def __hash__(self): return 42 m1 = Magic() a = attributeset.LDAPAttributeSet('k', ['a', m1]) b = a.__deepcopy__({}) self.assertEquals(a, b) self.assertNotIdentical(a, b) magicFromA = [val for val in a if isinstance(val, Magic)][0] magicFromB = [val for val in b if isinstance(val, Magic)][0] self.assertEquals(magicFromA, magicFromB) self.assertNotIdentical(magicFromA, magicFromB) a.update('x') self.assertEquals(a, sets.Set(['a', m1, 'x'])) self.assertEquals(b, sets.Set(['a', m1])) ldaptor-0.0.43/ldaptor/test/test_autofill_samba.py0000644000175000017500000003347410345261504020377 0ustar janjan""" Test cases for ldaptor.protocols.ldap.autofill.sambaAccount module. """ import sets from twisted.trial import unittest from ldaptor.protocols.ldap import ldapsyntax from ldaptor.protocols.ldap.autofill import sambaAccount, sambaSamAccount from ldaptor import testutil class LDAPAutoFill_sambaAccount(unittest.TestCase): def testMustHaveObjectClass(self): """Test that Autofill_samba fails unless object is a sambaAccount.""" client = testutil.LDAPClientTestDriver() o=ldapsyntax.LDAPEntryWithAutoFill(client=client, dn='cn=foo,dc=example,dc=com', attributes={ 'objectClass': ['something', 'other'], }) autoFiller = sambaAccount.Autofill_samba() d = o.addAutofiller(autoFiller) def eb(val): client.assertNothingSent() val.trap(sambaAccount.ObjectMissingObjectClassException) d.addCallbacks(testutil.mustRaise, eb) return d def testDefaultSetting(self): """Test that fields get their default values.""" client = testutil.LDAPClientTestDriver() o=ldapsyntax.LDAPEntryWithAutoFill(client=client, dn='cn=foo,dc=example,dc=com', attributes={ 'objectClass': ['sambaAccount', 'other'], }) d = o.addAutofiller(sambaAccount.Autofill_samba()) def cb(dummy): client.assertNothingSent() self.failUnless('acctFlags' in o) self.failUnlessEqual(o['acctFlags'], ['[UX ]']) self.failUnless('pwdLastSet' in o) self.failUnlessEqual(o['pwdLastSet'], ['0']) self.failUnless('logonTime' in o) self.failUnlessEqual(o['logonTime'], ['0']) self.failUnless('logoffTime' in o) self.failUnlessEqual(o['logoffTime'], ['0']) self.failUnless('pwdCanChange' in o) self.failUnlessEqual(o['pwdCanChange'], ['0']) self.failUnless('pwdMustChange' in o) self.failUnlessEqual(o['pwdMustChange'], ['0']) d.addCallback(cb) return d def testRid(self): """Test that rid field is updated based on uidNumber.""" client = testutil.LDAPClientTestDriver() o=ldapsyntax.LDAPEntryWithAutoFill(client=client, dn='cn=foo,dc=example,dc=com', attributes={ 'objectClass': ['sambaAccount', 'other'], }) d = o.addAutofiller(sambaAccount.Autofill_samba()) def cb(dummy): client.assertNothingSent() o['uidNumber'] = ['1000'] self.failUnless('rid' in o) self.failUnlessEqual(o['rid'], [str(2*1000+1000)]) o['uidNumber'] = ['1001'] self.failUnlessEqual(o['rid'], [str(2*1001+1000)]) o['uidNumber'] = ['1002'] self.failUnlessEqual(o['rid'], [str(2*1002+1000)]) o['uidNumber'] = ['2000'] self.failUnlessEqual(o['rid'], [str(2*2000+1000)]) o['uidNumber'] = ['3000'] self.failUnlessEqual(o['rid'], [str(2*3000+1000)]) o['uidNumber'] = ['0'] self.failUnlessEqual(o['rid'], [str(2*0+1000)]) o['uidNumber'] = ['16000'] self.failUnlessEqual(o['rid'], [str(2*16000+1000)]) d.addCallback(cb) return d def testPrimaryGroupId(self): """Test that primaryGroupID field is updated based on gidNumber.""" client = testutil.LDAPClientTestDriver() o=ldapsyntax.LDAPEntryWithAutoFill(client=client, dn='cn=foo,dc=example,dc=com', attributes={ 'objectClass': ['sambaAccount', 'other'], }) d = o.addAutofiller(sambaAccount.Autofill_samba()) def cb(dummy): client.assertNothingSent() o['gidNumber'] = ['1000'] self.failUnless('primaryGroupID' in o) self.failUnlessEqual(o['primaryGroupID'], [str(2*1000+1001)]) o['gidNumber'] = ['1001'] self.failUnlessEqual(o['primaryGroupID'], [str(2*1001+1001)]) o['gidNumber'] = ['1002'] self.failUnlessEqual(o['primaryGroupID'], [str(2*1002+1001)]) o['gidNumber'] = ['2000'] self.failUnlessEqual(o['primaryGroupID'], [str(2*2000+1001)]) o['gidNumber'] = ['3000'] self.failUnlessEqual(o['primaryGroupID'], [str(2*3000+1001)]) o['gidNumber'] = ['0'] self.failUnlessEqual(o['primaryGroupID'], [str(2*0+1001)]) o['gidNumber'] = ['16000'] self.failUnlessEqual(o['primaryGroupID'], [str(2*16000+1001)]) d.addCallback(cb) return d class LDAPAutoFill_sambaSamAccount(unittest.TestCase): def testMustHaveObjectClass(self): """Test that Autofill_samba fails unless object is a sambaSamAccount.""" client = testutil.LDAPClientTestDriver() o=ldapsyntax.LDAPEntryWithAutoFill(client=client, dn='cn=foo,dc=example,dc=com', attributes={ 'objectClass': ['something', 'other'], }) autoFiller = sambaSamAccount.Autofill_samba(domainSID='foo') d = o.addAutofiller(autoFiller) def eb(val): client.assertNothingSent() val.trap(sambaSamAccount.ObjectMissingObjectClassException) d.addCallbacks(testutil.mustRaise, eb) return d def testDefaultSetting(self): """Test that fields get their default values.""" client = testutil.LDAPClientTestDriver() o=ldapsyntax.LDAPEntryWithAutoFill(client=client, dn='cn=foo,dc=example,dc=com', attributes={ 'objectClass': ['sambaSamAccount', 'other'], }) d = o.addAutofiller(sambaSamAccount.Autofill_samba(domainSID='foo')) def cb(dummy): client.assertNothingSent() self.failUnlessEqual(sets.Set(o.keys()), sets.Set([ 'objectClass', 'sambaAcctFlags', 'sambaLogoffTime', 'sambaLogonTime', 'sambaPwdCanChange', 'sambaPwdLastSet', 'sambaPwdMustChange', ])) self.failUnlessEqual(o['sambaAcctFlags'], ['[UX ]']) self.failUnlessEqual(o['sambaPwdLastSet'], ['0']) self.failUnlessEqual(o['sambaLogonTime'], ['0']) self.failUnlessEqual(o['sambaLogoffTime'], ['0']) self.failUnlessEqual(o['sambaPwdCanChange'], ['0']) self.failUnlessEqual(o['sambaPwdMustChange'], ['0']) d.addCallback(cb) return d def testDefaultSetting_fixedPrimaryGroupSID(self): """Test that fields get their default values.""" client = testutil.LDAPClientTestDriver() o=ldapsyntax.LDAPEntryWithAutoFill(client=client, dn='cn=foo,dc=example,dc=com', attributes={ 'objectClass': ['sambaSamAccount', 'other'], }) d = o.addAutofiller(sambaSamAccount.Autofill_samba(domainSID='foo', fixedPrimaryGroupSID=4131312)) def cb(dummy): client.assertNothingSent() self.failUnlessEqual(sets.Set(o.keys()), sets.Set([ 'objectClass', 'sambaAcctFlags', 'sambaLogoffTime', 'sambaLogonTime', 'sambaPwdCanChange', 'sambaPwdLastSet', 'sambaPwdMustChange', 'sambaPrimaryGroupSID', ])) self.failUnlessEqual(o['sambaPrimaryGroupSID'], ['foo-4131312']) self.failUnlessEqual(o['sambaAcctFlags'], ['[UX ]']) self.failUnlessEqual(o['sambaPwdLastSet'], ['0']) self.failUnlessEqual(o['sambaLogonTime'], ['0']) self.failUnlessEqual(o['sambaLogoffTime'], ['0']) self.failUnlessEqual(o['sambaPwdCanChange'], ['0']) self.failUnlessEqual(o['sambaPwdMustChange'], ['0']) d.addCallback(cb) return d def testSambaSID(self): """Test that sambaSID field is updated based on uidNumber.""" client = testutil.LDAPClientTestDriver() o=ldapsyntax.LDAPEntryWithAutoFill(client=client, dn='cn=foo,dc=example,dc=com', attributes={ 'objectClass': ['sambaSamAccount', 'other'], }) d = o.addAutofiller(sambaSamAccount.Autofill_samba(domainSID='foo')) def cb(dummy): client.assertNothingSent() o['uidNumber'] = ['1000'] self.failUnless('sambaSID' in o) self.failUnlessEqual(o['sambaSID'], ['foo-%s' % (2*1000+1000)]) o['uidNumber'] = ['1001'] self.failUnlessEqual(o['sambaSID'], ['foo-%s' % (2*1001+1000)]) o['uidNumber'] = ['1002'] self.failUnlessEqual(o['sambaSID'], ['foo-%s' % (2*1002+1000)]) o['uidNumber'] = ['2000'] self.failUnlessEqual(o['sambaSID'], ['foo-%s' % (2*2000+1000)]) o['uidNumber'] = ['3000'] self.failUnlessEqual(o['sambaSID'], ['foo-%s' % (2*3000+1000)]) o['uidNumber'] = ['0'] self.failUnlessEqual(o['sambaSID'], ['foo-%s' % (2*0+1000)]) o['uidNumber'] = ['16000'] self.failUnlessEqual(o['sambaSID'], ['foo-%s' % (2*16000+1000)]) d.addCallback(cb) return d def testSambaSID_preExisting(self): """Test that sambaSID field is updated based on uidNumber.""" client = testutil.LDAPClientTestDriver() o=ldapsyntax.LDAPEntryWithAutoFill(client=client, dn='cn=foo,dc=example,dc=com', attributes={ 'objectClass': ['sambaSamAccount', 'other'], 'uidNumber': ['1000'], }) d = o.addAutofiller(sambaSamAccount.Autofill_samba(domainSID='foo')) def cb(dummy): client.assertNothingSent() self.failUnless('sambaSID' in o) self.failUnlessEqual(o['sambaSID'], ['foo-%s' % (2*1000+1000)]) d.addCallback(cb) return d def testSambaPrimaryGroupSID(self): """Test that sambaPrimaryGroupSID field is updated based on gidNumber.""" client = testutil.LDAPClientTestDriver() o=ldapsyntax.LDAPEntryWithAutoFill(client=client, dn='cn=foo,dc=example,dc=com', attributes={ 'objectClass': ['sambaSamAccount', 'other'], }) d = o.addAutofiller(sambaSamAccount.Autofill_samba(domainSID='foo')) def cb(dummy): client.assertNothingSent() o['gidNumber'] = ['1000'] self.failUnless('sambaPrimaryGroupSID' in o) self.failUnlessEqual(o['sambaPrimaryGroupSID'], ['foo-%s' % (2*1000+1001)]) o['gidNumber'] = ['1001'] self.failUnlessEqual(o['sambaPrimaryGroupSID'], ['foo-%s' % (2*1001+1001)]) o['gidNumber'] = ['1002'] self.failUnlessEqual(o['sambaPrimaryGroupSID'], ['foo-%s' % (2*1002+1001)]) o['gidNumber'] = ['2000'] self.failUnlessEqual(o['sambaPrimaryGroupSID'], ['foo-%s' % (2*2000+1001)]) o['gidNumber'] = ['3000'] self.failUnlessEqual(o['sambaPrimaryGroupSID'], ['foo-%s' % (2*3000+1001)]) o['gidNumber'] = ['0'] self.failUnlessEqual(o['sambaPrimaryGroupSID'], ['foo-%s' % (2*0+1001)]) o['gidNumber'] = ['16000'] self.failUnlessEqual(o['sambaPrimaryGroupSID'], ['foo-%s' % (2*16000+1001)]) d.addCallback(cb) return d def testSambaPrimaryGroupSID_preExisting(self): """Test that sambaPrimaryGroupSID field is updated based on gidNumber.""" client = testutil.LDAPClientTestDriver() o=ldapsyntax.LDAPEntryWithAutoFill(client=client, dn='cn=foo,dc=example,dc=com', attributes={ 'objectClass': ['sambaSamAccount', 'other'], 'gidNumber': ['1000'], }) d = o.addAutofiller(sambaSamAccount.Autofill_samba(domainSID='foo')) def cb(dummy): client.assertNothingSent() self.failUnless('sambaPrimaryGroupSID' in o) self.failUnlessEqual(o['sambaPrimaryGroupSID'], ['foo-%s' % (2*1000+1001)]) d.addCallback(cb) return d def testSambaPrimaryGroupSID_notUpdatedWhenFixed(self): """Test that sambaPrimaryGroupSID field is updated based on gidNumber.""" client = testutil.LDAPClientTestDriver() o=ldapsyntax.LDAPEntryWithAutoFill(client=client, dn='cn=foo,dc=example,dc=com', attributes={ 'objectClass': ['sambaSamAccount', 'other'], }) d = o.addAutofiller(sambaSamAccount.Autofill_samba(domainSID='foo', fixedPrimaryGroupSID=4242)) def cb(dummy): client.assertNothingSent() self.failUnless('sambaPrimaryGroupSID' in o) self.failUnlessEqual(o['sambaPrimaryGroupSID'], ['foo-4242']) o['gidNumber'] = ['1000'] self.failUnless('sambaPrimaryGroupSID' in o) self.failUnlessEqual(o['sambaPrimaryGroupSID'], ['foo-4242']) d.addCallback(cb) return d ldaptor-0.0.43/ldaptor/test/test_server.py0000644000175000017500000006004510365661425016725 0ustar janjan""" Test cases for ldaptor.protocols.ldap.ldapserver module. """ from twisted.trial import unittest import sets, base64 from twisted.internet import protocol, address from twisted.python import components from ldaptor import inmemory, interfaces, schema, delta, entry from ldaptor.protocols.ldap import ldapserver, ldapclient, ldaperrors, fetchschema from ldaptor.protocols import pureldap, pureber from twisted.test import proto_helpers from ldaptor.test import util, test_schema class LDAPServerTest(unittest.TestCase): def setUp(self): self.root = inmemory.ReadOnlyInMemoryLDAPEntry( dn='dc=example,dc=com', attributes={ 'dc': 'example', }) self.stuff = self.root.addChild( rdn='ou=stuff', attributes={ 'objectClass': ['a', 'b'], 'ou': ['stuff'], }) self.thingie = self.stuff.addChild( rdn='cn=thingie', attributes={ 'objectClass': ['a', 'b'], 'cn': ['thingie'], }) self.another = self.stuff.addChild( rdn='cn=another', attributes={ 'objectClass': ['a', 'b'], 'cn': ['another'], }) server = ldapserver.LDAPServer() server.factory = self.root server.transport = proto_helpers.StringTransport() server.connectionMade() self.server = server def test_bind(self): self.server.dataReceived(str(pureldap.LDAPMessage(pureldap.LDAPBindRequest(), id=4))) self.assertEquals(self.server.transport.value(), str(pureldap.LDAPMessage(pureldap.LDAPBindResponse(resultCode=0), id=4))) def test_bind_success(self): self.thingie['userPassword'] = ['{SSHA}yVLLj62rFf3kDAbzwEU0zYAVvbWrze8='] # "secret" self.server.dataReceived(str(pureldap.LDAPMessage(pureldap.LDAPBindRequest( dn='cn=thingie,ou=stuff,dc=example,dc=com', auth='secret'), id=4))) self.assertEquals(self.server.transport.value(), str(pureldap.LDAPMessage( pureldap.LDAPBindResponse(resultCode=0, matchedDN='cn=thingie,ou=stuff,dc=example,dc=com'), id=4))) def test_bind_invalidCredentials_badPassword(self): self.server.dataReceived(str(pureldap.LDAPMessage( pureldap.LDAPBindRequest(dn='cn=thingie,ou=stuff,dc=example,dc=com', auth='invalid'), id=734))) self.assertEquals(self.server.transport.value(), str(pureldap.LDAPMessage( pureldap.LDAPBindResponse( resultCode=ldaperrors.LDAPInvalidCredentials.resultCode), id=734))) def test_bind_invalidCredentials_nonExisting(self): self.server.dataReceived(str(pureldap.LDAPMessage( pureldap.LDAPBindRequest(dn='cn=non-existing,dc=example,dc=com', auth='invalid'), id=78))) self.assertEquals(self.server.transport.value(), str(pureldap.LDAPMessage( pureldap.LDAPBindResponse( resultCode=ldaperrors.LDAPInvalidCredentials.resultCode), id=78))) def test_bind_badVersion_1_anonymous(self): self.server.dataReceived(str(pureldap.LDAPMessage( pureldap.LDAPBindRequest(version=1), id=32))) self.assertEquals(self.server.transport.value(), str(pureldap.LDAPMessage( pureldap.LDAPBindResponse( resultCode=ldaperrors.LDAPProtocolError.resultCode, errorMessage='Version 1 not supported'), id=32))) def test_bind_badVersion_2_anonymous(self): self.server.dataReceived(str(pureldap.LDAPMessage( pureldap.LDAPBindRequest(version=2), id=32))) self.assertEquals(self.server.transport.value(), str(pureldap.LDAPMessage( pureldap.LDAPBindResponse( resultCode=ldaperrors.LDAPProtocolError.resultCode, errorMessage='Version 2 not supported'), id=32))) def test_bind_badVersion_4_anonymous(self): self.server.dataReceived(str(pureldap.LDAPMessage( pureldap.LDAPBindRequest(version=4), id=32))) self.assertEquals(self.server.transport.value(), str(pureldap.LDAPMessage( pureldap.LDAPBindResponse( resultCode=ldaperrors.LDAPProtocolError.resultCode, errorMessage='Version 4 not supported'), id=32))) def test_bind_badVersion_4_nonExisting(self): # TODO make a test just like this one that would pass authentication # if version was correct, to ensure we don't leak that info either. self.server.dataReceived(str(pureldap.LDAPMessage( pureldap.LDAPBindRequest(version=4, dn='cn=non-existing,dc=example,dc=com', auth='invalid'), id=11))) self.assertEquals(self.server.transport.value(), str(pureldap.LDAPMessage( pureldap.LDAPBindResponse( resultCode=ldaperrors.LDAPProtocolError.resultCode, errorMessage='Version 4 not supported'), id=11))) def test_unbind(self): self.server.dataReceived(str(pureldap.LDAPMessage(pureldap.LDAPUnbindRequest(), id=7))) self.assertEquals(self.server.transport.value(), '') def test_search_outOfTree(self): self.server.dataReceived(str(pureldap.LDAPMessage( pureldap.LDAPSearchRequest( baseObject='dc=invalid', ), id=2))) self.assertEquals(self.server.transport.value(), str(pureldap.LDAPMessage( pureldap.LDAPSearchResultDone(resultCode=ldaperrors.LDAPNoSuchObject.resultCode), id=2)), ) def test_search_matchAll_oneResult(self): self.server.dataReceived(str(pureldap.LDAPMessage( pureldap.LDAPSearchRequest( baseObject='cn=thingie,ou=stuff,dc=example,dc=com', ), id=2))) self.assertEquals(self.server.transport.value(), str(pureldap.LDAPMessage( pureldap.LDAPSearchResultEntry( objectName='cn=thingie,ou=stuff,dc=example,dc=com', attributes=[ ('objectClass', ['a', 'b']), ('cn', ['thingie']), ]), id=2)) + str(pureldap.LDAPMessage( pureldap.LDAPSearchResultDone(resultCode=0), id=2)), ) def test_search_matchAll_manyResults(self): self.server.dataReceived(str(pureldap.LDAPMessage( pureldap.LDAPSearchRequest( baseObject='ou=stuff,dc=example,dc=com', ), id=2))) self.assertEquals(self.server.transport.value(), str(pureldap.LDAPMessage( pureldap.LDAPSearchResultEntry( objectName='ou=stuff,dc=example,dc=com', attributes=[ ('objectClass', ['a', 'b']), ('ou', ['stuff']), ]), id=2)) + str(pureldap.LDAPMessage( pureldap.LDAPSearchResultEntry( objectName='cn=another,ou=stuff,dc=example,dc=com', attributes=[ ('objectClass', ['a', 'b']), ('cn', ['another']), ]), id=2)) + str(pureldap.LDAPMessage( pureldap.LDAPSearchResultEntry( objectName='cn=thingie,ou=stuff,dc=example,dc=com', attributes=[ ('objectClass', ['a', 'b']), ('cn', ['thingie']), ]), id=2)) + str(pureldap.LDAPMessage( pureldap.LDAPSearchResultDone(resultCode=0), id=2)), ) def test_search_scope_oneLevel(self): self.server.dataReceived(str(pureldap.LDAPMessage( pureldap.LDAPSearchRequest( baseObject='ou=stuff,dc=example,dc=com', scope=pureldap.LDAP_SCOPE_singleLevel, ), id=2))) self.assertEquals(self.server.transport.value(), str(pureldap.LDAPMessage( pureldap.LDAPSearchResultEntry( objectName='cn=thingie,ou=stuff,dc=example,dc=com', attributes=[ ('objectClass', ['a', 'b']), ('cn', ['thingie']), ]), id=2)) + str(pureldap.LDAPMessage( pureldap.LDAPSearchResultEntry( objectName='cn=another,ou=stuff,dc=example,dc=com', attributes=[ ('objectClass', ['a', 'b']), ('cn', ['another']), ]), id=2)) + str(pureldap.LDAPMessage( pureldap.LDAPSearchResultDone(resultCode=0), id=2)), ) def test_search_scope_wholeSubtree(self): self.server.dataReceived(str(pureldap.LDAPMessage( pureldap.LDAPSearchRequest( baseObject='ou=stuff,dc=example,dc=com', scope=pureldap.LDAP_SCOPE_wholeSubtree, ), id=2))) self.assertEquals(self.server.transport.value(), str(pureldap.LDAPMessage( pureldap.LDAPSearchResultEntry( objectName='ou=stuff,dc=example,dc=com', attributes=[ ('objectClass', ['a', 'b']), ('ou', ['stuff']), ]), id=2)) + str(pureldap.LDAPMessage( pureldap.LDAPSearchResultEntry( objectName='cn=another,ou=stuff,dc=example,dc=com', attributes=[ ('objectClass', ['a', 'b']), ('cn', ['another']), ]), id=2)) + str(pureldap.LDAPMessage( pureldap.LDAPSearchResultEntry( objectName='cn=thingie,ou=stuff,dc=example,dc=com', attributes=[ ('objectClass', ['a', 'b']), ('cn', ['thingie']), ]), id=2)) + str(pureldap.LDAPMessage( pureldap.LDAPSearchResultDone(resultCode=0), id=2)), ) def test_search_scope_baseObject(self): self.server.dataReceived(str(pureldap.LDAPMessage( pureldap.LDAPSearchRequest( baseObject='ou=stuff,dc=example,dc=com', scope=pureldap.LDAP_SCOPE_baseObject, ), id=2))) self.assertEquals(self.server.transport.value(), str(pureldap.LDAPMessage( pureldap.LDAPSearchResultEntry( objectName='ou=stuff,dc=example,dc=com', attributes=[ ('objectClass', ['a', 'b']), ('ou', ['stuff']), ]), id=2)) + str(pureldap.LDAPMessage( pureldap.LDAPSearchResultDone(resultCode=0), id=2)), ) def test_rootDSE(self): self.server.dataReceived(str(pureldap.LDAPMessage( pureldap.LDAPSearchRequest( baseObject='', scope=pureldap.LDAP_SCOPE_baseObject, filter=pureldap.LDAPFilter_present('objectClass'), ), id=2))) self.assertEquals(self.server.transport.value(), str(pureldap.LDAPMessage( pureldap.LDAPSearchResultEntry( objectName='', attributes=[ ('supportedLDAPVersion', ['3']), ('namingContexts', ['dc=example,dc=com']), ('supportedExtension', [ pureldap.LDAPPasswordModifyRequest.oid, ]), ]), id=2)) + str(pureldap.LDAPMessage( pureldap.LDAPSearchResultDone(resultCode=ldaperrors.Success.resultCode), id=2)), ) def test_delete(self): self.server.dataReceived(str(pureldap.LDAPMessage( pureldap.LDAPDelRequest(str(self.thingie.dn)), id=2))) self.assertEquals(self.server.transport.value(), str(pureldap.LDAPMessage( pureldap.LDAPDelResponse(resultCode=0), id=2)), ) d = self.stuff.children() d.addCallback(self.assertEquals, [self.another]) return d def test_add_success(self): dn = 'cn=new,ou=stuff,dc=example,dc=com' self.server.dataReceived(str(pureldap.LDAPMessage( pureldap.LDAPAddRequest(entry=dn, attributes=[ (pureldap.LDAPAttributeDescription("objectClass"), pureber.BERSet(value=[ pureldap.LDAPAttributeValue('something'), ])), ]), id=2))) self.assertEquals(self.server.transport.value(), str(pureldap.LDAPMessage( pureldap.LDAPAddResponse( resultCode=ldaperrors.Success.resultCode), id=2)), ) # tree changed d = self.stuff.children() d.addCallback(self.assertEquals, [ self.thingie, self.another, inmemory.ReadOnlyInMemoryLDAPEntry( 'cn=new,ou=stuff,dc=example,dc=com', {'objectClass': ['something']}), ]) return d def test_add_fail_existsAlready(self): self.server.dataReceived(str(pureldap.LDAPMessage( pureldap.LDAPAddRequest(entry=str(self.thingie.dn), attributes=[ (pureldap.LDAPAttributeDescription("objectClass"), pureber.BERSet(value=[ pureldap.LDAPAttributeValue('something'), ])), ]), id=2))) self.assertEquals(self.server.transport.value(), str(pureldap.LDAPMessage( pureldap.LDAPAddResponse( resultCode=ldaperrors.LDAPEntryAlreadyExists.resultCode, errorMessage=str(self.thingie.dn)), id=2)), ) # tree did not change d = self.stuff.children() d.addCallback(self.assertEquals, [self.thingie, self.another]) return d def test_modifyDN_rdnOnly_deleteOldRDN_success(self): newrdn = 'cn=thingamagic' self.server.dataReceived(str(pureldap.LDAPMessage( pureldap.LDAPModifyDNRequest(entry=self.thingie.dn, newrdn=newrdn, deleteoldrdn=True), id=2))) self.assertEquals(self.server.transport.value(), str(pureldap.LDAPMessage( pureldap.LDAPModifyDNResponse( resultCode=ldaperrors.Success.resultCode), id=2)), ) # tree changed d = self.stuff.children() d.addCallback(self.assertEquals, [ inmemory.ReadOnlyInMemoryLDAPEntry( '%s,ou=stuff,dc=example,dc=com' % newrdn, {'objectClass': ['a', 'b'], 'cn': ['thingamagic']}), self.another, ]) return d def test_modifyDN_rdnOnly_noDeleteOldRDN_success(self): newrdn = 'cn=thingamagic' self.server.dataReceived(str(pureldap.LDAPMessage( pureldap.LDAPModifyDNRequest(entry=self.thingie.dn, newrdn=newrdn, deleteoldrdn=False), id=2))) self.assertEquals(self.server.transport.value(), str(pureldap.LDAPMessage( pureldap.LDAPModifyDNResponse( resultCode=ldaperrors.Success.resultCode), id=2)), ) # tree changed d = self.stuff.children() d.addCallback(self.assertEquals, sets.Set([ self.another, inmemory.ReadOnlyInMemoryLDAPEntry( '%s,ou=stuff,dc=example,dc=com' % newrdn, {'objectClass': ['a', 'b'], 'cn': ['thingamagic', 'thingie']}), ])) return d test_modifyDN_rdnOnly_noDeleteOldRDN_success.todo = 'Not supported yet.' def test_modify(self): self.server.dataReceived(str(pureldap.LDAPMessage( pureldap.LDAPModifyRequest(self.stuff.dn, modification=[ delta.Add('foo', ['bar']).asLDAP(), ], ), id=2))) self.assertEquals(self.server.transport.value(), str(pureldap.LDAPMessage( pureldap.LDAPModifyResponse( resultCode=ldaperrors.Success.resultCode), id=2)), ) # tree changed self.assertEquals( self.stuff, inmemory.ReadOnlyInMemoryLDAPEntry( 'ou=stuff,dc=example,dc=com', {'objectClass': ['a', 'b'], 'ou': ['stuff'], 'foo': ['bar']})) def test_extendedRequest_unknown(self): self.server.dataReceived(str(pureldap.LDAPMessage( pureldap.LDAPExtendedRequest(requestName='42.42.42', requestValue='foo'), id=2))) self.assertEquals(self.server.transport.value(), str(pureldap.LDAPMessage( pureldap.LDAPExtendedResponse( resultCode=ldaperrors.LDAPProtocolError.resultCode, errorMessage='Unknown extended request: 42.42.42'), id=2)), ) def test_passwordModify_notBound(self): self.server.dataReceived(str(pureldap.LDAPMessage( pureldap.LDAPPasswordModifyRequest( userIdentity='cn=thingie,ou=stuff,dc=example,dc=com', newPasswd='hushhush'), id=2))) self.assertEquals(self.server.transport.value(), str(pureldap.LDAPMessage( pureldap.LDAPExtendedResponse( resultCode=ldaperrors.LDAPStrongAuthRequired.resultCode, responseName=pureldap.LDAPPasswordModifyRequest.oid), id=2)), ) def test_passwordModify_simple(self): # first bind to some entry self.thingie['userPassword'] = ['{SSHA}yVLLj62rFf3kDAbzwEU0zYAVvbWrze8='] # "secret" self.server.dataReceived(str(pureldap.LDAPMessage(pureldap.LDAPBindRequest( dn='cn=thingie,ou=stuff,dc=example,dc=com', auth='secret'), id=4))) self.assertEquals(self.server.transport.value(), str(pureldap.LDAPMessage( pureldap.LDAPBindResponse(resultCode=0, matchedDN='cn=thingie,ou=stuff,dc=example,dc=com'), id=4))) self.server.transport.clear() self.server.dataReceived(str(pureldap.LDAPMessage( pureldap.LDAPPasswordModifyRequest( userIdentity='cn=thingie,ou=stuff,dc=example,dc=com', newPasswd='hushhush'), id=2))) self.assertEquals(self.server.transport.value(), str(pureldap.LDAPMessage( pureldap.LDAPExtendedResponse( resultCode=ldaperrors.Success.resultCode, responseName=pureldap.LDAPPasswordModifyRequest.oid), id=2)), ) # tree changed secrets = self.thingie.get('userPassword', []) self.assertEquals(len(secrets), 1) for secret in secrets: self.assertEquals(secret[:len('{SSHA}')], '{SSHA}') raw = base64.decodestring(secret[len('{SSHA}'):]) salt = raw[20:] self.assertEquals(entry.sshaDigest('hushhush', salt), secret) def test_unknownRequest(self): # make server miss one of the handle_* attributes # without having to modify the LDAPServer class class MockServer(ldapserver.LDAPServer): handle_LDAPBindRequest = property() self.server.__class__ = MockServer self.server.dataReceived(str(pureldap.LDAPMessage( pureldap.LDAPBindRequest(), id=2))) self.assertEquals(self.server.transport.value(), str(pureldap.LDAPMessage( pureldap.LDAPExtendedResponse(resultCode=ldaperrors.LDAPProtocolError.resultCode, responseName='1.3.6.1.4.1.1466.20036', errorMessage='Unknown request'), id=2))) def test_control_unknown_critical(self): self.server.dataReceived(str(pureldap.LDAPMessage( pureldap.LDAPBindRequest(), id=2, controls=[('42.42.42.42', True, None), ]))) self.assertEquals(self.server.transport.value(), str(pureldap.LDAPMessage( pureldap.LDAPBindResponse( resultCode=ldaperrors.LDAPUnavailableCriticalExtension.resultCode, errorMessage='Unknown control 42.42.42.42'), id=2))) def test_control_unknown_nonCritical(self): self.thingie['userPassword'] = ['{SSHA}yVLLj62rFf3kDAbzwEU0zYAVvbWrze8='] # "secret" self.server.dataReceived(str(pureldap.LDAPMessage( pureldap.LDAPBindRequest(dn='cn=thingie,ou=stuff,dc=example,dc=com', auth='secret'), controls=[('42.42.42.42', False, None)], id=4))) self.assertEquals(self.server.transport.value(), str(pureldap.LDAPMessage( pureldap.LDAPBindResponse(resultCode=0, matchedDN='cn=thingie,ou=stuff,dc=example,dc=com'), id=4))) class TestSchema(unittest.TestCase): def setUp(self): db = inmemory.ReadOnlyInMemoryLDAPEntry('', {}) com = db.addChild('dc=com', {'objectClass': ['dcObject'], 'dc': ['com'], }) com.addChild('dc=example', {'objectClass': ['dcObject'], 'dc': ['example'], 'subschemaSubentry': ['cn=schema'], }) db.addChild('cn=schema', {'objectClass': ['TODO'], 'cn': ['schema'], 'attributeTypes': [test_schema.AttributeType_KnownValues.knownValues[0][0]], 'objectClasses': [test_schema.OBJECTCLASSES['organization'], test_schema.OBJECTCLASSES['organizationalUnit'], ], }) class LDAPServerFactory(protocol.ServerFactory): protocol = ldapserver.LDAPServer def __init__(self, root): self.root = root components.registerAdapter(lambda x: x.root, LDAPServerFactory, interfaces.IConnectedLDAPEntry) serverFactory = LDAPServerFactory(db) self.client = ldapclient.LDAPClient() server = serverFactory.buildProtocol(address.IPv4Address('TCP', 'localhost', '1024')) util.returnConnected(server, self.client) def testSimple(self): d = fetchschema.fetch(self.client, 'dc=example,dc=com') (attributeTypes, objectClasses) = util.pumpingDeferredResult(d) self.failUnlessEqual([str(x) for x in attributeTypes], [str(schema.AttributeTypeDescription(x)) for x in [ test_schema.AttributeType_KnownValues.knownValues[0][0], ]]) self.failUnlessEqual([str(x) for x in objectClasses], [str(schema.ObjectClassDescription(x)) for x in [ test_schema.OBJECTCLASSES['organization'], test_schema.OBJECTCLASSES['organizationalUnit'], ]]) ldaptor-0.0.43/ldaptor/test/test_pureber.py0000644000175000017500000004015110345573607017061 0ustar janjan# Ldaptor -- TODO # Copyright (C) 2001 Matthew W. Lefkowitz # # This library is free software; you can redistribute it and/or # modify it under the terms of version 2.1 of the GNU Lesser General Public # License as published by the Free Software Foundation. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA """ Test cases for ldaptor.protocols.pureber module. """ from twisted.trial import unittest from ldaptor.protocols import pureber import types def s(*l): """Join all members of list to a string. Integer members are chr()ed""" r='' for e in l: if isinstance(e, types.IntType): e=chr(e) r=r+str(e) return r def l(s): """Split a string to ord's of chars.""" return map(lambda x: ord(x), s) class BerLengths(unittest.TestCase): knownValues=( (0, [0]), (1, [1]), (100, [100]), (126, [126]), (127, [127]), (128, [0x80|1, 128]), (129, [0x80|1, 129]), (255, [0x80|1, 255]), (256, [0x80|2, 1, 0]), (257, [0x80|2, 1, 1]), (65535, [0x80|2, 0xFF, 0xFF]), (65536, [0x80|3, 0x01, 0x00, 0x00]), (256**127-1, [0x80|127]+127*[0xFF]), ) def testToBER(self): for integer, encoded in self.knownValues: got = pureber.int2berlen(integer) got = str(got) got = map(ord, got) self.assertEquals(got, encoded) def testFromBER(self): for integer, encoded in self.knownValues: m=s(*encoded) got, bytes = pureber.berDecodeLength(m) self.assertEquals(bytes, len(m)) self.assertEquals(got, integer) def testPartialBER(self): m=str(pureber.int2berlen(3*256)) assert len(m)==3 self.assertRaises(pureber.BERExceptionInsufficientData, pureber.berDecodeLength, m[:2]) self.assertRaises(pureber.BERExceptionInsufficientData, pureber.berDecodeLength, m[:1]) m=str(pureber.int2berlen(256**100-1)) assert len(m)==101 self.assertRaises(pureber.BERExceptionInsufficientData, pureber.berDecodeLength, m[:100]) class BERBaseEquality(unittest.TestCase): valuesToTest=( (pureber.BERInteger, [0]), (pureber.BERInteger, [1]), (pureber.BERInteger, [4000]), (pureber.BERSequence, [[pureber.BERInteger(1000), pureber.BERInteger(2000)]]), (pureber.BERSequence, [[pureber.BERInteger(2000), pureber.BERInteger(1000)]]), (pureber.BEROctetString, ["foo"]), (pureber.BEROctetString, ["b"+chr(0xe4)+chr(0xe4)]), ) def testBERBaseEquality(self): """BER objects equal BER objects with same type and content""" for class_, args in self.valuesToTest: x=class_(*args) y=class_(*args) assert x==x assert x==y def testBERBaseInEquality(self): """BER objects do not equal BER objects with different type or content""" for i in xrange(len(self.valuesToTest)): for j in xrange(len(self.valuesToTest)): if i!=j: i_class, i_args = self.valuesToTest[i] j_class, j_args = self.valuesToTest[j] x=i_class(*i_args) y=j_class(*j_args) assert x!=y class BERIntegerKnownValues(unittest.TestCase): knownValues=( (0, [0x02, 0x01, 0]), (1, [0x02, 0x01, 1]), (2, [0x02, 0x01, 2]), (125, [0x02, 0x01, 125]), (126, [0x02, 0x01, 126]), (127, [0x02, 0x01, 127]), (-1, [0x02, 0x01, 256-1]), (-2, [0x02, 0x01, 256-2]), (-3, [0x02, 0x01, 256-3]), (-126, [0x02, 0x01, 256-126]), (-127, [0x02, 0x01, 256-127]), (-128, [0x02, 0x01, 256-128]), (-129, [0x02, 0x02, 256-1, 256-129]), (128, [0x02, 0x02, 0, 128]), (256, [0x02, 0x02, 1, 0]), ) def testToBERIntegerKnownValues(self): """str(BERInteger(n)) should give known result with known input""" for integer, encoded in self.knownValues: result = pureber.BERInteger(integer) result = str(result) result = map(ord, result) assert encoded==result def testFromBERIntegerKnownValues(self): """BERInteger(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.BERInteger) result = result.value assert integer==result def testPartialBERIntegerEncodings(self): """BERInteger(encoded="...") with too short input should throw BERExceptionInsufficientData""" m=str(pureber.BERInteger(42)) assert len(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(), '')) class BERIntegerSanityCheck(unittest.TestCase): def testSanity(self): """BERInteger(encoded=BERInteger(n)).value==n for -1000..1000""" for n in range(-1000, 1001, 10): encoded = str(pureber.BERInteger(n)) result, bytes = pureber.berDecodeObject(pureber.BERDecoderContext(), encoded) self.assertEquals(bytes, len(encoded)) assert isinstance(result, pureber.BERInteger) result = result.value assert n==result class ObjectThatCanStringify(object): def __str__(self): return "bar" class BEROctetStringKnownValues(unittest.TestCase): knownValues=( ("", [0x04, 0]), ("foo", [0x04, 3]+l("foo")), (100*"x", [0x04, 100]+l(100*"x")), (ObjectThatCanStringify(), [0x04, 3]+l("bar")), ) def testToBEROctetStringKnownValues(self): """str(BEROctetString(n)) should give known result with known input""" for st, encoded in self.knownValues: result = pureber.BEROctetString(st) result = str(result) result = map(ord, result) assert encoded==result 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 def testPartialBEROctetStringEncodings(self): """BEROctetString(encoded="...") with too short input should throw BERExceptionInsufficientData""" m=str(pureber.BEROctetString("x")) assert len(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(), '')) class BEROctetStringSanityCheck(unittest.TestCase): 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 class BERNullKnownValues(unittest.TestCase): def testToBERNullKnownValues(self): """str(BERNull()) should give known result""" result = pureber.BERNull() result = str(result) result = map(ord, result) assert [0x05, 0x00]==result 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 def testPartialBERNullEncodings(self): """BERNull(encoded="...") with too short input should throw BERExceptionInsufficientData""" m=str(pureber.BERNull()) assert len(m)==2 self.assertRaises(pureber.BERExceptionInsufficientData, pureber.berDecodeObject, pureber.BERDecoderContext(), m[:1]) self.assertEquals((None, 0), pureber.berDecodeObject(pureber.BERDecoderContext(), '')) class BERBooleanKnownValues(unittest.TestCase): knownValues=( (0, [0x01, 0x01, 0], 0), (1, [0x01, 0x01, 0xFF], 0xFF), (2, [0x01, 0x01, 0xFF], 0xFF), (125, [0x01, 0x01, 0xFF], 0xFF), (126, [0x01, 0x01, 0xFF], 0xFF), (127, [0x01, 0x01, 0xFF], 0xFF), (-1, [0x01, 0x01, 0xFF], 0xFF), (-2, [0x01, 0x01, 0xFF], 0xFF), (-3, [0x01, 0x01, 0xFF], 0xFF), (-126, [0x01, 0x01, 0xFF], 0xFF), (-127, [0x01, 0x01, 0xFF], 0xFF), (-128, [0x01, 0x01, 0xFF], 0xFF), (-129, [0x01, 0x01, 0xFF], 0xFF), (-9999, [0x01, 0x01, 0xFF], 0xFF), (128, [0x01, 0x01, 0xFF], 0xFF), (255, [0x01, 0x01, 0xFF], 0xFF), (256, [0x01, 0x01, 0xFF], 0xFF), (9999, [0x01, 0x01, 0xFF], 0xFF), ) def testToBERBooleanKnownValues(self): """str(BERBoolean(n)) should give known result with known input""" for integer, encoded, dummy in self.knownValues: result = pureber.BERBoolean(integer) result = str(result) result = map(ord, result) assert encoded==result def testFromBERBooleanKnownValues(self): """BERBoolean(encoded="...") should give known result with known input""" for integer, encoded, canon in self.knownValues: m=s(*encoded) result, bytes = pureber.berDecodeObject(pureber.BERDecoderContext(), m) self.assertEquals(bytes, len(m)) assert isinstance(result, pureber.BERBoolean) result = result.value assert result==canon def testPartialBERBooleanEncodings(self): """BERBoolean(encoded="...") with too short input should throw BERExceptionInsufficientData""" m=str(pureber.BERBoolean(42)) assert len(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(), '')) class BEREnumeratedKnownValues(unittest.TestCase): knownValues=( (0, [0x0a, 0x01, 0]), (1, [0x0a, 0x01, 1]), (2, [0x0a, 0x01, 2]), (125, [0x0a, 0x01, 125]), (126, [0x0a, 0x01, 126]), (127, [0x0a, 0x01, 127]), (-1, [0x0a, 0x01, 256-1]), (-2, [0x0a, 0x01, 256-2]), (-3, [0x0a, 0x01, 256-3]), (-126, [0x0a, 0x01, 256-126]), (-127, [0x0a, 0x01, 256-127]), (-128, [0x0a, 0x01, 256-128]), (-129, [0x0a, 0x02, 256-1, 256-129]), (128, [0x0a, 0x02, 0, 128]), (256, [0x0a, 0x02, 1, 0]), ) def testToBEREnumeratedKnownValues(self): """str(BEREnumerated(n)) should give known result with known input""" for integer, encoded in self.knownValues: result = pureber.BEREnumerated(integer) result = str(result) result = map(ord, result) assert encoded==result 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 def testPartialBEREnumeratedEncodings(self): """BEREnumerated(encoded="...") with too short input should throw BERExceptionInsufficientData""" m=str(pureber.BEREnumerated(42)) assert len(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(), '')) class BEREnumeratedSanityCheck(unittest.TestCase): 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 class BERSequenceKnownValues(unittest.TestCase): knownValues=( ([], [0x30, 0x00]), ([pureber.BERInteger(2)], [0x30, 0x03, 0x02, 0x01, 2]), ([pureber.BERInteger(3)], [0x30, 0x03, 0x02, 0x01, 3]), ([pureber.BERInteger(128)], [0x30, 0x04, 0x02, 0x02, 0, 128]), ([pureber.BERInteger(2), pureber.BERInteger(3), pureber.BERInteger(128)], [0x30, 0x0a]+[0x02, 0x01, 2]+[0x02, 0x01, 3]+[0x02, 0x02, 0, 128]), ) def testToBERSequenceKnownValues(self): """str(BERSequence(x)) should give known result with known input""" for content, encoded in self.knownValues: result = pureber.BERSequence(content) result = str(result) result = map(ord, result) assert encoded==result 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 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(), '')) # TODO BERSequenceOf # TODO BERSet ldaptor-0.0.43/ldaptor/test/test_connector.py0000644000175000017500000000360710345261504017402 0ustar janjanfrom twisted.trial import unittest from twisted.internet import reactor, protocol, address from ldaptor.protocols.ldap import ldapconnector, distinguishedname class FakeProto(protocol.Protocol): pass class TestCallableOverride(unittest.TestCase): """ Callable values in serviceLocationOverride get to override the whole connecting process. """ def testSimple(self): dn = distinguishedname.DistinguishedName('dc=example,dc=com') c = ldapconnector.LDAPClientCreator(reactor, FakeProto) def _doConnect(factory): factory.doStart() factory.startedConnecting(c) proto = factory.buildProtocol(address.IPv4Address('TCP', 'localhost', '1')) d = c.connect(dn, overrides={ dn: _doConnect, }) def cb(r): self.failUnless(isinstance(r, FakeProto)) d.addCallback(cb) return d def testFindOverride_plainString(self): """Plain strings work as override keys.""" c=ldapconnector.LDAPConnector(reactor=None, dn='dc=example,dc=com', factory=None) o=c._findOverRide(dn=distinguishedname.DistinguishedName('cn=foo,dc=example,dc=com'), overrides={ 'dc=example,dc=com': ('server.example.com', 1389), }) self.assertEquals(o, ('server.example.com', 1389)) def testFindOverride_root(self): """Empty dn can be used as override.""" c=ldapconnector.LDAPConnector(reactor=None, dn='dc=example,dc=com', factory=None) o=c._findOverRide(dn=distinguishedname.DistinguishedName('cn=foo,dc=example,dc=com'), overrides={ '': ('server.example.com', 1389), }) self.assertEquals(o, ('server.example.com', 1389)) ldaptor-0.0.43/ldaptor/test/test_ldifdelta.py0000644000175000017500000001474110344407651017345 0ustar janjan""" Test cases for ldaptor.protocols.ldap.ldifdelta """ from twisted.trial import unittest from ldaptor.protocols.ldap import ldifdelta from ldaptor import delta, entry class LDIFDeltaDriver(ldifdelta.LDIFDelta): def __init__(self): self.listOfCompleted = [] def gotEntry(self, obj): self.listOfCompleted.append(obj) """ changerecord = "changetype:" FILL (change-add / change-delete / change-modify / change-moddn) change-add = "add" SEP 1*attrval-spec change-delete = "delete" SEP change-moddn = ("modrdn" / "moddn") SEP "newrdn:" ( FILL rdn / ":" FILL base64-rdn) SEP "deleteoldrdn:" FILL ("0" / "1") SEP 0*1("newsuperior:" ( FILL distinguishedName / ":" FILL base64-distinguishedName) SEP) change-modify = "modify" SEP *mod-spec mod-spec = ("add:" / "delete:" / "replace:") FILL AttributeDescription SEP *attrval-spec "-" SEP """ """ version: 1 dn: cn=foo,dc=example,dc=com changetype: delete """ """ version: 1 dn: cn=foo,dc=example,dc=com changetype: modrdn #OR moddn newrdn: rdn deleteoldrdn: 0 #OR 1 #0..1 newsuperior: distinguishedName """ class TestLDIFDeltaParsing(unittest.TestCase): def testModification_empty(self): proto = LDIFDeltaDriver() proto.dataReceived("""\ version: 1 dn: cn=foo,dc=example,dc=com changetype: modify """) proto.connectionLost() self.assertEqual(proto.listOfCompleted, [ delta.ModifyOp(dn='cn=foo,dc=example,dc=com'), ]) def testModification_oneAdd(self): proto = LDIFDeltaDriver() proto.dataReceived("""\ version: 1 dn: cn=foo,dc=example,dc=com changetype: modify add: foo foo: bar - """) proto.connectionLost() self.assertEqual( proto.listOfCompleted, [delta.ModifyOp(dn='cn=foo,dc=example,dc=com', modifications=[delta.Add('foo', ['bar']), ]), ]) def testModification_twoAdds(self): proto = LDIFDeltaDriver() proto.dataReceived("""\ version: 1 dn: cn=foo,dc=example,dc=com changetype: modify add: foo foo: bar - add: thud thud: quux thud: baz - """) proto.connectionLost() self.assertEqual( proto.listOfCompleted, [delta.ModifyOp(dn='cn=foo,dc=example,dc=com', modifications=[delta.Add('foo', ['bar']), delta.Add('thud', ['quux', 'baz']), ]), ]) def testModification_complex(self): proto = LDIFDeltaDriver() proto.dataReceived("""\ version: 1 dn: cn=foo,dc=example,dc=com changetype: modify delete: foo foo: bar - delete: garply - add: thud thud: quux thud: baz - replace: waldo - add: foo foo: baz - replace: thud thud: xyzzy - add: silly - """) proto.connectionLost() self.assertEqual( proto.listOfCompleted, [delta.ModifyOp(dn='cn=foo,dc=example,dc=com', modifications=[delta.Delete('foo', ['bar']), delta.Delete('garply'), delta.Add('thud', ['quux', 'baz']), delta.Replace('waldo'), delta.Add('foo', ['baz']), delta.Replace('thud', ['xyzzy']), delta.Add('silly'), ]), ]) def testModification_fail_noDash_1(self): proto = LDIFDeltaDriver() self.assertRaises(ldifdelta.LDIFDeltaModificationMissingEndDashError, proto.dataReceived, """\ version: 1 dn: cn=foo,dc=example,dc=com changetype: modify add: foo foo: bar """) def testModification_fail_noDash_2(self): proto = LDIFDeltaDriver() self.assertRaises(ldifdelta.LDIFDeltaModificationMissingEndDashError, proto.dataReceived, """\ version: 1 dn: cn=foo,dc=example,dc=com changetype: modify add: foo """) def testModification_fail_differentKey(self): proto = LDIFDeltaDriver() self.assertRaises(ldifdelta.LDIFDeltaModificationDifferentAttributeTypeError, proto.dataReceived, """\ version: 1 dn: cn=foo,dc=example,dc=com changetype: modify add: foo bar: quux - """) def testModification_fail_unknownModSpec(self): proto = LDIFDeltaDriver() self.assertRaises(ldifdelta.LDIFDeltaUnknownModificationError, proto.dataReceived, """\ version: 1 dn: cn=foo,dc=example,dc=com changetype: modify fiddle: foo foo: bar - """) def testNoChangeType(self): proto = LDIFDeltaDriver() self.assertRaises(ldifdelta.LDIFDeltaMissingChangeTypeError, proto.dataReceived, """\ version: 1 dn: cn=foo,dc=example,dc=com add: foo foo: bar - """) def testAdd(self): proto = LDIFDeltaDriver() proto.dataReceived("""\ version: 1 dn: cn=foo,dc=example,dc=com changetype: add foo: bar thud: quux thud: baz """) proto.connectionLost() self.assertEqual(proto.listOfCompleted, [delta.AddOp(entry.BaseLDAPEntry( dn='cn=foo,dc=example,dc=com', attributes={ 'foo': ['bar'], 'thud': ['quux', 'baz'], }))]) def testAdd_fail_noAttrvals(self): proto = LDIFDeltaDriver() self.assertRaises(ldifdelta.LDIFDeltaAddMissingAttributesError, proto.dataReceived, """\ version: 1 dn: cn=foo,dc=example,dc=com changetype: add """) def testDelete(self): proto = LDIFDeltaDriver() proto.dataReceived("""\ version: 1 dn: cn=foo,dc=example,dc=com changetype: delete """) proto.connectionLost() self.assertEqual(proto.listOfCompleted, [delta.DeleteOp(dn='cn=foo,dc=example,dc=com')]) ldaptor-0.0.43/ldaptor/test/test_match.py0000644000175000017500000003607610345037720016513 0ustar janjan""" Test cases for ldaptor.protocols.ldap.ldapserver module. """ from twisted.trial import unittest from ldaptor import inmemory from ldaptor.protocols import pureldap, pureber from ldaptor.protocols.ldap import ldapsyntax class TestEntryMatch(unittest.TestCase): def test_matchAll(self): o=inmemory.ReadOnlyInMemoryLDAPEntry(dn='cn=foo,dc=example,dc=com', attributes={ 'objectClass': ['a', 'b'], 'aValue': ['a'], 'bValue': ['b'], }) result = o.match(pureldap.LDAPFilterMatchAll) self.assertEquals(result, True) def test_present_match(self): o=inmemory.ReadOnlyInMemoryLDAPEntry(dn='cn=foo,dc=example,dc=com', attributes={ 'objectClass': ['a', 'b'], 'aValue': ['a'], 'bValue': ['b'], }) result = o.match(pureldap.LDAPFilter_present('aValue')) self.assertEquals(result, True) def test_present_noMatch(self): o=inmemory.ReadOnlyInMemoryLDAPEntry(dn='cn=foo,dc=example,dc=com', attributes={ 'objectClass': ['a', 'b'], 'aValue': ['a'], 'bValue': ['b'], }) result = o.match(pureldap.LDAPFilter_present('noSuchValue')) self.assertEquals(result, False) def test_and_match(self): o=inmemory.ReadOnlyInMemoryLDAPEntry(dn='cn=foo,dc=example,dc=com', attributes={ 'objectClass': ['a', 'b'], 'aValue': ['a'], 'bValue': ['b'], }) result = o.match( pureldap.LDAPFilter_and([ pureldap.LDAPFilter_present('aValue'), pureldap.LDAPFilter_present('bValue'), ])) self.assertEquals(result, True) def test_and_noMatch(self): o=inmemory.ReadOnlyInMemoryLDAPEntry(dn='cn=foo,dc=example,dc=com', attributes={ 'objectClass': ['a', 'b'], 'aValue': ['a'], 'bValue': ['b'], }) result = o.match( pureldap.LDAPFilter_and([ pureldap.LDAPFilter_present('cValue'), pureldap.LDAPFilter_present('dValue'), ])) self.assertEquals(result, False) def test_or_match(self): o=inmemory.ReadOnlyInMemoryLDAPEntry(dn='cn=foo,dc=example,dc=com', attributes={ 'objectClass': ['a', 'b'], 'aValue': ['a'], 'bValue': ['b'], }) result = o.match( pureldap.LDAPFilter_or([ pureldap.LDAPFilter_present('cValue'), pureldap.LDAPFilter_present('bValue'), ])) self.assertEquals(result, True) def test_or_noMatch(self): o=inmemory.ReadOnlyInMemoryLDAPEntry(dn='cn=foo,dc=example,dc=com', attributes={ 'objectClass': ['a', 'b'], 'aValue': ['a'], 'bValue': ['b'], }) result = o.match( pureldap.LDAPFilter_or([ pureldap.LDAPFilter_present('cValue'), pureldap.LDAPFilter_present('dValue'), ])) self.assertEquals(result, False) def test_not(self): o=inmemory.ReadOnlyInMemoryLDAPEntry(dn='cn=foo,dc=example,dc=com', attributes={ 'objectClass': ['a', 'b'], 'aValue': ['a'], 'bValue': ['b'], }) result = o.match( pureldap.LDAPFilter_not( pureldap.LDAPFilter_or([ pureldap.LDAPFilter_present('cValue'), pureldap.LDAPFilter_present('dValue'), ]))) self.assertEquals(result, True) def test_equality_match(self): o=inmemory.ReadOnlyInMemoryLDAPEntry(dn='cn=foo,dc=example,dc=com', attributes={ 'objectClass': ['a', 'b'], 'aValue': ['a'], 'bValue': ['b'], }) result = o.match(pureldap.LDAPFilter_equalityMatch( attributeDesc=pureber.BEROctetString('aValue'), assertionValue=pureber.BEROctetString('a'))) self.assertEquals(result, True) def test_equality_match_caseInsensitive(self): o=inmemory.ReadOnlyInMemoryLDAPEntry(dn='cn=foo,dc=example,dc=com', attributes={ 'objectClass': ['a', 'b'], 'aValue': ['a'], 'bValue': ['b'], }) result = o.match(pureldap.LDAPFilter_equalityMatch( attributeDesc=pureber.BEROctetString('avaLUe'), assertionValue=pureber.BEROctetString('A'))) self.assertEquals(result, True) def test_equality_noMatch(self): o=inmemory.ReadOnlyInMemoryLDAPEntry(dn='cn=foo,dc=example,dc=com', attributes={ 'objectClass': ['a', 'b'], 'aValue': ['a'], 'bValue': ['b'], }) result = o.match(pureldap.LDAPFilter_equalityMatch( attributeDesc=pureber.BEROctetString('aValue'), assertionValue=pureber.BEROctetString('b'))) self.assertEquals(result, False) def test_substrings_match(self): o=inmemory.ReadOnlyInMemoryLDAPEntry(dn='cn=foo,dc=example,dc=com', attributes={ 'objectClass': ['a', 'b'], 'aValue': ['a'], 'bValue': ['b'], }) result = o.match(pureldap.LDAPFilter_substrings( type='aValue', substrings=[ pureldap.LDAPFilter_substrings_initial('a'), ])) self.assertEquals(result, True) def test_substrings_match2(self): o=inmemory.ReadOnlyInMemoryLDAPEntry(dn='cn=foo,dc=example,dc=com', attributes={ 'objectClass': ['a', 'b'], 'aValue': ['abcde'], 'bValue': ['b'], }) result = o.match(pureldap.LDAPFilter_substrings( type='aValue', substrings=[ pureldap.LDAPFilter_substrings_initial('a'), pureldap.LDAPFilter_substrings_final('e'), ])) self.assertEquals(result, True) def test_substrings_match3(self): o=inmemory.ReadOnlyInMemoryLDAPEntry(dn='cn=foo,dc=example,dc=com', attributes={ 'objectClass': ['a', 'b'], 'aValue': ['abcde'], 'bValue': ['b'], }) result = o.match(pureldap.LDAPFilter_substrings( type='aValue', substrings=[ pureldap.LDAPFilter_substrings_initial('a'), pureldap.LDAPFilter_substrings_any('c'), pureldap.LDAPFilter_substrings_final('e'), ])) self.assertEquals(result, True) def test_substrings_match4(self): o=inmemory.ReadOnlyInMemoryLDAPEntry(dn='cn=foo,dc=example,dc=com', attributes={ 'objectClass': ['a', 'b'], 'aValue': ['abcde'], 'bValue': ['b'], }) result = o.match(pureldap.LDAPFilter_substrings( type='aValue', substrings=[ pureldap.LDAPFilter_substrings_initial('a'), pureldap.LDAPFilter_substrings_any('b'), pureldap.LDAPFilter_substrings_any('c'), pureldap.LDAPFilter_substrings_any('d'), pureldap.LDAPFilter_substrings_final('e'), ])) self.assertEquals(result, True) def test_substrings_match5(self): o=inmemory.ReadOnlyInMemoryLDAPEntry(dn='cn=foo,dc=example,dc=com', attributes={ 'objectClass': ['a', 'b'], 'aValue': ['aoeuboeucoeudoeue'], 'bValue': ['b'], }) result = o.match(pureldap.LDAPFilter_substrings( type='aValue', substrings=[ pureldap.LDAPFilter_substrings_initial('a'), pureldap.LDAPFilter_substrings_any('b'), pureldap.LDAPFilter_substrings_any('c'), pureldap.LDAPFilter_substrings_any('d'), pureldap.LDAPFilter_substrings_final('e'), ])) self.assertEquals(result, True) def test_substrings_match6(self): o=inmemory.ReadOnlyInMemoryLDAPEntry(dn='cn=foo,dc=example,dc=com', attributes={ 'objectClass': ['a', 'b'], 'aValue': ['aBCdE'], 'bValue': ['b'], }) result = o.match(pureldap.LDAPFilter_substrings( type='aValue', substrings=[ pureldap.LDAPFilter_substrings_initial('A'), pureldap.LDAPFilter_substrings_any('b'), pureldap.LDAPFilter_substrings_any('C'), pureldap.LDAPFilter_substrings_any('D'), pureldap.LDAPFilter_substrings_final('e'), ])) self.assertEquals(result, True) def test_substrings_match7(self): o=inmemory.ReadOnlyInMemoryLDAPEntry(dn='cn=foo,dc=example,dc=com', attributes={ 'objectClass': ['a', 'b'], 'aValue': ['Foo'], }) result = o.match(pureldap.LDAPFilter_substrings( type='aValue', substrings=[ pureldap.LDAPFilter_substrings_initial('f'), ])) self.assertEquals(result, True) def test_substrings_noMatch(self): o=inmemory.ReadOnlyInMemoryLDAPEntry(dn='cn=foo,dc=example,dc=com', attributes={ 'objectClass': ['a', 'b'], 'aValue': ['a'], 'bValue': ['b'], }) result = o.match(pureldap.LDAPFilter_substrings( type='aValue', substrings=[ pureldap.LDAPFilter_substrings_initial('bad'), pureldap.LDAPFilter_substrings_any('dog'), pureldap.LDAPFilter_substrings_any('no'), pureldap.LDAPFilter_substrings_final('bone'), ])) self.assertEquals(result, False) def test_substrings_noMatch2(self): o=inmemory.ReadOnlyInMemoryLDAPEntry(dn='cn=foo,dc=example,dc=com', attributes={ 'objectClass': ['a', 'b'], 'aValue': ['aoeuboeucoeudoeue'], 'bValue': ['b'], }) result = o.match(pureldap.LDAPFilter_substrings( type='aValue', substrings=[ pureldap.LDAPFilter_substrings_initial('a'), pureldap.LDAPFilter_substrings_any('b'), pureldap.LDAPFilter_substrings_any('Z'), pureldap.LDAPFilter_substrings_any('d'), pureldap.LDAPFilter_substrings_final('e'), ])) self.assertEquals(result, False) def test_greaterOrEqual_noMatch_nosuchattr(self): o=inmemory.ReadOnlyInMemoryLDAPEntry(dn='cn=foo,dc=example,dc=com', attributes={ 'objectClass': ['a', 'b'], 'aValue': ['b'], 'num': [4], }) result = o.match(pureldap.LDAPFilter_greaterOrEqual('foo', 42)) self.assertEquals(result, False) def test_greaterOrEqual_match_greater(self): o=inmemory.ReadOnlyInMemoryLDAPEntry(dn='cn=foo,dc=example,dc=com', attributes={ 'objectClass': ['a', 'b'], 'aValue': ['b'], 'num': [4], }) result = o.match(pureldap.LDAPFilter_greaterOrEqual('num', 3)) self.assertEquals(result, True) def test_greaterOrEqual_match_equal(self): o=inmemory.ReadOnlyInMemoryLDAPEntry(dn='cn=foo,dc=example,dc=com', attributes={ 'objectClass': ['a', 'b'], 'aValue': ['b'], 'num': [4], }) result = o.match(pureldap.LDAPFilter_greaterOrEqual('num', 4)) self.assertEquals(result, True) def test_greaterOrEqual_noMatch(self): o=inmemory.ReadOnlyInMemoryLDAPEntry(dn='cn=foo,dc=example,dc=com', attributes={ 'objectClass': ['a', 'b'], 'aValue': ['b'], 'bValue': [4], }) result = o.match(pureldap.LDAPFilter_greaterOrEqual('num', 5)) self.assertEquals(result, False) def test_lessOrEqual_noMatch_nosuchattr(self): o=inmemory.ReadOnlyInMemoryLDAPEntry(dn='cn=foo,dc=example,dc=com', attributes={ 'objectClass': ['a', 'b'], 'aValue': ['b'], 'num': [4], }) result = o.match(pureldap.LDAPFilter_lessOrEqual('foo', 42)) self.assertEquals(result, False) def test_lessOrEqual_match_less(self): o=inmemory.ReadOnlyInMemoryLDAPEntry(dn='cn=foo,dc=example,dc=com', attributes={ 'objectClass': ['a', 'b'], 'aValue': ['b'], 'num': [4], }) result = o.match(pureldap.LDAPFilter_lessOrEqual('num', 5)) self.assertEquals(result, True) def test_lessOrEqual_match_equal(self): o=inmemory.ReadOnlyInMemoryLDAPEntry(dn='cn=foo,dc=example,dc=com', attributes={ 'objectClass': ['a', 'b'], 'aValue': ['b'], 'num': [4], }) result = o.match(pureldap.LDAPFilter_lessOrEqual('num', 4)) self.assertEquals(result, True) def test_lessOrEqual_noMatch(self): o=inmemory.ReadOnlyInMemoryLDAPEntry(dn='cn=foo,dc=example,dc=com', attributes={ 'objectClass': ['a', 'b'], 'aValue': ['b'], 'num': [4], }) result = o.match(pureldap.LDAPFilter_lessOrEqual('num', 3)) self.assertEquals(result, False) def test_notImplemented(self): o=inmemory.ReadOnlyInMemoryLDAPEntry(dn='cn=foo,dc=example,dc=com', attributes={ 'objectClass': ['a', 'b'], 'aValue': ['b'], 'num': [4], }) class UnknownMatch(object): pass unknownMatch = UnknownMatch() self.assertRaises(ldapsyntax.MatchNotImplemented, o.match, unknownMatch) # TODO LDAPFilter_approxMatch # TODO LDAPFilter_extensibleMatch ldaptor-0.0.43/ldaptor/test/test_inmemory.py0000644000175000017500000004566110352535700017255 0ustar janjan""" Test cases for ldaptor.inmemory module. """ from twisted.trial import unittest from cStringIO import StringIO from ldaptor import inmemory, delta, testutil from ldaptor.protocols.ldap import distinguishedname, ldaperrors class TestInMemoryDatabase(unittest.TestCase): def setUp(self): self.root = inmemory.ReadOnlyInMemoryLDAPEntry( dn=distinguishedname.DistinguishedName('dc=example,dc=com')) self.meta=self.root.addChild( rdn='ou=metasyntactic', attributes={ 'objectClass': ['a', 'b'], 'ou': ['metasyntactic'], }) self.foo=self.meta.addChild( rdn='cn=foo', attributes={ 'objectClass': ['a', 'b'], 'cn': ['foo'], }) self.bar=self.meta.addChild( rdn='cn=bar', attributes={ 'objectClass': ['a', 'b'], 'cn': ['bar'], }) self.empty=self.root.addChild( rdn='ou=empty', attributes={ 'objectClass': ['a', 'b'], 'ou': ['empty'], }) self.oneChild=self.root.addChild( rdn='ou=oneChild', attributes={ 'objectClass': ['a', 'b'], 'ou': ['oneChild'], }) self.theChild=self.oneChild.addChild( rdn='cn=theChild', attributes={ 'objectClass': ['a', 'b'], 'cn': ['theChild'], }) def test_children_empty(self): d = self.empty.children() d.addCallback(self.assertEquals, []) return d def test_children_oneChild(self): d = self.oneChild.children() def cb(children): self.assertEquals(len(children), 1) got = [e.dn for e in children] want = [distinguishedname.DistinguishedName('cn=theChild,ou=oneChild,dc=example,dc=com')] got.sort() want.sort() self.assertEquals(got, want) d.addCallback(cb) return d def test_children_repeat(self): """Test that .children() returns a copy of the data so that modifying it does not affect behaviour.""" d = self.oneChild.children() def cb1(children1): self.assertEquals(len(children1), 1) children1.pop() d = self.oneChild.children() return d d.addCallback(cb1) def cb2(children2): self.assertEquals(len(children2), 1) d.addCallback(cb2) return d def test_children_twoChildren(self): d = self.meta.children() def cb(children): self.assertEquals(len(children), 2) want = [ distinguishedname.DistinguishedName('cn=foo,ou=metasyntactic,dc=example,dc=com'), distinguishedname.DistinguishedName('cn=bar,ou=metasyntactic,dc=example,dc=com'), ] got = [e.dn for e in children] self.assertEquals(got, want) d.addCallback(cb) return d def test_addChild(self): self.empty.addChild( rdn='a=b', attributes={ 'objectClass': ['a', 'b'], 'a': 'b', }) d = self.empty.children() def cb(children): self.assertEquals(len(children), 1) got = [e.dn for e in children] want = [ distinguishedname.DistinguishedName('a=b,ou=empty,dc=example,dc=com'), ] got.sort() want.sort() self.assertEquals(got, want) d.addCallback(cb) return d def test_addChild_Exists(self): self.assertRaises(ldaperrors.LDAPEntryAlreadyExists, self.meta.addChild, rdn='cn=foo', attributes={ 'objectClass': ['a'], 'cn': 'foo', }) def test_parent(self): self.assertEquals(self.foo.parent(), self.meta) self.assertEquals(self.meta.parent(), self.root) self.assertEquals(self.root.parent(), None) def test_subtree_empty(self): d = self.empty.subtree() def cb(entries): self.assertEquals(len(entries), 1) d.addCallback(cb) return d def test_subtree_oneChild(self): d = self.oneChild.subtree() d.addCallback(self.assertEquals, [ self.oneChild, self.theChild, ]) return d def test_subtree_oneChild_cb(self): got = [] d = self.oneChild.subtree(got.append) d.addCallback(self.assertEquals, None) def cb(dummy): want = [ self.oneChild, self.theChild, ] self.assertEquals(got, want) d.addCallback(cb) return d def test_subtree_many(self): d = self.root.subtree() def cb(results): got = results want = [ self.root, self.oneChild, self.theChild, self.empty, self.meta, self.bar, self.foo, ] self.assertEquals(got, want) d.addCallback(cb) return d def test_subtree_many_cb(self): got = [] d = self.root.subtree(callback=got.append) def cb(r): self.assertEquals(r, None) want = [ self.root, self.oneChild, self.theChild, self.empty, self.meta, self.bar, self.foo, ] self.assertEquals(got, want) d.addCallback(cb) return d def test_lookup_fail(self): dn = distinguishedname.DistinguishedName('cn=thud,ou=metasyntactic,dc=example,dc=com') d = self.root.lookup(dn) def eb(fail): fail.trap(ldaperrors.LDAPNoSuchObject) self.assertEquals(fail.value.message, dn) d.addCallbacks(testutil.mustRaise, eb) return d def test_lookup_fail_outOfTree(self): dn = distinguishedname.DistinguishedName('dc=invalid') d = self.root.lookup(dn) def eb(fail): fail.trap(ldaperrors.LDAPNoSuchObject) self.assertEquals(fail.value.message, dn) d.addCallbacks(testutil.mustRaise, eb) return d def test_lookup_deep(self): dn = distinguishedname.DistinguishedName('cn=bar,ou=metasyntactic,dc=example,dc=com') d = self.root.lookup(dn) d.addCallback(self.assertEquals, self.bar) return d def test_delete_root(self): newRoot = inmemory.ReadOnlyInMemoryLDAPEntry( dn=distinguishedname.DistinguishedName('dc=example,dc=com')) d = newRoot.delete() def eb(fail): fail.trap(inmemory.LDAPCannotRemoveRootError) d.addCallbacks(testutil.mustRaise, eb) return d def test_delete_nonLeaf(self): d = self.meta.delete() def eb(fail): fail.trap(ldaperrors.LDAPNotAllowedOnNonLeaf) d.addCallbacks(testutil.mustRaise, eb) return d def test_delete(self): d = self.foo.delete() d.addCallback(self.assertEquals, self.foo) d.addCallback(lambda _: self.meta.children()) d.addCallback(self.assertEquals, [self.bar]) return d def test_deleteChild(self): d = self.meta.deleteChild('cn=bar') d.addCallback(self.assertEquals, self.bar) d.addCallback(lambda _: self.meta.children()) d.addCallback(self.assertEquals, [self.foo]) return d def test_deleteChild_NonExisting(self): d = self.root.deleteChild('cn=not-exist') def eb(fail): fail.trap(ldaperrors.LDAPNoSuchObject) d.addCallbacks(testutil.mustRaise, eb) return d def test_setPassword(self): self.foo.setPassword('s3krit', salt='\xf2\x4a') self.failUnless('userPassword' in self.foo) self.assertEquals(self.foo['userPassword'], ['{SSHA}0n/Iw1NhUOKyaI9gm9v5YsO3ZInySg==']) def test_setPassword_noSalt(self): self.foo.setPassword('s3krit') self.failUnless('userPassword' in self.foo) d = self.foo.bind('s3krit') d.addCallback(self.assertIdentical, self.foo) d.addCallback(lambda _: self.foo.bind('s4krit')) def eb(fail): fail.trap(ldaperrors.LDAPInvalidCredentials) d.addCallbacks(testutil.mustRaise, eb) return d def testSearch_withCallback(self): got = [] d = self.root.search(filterText='(|(cn=foo)(cn=bar))', callback=got.append) def cb(r): self.assertEquals(r, None) want = [ self.bar, self.foo, ] self.assertEquals(got, want) d.addCallback(cb) return d def testSearch_withoutCallback(self): d = self.root.search(filterText='(|(cn=foo)(cn=bar))') d.addCallback(self.assertEquals, [ self.bar, self.foo, ]) return d def test_move_noChildren_sameSuperior(self): d = self.empty.move('ou=moved,dc=example,dc=com') def getChildren(dummy): return self.root.children() d.addCallback(getChildren) d.addCallback(self.assertEquals, [ self.meta, inmemory.ReadOnlyInMemoryLDAPEntry( dn='ou=moved,dc=example,dc=com', attributes={ 'objectClass': ['a', 'b'], 'ou': ['moved'], }), self.oneChild, ]) return d def test_move_children_sameSuperior(self): d = self.meta.move('ou=moved,dc=example,dc=com') def getChildren(dummy): return self.root.children() d.addCallback(getChildren) d.addCallback(self.assertEquals, [ inmemory.ReadOnlyInMemoryLDAPEntry( dn='ou=moved,dc=example,dc=com', attributes={ 'objectClass': ['a', 'b'], 'ou': ['moved'], }), self.empty, self.oneChild, ]) return d def test_move_noChildren_newSuperior(self): d = self.empty.move('ou=moved,ou=oneChild,dc=example,dc=com') def getChildren(dummy): return self.root.children() d.addCallback(getChildren) d.addCallback(self.assertEquals, [ self.meta, self.oneChild, ]) def getChildren2(dummy): return self.oneChild.children() d.addCallback(getChildren2) d.addCallback(self.assertEquals, [ self.theChild, inmemory.ReadOnlyInMemoryLDAPEntry( dn='ou=moved,ou=oneChild,dc=example,dc=com', attributes={ 'objectClass': ['a', 'b'], 'ou': ['moved'], }), ]) return d def test_move_children_newSuperior(self): d = self.meta.move('ou=moved,ou=oneChild,dc=example,dc=com') def getChildren(dummy): return self.root.children() d.addCallback(getChildren) d.addCallback(self.assertEquals, [ self.empty, self.oneChild, ]) def getChildren2(dummy): return self.oneChild.children() d.addCallback(getChildren2) d.addCallback(self.assertEquals, [ self.theChild, inmemory.ReadOnlyInMemoryLDAPEntry( dn='ou=moved,ou=oneChild,dc=example,dc=com', attributes={ 'objectClass': ['a', 'b'], 'ou': ['moved'], }), ]) return d def test_commit(self): """ReadOnlyInMemoryLDAPEntry.commit() succeeds immediately.""" self.meta['foo'] = ['bar'] d = self.meta.commit() self.failUnless(d.called) d.addCallback(self.assertIdentical, self.meta) return d class FromLDIF(unittest.TestCase): def test_single(self): ldif = StringIO('''\ dn: cn=foo,dc=example,dc=com objectClass: a objectClass: b aValue: a aValue: b bValue: c ''') d = inmemory.fromLDIFFile(ldif) def cb1(db): self.assertEquals( db.dn, distinguishedname.DistinguishedName('cn=foo,dc=example,dc=com')) return db.children() d.addCallback(cb1) d.addCallback(self.assertEquals, []) return d def test_two(self): ldif = StringIO('''\ dn: dc=example,dc=com objectClass: dcObject dc: example dn: cn=foo,dc=example,dc=com objectClass: a cn: foo ''') d = inmemory.fromLDIFFile(ldif) def cb1(db): self.assertEquals( db.dn, distinguishedname.DistinguishedName('dc=example,dc=com')) return db.subtree() d.addCallback(cb1) def cb2(children): self.assertEquals(len(children), 2) want = [ distinguishedname.DistinguishedName('dc=example,dc=com'), distinguishedname.DistinguishedName('cn=foo,dc=example,dc=com'), ] got = [e.dn for e in children] self.assertEquals(got, want) d.addCallback(cb2) return d def test_missingNode(self): ldif = StringIO('''\ dn: dc=example,dc=com objectClass: dcObject dc: example dn: cn=foo,ou=nonexisting,dc=example,dc=com objectClass: a cn: foo ''') d = inmemory.fromLDIFFile(ldif) def eb(fail): fail.trap(ldaperrors.LDAPNoSuchObject) self.failUnlessEqual( str(fail.value), 'noSuchObject: ou=nonexisting,dc=example,dc=com') d.addCallbacks(testutil.mustRaise, eb) return d class TestDiff(unittest.TestCase): def testNoChange(self): a = inmemory.ReadOnlyInMemoryLDAPEntry('dc=example,dc=com', { 'dc': ['example'], }) b = inmemory.ReadOnlyInMemoryLDAPEntry('dc=example,dc=com', { 'dc': ['example'], }) d = a.diffTree(b) d.addCallback(self.assertEquals, []) return d def testRootChange_Add(self): a = inmemory.ReadOnlyInMemoryLDAPEntry('dc=example,dc=com', { 'dc': ['example'], }) b = inmemory.ReadOnlyInMemoryLDAPEntry('dc=example,dc=com', { 'dc': ['example'], 'foo': ['bar'], }) d = a.diffTree(b) d.addCallback(self.assertEquals, [ delta.ModifyOp('dc=example,dc=com', [ delta.Add('foo', ['bar']), ]), ]) return d def testChildChange_Add(self): a = inmemory.ReadOnlyInMemoryLDAPEntry('dc=example,dc=com', { 'dc': ['example'], }) a.addChild('cn=foo', { 'cn': ['foo'], }) b = inmemory.ReadOnlyInMemoryLDAPEntry('dc=example,dc=com', { 'dc': ['example'], }) b.addChild('cn=foo', { 'cn': ['foo'], 'foo': ['bar'], }) d = a.diffTree(b) d.addCallback(self.assertEquals, [ delta.ModifyOp('cn=foo,dc=example,dc=com', [ delta.Add('foo', ['bar']), ]), ]) return d def testAddChild(self): a = inmemory.ReadOnlyInMemoryLDAPEntry( dn=distinguishedname.DistinguishedName('dc=example,dc=com')) b = inmemory.ReadOnlyInMemoryLDAPEntry( dn=distinguishedname.DistinguishedName('dc=example,dc=com')) foo=b.addChild( rdn='cn=foo', attributes={ 'objectClass': ['a', 'b'], 'cn': ['foo'], }) bar=b.addChild( rdn='cn=bar', attributes={ 'objectClass': ['a', 'b'], 'cn': ['bar'], }) d = a.diffTree(b) d.addCallback(self.assertEquals, [ delta.AddOp(bar), delta.AddOp(foo), ]) return d def testAddSubtree(self): a = inmemory.ReadOnlyInMemoryLDAPEntry( dn=distinguishedname.DistinguishedName('dc=example,dc=com')) b = inmemory.ReadOnlyInMemoryLDAPEntry( dn=distinguishedname.DistinguishedName('dc=example,dc=com')) foo=b.addChild( rdn='ou=foo', attributes={ 'objectClass': ['a', 'b'], 'ou': ['foo'], }) baz=foo.addChild( rdn='cn=baz', attributes={ 'objectClass': ['a', 'b'], 'cn': ['baz'], }) bar=b.addChild( rdn='cn=bar', attributes={ 'objectClass': ['a', 'b'], 'cn': ['bar'], }) d = a.diffTree(b) d.addCallback(self.assertEquals, [ delta.AddOp(bar), delta.AddOp(foo), delta.AddOp(baz), ]) return d def testDeleteChild(self): a = inmemory.ReadOnlyInMemoryLDAPEntry( dn=distinguishedname.DistinguishedName('dc=example,dc=com')) b = inmemory.ReadOnlyInMemoryLDAPEntry( dn=distinguishedname.DistinguishedName('dc=example,dc=com')) foo=a.addChild( rdn='cn=foo', attributes={ 'objectClass': ['a', 'b'], 'cn': ['foo'], }) bar=a.addChild( rdn='cn=bar', attributes={ 'objectClass': ['a', 'b'], 'cn': ['bar'], }) d = a.diffTree(b) d.addCallback(self.assertEquals, [ delta.DeleteOp(bar), delta.DeleteOp(foo), ]) return d def testDeleteSubtree(self): a = inmemory.ReadOnlyInMemoryLDAPEntry( dn=distinguishedname.DistinguishedName('dc=example,dc=com')) b = inmemory.ReadOnlyInMemoryLDAPEntry( dn=distinguishedname.DistinguishedName('dc=example,dc=com')) foo=a.addChild( rdn='ou=foo', attributes={ 'objectClass': ['a', 'b'], 'ou': ['foo'], }) baz=foo.addChild( rdn='cn=baz', attributes={ 'objectClass': ['a', 'b'], 'cn': ['baz'], }) bar=a.addChild( rdn='cn=bar', attributes={ 'objectClass': ['a', 'b'], 'cn': ['bar'], }) d = a.diffTree(b) d.addCallback(self.assertEquals, [ delta.DeleteOp(bar), delta.DeleteOp(baz), delta.DeleteOp(foo), ]) return d ldaptor-0.0.43/ldaptor/test/test_delta.py0000644000175000017500000002061610345261504016500 0ustar janjan""" Test cases for ldaptor.protocols.ldap.delta """ from twisted.trial import unittest from ldaptor import testutil from ldaptor import delta, entry, attributeset, inmemory from ldaptor.protocols.ldap import ldapsyntax, distinguishedname, ldaperrors class TestModifications(unittest.TestCase): def setUp(self): self.foo = ldapsyntax.LDAPEntry( None, dn='cn=foo,dc=example,dc=com', attributes={ 'objectClass': ['person'], 'cn': ['foo', 'thud'], 'sn': ['bar'], 'more': ['junk'], }) def testAddOld(self): mod = delta.Add('cn', ['quux']) mod.patch(self.foo) self.failIf('stuff' in self.foo) self.failUnlessEqual(self.foo['cn'], ['foo', 'thud', 'quux']) def testAddNew(self): mod = delta.Add('stuff', ['val1', 'val2']) mod.patch(self.foo) self.failUnlessEqual(self.foo['stuff'], ['val1', 'val2']) self.failUnlessEqual(self.foo['cn'], ['foo', 'thud']) def testDelete(self): mod = delta.Delete('cn', ['thud']) mod.patch(self.foo) self.failIf('stuff' in self.foo) self.failUnlessEqual(self.foo['cn'], ['foo']) def testDeleteAll(self): mod = delta.Delete('more') mod.patch(self.foo) self.failIf('stuff' in self.foo) self.failUnlessEqual(self.foo['cn'], ['foo', 'thud']) def testDelete_FailOnNonExistingAttributeType_All(self): mod = delta.Delete('notexist', []) self.assertRaises(KeyError, mod.patch, self.foo) def testDelete_FailOnNonExistingAttributeType_OneValue(self): mod = delta.Delete('notexist', ['a']) self.assertRaises(KeyError, mod.patch, self.foo) def testDelete_FailOnNonExistingAttributeValue(self): mod = delta.Delete('cn', ['notexist']) self.assertRaises(LookupError, mod.patch, self.foo) def testReplace_Add(self): mod = delta.Replace('stuff', ['val1', 'val2']) mod.patch(self.foo) self.failUnlessEqual(self.foo['stuff'], ['val1', 'val2']) self.failUnlessEqual(self.foo['sn'], ['bar']) self.failUnlessEqual(self.foo['more'], ['junk']) def testReplace_Modify(self): mod = delta.Replace('sn', ['baz']) mod.patch(self.foo) self.failIf('stuff' in self.foo) self.failUnlessEqual(self.foo['sn'], ['baz']) self.failUnlessEqual(self.foo['more'], ['junk']) def testReplace_Delete_Existing(self): mod = delta.Replace('more', []) mod.patch(self.foo) self.failIf('stuff' in self.foo) self.failUnlessEqual(self.foo['sn'], ['bar']) self.failIf('more' in self.foo) def testReplace_Delete_NonExisting(self): mod = delta.Replace('nonExisting', []) mod.patch(self.foo) self.failIf('stuff' in self.foo) self.failUnlessEqual(self.foo['sn'], ['bar']) self.failUnlessEqual(self.foo['more'], ['junk']) class TestModificationOpLDIF(unittest.TestCase): def testAdd(self): m=delta.Add('foo', ['bar', 'baz']) self.assertEquals(m.asLDIF(), """\ add: foo foo: bar foo: baz - """) def testDelete(self): m=delta.Delete('foo', ['bar', 'baz']) self.assertEquals(m.asLDIF(), """\ delete: foo foo: bar foo: baz - """) def testDeleteAll(self): m=delta.Delete('foo') self.assertEquals(m.asLDIF(), """\ delete: foo - """) def testReplace(self): m=delta.Replace('foo', ['bar', 'baz']) self.assertEquals(m.asLDIF(), """\ replace: foo foo: bar foo: baz - """) def testReplaceAll(self): m=delta.Replace('thud') self.assertEquals(m.asLDIF(), """\ replace: thud - """) class TestAddOpLDIF(unittest.TestCase): def testSimple(self): op=delta.AddOp(entry.BaseLDAPEntry( dn='dc=example,dc=com', attributes={'foo': ['bar', 'baz'], 'quux': ['thud']})) self.assertEquals(op.asLDIF(), """\ dn: dc=example,dc=com changetype: add foo: bar foo: baz quux: thud """) class TestDeleteOpLDIF(unittest.TestCase): def testSimple(self): op=delta.DeleteOp('dc=example,dc=com') self.assertEquals(op.asLDIF(), """\ dn: dc=example,dc=com changetype: delete """) class TestOperationLDIF(unittest.TestCase): def testModify(self): op=delta.ModifyOp('cn=Paula Jensen, ou=Product Development, dc=airius, dc=com', [ delta.Add('postaladdress', ['123 Anystreet $ Sunnyvale, CA $ 94086']), delta.Delete('description'), delta.Replace('telephonenumber', ['+1 408 555 1234', '+1 408 555 5678']), delta.Delete('facsimiletelephonenumber', ['+1 408 555 9876']), ]) self.assertEquals(op.asLDIF(), """\ dn: cn=Paula Jensen,ou=Product Development,dc=airius,dc=com changetype: modify add: postaladdress postaladdress: 123 Anystreet $ Sunnyvale, CA $ 94086 - delete: description - replace: telephonenumber telephonenumber: +1 408 555 1234 telephonenumber: +1 408 555 5678 - delete: facsimiletelephonenumber facsimiletelephonenumber: +1 408 555 9876 - """) class TestModificationComparison(unittest.TestCase): def testEquality_Add_True(self): a = delta.Add('k', ['b', 'c', 'd']) b = delta.Add('k', ['b', 'c', 'd']) self.assertEquals(a, b) def testEquality_AddVsDelete_False(self): a = delta.Add('k', ['b', 'c', 'd']) b = delta.Delete('k', ['b', 'c', 'd']) self.assertNotEquals(a, b) def testEquality_AttributeSet_False(self): a = delta.Add('k', ['b', 'c', 'd']) b = attributeset.LDAPAttributeSet('k', ['b', 'c', 'd']) self.assertNotEquals(a, b) def testEquality_List_False(self): a = delta.Add('k', ['b', 'c', 'd']) b = ['b', 'c', 'd'] self.assertNotEquals(a, b) class TestOperations(unittest.TestCase): def setUp(self): self.root = inmemory.ReadOnlyInMemoryLDAPEntry( dn=distinguishedname.DistinguishedName('dc=example,dc=com')) self.meta=self.root.addChild( rdn='ou=metasyntactic', attributes={ 'objectClass': ['a', 'b'], 'ou': ['metasyntactic'], }) self.foo=self.meta.addChild( rdn='cn=foo', attributes={ 'objectClass': ['a', 'b'], 'cn': ['foo'], }) self.bar=self.meta.addChild( rdn='cn=bar', attributes={ 'objectClass': ['a', 'b'], 'cn': ['bar'], }) self.empty=self.root.addChild( rdn='ou=empty', attributes={ 'objectClass': ['a', 'b'], 'ou': ['empty'], }) self.oneChild=self.root.addChild( rdn='ou=oneChild', attributes={ 'objectClass': ['a', 'b'], 'ou': ['oneChild'], }) self.theChild=self.oneChild.addChild( rdn='cn=theChild', attributes={ 'objectClass': ['a', 'b'], 'cn': ['theChild'], }) def testAddOp_DNExists(self): foo2 = entry.BaseLDAPEntry( dn='cn=foo,ou=metasyntactic,dc=example,dc=com', attributes={'foo': ['bar', 'baz'], 'quux': ['thud']}) op = delta.AddOp(foo2) d = op.patch(self.root) def eb(fail): fail.trap(ldaperrors.LDAPEntryAlreadyExists) d.addCallbacks(testutil.mustRaise, eb) return d def testDeleteOp_DNNotFound(self): op = delta.DeleteOp('cn=nope,dc=example,dc=com') d = op.patch(self.root) def eb(fail): fail.trap(ldaperrors.LDAPNoSuchObject) d.addCallbacks(testutil.mustRaise, eb) return d def testModifyOp_DNNotFound(self): op = delta.ModifyOp('cn=nope,dc=example,dc=com', [delta.Add('foo', ['bar'])]) d = op.patch(self.root) def eb(fail): fail.trap(ldaperrors.LDAPNoSuchObject) d.addCallbacks(testutil.mustRaise, eb) return d ldaptor-0.0.43/ldaptor/entryhelpers.py0000644000175000017500000002302410345037720016112 0ustar janjanimport sets from twisted.internet import defer from ldaptor import delta, ldapfilter from ldaptor.protocols import pureldap from ldaptor.protocols.ldap import ldapsyntax, ldaperrors class DiffTreeMixin(object): def _diffTree_gotMyChildren(self, myChildren, other, result): d = other.children() d.addCallback(self._diffTree_gotBothChildren, myChildren, other, result) return d def _diffTree_gotBothChildren(self, otherChildren, myChildren, other, result): def rdnToChild(rdn, l): r = [x for x in l if x.dn.split()[0] == rdn] assert len(r) == 1 return r[0] my = sets.Set([x.dn.split()[0] for x in myChildren]) his = sets.Set([x.dn.split()[0] for x in otherChildren]) # differences in common children commonRDN = list(my & his) commonRDN.sort() # for reproducability only d = self._diffTree_commonChildren([ (rdnToChild(rdn, myChildren), rdnToChild(rdn, otherChildren)) for rdn in commonRDN ], result) # added children addedRDN = list(his - my) addedRDN.sort() # for reproducability only d2 = self._diffTree_addedChildren([ rdnToChild(rdn, otherChildren) for rdn in addedRDN ], result) d.addCallback(lambda _: d2) # deleted children deletedRDN = list(my - his) deletedRDN.sort() # for reproducability only d3 = self._diffTree_deletedChildren([ rdnToChild(rdn, myChildren) for rdn in deletedRDN ], result) d.addCallback(lambda _: d3) return d def _diffTree_commonChildren(self, children, result): if not children: return defer.succeed(result) first, rest = children[0], children[1:] a, b = first d = a.diffTree(b, result) d.addCallback(lambda _: self._diffTree_commonChildren(rest, result)) return d def _diffTree_addedChildren(self, children, result): if not children: return result first, rest = children[0], children[1:] d = first.subtree() def _gotSubtree(l, result): for c in l: o = delta.AddOp(c) result.append(o) return result d.addCallback(_gotSubtree, result) d.addCallback(lambda _: self._diffTree_addedChildren(rest, result)) return d def _diffTree_deletedChildren(self, children, result): if not children: return result first, rest = children[0], children[1:] d = first.subtree() def _gotSubtree(l, result): l.reverse() # remove children before their parent for c in l: o = delta.DeleteOp(c) result.append(o) return result d.addCallback(_gotSubtree, result) d.addCallback(lambda _: self._diffTree_deletedChildren(rest, result)) return d def diffTree(self, other, result=None): assert self.dn == other.dn, \ ("diffTree arguments must refer to same LDAP tree:" "%r != %r" % (str(self.dn), str(other.dn)) ) if result is None: result = [] # differences in root rootDiff = self.diff(other) if rootDiff is not None: result.append(rootDiff) d = self.children() d.addCallback(self._diffTree_gotMyChildren, other, result) return d class SubtreeFromChildrenMixin(object): def subtree(self, callback=None): if callback is None: result = [] d = self.subtree(callback=result.append) d.addCallback(lambda _: result) return d else: callback(self) d = self.children() def _processOneChild(_, children, callback): if not children: return None c = children.pop() d = c.subtree(callback) d.addCallback(_processOneChild, children, callback) def _gotChildren(children, callback): _processOneChild(None, children, callback) d.addCallback(_gotChildren, callback) return d class MatchMixin(object): def match(self, filter): if isinstance(filter, pureldap.LDAPFilter_present): return filter.value in self elif isinstance(filter, pureldap.LDAPFilter_equalityMatch): # TODO case insensitivity depends on different attribute syntaxes if (filter.assertionValue.value.lower() in [val.lower() for val in self.get(filter.attributeDesc.value, [])]): return True return False elif isinstance(filter, pureldap.LDAPFilter_substrings): if filter.type not in self: return False possibleMatches = self[filter.type] substrings = filter.substrings[:] if (substrings and isinstance(filter.substrings[0], pureldap.LDAPFilter_substrings_initial)): possibleMatches = [ x[len(filter.substrings[0].value):] for x in possibleMatches if x.lower().startswith(filter.substrings[0].value.lower()) ] del substrings[0] if (substrings and isinstance(filter.substrings[-1], pureldap.LDAPFilter_substrings_final)): possibleMatches = [ x[:-len(filter.substrings[0].value)] for x in possibleMatches if x.lower().endswith(filter.substrings[-1].value.lower()) ] del substrings[-1] while possibleMatches and substrings: assert isinstance(substrings[0], pureldap.LDAPFilter_substrings_any) r = [] for possible in possibleMatches: i = possible.lower().find(substrings[0].value.lower()) if i >= 0: r.append(possible[i:]) possibleMatches = r del substrings[0] if possibleMatches and not substrings: return True return False elif isinstance(filter, pureldap.LDAPFilter_greaterOrEqual): if filter.attributeDesc not in self: return False for value in self[filter.attributeDesc]: if value >= filter.assertionValue: return True return False elif isinstance(filter, pureldap.LDAPFilter_lessOrEqual): if filter.attributeDesc not in self: return False for value in self[filter.attributeDesc]: if value <= filter.assertionValue: return True return False elif isinstance(filter, pureldap.LDAPFilter_and): for filt in filter: if not self.match(filt): return False return True elif isinstance(filter, pureldap.LDAPFilter_or): for filt in filter: if self.match(filt): return True return False elif isinstance(filter, pureldap.LDAPFilter_not): return not self.match(filter.value) else: raise ldapsyntax.MatchNotImplemented, filter class SearchByTreeWalkingMixin(object): def search(self, filterText=None, filterObject=None, attributes=(), scope=None, derefAliases=None, sizeLimit=0, timeLimit=0, typesOnly=0, callback=None): if filterObject is None and filterText is None: filterObject=pureldap.LDAPFilterMatchAll elif filterObject is None and filterText is not None: filterObject=ldapfilter.parseFilter(filterText) elif filterObject is not None and filterText is None: pass elif filterObject is not None and filterText is not None: f=ldapfilter.parseFilter(filterText) filterObject=pureldap.LDAPFilter_and((f, filterObject)) if scope is None: scope = pureldap.LDAP_SCOPE_wholeSubtree if derefAliases is None: derefAliases = pureldap.LDAP_DEREF_neverDerefAliases # choose iterator: base/children/subtree if scope == pureldap.LDAP_SCOPE_wholeSubtree: iterator = self.subtree elif scope == pureldap.LDAP_SCOPE_singleLevel: iterator = self.children elif scope == pureldap.LDAP_SCOPE_baseObject: def iterateSelf(callback): callback(self) return defer.succeed(None) iterator = iterateSelf else: raise ldaperrors.LDAPProtocolError, \ 'unknown search scope: %r' % scope results = [] if callback is None: matchCallback = results.append else: matchCallback = callback # gather results, send them def _tryMatch(entry): if entry.match(filterObject): matchCallback(entry) d = iterator(callback=_tryMatch) if callback is None: return defer.succeed(results) else: return defer.succeed(None) ldaptor-0.0.43/ldaptor/protocols/0000755000175000017500000000000010403234307015032 5ustar janjanldaptor-0.0.43/ldaptor/protocols/pureber.py0000644000175000017500000002566310345573607017102 0ustar janjan# Twisted, the Framework of Your Internet # Copyright (C) 2001 Matthew W. Lefkowitz # # This library is free software; you can redistribute it and/or # modify it under the terms of version 2.1 of the GNU Lesser General Public # License as published by the Free Software Foundation. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA """Pure, simple, BER encoding and decoding""" # This BER library is currently aimed at supporting LDAP, thus # the following restrictions from RFC2251 apply: # # (1) Only the definite form of length encoding will be used. # # (2) OCTET STRING values will be encoded in the primitive form # only. # # (3) If the value of a BOOLEAN type is true, the encoding MUST have # its contents octets set to hex "FF". # # (4) If a value of a type is its default value, it MUST be absent. # Only some BOOLEAN and INTEGER types have default values in # this protocol definition. import string # xxxxxxxx # |/|\.../ # | | | # | | tag # | | # | primitive (0) or structured (1) # | # class CLASS_MASK = 0xc0 CLASS_UNIVERSAL = 0x00 CLASS_APPLICATION = 0x40 CLASS_CONTEXT = 0x80 CLASS_PRIVATE = 0xc0 STRUCTURED_MASK = 0x20 STRUCTURED = 0x20 NOT_STRUCTURED = 0x00 TAG_MASK = 0x1f # LENGTH # 0xxxxxxx = 0..127 # 1xxxxxxx = len is stored in the next 0xxxxxxx octets # indefinite form not supported class UnknownBERTag(Exception): def __init__(self, tag, context): Exception.__init__(self) self.tag = tag self.context = context def __str__(self): return "BERDecoderContext has no tag 0x%02x: %s" \ % (self.tag, self.context) import UserList def berDecodeLength(m, offset=0): """ Return a tuple of (length, lengthLength). m must be atleast one byte long. """ l=ber2int(m[offset+0]) ll=1 if l&0x80: ll=1+(l&0x7F) need(m, offset+ll) l=ber2int(m[offset+1:offset+ll], signed=0) return (l, ll) def int2berlen(i): assert i>=0 e=int2ber(i, signed=False) if i <= 127: return e else: l=len(e) assert l>0 assert l<=127 return chr(0x80|l) + e def int2ber(i, signed=True): encoded='' while ((signed and (i>127 or i<-128)) or (not signed and (i>255))): encoded=chr(i%256)+encoded i=i>>8 encoded=chr(i%256)+encoded return encoded def ber2int(e, signed=True): need(e, 1) v=0L+ord(e[0]) if v&0x80 and signed: v=v-256 for i in range(1, len(e)): v=(v<<8) | ord(e[i]) return v class BERBase: tag = None def identification(self): return self.tag def __init__(self, tag=None): if tag is not None: self.tag=tag def __len__(self): return len(str(self)) def __cmp__(self, other): if isinstance(other, BERBase): return cmp(str(self), str(other)) else: return -1 def __eq__(self, other): if isinstance(other, BERBase): return str(self) == str(other) else: return False def __ne__(self, other): if isinstance(other, BERBase): return str(self) != str(other) else: return False class BERStructured(BERBase): def identification(self): return STRUCTURED|self.tag class BERException(Exception): pass class BERExceptionInsufficientData(Exception): pass def need(buf, n): d=n-len(buf) if d>0: raise BERExceptionInsufficientData, d class BERInteger(BERBase): tag = 0x02 value = None def fromBER(klass, tag, content, berdecoder=None): assert len(content)>0 value=ber2int(content) r = klass(value=value, tag=tag) return r fromBER = classmethod(fromBER) def __init__(self, value=None, tag=None): """Create a new BERInteger object. value is an integer. """ BERBase.__init__(self, tag) assert value is not None self.value=value def __str__(self): encoded=int2ber(self.value) return chr(self.identification()) \ +int2berlen(len(encoded)) \ +encoded def __repr__(self): if self.tag==self.__class__.tag: return self.__class__.__name__+"(value=%r)"%self.value else: return self.__class__.__name__+"(value=%r, tag=%d)" \ %(self.value, self.tag) class BEROctetString(BERBase): tag = 0x04 value = None def fromBER(klass, tag, content, berdecoder=None): assert len(content)>=0 r = klass(value=content, tag=tag) return r fromBER = classmethod(fromBER) def __init__(self, value=None, tag=None): BERBase.__init__(self, tag) assert value is not None self.value=value def __str__(self): value = str(self.value) return chr(self.identification()) \ +int2berlen(len(value)) \ +value def __repr__(self): if self.tag==self.__class__.tag: return self.__class__.__name__+"(value=%s)" \ %repr(self.value) else: return self.__class__.__name__ \ +"(value=%s, tag=%d)" \ %(repr(self.value), self.tag) class BERNull(BERBase): tag = 0x05 def fromBER(klass, tag, content, berdecoder=None): assert len(content) == 0 r = klass(tag=tag) return r fromBER = classmethod(fromBER) def __init__(self, tag=None): BERBase.__init__(self, tag) def __str__(self): return chr(self.identification())+chr(0) def __repr__(self): if self.tag==self.__class__.tag: return self.__class__.__name__+"()" else: return self.__class__.__name__+"(tag=%d)"%self.tag class BERBoolean(BERBase): tag = 0x01 def fromBER(klass, tag, content, berdecoder=None): assert len(content) > 0 value = ber2int(content) r = klass(value=value, tag=tag) return r fromBER = classmethod(fromBER) def __init__(self, value=None, tag=None): """Create a new BERInteger object. value is an integer. """ BERBase.__init__(self, tag) assert value is not None if value: value=0xFF self.value=value def __str__(self): assert self.value==0 or self.value==0xFF return chr(self.identification()) \ +int2berlen(1) \ +chr(self.value) def __repr__(self): if self.tag==self.__class__.tag: return self.__class__.__name__+"(value=%d)"%self.value else: return self.__class__.__name__+"(value=%d, tag=%d)" \ %(self.value, self.tag) class BEREnumerated(BERInteger): tag = 0x0a class BERSequence(BERStructured, UserList.UserList): # TODO __getslice__ calls __init__ with no args. tag = 0x10 def fromBER(klass, tag, content, berdecoder=None): l = berDecodeMultiple(content, berdecoder) r = klass(l, tag=tag) return r fromBER = classmethod(fromBER) def __init__(self, value=None, tag=None): BERStructured.__init__(self, tag) UserList.UserList.__init__(self) assert value is not None self[:]=value def __str__(self): r=string.join(map(str, self.data), '') return chr(self.identification())+int2berlen(len(r))+r def __repr__(self): if self.tag==self.__class__.tag: return self.__class__.__name__+"(value=%s)"%repr(self.data) else: return self.__class__.__name__+"(value=%s, tag=%d)" \ %(repr(self.data), self.tag) class BERSequenceOf(BERSequence): pass class BERSet(BERSequence): tag = 0x11 pass class BERDecoderContext: Identities = { BERInteger.tag: BERInteger, BEROctetString.tag: BEROctetString, BERNull.tag: BERNull, BERBoolean.tag: BERBoolean, BEREnumerated.tag: BEREnumerated, BERSequence.tag: BERSequence, BERSet.tag: BERSet, } def __init__(self, fallback=None, inherit=None): self.fallback=fallback self.inherit_context=inherit def lookup_id(self, id): try: return self.Identities[id] except KeyError: if self.fallback: return self.fallback.lookup_id(id) else: return None def inherit(self): return self.inherit_context or self def __repr__(self): identities = [] for tag, class_ in self.Identities.items(): identities.append('0x%02x: %s' % (tag, class_.__name__)) return "<"+self.__class__.__name__ \ +" identities={%s}" % ', '.join(identities) \ +" fallback="+repr(self.fallback) \ +" inherit="+repr(self.inherit_context) \ +">" def berDecodeObject(context, m): """berDecodeObject(context, string) -> (berobject, bytesUsed) berobject may be None. """ while m: need(m, 2) i=ber2int(m[0], signed=0)&(CLASS_MASK|TAG_MASK) length, lenlen = berDecodeLength(m, offset=1) need(m, 1+lenlen+length) m2 = m[1+lenlen:1+lenlen+length] berclass=context.lookup_id(i) if berclass: inh=context.inherit() assert inh r = berclass.fromBER(tag=i, content=m2, berdecoder=inh) return (r, 1+lenlen+length) else: #raise UnknownBERTag, (i, context) print str(UnknownBERTag(i, context)) #TODO return (None, 1+lenlen+length) return (None, 0) def berDecodeMultiple(content, berdecoder): """berDecodeMultiple(content, berdecoder) -> [objects] Decodes everything in content and returns a list of decoded objects. All of content will be decoded, and content must contain complete BER objects. """ l = [] while content: n, bytes = berDecodeObject(berdecoder, content) if n is not None: l.append(n) assert bytes <= len(content) content = content[bytes:] return l #TODO unimplemented classes are below: #class BERObjectIdentifier(BERBase): # tag = 0x06 # pass #class BERIA5String(BERBase): # tag = 0x16 # pass #class BERPrintableString(BERBase): # tag = 0x13 # pass #class BERT61String(BERBase): # tag = 0x14 # pass #class BERUTCTime(BERBase): # tag = 0x17 # pass #class BERBitString(BERBase): # tag = 0x03 # pass ldaptor-0.0.43/ldaptor/protocols/pureldap.py0000644000175000017500000012241310365653714017241 0ustar janjan# Twisted, the Framework of Your Internet # Copyright (C) 2001 Matthew W. Lefkowitz # # This library is free software; you can redistribute it and/or # modify it under the terms of version 2.1 of the GNU Lesser General Public # License as published by the Free Software Foundation. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA """LDAP protocol message conversion; no application logic here.""" from pureber import * next_ldap_message_id=1 def alloc_ldap_message_id(): global next_ldap_message_id r=next_ldap_message_id next_ldap_message_id=next_ldap_message_id+1 return r def escape(s): s = s.replace('\\', r'\5c') s = s.replace('*', r'\2a') s = s.replace('(', r'\28') s = s.replace(')', r'\29') s = s.replace('\0', r'\00') return s class LDAPString(BEROctetString): pass class LDAPAttributeValue(BEROctetString): pass class LDAPMessage(BERSequence): id = None value = None def fromBER(klass, tag, content, berdecoder=None): l = berDecodeMultiple(content, berdecoder) id_=l[0].value value=l[1] if l[2:]: controls = [] for c in l[2]: controls.append(( c.controlType, c.criticality, c.controlValue, )) else: controls = None assert not l[3:] r = klass(id=id_, value=value, controls=controls, tag=tag) return r fromBER = classmethod(fromBER) def __init__(self, value=None, controls=None, id=None, tag=None): BERSequence.__init__(self, value=[], tag=tag) assert value is not None self.id=id if self.id is None: self.id=alloc_ldap_message_id() self.value=value self.controls = controls def __str__(self): l = [BERInteger(self.id), self.value] if self.controls is not None: l.append(LDAPControls([LDAPControl(*a) for a in self.controls])) return str(BERSequence(l)) def __repr__(self): l=[] l.append('id=%r' % self.id) l.append('value=%r' % self.value) if self.tag!=self.__class__.tag: l.append('tag=%d' % self.tag) return self.__class__.__name__+'('+', '.join(l)+')' class LDAPProtocolOp: def __init__(self): pass def __str__(self): raise NotImplementedError class LDAPProtocolRequest(LDAPProtocolOp): needs_answer=1 pass class LDAPProtocolResponse(LDAPProtocolOp): pass class LDAPBERDecoderContext_LDAPBindRequest(BERDecoderContext): Identities = { CLASS_CONTEXT|0x00: BEROctetString, } class LDAPBindRequest(LDAPProtocolRequest, BERSequence): tag=CLASS_APPLICATION|0x00 def fromBER(klass, tag, content, berdecoder=None): l = berDecodeMultiple(content, LDAPBERDecoderContext_LDAPBindRequest( fallback=berdecoder)) r = klass(version=l[0].value, dn=l[1].value, auth=l[2].value, tag=tag) return r fromBER = classmethod(fromBER) def __init__(self, version=None, dn=None, auth=None, tag=None): LDAPProtocolRequest.__init__(self) BERSequence.__init__(self, [], tag=tag) self.version=version if self.version is None: self.version=3 self.dn=dn if self.dn is None: self.dn='' self.auth=auth if self.auth is None: self.auth='' def __str__(self): return str(BERSequence([ BERInteger(self.version), BEROctetString(self.dn), BEROctetString(self.auth, tag=CLASS_CONTEXT|0), ], tag=self.tag)) def __repr__(self): l=[] l.append('version=%d' % self.version) l.append('dn=%s' % repr(self.dn)) l.append('auth=%s' % repr(self.auth)) if self.tag!=self.__class__.tag: l.append('tag=%d' % self.tag) return self.__class__.__name__+'('+', '.join(l)+')' class LDAPReferral(BERSequence): tag = CLASS_CONTEXT | 0x03 class LDAPResult(LDAPProtocolResponse, BERSequence): def fromBER(klass, tag, content, berdecoder=None): l = berDecodeMultiple(content, LDAPBERDecoderContext_LDAPBindRequest( fallback=berdecoder)) assert 3<=len(l)<=4 referral = None #if (l[3:] and isinstance(l[3], LDAPReferral)): #TODO support referrals #self.referral=self.data[0] r = klass(resultCode=l[0].value, matchedDN=l[1].value, errorMessage=l[2].value, referral=referral, tag=tag) return r fromBER = classmethod(fromBER) def __init__(self, resultCode=None, matchedDN=None, errorMessage=None, referral=None, serverSaslCreds=None, tag=None): LDAPProtocolResponse.__init__(self) BERSequence.__init__(self, value=[], tag=tag) assert resultCode is not None self.resultCode=resultCode if matchedDN is None: matchedDN='' self.matchedDN=matchedDN if errorMessage is None: errorMessage='' self.errorMessage=errorMessage self.referral=referral self.serverSaslCreds=serverSaslCreds def __str__(self): assert self.referral is None #TODO return str(BERSequence([ BEREnumerated(self.resultCode), BEROctetString(self.matchedDN), BEROctetString(self.errorMessage), #TODO referral [3] Referral OPTIONAL ], tag=self.tag)) def __repr__(self): l=[] l.append('resultCode=%r' % self.resultCode) if self.matchedDN: l.append('matchedDN=%r' % str(self.matchedDN)) if self.errorMessage: l.append('errorMessage=%r' % str(self.errorMessage)) if self.referral: l.append('referral=%r' % self.referral) if self.tag!=self.__class__.tag: l.append('tag=%d' % self.tag) return self.__class__.__name__+'('+', '.join(l)+')' class LDAPBindResponse_serverSaslCreds(BERSequence): tag = CLASS_CONTEXT|0x03 pass class LDAPBERDecoderContext_BindResponse(BERDecoderContext): Identities = { LDAPBindResponse_serverSaslCreds.tag: LDAPBindResponse_serverSaslCreds, } class LDAPBindResponse(LDAPResult): tag=CLASS_APPLICATION|0x01 resultCode = None matchedDN = None errorMessage = None referral = None serverSaslCreds = None def fromBER(klass, tag, content, berdecoder=None): l = berDecodeMultiple(content, LDAPBERDecoderContext_BindResponse( fallback=berdecoder)) assert 3<=len(l)<=4 try: if isinstance(l[0], LDAPBindResponse_serverSaslCreds): serverSaslCreds=l[0] del l[0] else: serverSaslCreds=None except IndexError: serverSaslCreds=None referral = None #if (l[3:] and isinstance(l[3], LDAPReferral)): #TODO support referrals #self.referral=self.data[0] r = klass(resultCode=l[0].value, matchedDN=l[1].value, errorMessage=l[2].value, referral=referral, serverSaslCreds=serverSaslCreds, tag=tag) return r fromBER = classmethod(fromBER) def __init__(self, resultCode=None, matchedDN=None, errorMessage=None, referral=None, serverSaslCreds=None, tag=None): LDAPResult.__init__(self, resultCode=resultCode, matchedDN=matchedDN, errorMessage=errorMessage, referral=referral, tag=None) assert self.serverSaslCreds is None #TODO def __str__(self): assert self.serverSaslCreds is None #TODO return LDAPResult.__str__(self) def __repr__(self): assert self.serverSaslCreds is None #TODO return LDAPResult.__repr__(self) class LDAPUnbindRequest(LDAPProtocolRequest, BERNull): tag=CLASS_APPLICATION|0x02 needs_answer=0 def __init__(self, *args, **kwargs): LDAPProtocolRequest.__init__(self) BERNull.__init__(self, *args, **kwargs) def __str__(self): return BERNull.__str__(self) class LDAPAttributeDescription(BEROctetString): pass class LDAPAttributeValueAssertion(BERSequence): def fromBER(klass, tag, content, berdecoder=None): l = berDecodeMultiple(content, berdecoder) assert len(l) == 2 r = klass(attributeDesc=l[0], assertionValue=l[1], tag=tag) return r fromBER = classmethod(fromBER) def __init__(self, attributeDesc=None, assertionValue=None, tag=None): BERSequence.__init__(self, value=[], tag=tag) assert attributeDesc is not None self.attributeDesc=attributeDesc self.assertionValue=assertionValue def __str__(self): return str(BERSequence([self.attributeDesc, self.assertionValue], tag=self.tag)) def __repr__(self): if self.tag==self.__class__.tag: return self.__class__.__name__+"(attributeDesc=%s, assertionValue=%s)"\ %(repr(self.attributeDesc), repr(self.assertionValue)) else: return self.__class__.__name__+"(attributeDesc=%s, assertionValue=%s, tag=%d)"\ %(repr(self.attributeDesc), repr(self.assertionValue), self.tag) class LDAPFilter(BERStructured): def __init__(self, tag=None): BERStructured.__init__(self, tag=tag) class LDAPFilterSet(BERSet): def fromBER(klass, tag, content, berdecoder=None): l = berDecodeMultiple(content, LDAPBERDecoderContext_Filter(fallback=berdecoder)) r = klass(l, tag=tag) return r fromBER = classmethod(fromBER) class LDAPFilter_and(LDAPFilterSet): tag = CLASS_CONTEXT|0x00 def asText(self): return '(&'+''.join([x.asText() for x in self])+')' class LDAPFilter_or(LDAPFilterSet): tag = CLASS_CONTEXT|0x01 def asText(self): return '(|'+''.join([x.asText() for x in self])+')' class LDAPFilter_not(LDAPFilter): tag = CLASS_CONTEXT|0x02 def fromBER(klass, tag, content, berdecoder=None): value, bytes = berDecodeObject(LDAPBERDecoderContext_Filter(fallback=berdecoder, inherit=berdecoder), content) assert bytes == len(content) r = klass(value=value, tag=tag) return r fromBER = classmethod(fromBER) def __init__(self, value, tag=tag): LDAPFilter.__init__(self, tag=tag) assert value is not None self.value=value def __repr__(self): if self.tag==self.__class__.tag: return self.__class__.__name__\ +"(value=%s)"\ %repr(self.value) else: return self.__class__.__name__\ +"(value=%s, tag=%d)"\ %(repr(self.value), self.tag) def __str__(self): r=str(self.value) return chr(self.identification())+int2berlen(len(r))+r def asText(self): return '(!'+self.value.asText()+')' class LDAPFilter_equalityMatch(LDAPAttributeValueAssertion): tag = CLASS_CONTEXT|0x03 def asText(self): return '('+self.attributeDesc.value+'=' \ +escape(self.assertionValue.value)+')' class LDAPFilter_substrings_initial(LDAPString): tag = CLASS_CONTEXT|0x00 def asText(self): return escape(self.value) class LDAPFilter_substrings_any(LDAPString): tag = CLASS_CONTEXT|0x01 def asText(self): return escape(self.value) class LDAPFilter_substrings_final(LDAPString): tag = CLASS_CONTEXT|0x02 def asText(self): return escape(self.value) class LDAPBERDecoderContext_Filter_substrings(BERDecoderContext): Identities = { LDAPFilter_substrings_initial.tag: LDAPFilter_substrings_initial, LDAPFilter_substrings_any.tag: LDAPFilter_substrings_any, LDAPFilter_substrings_final.tag: LDAPFilter_substrings_final, } class LDAPFilter_substrings(BERSequence): tag = CLASS_CONTEXT|0x04 def fromBER(klass, tag, content, berdecoder=None): l = berDecodeMultiple(content, LDAPBERDecoderContext_Filter_substrings(fallback=berdecoder)) assert len(l) == 2 assert len(l[1])>=1 r = klass(type=l[0].value, substrings=list(l[1]), tag=tag) return r fromBER = classmethod(fromBER) def __init__(self, type=None, substrings=None, tag=None): BERSequence.__init__(self, value=[], tag=tag) assert type is not None assert substrings is not None self.type=type self.substrings=substrings def __str__(self): return str(BERSequence([ LDAPString(self.type), BERSequence(self.substrings)], tag=self.tag)) def __repr__(self): if self.tag==self.__class__.tag: return self.__class__.__name__\ +"(type=%s, substrings=%s)"\ %(repr(self.type), repr(self.substrings)) else: return self.__class__.__name__\ +"(type=%s, substrings=%s, tag=%d)"\ %(repr(self.type), repr(self.substrings), self.tag) def asText(self): initial=None final=None any=[] for s in self.substrings: assert s is not None if isinstance(s, LDAPFilter_substrings_initial): assert initial is None assert not any assert final is None initial=s.asText() elif isinstance(s, LDAPFilter_substrings_final): assert final is None final=s.asText() elif isinstance(s, LDAPFilter_substrings_any): assert final is None any.append(s.asText()) else: raise 'TODO' if initial is None: initial='' if final is None: final='' return '('+self.type+'=' \ +'*'.join([initial]+any+[final])+')' class LDAPFilter_greaterOrEqual(LDAPAttributeValueAssertion): tag = CLASS_CONTEXT|0x05 def asText(self): return '('+self.attributeDesc.value+'>=' \ +escape(self.assertionValue.value)+')' class LDAPFilter_lessOrEqual(LDAPAttributeValueAssertion): tag = CLASS_CONTEXT|0x06 def asText(self): return '('+self.attributeDesc.value+'<=' \ +escape(self.assertionValue.value)+')' class LDAPFilter_present(LDAPAttributeDescription): tag = CLASS_CONTEXT|0x07 def asText(self): return '(%s=*)' % self.value class LDAPFilter_approxMatch(LDAPAttributeValueAssertion): tag = CLASS_CONTEXT|0x08 def asText(self): return '('+self.attributeDesc.value+'~=' \ +escape(self.assertionValue.value)+')' class LDAPMatchingRuleId(LDAPString): pass class LDAPAssertionValue(BEROctetString): pass class LDAPMatchingRuleAssertion_matchingRule(LDAPMatchingRuleId): tag = CLASS_CONTEXT|0x01 pass class LDAPMatchingRuleAssertion_type(LDAPAttributeDescription): tag = CLASS_CONTEXT|0x02 pass class LDAPMatchingRuleAssertion_matchValue(LDAPAssertionValue): tag = CLASS_CONTEXT|0x03 pass class LDAPMatchingRuleAssertion_dnAttributes(BERBoolean): tag = CLASS_CONTEXT|0x04 pass class LDAPBERDecoderContext_MatchingRuleAssertion(BERDecoderContext): Identities = { LDAPMatchingRuleAssertion_matchingRule.tag: LDAPMatchingRuleAssertion_matchingRule, LDAPMatchingRuleAssertion_type.tag: LDAPMatchingRuleAssertion_type, LDAPMatchingRuleAssertion_matchValue.tag: LDAPMatchingRuleAssertion_matchValue, LDAPMatchingRuleAssertion_dnAttributes.tag: LDAPMatchingRuleAssertion_dnAttributes, } class LDAPMatchingRuleAssertion(BERSequence): matchingRule=None type=None matchValue=None dnAttributes=None def fromBER(klass, tag, content, berdecoder=None): l = berDecodeMultiple(content, LDAPBERDecoderContext_MatchingRuleAssertion(fallback=berdecoder, inherit=berdecoder)) assert 1<=len(l)<=4 if isinstance(l[0], LDAPMatchingRuleAssertion_matchingRule): matchingRule=l[0] del l[0] if len(l)>1 \ and isinstance(l[0], LDAPMatchingRuleAssertion_type): type=l[0] del l[0] if len(l)>1 \ and isinstance(l[0], LDAPMatchingRuleAssertion_matchValue): matchValue=l[0] del l[0] if len(l)>1 \ and isinstance(l[0], LDAPMatchingRuleAssertion_dnAttributes): dnAttributes=l[0] del l[0] assert matchValue if not dnAttributes: dnAttributes=None assert 8<=len(l)<=8 r = klass(matchingRule=matchingRule, type=type, matchValue=matchValue, dnAttributes=dnAttributes, tag=tag) return r fromBER = classmethod(fromBER) def __init__(self, matchingRule=None, type=None, matchValue=None, dnAttributes=None, tag=None): BERSequence.__init__(self, value=[], tag=tag) assert matchValue is not None self.matchingRule=matchingRule self.type=type self.matchValue=matchValue self.dnAttributes=dnAttributes if not self.dnAttributes: self.dnAttributes=None def __str__(self): return str(BERSequence( filter(lambda x: x is not None, [self.matchingRule, self.type, self.matchValue, self.dnAttributes]), tag=self.tag)) def __repr__(self): l=[] l.append('matchingRule=%s' % repr(self.matchingRule)) l.append('type=%s' % repr(self.type)) l.append('matchValue=%s' % repr(self.matchValue)) l.append('dnAttributes=%s' % repr(self.dnAttributes)) if self.tag!=self.__class__.tag: l.append('tag=%d' % self.tag) return self.__class__.__name__+'('+', '.join(l)+')' class LDAPFilter_extensibleMatch(LDAPMatchingRuleAssertion): tag = CLASS_CONTEXT|0x09 pass class LDAPBERDecoderContext_Filter(BERDecoderContext): Identities = { LDAPFilter_and.tag: LDAPFilter_and, LDAPFilter_or.tag: LDAPFilter_or, LDAPFilter_not.tag: LDAPFilter_not, LDAPFilter_equalityMatch.tag: LDAPFilter_equalityMatch, LDAPFilter_substrings.tag: LDAPFilter_substrings, LDAPFilter_greaterOrEqual.tag: LDAPFilter_greaterOrEqual, LDAPFilter_lessOrEqual.tag: LDAPFilter_lessOrEqual, LDAPFilter_present.tag: LDAPFilter_present, LDAPFilter_approxMatch.tag: LDAPFilter_approxMatch, LDAPFilter_extensibleMatch.tag: LDAPFilter_extensibleMatch, } LDAP_SCOPE_baseObject=0 LDAP_SCOPE_singleLevel=1 LDAP_SCOPE_wholeSubtree=2 LDAP_DEREF_neverDerefAliases=0 LDAP_DEREF_derefInSearching=1 LDAP_DEREF_derefFindingBaseObj=2 LDAP_DEREF_derefAlways=3 LDAPFilterMatchAll = LDAPFilter_present('objectClass') class LDAPSearchRequest(LDAPProtocolRequest, BERSequence): tag=CLASS_APPLICATION|0x03 baseObject='' scope=LDAP_SCOPE_wholeSubtree derefAliases=LDAP_DEREF_neverDerefAliases sizeLimit=0 timeLimit=0 typesOnly=0 filter=LDAPFilterMatchAll attributes=[] #TODO AttributeDescriptionList #TODO decode def fromBER(klass, tag, content, berdecoder=None): l = berDecodeMultiple(content, LDAPBERDecoderContext_Filter(fallback=berdecoder, inherit=berdecoder)) assert 8<=len(l)<=8 r = klass(baseObject=l[0].value, scope=l[1].value, derefAliases=l[2].value, sizeLimit=l[3].value, timeLimit=l[4].value, typesOnly=l[5].value, filter=l[6], attributes=[x.value for x in l[7]], tag=tag) return r fromBER = classmethod(fromBER) def __init__(self, baseObject=None, scope=None, derefAliases=None, sizeLimit=None, timeLimit=None, typesOnly=None, filter=None, attributes=None, tag=None): LDAPProtocolRequest.__init__(self) BERSequence.__init__(self, [], tag=tag) if baseObject is not None: self.baseObject=baseObject if scope is not None: self.scope=scope if derefAliases is not None: self.derefAliases=derefAliases if sizeLimit is not None: self.sizeLimit=sizeLimit if timeLimit is not None: self.timeLimit=timeLimit if typesOnly is not None: self.typesOnly=typesOnly if filter is not None: self.filter=filter if attributes is not None: self.attributes=attributes def __str__(self): return str(BERSequence([ BEROctetString(self.baseObject), BEREnumerated(self.scope), BEREnumerated(self.derefAliases), BERInteger(self.sizeLimit), BERInteger(self.timeLimit), BERBoolean(self.typesOnly), self.filter, BERSequenceOf(map(BEROctetString, self.attributes)), ], tag=self.tag)) def __repr__(self): if self.tag==self.__class__.tag: return self.__class__.__name__\ +("(baseObject=%s, scope=%s, derefAliases=%s, " \ +"sizeLimit=%s, timeLimit=%s, typesOnly=%s, " \ "filter=%s, attributes=%s)") \ %(repr(self.baseObject), self.scope, self.derefAliases, self.sizeLimit, self.timeLimit, self.typesOnly, repr(self.filter), self.attributes) else: return self.__class__.__name__\ +("(baseObject=%s, scope=%s, derefAliases=%s, " \ +"sizeLimit=%s, timeLimit=%s, typesOnly=%s, " \ "filter=%s, attributes=%s, tag=%d)") \ %(repr(self.baseObject), self.scope, self.derefAliases, self.sizeLimit, self.timeLimit, self.typesOnly, self.filter, self.attributes, self.tag) class LDAPSearchResultEntry(LDAPProtocolResponse, BERSequence): tag=CLASS_APPLICATION|0x04 def fromBER(klass, tag, content, berdecoder=None): l = berDecodeMultiple(content, LDAPBERDecoderContext_Filter(fallback=berdecoder, inherit=berdecoder)) objectName=l[0].value attributes=[] for attr, li in l[1].data: attributes.append((attr.value, map(lambda x: x.value, li))) r = klass(objectName=objectName, attributes=attributes, tag=tag) return r fromBER = classmethod(fromBER) def __init__(self, objectName, attributes, tag=None): LDAPProtocolResponse.__init__(self) BERSequence.__init__(self, [], tag=tag) assert objectName is not None assert attributes is not None self.objectName=objectName self.attributes=attributes def __str__(self): return str(BERSequence([ BEROctetString(self.objectName), BERSequence(map(lambda (attr,li): BERSequence([BEROctetString(attr), BERSet(map(BEROctetString, li))]), self.attributes)), ], tag=self.tag)) def __repr__(self): if self.tag==self.__class__.tag: return self.__class__.__name__\ +"(objectName=%s, attributes=%s"\ %(repr(str(self.objectName)), repr(map(lambda (a,l): (str(a), map(lambda i, l=l: str(i), l)), self.attributes))) else: return self.__class__.__name__\ +"(objectName=%s, attributes=%s, tag=%d"\ %(repr(str(self.objectName)), repr(map(lambda (a,l): (str(a), map(lambda i, l=l: str(i), l)), self.attributes)), self.tag) class LDAPSearchResultDone(LDAPResult): tag=CLASS_APPLICATION|0x05 pass class LDAPControls(BERSequence): tag = CLASS_CONTEXT|0x00 def fromBER(klass, tag, content, berdecoder=None): l = berDecodeMultiple(content, LDAPBERDecoderContext_LDAPControls( inherit=berdecoder)) r = klass(l, tag=tag) return r fromBER = classmethod(fromBER) class LDAPControl(BERSequence): criticality = None controlValue = None def fromBER(klass, tag, content, berdecoder=None): l = berDecodeMultiple(content, berdecoder) kw = {} if l[1:]: kw['criticality'] = l[1].value if l[2:]: kw['controlValue'] = l[2].value # TODO is controlType, controlValue allowed without criticality? assert not l[3:] r = klass(controlType=l[0].value, tag=tag, **kw) return r fromBER = classmethod(fromBER) def __init__(self, controlType, criticality=None, controlValue=None, id=None, tag=None): BERSequence.__init__(self, value=[], tag=tag) assert controlType is not None self.controlType = controlType self.criticality = criticality self.controlValue = controlValue def __str__(self): self.data=[LDAPOID(self.controlType)] if self.criticality is not None: self.data.append(BERBoolean(self.criticality)) if self.controlValue is not None: self.data.append(BEROctetString(self.controlValue)) return BERSequence.__str__(self) class LDAPBERDecoderContext_LDAPControls(BERDecoderContext): Identities = { LDAPControl.tag: LDAPControl, } class LDAPBERDecoderContext_LDAPMessage(BERDecoderContext): Identities = { LDAPControls.tag: LDAPControls, } class LDAPBERDecoderContext_TopLevel(BERDecoderContext): Identities = { BERSequence.tag: LDAPMessage, } class LDAPModifyRequest(LDAPProtocolRequest, BERSequence): tag=CLASS_APPLICATION|0x06 object = None modification = None def fromBER(klass, tag, content, berdecoder=None): l = berDecodeMultiple(content, berdecoder) assert len(l) == 2 r = klass(object=l[0].value, modification=l[1].data, tag=tag) return r fromBER = classmethod(fromBER) def __init__(self, object=None, modification=None, tag=None): """ Initialize the object Example usage:: l = LDAPModifyRequest( object='cn=foo,dc=example,dc=com', modification=[ BERSequence([ BEREnumerated(0), BERSequence([ LDAPAttributeDescription('attr1'), BERSet([ LDAPString('value1'), LDAPString('value2'), ]), ]), ]), BERSequence([ BEREnumerated(1), BERSequence([ LDAPAttributeDescription('attr2'), ]), ]), ]) But more likely you just want to say:: mod = delta.ModifyOp('cn=foo,dc=example,dc=com', [delta.Add('attr1', ['value1', 'value2']), delta.Delete('attr1', ['value1', 'value2'])]) l = mod.asLDAP() """ LDAPProtocolRequest.__init__(self) BERSequence.__init__(self, [], tag=tag) self.object=object self.modification=modification def __str__(self): l=[LDAPString(self.object)] if self.modification is not None: l.append(BERSequence(self.modification)) return str(BERSequence(l, tag=self.tag)) def __repr__(self): if self.tag==self.__class__.tag: return self.__class__.__name__+"(object=%s, modification=%s)"\ %(repr(self.object), repr(self.modification)) else: return self.__class__.__name__+"(object=%s, modification=%s, tag=%d)" \ %(repr(self.object), repr(self.modification), self.tag) class LDAPModifyResponse(LDAPResult): tag = CLASS_APPLICATION|0x07 class LDAPAddRequest(LDAPProtocolRequest, BERSequence): tag = CLASS_APPLICATION|0x08 def fromBER(klass, tag, content, berdecoder=None): l = berDecodeMultiple(content, berdecoder) r = klass(entry=l[0].value, attributes=l[1], tag=tag) return r fromBER = classmethod(fromBER) def __init__(self, entry=None, attributes=None, tag=None): """ Initialize the object Example usage:: l=LDAPAddRequest(entry='cn=foo,dc=example,dc=com', attributes=[(LDAPAttributeDescription("attrFoo"), BERSet(value=( LDAPAttributeValue("value1"), LDAPAttributeValue("value2"), ))), (LDAPAttributeDescription("attrBar"), BERSet(value=( LDAPAttributeValue("value1"), LDAPAttributeValue("value2"), ))), ]) """ LDAPProtocolRequest.__init__(self) BERSequence.__init__(self, [], tag=tag) self.entry=entry self.attributes=attributes def __str__(self): return str(BERSequence([ LDAPString(self.entry), BERSequence(map(BERSequence, self.attributes)), ], tag=self.tag)) def __repr__(self): if self.tag==self.__class__.tag: return self.__class__.__name__+"(entry=%s, attributes=%s)"\ %(repr(self.entry), repr(self.attributes)) else: return self.__class__.__name__+"(entry=%s, attributes=%s, tag=%d)" \ %(repr(self.entry), repr(self.attributes), self.tag) class LDAPAddResponse(LDAPResult): tag = CLASS_APPLICATION|0x09 class LDAPDelRequest(LDAPProtocolRequest, LDAPString): tag = CLASS_APPLICATION|0x0a def __init__(self, value=None, entry=None, tag=None): """ Initialize the object l=LDAPDelRequest(entry='cn=foo,dc=example,dc=com') """ if entry is None and value is not None: entry = value LDAPProtocolRequest.__init__(self) LDAPString.__init__(self, value=entry, tag=tag) def __str__(self): return LDAPString.__str__(self) def __repr__(self): if self.tag==self.__class__.tag: return self.__class__.__name__+"(entry=%s)" \ %repr(self.value) else: return self.__class__.__name__ \ +"(entry=%s, tag=%d)" \ %(repr(self.value), self.tag) class LDAPDelResponse(LDAPResult): tag = CLASS_APPLICATION|0x0b pass class LDAPModifyDNResponse_newSuperior(LDAPString): tag = CLASS_CONTEXT|0x00 pass class LDAPBERDecoderContext_ModifyDNRequest(BERDecoderContext): Identities = { LDAPModifyDNResponse_newSuperior.tag: LDAPModifyDNResponse_newSuperior, } class LDAPModifyDNRequest(LDAPProtocolRequest, BERSequence): tag=CLASS_APPLICATION|12 entry=None newrdn=None deleteoldrdn=None newSuperior=None def fromBER(klass, tag, content, berdecoder=None): l = berDecodeMultiple(content, LDAPBERDecoderContext_ModifyDNRequest(fallback=berdecoder)) kw = {} try: kw['newSuperior'] = str(l[3].value) except IndexError: pass r = klass(entry=str(l[0].value), newrdn=str(l[1].value), deleteoldrdn=l[2].value, tag=tag, **kw) return r fromBER = classmethod(fromBER) def __init__(self, entry, newrdn, deleteoldrdn, newSuperior=None, tag=None): """ Initialize the object Example usage:: l=LDAPModifyDNRequest(entry='cn=foo,dc=example,dc=com', newrdn='someAttr=value', deleteoldrdn=0) """ LDAPProtocolRequest.__init__(self) BERSequence.__init__(self, [], tag=tag) assert entry is not None assert newrdn is not None assert deleteoldrdn is not None self.entry=entry self.newrdn=newrdn self.deleteoldrdn=deleteoldrdn self.newSuperior=newSuperior def __str__(self): l=[ LDAPString(self.entry), LDAPString(self.newrdn), BERBoolean(self.deleteoldrdn), ] if self.newSuperior is not None: l.append(LDAPString(self.newSuperior, tag=CLASS_CONTEXT|0)) return str(BERSequence(l, tag=self.tag)) def __repr__(self): l = [ "entry=%s" % repr(self.entry), "newrdn=%s" % repr(self.newrdn), "deleteoldrdn=%s" % repr(self.deleteoldrdn), ] if self.newSuperior is not None: l.append("newSuperior=%s" % repr(self.newSuperior)) if self.tag!=self.__class__.tag: l.append("tag=%d" % self.tag) return self.__class__.__name__ + "(" + ', '.join(l) + ")" class LDAPModifyDNResponse(LDAPResult): tag=CLASS_APPLICATION|13 #class LDAPCompareResponse(LDAPProtocolResponse): #class LDAPCompareRequest(LDAPProtocolRequest): #class LDAPAbandonRequest(LDAPProtocolRequest): # needs_answer=0 class LDAPOID(BEROctetString): pass class LDAPResponseName(LDAPOID): tag = CLASS_CONTEXT|10 class LDAPResponse(BEROctetString): tag = CLASS_CONTEXT|11 class LDAPBERDecoderContext_LDAPExtendedRequest(BERDecoderContext): Identities = { CLASS_CONTEXT|0x00: BEROctetString, CLASS_CONTEXT|0x01: BEROctetString, } class LDAPExtendedRequest(LDAPProtocolRequest, BERSequence): tag = CLASS_APPLICATION|23 requestName = None requestValue = None def fromBER(klass, tag, content, berdecoder=None): l = berDecodeMultiple(content, LDAPBERDecoderContext_LDAPExtendedRequest( fallback=berdecoder)) kw = {} try: kw['requestValue'] = l[1].value except IndexError: pass r = klass(requestName=l[0].value, tag=tag, **kw) return r fromBER = classmethod(fromBER) def __init__(self, requestName, requestValue=None, tag=None): LDAPProtocolRequest.__init__(self) BERSequence.__init__(self, [], tag=tag) assert requestName is not None assert isinstance(requestName, basestring) self.requestName=requestName assert isinstance(requestValue, basestring) self.requestValue=requestValue def __str__(self): l=[LDAPOID(self.requestName, tag=CLASS_CONTEXT|0)] if self.requestValue is not None: l.append(BEROctetString(str(self.requestValue), tag=CLASS_CONTEXT|1)) return str(BERSequence(l, tag=self.tag)) class LDAPPasswordModifyRequest_userIdentity(BEROctetString): tag=CLASS_CONTEXT|0 class LDAPPasswordModifyRequest_oldPasswd(BEROctetString): tag=CLASS_CONTEXT|1 class LDAPPasswordModifyRequest_newPasswd(BEROctetString): tag=CLASS_CONTEXT|2 class LDAPBERDecoderContext_LDAPPasswordModifyRequest(BERDecoderContext): Identities = { LDAPPasswordModifyRequest_userIdentity.tag: LDAPPasswordModifyRequest_userIdentity, LDAPPasswordModifyRequest_oldPasswd.tag: LDAPPasswordModifyRequest_oldPasswd, LDAPPasswordModifyRequest_newPasswd.tag: LDAPPasswordModifyRequest_newPasswd, } class LDAPPasswordModifyRequest(LDAPExtendedRequest): oid = '1.3.6.1.4.1.4203.1.11.1' def __init__(self, requestName=None, userIdentity=None, oldPasswd=None, newPasswd=None, tag=None): assert (requestName is None or requestName == self.oid), \ '%s requestName was %s instead of %s' \ % (self.__class__.__name__, requestName, self.oid) #TODO genPasswd l=[] if userIdentity is not None: l.append(LDAPPasswordModifyRequest_userIdentity(userIdentity)) if oldPasswd is not None: l.append(LDAPPasswordModifyRequest_oldPasswd(oldPasswd)) if newPasswd is not None: l.append(LDAPPasswordModifyRequest_newPasswd(newPasswd)) LDAPExtendedRequest.__init__( self, requestName=self.oid, requestValue=str(BERSequence(l)), tag=tag) def __repr__(self): l=[] # TODO userIdentity, oldPasswd, newPasswd if self.tag!=self.__class__.tag: l.append('tag=%d' % self.tag) return self.__class__.__name__+'('+', '.join(l)+')' class LDAPBERDecoderContext_LDAPExtendedResponse(BERDecoderContext): Identities = { LDAPResponseName.tag: LDAPResponseName, LDAPResponse.tag: LDAPResponse, } class LDAPExtendedResponse(LDAPResult): tag = CLASS_APPLICATION|0x18 responseName = None response = None def fromBER(klass, tag, content, berdecoder=None): l = berDecodeMultiple(content, LDAPBERDecoderContext_LDAPExtendedResponse( fallback=berdecoder)) assert 3<=len(l)<=4 referral = None #if (l[3:] and isinstance(l[3], LDAPReferral)): #TODO support referrals #self.referral=self.data[0] r = klass(resultCode=l[0].value, matchedDN=l[1].value, errorMessage=l[2].value, referral=referral, tag=tag) return r fromBER = classmethod(fromBER) def __init__(self, resultCode=None, matchedDN=None, errorMessage=None, referral=None, serverSaslCreds=None, responseName=None, response=None, tag=None): LDAPResult.__init__(self, resultCode=resultCode, matchedDN=matchedDN, errorMessage=errorMessage, referral=referral, serverSaslCreds=serverSaslCreds) self.responseName=responseName self.response=response def __str__(self): assert self.referral is None #TODO l=[BEREnumerated(self.resultCode), BEROctetString(self.matchedDN), BEROctetString(self.errorMessage), #TODO referral [3] Referral OPTIONAL ] if self.responseName is not None: l.append(LDAPOID(self.responseName, tag=CLASS_CONTEXT|0x0a)) if self.response is not None: l.append(BEROctetString(self.response, tag=CLASS_CONTEXT|0x0b)) return str(BERSequence(l, tag=self.tag)) class LDAPStartTLSRequest(LDAPExtendedRequest): """ Request to start Transport Layer Security. See RFC 2830 for details. """ oid = '1.3.6.1.4.1.1466.20037' def __init__(self, requestName=None, tag=None): assert (requestName is None or requestName == self.oid), \ '%s requestName was %s instead of %s' \ % (self.__class__.__name__, requestName, self.oid) LDAPExtendedRequest.__init__( self, requestName=self.oid, tag=tag) def __repr__(self): l=[] if self.tag!=self.__class__.tag: l.append('tag=%d' % self.tag) return self.__class__.__name__+'('+', '.join(l)+')' class LDAPBERDecoderContext(BERDecoderContext): Identities = { LDAPBindResponse.tag: LDAPBindResponse, LDAPBindRequest.tag: LDAPBindRequest, LDAPUnbindRequest.tag: LDAPUnbindRequest, LDAPSearchRequest.tag: LDAPSearchRequest, LDAPSearchResultEntry.tag: LDAPSearchResultEntry, LDAPSearchResultDone.tag: LDAPSearchResultDone, LDAPReferral.tag: LDAPReferral, LDAPModifyRequest.tag: LDAPModifyRequest, LDAPModifyResponse.tag: LDAPModifyResponse, LDAPAddRequest.tag: LDAPAddRequest, LDAPAddResponse.tag: LDAPAddResponse, LDAPDelRequest.tag: LDAPDelRequest, LDAPDelResponse.tag: LDAPDelResponse, LDAPExtendedRequest.tag: LDAPExtendedRequest, LDAPExtendedResponse.tag: LDAPExtendedResponse, LDAPModifyDNRequest.tag: LDAPModifyDNRequest, LDAPModifyDNResponse.tag: LDAPModifyDNResponse, } ldaptor-0.0.43/ldaptor/protocols/__init__.py0000644000175000017500000000000007516345760017152 0ustar janjanldaptor-0.0.43/ldaptor/protocols/ldap/0000755000175000017500000000000010403234307015752 5ustar janjanldaptor-0.0.43/ldaptor/protocols/ldap/ldaperrors.py0000644000175000017500000000752710344535643020527 0ustar janjan# Twisted, the Framework of Your Internet # Copyright (C) 2001 Matthew W. Lefkowitz # # This library is free software; you can redistribute it and/or # modify it under the terms of version 2.1 of the GNU Lesser General Public # License as published by the Free Software Foundation. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # make pyflakes not complain about undefined names reverse = None LDAPOther = None def get(resultCode, errorMessage): """Get an instance of the correct exception for this resultCode.""" klass = reverse.get(resultCode) if klass is not None: return klass(errorMessage) else: return LDAPUnknownError(resultCode, errorMessage) class LDAPResult: resultCode=None name=None class Success(LDAPResult): resultCode=0 name='success' def __init__(self, msg): pass class LDAPException(Exception, LDAPResult): def __init__(self, message=None): Exception.__init__(self) self.message=message def __str__(self): message=self.message if message: return '%s: %s' % (self.name, message) elif self.name: return self.name else: return 'Unknown LDAP error %r' % self class LDAPUnknownError(LDAPException): resultCode=None def __init__(self, resultCode, message=None): assert resultCode not in reverse, \ "resultCode %r must be unknown" % resultCode self.code=resultCode LDAPException.__init__(self, message) def __str__(self): codeName='unknownError(%d)'%self.code if self.message: return '%s: %s' % (codeName, self.message) else: return codeName import new def init(**errors): global reverse reverse = {} for name, value in errors.items(): if value == errors['success']: klass = Success else: classname = 'LDAP'+name[0].upper()+name[1:] klass = new.classobj(classname, (LDAPException,), { 'resultCode': value, 'name': name, }) globals()[classname] = klass reverse[value] = klass init( success=0, operationsError=1, protocolError=2, timeLimitExceeded=3, sizeLimitExceeded=4, compareFalse=5, compareTrue=6, authMethodNotSupported=7, strongAuthRequired=8, # 9 reserved referral=10 , adminLimitExceeded=11 , unavailableCriticalExtension=12 , confidentialityRequired=13 , saslBindInProgress=14 , noSuchAttribute=16, undefinedAttributeType=17, inappropriateMatching=18, constraintViolation=19, attributeOrValueExists=20, invalidAttributeSyntax=21, # 22-31 unused noSuchObject=32, aliasProblem=33, invalidDNSyntax=34, # 35 reserved for undefined isLeaf aliasDereferencingProblem=36, # 37-47 unused inappropriateAuthentication=48, invalidCredentials=49, insufficientAccessRights=50, busy=51, unavailable=52, unwillingToPerform=53, loopDetect=54, # 55-63 unused namingViolation=64, objectClassViolation=65, notAllowedOnNonLeaf=66, notAllowedOnRDN=67, entryAlreadyExists=68, objectClassModsProhibited=69, # 70 reserved for CLDAP affectsMultipleDSAs=71, # 72-79 unused other=80, # 81-90 reserved for APIs ) other=LDAPOther.resultCode ldaptor-0.0.43/ldaptor/protocols/ldap/ldapsyntax.py0000644000175000017500000005657610345562475020555 0ustar janjan"""Pythonic API for LDAP operations.""" from zope.interface import implements from twisted.internet import defer from twisted.python.failure import Failure from ldaptor.protocols.ldap import ldapclient, ldif, distinguishedname, ldaperrors from ldaptor.protocols import pureldap, pureber from ldaptor.samba import smbpassword from ldaptor import ldapfilter, interfaces, delta, attributeset, entry class PasswordSetAggregateError(Exception): """Some of the password plugins failed""" def __init__(self, errors): Exception.__init__(self) self.errors=errors def __str__(self): return '%s: %s.' % ( self.__doc__, '; '.join([ '%s failed with %s' % (name, fail.getErrorMessage()) for name, fail in self.errors])) def __repr__(self): return '<'+self.__class__.__name__+' errors='+repr(self.errors)+'>' class PasswordSetAborted(Exception): """Aborted""" def __str__(self): return self.__doc__ class DNNotPresentError(Exception): """The requested DN cannot be found by the server.""" pass class ObjectInBadStateError(Exception): """The LDAP object in in a bad state.""" pass class ObjectDeletedError(ObjectInBadStateError): """The LDAP object has already been removed, unable to perform operations on it.""" pass class ObjectDirtyError(ObjectInBadStateError): """The LDAP object has a journal which needs to be committed or undone before this operation.""" pass class NoContainingNamingContext(Exception): """The server contains to LDAP naming context that would contain this object.""" pass class CannotRemoveRDNError(Exception): """The attribute to be removed is the RDN for the object and cannot be removed.""" def __init__(self, key, val=None): Exception.__init__(self) self.key=key self.val=val def __str__(self): if self.val is None: r=repr(self.key) else: r='%s=%s' % (repr(self.key), repr(self.val)) return """The attribute to be removed, %s, is the RDN for the object and cannot be removed.""" % r class MatchNotImplemented(NotImplementedError): """Match type not implemented""" def __init__(self, op): Exception.__init__(self) self.op=op def __str__(self): return '%s: %r' % (self.__doc__, self.op) class JournaledLDAPAttributeSet(attributeset.LDAPAttributeSet): def __init__(self, ldapObject, *a, **kw): self.ldapObject = ldapObject super(JournaledLDAPAttributeSet, self).__init__(*a, **kw) def add(self, value): self.ldapObject.journal(delta.Add(self.key, [value])) super(JournaledLDAPAttributeSet, self).add(value) def update(self, sequence): self.ldapObject.journal(delta.Add(self.key, sequence)) super(JournaledLDAPAttributeSet, self).update(sequence) def remove(self, value): if value not in self: raise LookupError, value self.ldapObject._canRemove(self.key, value) self.ldapObject.journal(delta.Delete(self.key, [value])) super(JournaledLDAPAttributeSet, self).remove(value) def clear(self): self.ldapObject._canRemoveAll(self.key) super(JournaledLDAPAttributeSet, self).clear() self.ldapObject.journal(delta.Delete(self.key)) class LDAPEntryWithClient(entry.EditableLDAPEntry): implements(interfaces.ILDAPEntry, interfaces.IEditableLDAPEntry, interfaces.IConnectedLDAPEntry, ) _state = 'invalid' """ State of an LDAPEntry is one of: invalid - object not initialized yet ready - normal deleted - object has been deleted """ def __init__(self, client, dn, attributes={}, complete=0): """ Initialize the object. @param client: The LDAP client connection this object belongs to. @param dn: Distinguished Name of the object, as a string. @param attributes: Attributes of the object. A dictionary of attribute types to list of attribute values. """ super(LDAPEntryWithClient, self).__init__(dn, attributes) self.client=client self.complete = complete self._journal=[] self._remoteData = entry.EditableLDAPEntry(dn, attributes) self._state = 'ready' def buildAttributeSet(self, key, values): return JournaledLDAPAttributeSet(self, key, values) def _canRemove(self, key, value): """ Called by JournaledLDAPAttributeSet when it is about to remove a value of an attributeType. """ self._checkState() for rdn in self.dn.split()[0].split(): if rdn.attributeType == key and rdn.value == value: raise CannotRemoveRDNError, (key, value) def _canRemoveAll(self, key): """ Called by JournaledLDAPAttributeSet when it is about to remove all values of an attributeType. """ self._checkState() import types assert not isinstance(self.dn, types.StringType) for keyval in self.dn.split()[0].split(): if keyval.attributeType == key: raise CannotRemoveRDNError, (key) def _checkState(self): if self._state != 'ready': if self._state == 'deleted': raise ObjectDeletedError else: raise ObjectInBadStateError, \ "State is %s while expecting %s" \ % (repr(self._state), repr('ready')) def journal(self, journalOperation): """ Add a Modification into the list of modifications that need to be flushed to the LDAP server. Normal callers should not use this, they should use the o['foo']=['bar', 'baz'] -style API that enforces schema, handles errors and updates the cached data. """ self._journal.append(journalOperation) # start ILDAPEntry def __getitem__(self, *a, **kw): self._checkState() return super(LDAPEntryWithClient, self).__getitem__(*a, **kw) def get(self, *a, **kw): self._checkState() return super(LDAPEntryWithClient, self).get(*a, **kw) def has_key(self, *a, **kw): self._checkState() return super(LDAPEntryWithClient, self).has_key(*a, **kw) def __contains__(self, key): self._checkState() return self.has_key(key) def keys(self): self._checkState() return super(LDAPEntryWithClient, self).keys() def items(self): self._checkState() return super(LDAPEntryWithClient, self).items() def __str__(self): a=[] objectClasses = list(self.get('objectClass', [])) objectClasses.sort() a.append(('objectClass', objectClasses)) l=list(self.items()) l.sort() for key, values in l: if key!='objectClass': a.append((key, values)) return ldif.asLDIF(self.dn, a) def __eq__(self, other): if not isinstance(other, self.__class__): return 0 if self.dn != other.dn: return 0 my=self.keys() my.sort() its=other.keys() its.sort() if my!=its: return 0 for key in my: myAttr=self[key] itsAttr=other[key] if myAttr!=itsAttr: return 0 return 1 def __ne__(self, other): return not self==other def __len__(self): return len(self.keys()) def __nonzero__(self): return True def bind(self, password): r=pureldap.LDAPBindRequest(dn=str(self.dn), auth=password) d = self.client.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 self # end ILDAPEntry # start IEditableLDAPEntry def __setitem__(self, key, value): self._checkState() self._canRemoveAll(key) new=JournaledLDAPAttributeSet(self, key, value) super(LDAPEntryWithClient, self).__setitem__(key, new) self.journal(delta.Replace(key, value)) def __delitem__(self, key): self._checkState() self._canRemoveAll(key) super(LDAPEntryWithClient, self).__delitem__(key) self.journal(delta.Delete(key)) def undo(self): self._checkState() self._attributes.clear() for k, vs in self._remoteData.items(): self._attributes[k] = self.buildAttributeSet(k, vs) self._journal=[] def _commit_success(self, msg): assert isinstance(msg, pureldap.LDAPModifyResponse) assert msg.referral is None #TODO if msg.resultCode!=ldaperrors.Success.resultCode: raise ldaperrors.get(msg.resultCode, msg.errorMessage) assert msg.matchedDN=='' self._remoteData = entry.EditableLDAPEntry(self.dn, self) self._journal=[] return self def commit(self): self._checkState() if not self._journal: return defer.succeed(self) op=pureldap.LDAPModifyRequest( object=str(self.dn), modification=[x.asLDAP() for x in self._journal]) d = defer.maybeDeferred(self.client.send, op) d.addCallback(self._commit_success) return d def _cbMoveDone(self, msg, newDN): assert isinstance(msg, pureldap.LDAPModifyDNResponse) assert msg.referral is None #TODO if msg.resultCode!=ldaperrors.Success.resultCode: raise ldaperrors.get(msg.resultCode, msg.errorMessage) assert msg.matchedDN=='' self.dn = newDN return self def move(self, newDN): self._checkState() newDN = distinguishedname.DistinguishedName(newDN) newrdn=newDN.split()[0] newSuperior=distinguishedname.DistinguishedName(listOfRDNs=newDN.split()[1:]) newDN = distinguishedname.DistinguishedName((newrdn,) + newSuperior.split()) op = pureldap.LDAPModifyDNRequest(entry=str(self.dn), newrdn=str(newrdn), deleteoldrdn=1, newSuperior=str(newSuperior)) d = self.client.send(op) d.addCallback(self._cbMoveDone, newDN) return d def _cbDeleteDone(self, msg): assert isinstance(msg, pureldap.LDAPResult) if not isinstance(msg, pureldap.LDAPDelResponse): raise ldaperrors.get(msg.resultCode, msg.errorMessage) assert msg.referral is None #TODO if msg.resultCode!=ldaperrors.Success.resultCode: raise ldaperrors.get(msg.resultCode, msg.errorMessage) assert msg.matchedDN=='' return self def delete(self): self._checkState() op = pureldap.LDAPDelRequest(entry=str(self.dn)) d = self.client.send(op) d.addCallback(self._cbDeleteDone) self._state = 'deleted' return d def _cbAddDone(self, msg, dn): assert isinstance(msg, pureldap.LDAPAddResponse), \ "LDAPRequest response was not an LDAPAddResponse: %r" % msg assert msg.referral is None #TODO if msg.resultCode!=ldaperrors.Success.resultCode: raise ldaperrors.get(msg.resultCode, msg.errorMessage) assert msg.matchedDN=='' e = self.__class__(dn=dn, client=self.client) return e def addChild(self, rdn, attributes): self._checkState() rdn = distinguishedname.RelativeDistinguishedName(rdn) dn = distinguishedname.DistinguishedName( listOfRDNs=(rdn,)+self.dn.split()) ldapAttrs = [] for attrType, values in attributes.items(): ldapAttrType = pureldap.LDAPAttributeDescription(attrType) l = [] for value in values: l.append(pureldap.LDAPAttributeValue(value)) ldapValues = pureber.BERSet(l) ldapAttrs.append((ldapAttrType, ldapValues)) op=pureldap.LDAPAddRequest(entry=str(dn), attributes=ldapAttrs) d = self.client.send(op) d.addCallback(self._cbAddDone, dn) return d def _cbSetPassword_ExtendedOperation(self, msg): assert isinstance(msg, pureldap.LDAPExtendedResponse) assert msg.referral is None #TODO if msg.resultCode!=ldaperrors.Success.resultCode: raise ldaperrors.get(msg.resultCode, msg.errorMessage) assert msg.matchedDN=='' return self def setPassword_ExtendedOperation(self, newPasswd): """ Set the password on this object. @param newPasswd: A string containing the new password. @return: A Deferred that will complete when the operation is done. """ self._checkState() op = pureldap.LDAPPasswordModifyRequest(userIdentity=str(self.dn), newPasswd=newPasswd) d = self.client.send(op) d.addCallback(self._cbSetPassword_ExtendedOperation) return d _setPasswordPriority_ExtendedOperation=0 setPasswordMaybe_ExtendedOperation = setPassword_ExtendedOperation def setPassword_Samba(self, newPasswd, style=None): """ Set the Samba password on this object. @param newPasswd: A string containing the new password. @param style: one of 'sambaSamAccount', 'sambaAccount' or None. Specifies the style of samba accounts used. None is default and is the same as 'sambaSamAccount'. @return: A Deferred that will complete when the operation is done. """ self._checkState() nthash=smbpassword.nthash(newPasswd) lmhash=smbpassword.lmhash(newPasswd) if style is None: style = 'sambaSamAccount' if style == 'sambaSamAccount': self['sambaNTPassword'] = [nthash] self['sambaLMPassword'] = [lmhash] elif style == 'sambaAccount': self['ntPassword'] = [nthash] self['lmPassword'] = [lmhash] else: raise RuntimeError, "Unknown samba password style %r" % style return self.commit() _setPasswordPriority_Samba=20 def setPasswordMaybe_Samba(self, newPasswd): """ Set the Samba password on this object if it is a sambaSamAccount or sambaAccount. @param newPasswd: A string containing the new password. @return: A Deferred that will complete when the operation is done. """ if not self.complete and not self.has_key('objectClass'): d=self.fetch('objectClass') d.addCallback(lambda dummy, self=self, newPasswd=newPasswd: self.setPasswordMaybe_Samba(newPasswd)) else: objectClasses = [s.upper() for s in self.get('objectClass', ())] if 'sambaAccount'.upper() in objectClasses: d = self.setPassword_Samba(newPasswd, style="sambaAccount") elif 'sambaSamAccount'.upper() in objectClasses: d = self.setPassword_Samba(newPasswd, style="sambaSamAccount") else: d = defer.succeed(self) return d def _cbSetPassword(self, dl, names): assert len(dl)==len(names) l=[] for name, (ok, x) in zip(names, dl): if not ok: l.append((name, x)) if l: raise PasswordSetAggregateError, l return self def _cbSetPassword_one(self, result): return (True, None) def _ebSetPassword_one(self, fail): fail.trap(ldaperrors.LDAPException, DNNotPresentError) return (False, fail) def _setPasswordAll(self, results, newPasswd, prefix, names): if not names: return results name, names = names[0], names[1:] if results and not results[-1][0]: # failing fail = Failure(PasswordSetAborted()) d = defer.succeed(results+[(None, fail)]) else: fn = getattr(self, prefix+name) d = defer.maybeDeferred(fn, newPasswd) d.addCallbacks(self._cbSetPassword_one, self._ebSetPassword_one) def cb((success, info)): return results+[(success, info)] d.addCallback(cb) d.addCallback(self._setPasswordAll, newPasswd, prefix, names) return d def setPassword(self, newPasswd): def _passwordChangerPriorityComparison(me, other): mePri = getattr(self, '_setPasswordPriority_'+me) otherPri = getattr(self, '_setPasswordPriority_'+other) return cmp(mePri, otherPri) prefix='setPasswordMaybe_' names=[name[len(prefix):] for name in dir(self) if name.startswith(prefix)] names.sort(_passwordChangerPriorityComparison) d = defer.maybeDeferred(self._setPasswordAll, [], newPasswd, prefix, names) d.addCallback(self._cbSetPassword, names) return d # end IEditableLDAPEntry # start IConnectedLDAPEntry def _cbNamingContext_Entries(self, results): for result in results: for namingContext in result.get('namingContexts', ()): dn = distinguishedname.DistinguishedName(namingContext) if dn.contains(self.dn): return LDAPEntry(self.client, dn) raise NoContainingNamingContext, self.dn def namingContext(self): o=LDAPEntry(client=self.client, dn='') d=o.search(filterText='(objectClass=*)', scope=pureldap.LDAP_SCOPE_baseObject, attributes=['namingContexts']) d.addCallback(self._cbNamingContext_Entries) return d def _cbFetch(self, results, overWrite): if len(results)!=1: raise DNNotPresentError, self.dn o=results[0] assert not self._journal if not overWrite: for key in self._remoteData.keys(): del self._remoteData[key] overWrite=o.keys() self.complete = 1 for k in overWrite: vs=o.get(k) if vs is not None: self._remoteData[k] = vs self.undo() return self def fetch(self, *attributes): self._checkState() if self._journal: raise ObjectDirtyError, 'cannot fetch attributes of %s, it is dirty' % repr(self) d = self.search(scope=pureldap.LDAP_SCOPE_baseObject, attributes=attributes) d.addCallback(self._cbFetch, overWrite=attributes) return d def _cbSearchEntry(self, callback, objectName, attributes, complete): attrib={} for key, values in attributes: attrib[str(key)]=[str(x) for x in values] o=LDAPEntry(client=self.client, dn=objectName, attributes=attrib, complete=complete) callback(o) def _cbSearchMsg(self, msg, d, callback, complete, sizeLimitIsNonFatal): if isinstance(msg, pureldap.LDAPSearchResultDone): assert msg.referral is None #TODO e = ldaperrors.get(msg.resultCode, msg.errorMessage) if not isinstance(e, ldaperrors.Success): try: raise e except ldaperrors.LDAPSizeLimitExceeded, e: if sizeLimitIsNonFatal: pass except: d.errback(Failure()) return True # search ended successfully assert msg.matchedDN=='' d.callback(None) return True elif isinstance(msg, pureldap.LDAPSearchResultEntry): self._cbSearchEntry(callback, msg.objectName, msg.attributes, complete=complete) return False else: raise ldaperrors.LDAPProtocolError, \ 'bad search response: %r' % msg def search(self, filterText=None, filterObject=None, attributes=(), scope=None, derefAliases=None, sizeLimit=0, sizeLimitIsNonFatal=False, timeLimit=0, typesOnly=0, callback=None): self._checkState() d=defer.Deferred() if filterObject is None and filterText is None: filterObject=pureldap.LDAPFilterMatchAll elif filterObject is None and filterText is not None: filterObject=ldapfilter.parseFilter(filterText) elif filterObject is not None and filterText is None: pass elif filterObject is not None and filterText is not None: f=ldapfilter.parseFilter(filterText) filterObject=pureldap.LDAPFilter_and((f, filterObject)) if scope is None: scope = pureldap.LDAP_SCOPE_wholeSubtree if derefAliases is None: derefAliases = pureldap.LDAP_DEREF_neverDerefAliases if attributes is None: attributes = ['1.1'] results=[] if callback is None: cb=results.append else: cb=callback try: op = pureldap.LDAPSearchRequest( baseObject=str(self.dn), scope=scope, derefAliases=derefAliases, sizeLimit=sizeLimit, timeLimit=timeLimit, typesOnly=typesOnly, filter=filterObject, attributes=attributes) self.client.send_multiResponse( op, self._cbSearchMsg, d, cb, complete=not attributes, sizeLimitIsNonFatal=sizeLimitIsNonFatal) except ldapclient.LDAPClientConnectionLostException: d.errback(Failure()) else: if callback is None: d.addCallback(lambda dummy: results) return d def lookup(self, dn): e = self.__class__(self.client, dn) d = e.fetch('1.1') return d # end IConnectedLDAPEntry def __repr__(self): x={} for key in super(LDAPEntryWithClient, self).keys(): x[key]=self[key] keys=x.keys() keys.sort() a=[] for key in keys: a.append('%s: %s' % (repr(key), repr(self[key]))) attributes=', '.join(a) return '%s(dn=%s, attributes={%s})' % ( self.__class__.__name__, repr(str(self.dn)), attributes) # API backwards compatibility LDAPEntry = LDAPEntryWithClient class LDAPEntryWithAutoFill(LDAPEntry): def __init__(self, *args, **kwargs): LDAPEntry.__init__(self, *args, **kwargs) self.autoFillers = [] def _cb_addAutofiller(self, r, autoFiller): self.autoFillers.append(autoFiller) return r def addAutofiller(self, autoFiller): d = defer.maybeDeferred(autoFiller.start, self) d.addCallback(self._cb_addAutofiller, autoFiller) return d def journal(self, journalOperation): LDAPEntry.journal(self, journalOperation) for autoFiller in self.autoFillers: autoFiller.notify(self, journalOperation.key) ldaptor-0.0.43/ldaptor/protocols/ldap/ldif.py0000644000175000017500000000265010344535643017260 0ustar janjan""" Support for writing a set of directory entries as LDIF. You probably want to use this only indirectly, as in str(LDAPEntry(...)). TODO support writing modify operations TODO support reading modify operations TODO implement rest of syntax from RFC2849 """ # RFC2849: The LDAP Data Interchange Format (LDIF) - Technical Specification import base64 def base64_encode(s): return ''.join(base64.encodestring(s).split('\n'))+'\n' def attributeAsLDIF_base64(attribute, value): return "%s:: %s" % (attribute, base64_encode(value)) def containsNonprintable(s): for c in s: if ord(c) > 127 or c in ('\0', '\n', '\r'): return 1 return 0 def attributeAsLDIF(attribute, value): if value.startswith('\0') \ or value.startswith('\n') \ or value.startswith('\r') \ or value.startswith(' ') \ or value.startswith(':') \ or value.startswith('<') \ or value.endswith(' ') \ or containsNonprintable(value): return attributeAsLDIF_base64(attribute, value) else: return "%s: %s\n" % (attribute, value) def asLDIF(dn, attributes): s="dn: %s\n"%dn for k,vs in attributes: for v in vs: s=s+attributeAsLDIF(k, v) s=s+"\n" return s def header(): return "version: 1\n\n" def manyAsLDIF(objects): s=[header()] for dn, attributes in objects: s.append(asLDIF(dn, attributes)) return ''.join(s) ldaptor-0.0.43/ldaptor/protocols/ldap/ldapclient.py0000644000175000017500000001733210344535643020464 0ustar janjan# Twisted, the Framework of Your Internet # Copyright (C) 2001 Matthew W. Lefkowitz # # This library is free software; you can redistribute it and/or # modify it under the terms of version 2.1 of the GNU Lesser General Public # License as published by the Free Software Foundation. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA """LDAP protocol client""" from ldaptor.protocols import pureldap, pureber from ldaptor.protocols.ldap import ldaperrors from twisted.python import log from twisted.internet import protocol, defer, ssl, reactor class LDAPClientConnectionLostException(ldaperrors.LDAPException): def __str__(self): return 'Connection lost' class LDAPStartTLSBusyError(ldaperrors.LDAPOperationsError): def __init__(self, onwire, message=None): self.onwire = onwire ldaperrors.LDAPOperationsError.__init__(self, message=message) def __str__(self): return 'Cannot STARTTLS while operations on wire: %r' % self.onwire 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 "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 ldaptor-0.0.43/ldaptor/protocols/ldap/__init__.py0000644000175000017500000000140710344535643020100 0ustar janjan# Twisted, the Framework of Your Internet # Copyright (C) 2001 Matthew W. Lefkowitz # # This library is free software; you can redistribute it and/or # modify it under the terms of version 2.1 of the GNU Lesser General Public # License as published by the Free Software Foundation. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA """LDAP protocol logic""" ldaptor-0.0.43/ldaptor/protocols/ldap/proxy.py0000644000175000017500000000646510344407651017530 0ustar janjan"""LDAP protocol proxy server""" from twisted.internet import reactor, defer from ldaptor.protocols.ldap import ldapserver, ldapconnector, ldapclient from ldaptor.protocols import pureldap class Proxy(ldapserver.BaseLDAPServer): protocol = ldapclient.LDAPClient client = None waitingConnect = [] unbound = False def __init__(self, config): """ Initialize the object. @param config: The configuration. @type config: ldaptor.interfaces.ILDAPConfig """ ldapserver.BaseLDAPServer.__init__(self) self.config = config def _whenConnected(self, fn, *a, **kw): if self.client is None: d = defer.Deferred() self.waitingConnect.append((d, fn, a, kw)) return d else: return defer.maybeDeferred(fn, *a, **kw) def _cbConnectionMade(self, proto): self.client = proto while self.waitingConnect: d, fn, a, kw = self.waitingConnect.pop(0) d2 = defer.maybeDeferred(fn, *a, **kw) d2.chainDeferred(d) def _clientQueue(self, request, controls, reply): # TODO controls if request.needs_answer: d = self.client.send_multiResponse(request, self._gotResponse, reply) # TODO handle d errbacks else: self.client.send_noResponse(request) def _gotResponse(self, response, reply): reply(response) # TODO this is ugly return isinstance(response, ( pureldap.LDAPSearchResultDone, pureldap.LDAPBindResponse, )) def _failConnection(self, reason): #TODO self.loseConnection() return reason # TODO def connectionMade(self): clientCreator = ldapconnector.LDAPClientCreator( reactor, self.protocol) d = clientCreator.connect( dn='', overrides=self.config.getServiceLocationOverrides()) d.addCallback(self._cbConnectionMade) d.addErrback(self._failConnection) ldapserver.BaseLDAPServer.connectionMade(self) def connectionLost(self, reason): assert self.client is not None if self.client.connected: if not self.unbound: self.client.unbind() self.unbound = True else: self.client.transport.loseConnection() self.client = None ldapserver.BaseLDAPServer.connectionLost(self, reason) def _handleUnknown(self, request, controls, reply): self._whenConnected(self._clientQueue, request, controls, reply) return None def handleUnknown(self, request, controls, reply): d = defer.succeed(request) d.addCallback(self._handleUnknown, controls, reply) return d def handle_LDAPUnbindRequest(self, request, controls, reply): self.unbound = True self.handleUnknown(request, controls, reply) if __name__ == '__main__': """ Demonstration LDAP proxy; passes all requests to localhost:389. """ from twisted.internet import protocol from twisted.python import log import sys log.startLogging(sys.stderr) factory = protocol.ServerFactory() factory.protocol = lambda : Proxy(overrides={ '': ('localhost', 389), }) reactor.listenTCP(10389, factory) reactor.run() ldaptor-0.0.43/ldaptor/protocols/ldap/ldifdelta.py0000644000175000017500000001220010344407651020257 0ustar janjanfrom twisted.python.failure import Failure from twisted.internet import error from ldaptor.protocols.ldap import ldifprotocol from ldaptor import delta, entry WAIT_FOR_CHANGETYPE = 'WAIT_FOR_CHANGETYPE' WAIT_FOR_MOD_SPEC = 'WAIT_FOR_MOD_SPEC' IN_MOD_SPEC = 'IN_MOD_SPEC' IN_ADD_ENTRY = 'IN_ADD_ENTRY' IN_DELETE = 'IN_DELETE' class LDIFDeltaMissingChangeTypeError(ldifprotocol.LDIFParseError): """LDIF delta entry has no changetype.""" pass class LDIFDeltaUnknownModificationError(ldifprotocol.LDIFParseError): """LDIF delta modification has unknown mod-spec.""" pass class LDIFDeltaModificationMissingEndDashError(ldifprotocol.LDIFParseError): """LDIF delta modification has no ending dash.""" pass class LDIFDeltaModificationDifferentAttributeTypeError(ldifprotocol.LDIFParseError): """The attribute type for the change is not the as in the mod-spec header line.""" pass class LDIFDeltaAddMissingAttributesError(ldifprotocol.LDIFParseError): """Add operation needs to have atleast one attribute type and value.""" pass class LDIFDeltaDeleteHasJunkAfterChangeTypeError(ldifprotocol.LDIFParseError): """Delete operation takes no attribute types or values.""" pass class LDIFDelta(ldifprotocol.LDIF): def state_WAIT_FOR_DN(self, line): super(LDIFDelta, self).state_WAIT_FOR_DN(line) if self.mode == ldifprotocol.IN_ENTRY: self.mode = WAIT_FOR_CHANGETYPE def state_WAIT_FOR_CHANGETYPE(self, line): assert self.dn is not None, 'self.dn must be set when in entry' assert self.data is not None, 'self.data must be set when in entry' if line == '': raise LDIFDeltaMissingChangeTypeError, self.dn key, val = self._parseLine(line) if key != 'changetype': raise LDIFDeltaMissingChangeTypeError, (self.dn, key, val) if val == 'modify': self.modifications = [] self.mode = WAIT_FOR_MOD_SPEC elif val == 'add': self.mode = IN_ADD_ENTRY elif val == 'delete': self.mode = IN_DELETE elif val == 'modrdn' or val == 'moddn': raise NotImplementedError #TODO MOD_SPEC_TO_DELTA = { 'add': delta.Add, 'delete': delta.Delete, 'replace': delta.Replace, } def state_WAIT_FOR_MOD_SPEC(self, line): if line == '': # end of entry self.mode = ldifprotocol.WAIT_FOR_DN m = delta.ModifyOp(dn=self.dn, modifications=self.modifications) self.dn = None self.data = None self.modifications = None self.gotEntry(m) return key, val = self._parseLine(line) if key not in self.MOD_SPEC_TO_DELTA: raise LDIFDeltaUnknownModificationError, \ (self.dn, key) self.mod_spec = key self.mod_spec_attr = val self.mod_spec_data = [] self.mode = IN_MOD_SPEC def state_IN_MOD_SPEC(self, line): if line == '': raise LDIFDeltaModificationMissingEndDashError if line == '-': mod = self.MOD_SPEC_TO_DELTA[self.mod_spec] de = mod(self.mod_spec_attr, self.mod_spec_data) self.modifications.append(de) del self.mod_spec del self.mod_spec_attr del self.mod_spec_data self.mode = WAIT_FOR_MOD_SPEC return key, val = self._parseLine(line) if key != self.mod_spec_attr: raise LDIFDeltaModificationDifferentAttributeTypeError, \ (key, self.mod_spec_attr) self.mod_spec_data.append(val) def state_IN_ADD_ENTRY(self, line): assert self.dn is not None, 'self.dn must be set when in entry' assert self.data is not None, 'self.data must be set when in entry' if line == '': # end of entry if not self.data: raise LDIFDeltaAddMissingAttributesError, \ self.dn self.mode = ldifprotocol.WAIT_FOR_DN o = delta.AddOp(entry.BaseLDAPEntry(dn=self.dn, attributes=self.data)) self.dn = None self.data = None self.gotEntry(o) return key, val = self._parseLine(line) if not key in self.data: self.data[key] = [] self.data[key].append(val) def state_IN_DELETE(self, line): assert self.dn is not None, 'self.dn must be set when in entry' if line == '': # end of entry self.mode = ldifprotocol.WAIT_FOR_DN o = delta.DeleteOp(dn=self.dn) self.dn = None self.data = None self.gotEntry(o) return raise LDIFDeltaDeleteHasJunkAfterChangeTypeError, \ (self.dn, line) def fromLDIFFile(f): """Read LDIF data from a file.""" p = LDIFDelta() l = [] p.gotEntry = l.append while 1: data = f.read() if not data: break p.dataReceived(data) p.connectionLost(Failure(error.ConnectionDone())) return l ldaptor-0.0.43/ldaptor/protocols/ldap/ldapserver.py0000644000175000017500000004144710402661331020505 0ustar janjan# Ldaptor, a Pure-Python library for LDAP # Copyright (C) 2003 Tommi Virtanen # # This library is free software; you can redistribute it and/or # modify it under the terms of version 2.1 of the GNU Lesser General Public # License as published by the Free Software Foundation. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA """LDAP protocol server""" import sets from ldaptor import interfaces, delta from ldaptor.protocols import pureldap, pureber from ldaptor.protocols.ldap import distinguishedname, ldaperrors from twisted.python import log from twisted.internet import protocol, defer class LDAPServerConnectionLostException(ldaperrors.LDAPException): pass 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) 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()))) if __name__ == '__main__': """ Demonstration LDAP server; reads LDIF from stdin and serves that over LDAP on port 10389. """ from twisted.internet import reactor import sys log.startLogging(sys.stderr) from twisted.python import components from ldaptor import inmemory class LDAPServerFactory(protocol.ServerFactory): def __init__(self, root): self.root = root components.registerAdapter(lambda x: x.root, LDAPServerFactory, interfaces.IConnectedLDAPEntry) def start(db): factory = LDAPServerFactory(db) factory.protocol = LDAPServer reactor.listenTCP(10389, factory) d = inmemory.fromLDIFFile(sys.stdin) d.addCallback(start) d.addErrback(log.err) reactor.run() ldaptor-0.0.43/ldaptor/protocols/ldap/autofill/0000755000175000017500000000000010403234306017570 5ustar janjanldaptor-0.0.43/ldaptor/protocols/ldap/autofill/__init__.py0000644000175000017500000000043310104536552021707 0ustar janjan"""LDAP object field value suggestion and autoupdate mechanism.""" class AutofillException(Exception): pass class ObjectMissingObjectClassException(AutofillException): """ The LDAPEntry is missing an objectClass this autofiller needs to operate. """ pass ldaptor-0.0.43/ldaptor/protocols/ldap/autofill/posixAccount.py0000644000175000017500000000274410344407651022641 0ustar janjanfrom twisted.internet import defer from ldaptor import numberalloc from ldaptor.protocols.ldap import ldapsyntax, autofill class Autofill_posix: #TODO baseclass def __init__(self, baseDN, freeNumberGetter=numberalloc.getFreeNumber): self.baseDN = baseDN self.freeNumberGetter = freeNumberGetter def _cb_gotNumbers(self, r, ldapObject): uid, gid = r ok, val = uid if not ok: val.trap() ldapObject['uidNumber'] = [str(val)] ok, val = gid if not ok: val.trap() ldapObject['gidNumber'] = [str(val)] def start(self, ldapObject): assert 'objectClass' in ldapObject if 'posixAccount' not in ldapObject['objectClass']: raise autofill.ObjectMissingObjectClassException, ldapObject assert 'loginShell' not in ldapObject ldapObject['loginShell'] = ['/bin/sh'] baseObject = ldapsyntax.LDAPEntry(client=ldapObject.client, dn=self.baseDN) d1 = self.freeNumberGetter(baseObject, 'uidNumber', min=1000) d2 = self.freeNumberGetter(baseObject, 'gidNumber', min=1000) d = defer.DeferredList([d1, d2], fireOnOneErrback=1) # silence the log d1.addErrback(lambda x:None) d2.addErrback(lambda x:None) d.addCallback(self._cb_gotNumbers, ldapObject) return d def notify(self, ldapObject, attributeType): pass ldaptor-0.0.43/ldaptor/protocols/ldap/autofill/sambaAccount.py0000644000175000017500000000323610104536552022554 0ustar janjanfrom ldaptor.protocols.ldap.autofill import ObjectMissingObjectClassException class Autofill_samba: #TODO baseclass def start(self, ldapObject): assert 'objectClass' in ldapObject if 'sambaAccount' not in ldapObject['objectClass']: raise ObjectMissingObjectClassException, ldapObject assert 'acctFlags' not in ldapObject ldapObject['acctFlags'] = ['[UX ]'] assert 'pwdLastSet' not in ldapObject ldapObject['pwdLastSet'] = ['0'] assert 'logonTime' not in ldapObject ldapObject['logonTime'] = ['0'] assert 'logoffTime' not in ldapObject ldapObject['logoffTime'] = ['0'] assert 'pwdCanChange' not in ldapObject ldapObject['pwdCanChange'] = ['0'] assert 'pwdMustChange' not in ldapObject ldapObject['pwdMustChange'] = ['0'] def notify(self, ldapObject, attributeType): # rid=2*uid+1000 if attributeType == 'uidNumber': assert 'uidNumber' in ldapObject assert len(ldapObject['uidNumber']) == 1 for uidNumber in ldapObject['uidNumber']: uidNumber = int(uidNumber) rid = uidNumber*2+1000 ldapObject['rid'] = [str(rid)] return # primaryGroupID=2*gid+1001 if attributeType == 'gidNumber': assert 'gidNumber' in ldapObject assert len(ldapObject['gidNumber']) == 1 for gidNumber in ldapObject['gidNumber']: gidNumber = int(gidNumber) primaryGroupID = gidNumber*2+1001 ldapObject['primaryGroupID'] = [str(primaryGroupID)] return ldaptor-0.0.43/ldaptor/protocols/ldap/autofill/sambaSamAccount.py0000644000175000017500000000474510110431520023205 0ustar janjanfrom ldaptor.protocols.ldap.autofill import ObjectMissingObjectClassException class Autofill_samba: #TODO baseclass def __init__(self, domainSID, fixedPrimaryGroupSID=None): self.domainSID = domainSID self.fixedPrimaryGroupSID = fixedPrimaryGroupSID def start(self, ldapObject): assert 'objectClass' in ldapObject if 'sambaSamAccount' not in ldapObject['objectClass']: raise ObjectMissingObjectClassException, ldapObject assert 'sambaAcctFlags' not in ldapObject ldapObject['sambaAcctFlags'] = ['[UX ]'] assert 'sambaPwdLastSet' not in ldapObject ldapObject['sambaPwdLastSet'] = ['0'] assert 'sambaLogonTime' not in ldapObject ldapObject['sambaLogonTime'] = ['0'] assert 'sambaLogoffTime' not in ldapObject ldapObject['sambaLogoffTime'] = ['0'] assert 'sambaPwdCanChange' not in ldapObject ldapObject['sambaPwdCanChange'] = ['0'] assert 'sambaPwdMustChange' not in ldapObject ldapObject['sambaPwdMustChange'] = ['0'] if self.fixedPrimaryGroupSID is not None: assert 'sambaPrimaryGroupSID' not in ldapObject ldapObject['sambaPrimaryGroupSID'] = ['%s-%d' % ( self.domainSID, self.fixedPrimaryGroupSID)] # Handle attributeTypes that were added before we got # started. We know we don't defer in notify, so we can do a # simple loop here. for attributeType in ldapObject.keys(): self.notify(ldapObject, attributeType) def notify(self, ldapObject, attributeType): # sambaSID=2*uidNumber+1000 if attributeType == 'uidNumber': assert 'uidNumber' in ldapObject assert len(ldapObject['uidNumber']) == 1 for uidNumber in ldapObject['uidNumber']: uidNumber = int(uidNumber) sid = '%s-%d' % (self.domainSID, uidNumber*2+1000) ldapObject['sambaSID'] = [str(sid)] return # sambaPrimaryGroupSID = fixed or 2*gidNumber+1001 if (self.fixedPrimaryGroupSID is None and attributeType == 'gidNumber'): assert 'gidNumber' in ldapObject assert len(ldapObject['gidNumber']) == 1 for gidNumber in ldapObject['gidNumber']: gidNumber = int(gidNumber) sid = '%s-%d' % (self.domainSID, gidNumber*2+1001) ldapObject['sambaPrimaryGroupSID'] = [str(sid)] return ldaptor-0.0.43/ldaptor/protocols/ldap/ldifprotocol.py0000644000175000017500000001002310052211034021010 0ustar janjanimport base64 from twisted.protocols import basic from twisted.internet import protocol from ldaptor import entry class LDIFParseError(Exception): """Error parsing LDIF.""" def __str__(self): s = self.__doc__ if self[:]: s = ': '.join([s]+map(str, self[:])) return s+'.' class LDIFLineWithoutSemicolonError(LDIFParseError): """LDIF line without semicolon seen""" pass class LDIFEntryStartsWithNonDNError(LDIFParseError): """LDIF entry starts with a non-DN line""" pass class LDIFEntryStartsWithSpaceError(LDIFParseError): """Invalid LDIF value format""" pass class LDIFVersionNotANumberError(LDIFParseError): """Non-numeric LDIF version number""" pass class LDIFUnsupportedVersionError(LDIFParseError): """LDIF version not supported""" pass class LDIFTruncatedError(LDIFParseError): """LDIF appears to be truncated""" pass HEADER = 'HEADER' WAIT_FOR_DN = 'WAIT_FOR_DN' IN_ENTRY = 'IN_ENTRY' class LDIF(object, basic.LineReceiver): delimiter='\n' mode = HEADER dn = None data = None lastLine = None version = None def logicalLineReceived(self, line): if line.startswith('#'): # comments are allowed everywhere return getattr(self, 'state_' + self.mode)(line) def lineReceived(self, line): if line.startswith(' '): if self.lastLine is None: raise LDIFEntryStartsWithSpaceError self.lastLine = self.lastLine + line[1:] else: if self.lastLine is not None: self.logicalLineReceived(self.lastLine) self.lastLine = line if line == '': self.logicalLineReceived(line) self.lastLine = None def parseValue(self, val): if val.startswith(':'): return base64.decodestring(val[1:].lstrip(' ')) elif val.startswith('<'): raise NotImplementedError else: return val.lstrip(' ') def _parseLine(self, line): try: key, val = line.split(':', 1) except ValueError: # unpack list of wrong size # -> invalid input data raise LDIFLineWithoutSemicolonError, line val = self.parseValue(val) return key, val def state_HEADER(self, line): key, val = self._parseLine(line) self.mode = WAIT_FOR_DN if key != 'version': self.logicalLineReceived(line) else: try: version = int(val) except ValueError: raise LDIFVersionNotANumberError, val self.version = version if version > 1: raise LDIFUnsupportedVersionError, version def state_WAIT_FOR_DN(self, line): assert self.dn is None, 'self.dn must not be set when waiting for DN' assert self.data is None, 'self.data must not be set when waiting for DN' if line == '': # too many empty lines, but be tolerant return key, val = self._parseLine(line) if key.upper() != 'DN': raise LDIFEntryStartsWithNonDNError, line self.dn = val self.data = {} self.mode = IN_ENTRY def state_IN_ENTRY(self, line): assert self.dn is not None, 'self.dn must be set when in entry' assert self.data is not None, 'self.data must be set when in entry' if line == '': # end of entry self.mode = WAIT_FOR_DN o = entry.BaseLDAPEntry(dn=self.dn, attributes=self.data) self.dn = None self.data = None self.gotEntry(o) return key, val = self._parseLine(line) if not key in self.data: self.data[key] = [] self.data[key].append(val) def gotEntry(self, obj): pass def connectionLost(self, reason=protocol.connectionDone): if self.mode != WAIT_FOR_DN: raise LDIFTruncatedError, reason ldaptor-0.0.43/ldaptor/protocols/ldap/fetchschema.py0000644000175000017500000000375310344407651020616 0ustar janjanfrom ldaptor.protocols.ldap import ldaperrors, ldapsyntax from ldaptor.protocols import pureldap from ldaptor import schema def _fetchCb(subschemaSubentry, client): o=ldapsyntax.LDAPEntry(client=client, dn=subschemaSubentry) d=o.search(scope=pureldap.LDAP_SCOPE_baseObject, sizeLimit=1, attributes=["attributeTypes", "objectClasses"]) def handleSearchResults(l): if len(l)==0: raise ldaperrors.LDAPOther, "No such DN" elif len(l)==1: o=l[0] attributeTypes = [] objectClasses = [] for text in o.get("attributeTypes", []): attributeTypes.append(schema.AttributeTypeDescription(str(text))) for text in o.get("objectClasses", []): objectClasses.append(schema.ObjectClassDescription(str(text))) assert attributeTypes, "LDAP server doesn't give attributeTypes for subschemaSubentry dn=%s"%o.dn return (attributeTypes, objectClasses) else: raise ldaperrors.LDAPOther, "DN matched multiple entries" d.addCallback(handleSearchResults) return d def fetch(client, baseObject): o=ldapsyntax.LDAPEntry(client=client, dn=baseObject) d=o.search(scope=pureldap.LDAP_SCOPE_baseObject, sizeLimit=1, attributes=["subschemaSubentry"]) def handleSearchResults(l): if len(l)==0: raise ldaperrors.LDAPOther, "No such DN" elif len(l)==1: o=l[0] assert "subschemaSubentry" in o, "No subschemaSubentry. TODO" subSchemas = o["subschemaSubentry"] assert len(subSchemas)==1, "More than one subschemaSubentry is not support yet. TODO" for s in subSchemas: return s else: raise ldaperrors.LDAPOther, "DN matched multiple entries" d.addCallback(handleSearchResults) d.addCallback(_fetchCb, client) return d ldaptor-0.0.43/ldaptor/protocols/ldap/distinguishedname.py0000644000175000017500000002110110344535643022036 0ustar janjan# See rfc2253 # Note that RFC 2253 sections 2.4 and 3 disagree whether "=" needs to # be quoted. Let's trust the syntax, slapd refuses to accept unescaped # "=" in RDN values. escapedChars = r',+"\<>;=' escapedChars_leading = r' #' escapedChars_trailing = r' #' def escape(s): r='' r_trailer='' if s and s[0] in escapedChars_leading: r='\\'+s[0] s=s[1:] if s and s[-1] in escapedChars_trailing: r_trailer='\\'+s[-1] s=s[:-1] for c in s: if c in escapedChars: r=r+'\\'+c elif ord(c)<=31: r=r+'\\%02X' % ord(c) else: r=r+c return r+r_trailer def unescape(s): r='' while s: if s[0]=='\\': if s[1] in '0123456789abcdef': r=r+chr(int(s[1:3], 16)) s=s[3:] else: r=r+s[1] s=s[2:] else: r=r+s[0] s=s[1:] return r def _splitOnNotEscaped(s, separator): if not s: return [] r=[''] while s: if s[0]=='\\': r[-1]=r[-1]+s[:2] s=s[2:] else: if s[0] in separator: r.append('') s=s[1:] while s[0]==' ': s=s[1:] else: r[-1]=r[-1]+s[0] s=s[1:] return r class InvalidRelativeDistinguishedName(Exception): """Invalid relative distinguished name.""" def __init__(self, rdn): Exception.__init__(self) self.rdn = rdn def __str__(self): return "Invalid relative distinguished name %s." \ % repr(self.rdn) class LDAPAttributeTypeAndValue: # TODO I should be used everywhere attributeType = None value = None def __init__(self, stringValue=None, attributeType=None, value=None): if stringValue is None: assert attributeType is not None assert value is not None self.attributeType = attributeType self.value = value else: assert attributeType is None assert value is None if '=' not in stringValue: raise InvalidRelativeDistinguishedName, stringValue self.attributeType, self.value = stringValue.split('=', 1) def __str__(self): return '='.join((escape(self.attributeType), escape(self.value))) def __repr__(self): return (self.__class__.__name__ + '(attributeType=' + repr(self.attributeType) + ', value=' + repr(self.value) + ')') def __hash__(self): return hash((self.attributeType, self.value)) def __eq__(self, other): if not isinstance(other, LDAPAttributeTypeAndValue): return NotImplemented return (self.attributeType == other.attributeType and self.value == other.value) def __ne__(self, other): return not (self == other) def __lt__(self, other): if not isinstance(other, self.__class__): return False if self.attributeType != other.attributeType: return self.attributeType < other.attributeType else: return self.value < other.value def __gt__(self, other): return (self != other and self > other) def __le__(self, other): return not self > other def __ge__(self, other): return not self < other class RelativeDistinguishedName: """LDAP Relative Distinguished Name.""" attributeTypesAndValues = None def __init__(self, magic=None, stringValue=None, attributeTypesAndValues=None): if magic is not None: assert stringValue is None assert attributeTypesAndValues is None if isinstance(magic, RelativeDistinguishedName): attributeTypesAndValues = magic.split() elif isinstance(magic, basestring): stringValue = magic else: attributeTypesAndValues = magic if stringValue is None: assert attributeTypesAndValues is not None import types assert not isinstance(attributeTypesAndValues, types.StringType) self.attributeTypesAndValues = tuple(attributeTypesAndValues) else: assert attributeTypesAndValues is None self.attributeTypesAndValues = tuple([LDAPAttributeTypeAndValue(stringValue=unescape(x)) for x in _splitOnNotEscaped(stringValue, '+')]) def split(self): return self.attributeTypesAndValues def __str__(self): return '+'.join([str(x) for x in self.attributeTypesAndValues]) def __repr__(self): return (self.__class__.__name__ + '(attributeTypesAndValues=' + repr(self.attributeTypesAndValues) + ')') def __hash__(self): return hash(self.attributeTypesAndValues) def __eq__(self, other): if not isinstance(other, RelativeDistinguishedName): return NotImplemented return self.split() == other.split() def __ne__(self, other): return not (self == other) def __lt__(self, other): if not isinstance(other, self.__class__): return False return self.split() < other.split() def __gt__(self, other): return (self != other and self >= other) def __le__(self, other): return not self > other def __ge__(self, other): return not self < other def count(self): return len(self.attributeTypesAndValues) class DistinguishedName: """LDAP Distinguished Name.""" listOfRDNs = None def __init__(self, magic=None, stringValue=None, listOfRDNs=None): assert (magic is not None or stringValue is not None or listOfRDNs is not None) if magic is not None: assert stringValue is None assert listOfRDNs is None if isinstance(magic, DistinguishedName): listOfRDNs = magic.split() elif isinstance(magic, basestring): stringValue = magic else: listOfRDNs = magic if stringValue is None: assert listOfRDNs is not None for x in listOfRDNs: assert isinstance(x, RelativeDistinguishedName) self.listOfRDNs = tuple(listOfRDNs) else: assert listOfRDNs is None self.listOfRDNs = tuple([RelativeDistinguishedName(stringValue=x) for x in _splitOnNotEscaped(stringValue, ',')]) def split(self): return self.listOfRDNs def up(self): return DistinguishedName(listOfRDNs=self.listOfRDNs[1:]) def __str__(self): return ','.join([str(x) for x in self.listOfRDNs]) def __repr__(self): return (self.__class__.__name__ + '(listOfRDNs=' + repr(self.listOfRDNs) + ')') def __hash__(self): return hash(str(self)) def __eq__(self, other): if isinstance(other, basestring): return str(self) == other if not isinstance(other, DistinguishedName): return NotImplemented return self.split() == other.split() def __ne__(self, other): return not (self == other) def __cmp__(self, other): if isinstance(other, basestring): return cmp(str(self), other) if not isinstance(other, DistinguishedName): return NotImplemented return cmp(self.split(), other.split()) def getDomainName(self): domainParts = [] l=list(self.listOfRDNs) l.reverse() for rdn in l: if rdn.count() != 1: break attributeTypeAndValue = rdn.split()[0] if attributeTypeAndValue.attributeType.upper() != 'DC': break domainParts.insert(0, attributeTypeAndValue.value) if domainParts: return '.'.join(domainParts) else: return None def contains(self, other): """Does the tree rooted at DN contain or equal the other DN.""" if self == other: return 1 if not isinstance(other, DistinguishedName): other=DistinguishedName(other) its=list(other.split()) mine=list(self.split()) while mine and its: m=mine.pop() i=its.pop() if m!=i: return 0 if mine: return 0 return 1 ldaptor-0.0.43/ldaptor/protocols/ldap/svcbindproxy.py0000644000175000017500000001465510344407651021101 0ustar janjanfrom ldaptor.protocols.ldap import proxy from ldaptor.protocols.ldap import ldapsyntax, ldaperrors from ldaptor.protocols import pureldap import datetime class ServiceBindingProxy(proxy.Proxy): """ An LDAP proxy that handles non-anonymous bind requests specially. BindRequests are intercepted and authentication is attempted against each configured service. This authentication is performed against a separate LDAP entry, found by searching for entries with - objectClass: serviceSecurityObject - owner: the DN of the original bind attempt - cn: the service name. starting at the identity-base as configured in the config file. Finally, if the authentication does not succeed against any of the configured services, the proxy can fallback to passing the bind request to the real server. """ services = [] fallback = False def __init__(self, services=None, fallback=None, *a, **kw): """ Initialize the object. @param services: List of service names to try to bind against. @param fallback: If none of the attempts to authenticate against a specific service succeeded, whether to fall back to the normal LDAP bind mechanism. """ proxy.Proxy.__init__(self, *a, **kw) if services is not None: self.services = list(services) if fallback is not None: self.fallback = fallback def _startSearch(self, request, controls, reply): services = list(self.services) baseDN = self.config.getIdentityBaseDN() e = ldapsyntax.LDAPEntryWithClient(client=self.client, dn=baseDN) d = self._tryService(services, e, request, controls, reply) d.addCallback(self._maybeFallback, request, controls, reply) return d def _maybeFallback(self, entry, request, controls, reply): if entry is not None: msg = pureldap.LDAPBindResponse( resultCode=ldaperrors.Success.resultCode, matchedDN=request.dn) return msg elif self.fallback: self.handleUnknown(request, controls, reply) else: msg = pureldap.LDAPBindResponse( resultCode=ldaperrors.LDAPInvalidCredentials.resultCode) return msg def timestamp(self): now = datetime.datetime.now() return now.strftime('%Y%m%d%H%M%SZ') def _tryService(self, services, baseEntry, request, controls, reply): try: serviceName = services.pop(0) except IndexError: return None timestamp = self.timestamp() d = baseEntry.search(filterObject=pureldap.LDAPFilter_and([ pureldap.LDAPFilter_equalityMatch(attributeDesc=pureldap.LDAPAttributeDescription('objectClass'), assertionValue=pureldap.LDAPAssertionValue('serviceSecurityObject')), pureldap.LDAPFilter_equalityMatch(attributeDesc=pureldap.LDAPAttributeDescription('owner'), assertionValue=pureldap.LDAPAssertionValue(request.dn)), pureldap.LDAPFilter_equalityMatch(attributeDesc=pureldap.LDAPAttributeDescription('cn'), assertionValue=pureldap.LDAPAssertionValue(serviceName)), pureldap.LDAPFilter_or([ # no time pureldap.LDAPFilter_not(pureldap.LDAPFilter_present('validFrom')), # or already valid pureldap.LDAPFilter_lessOrEqual(attributeDesc=pureldap.LDAPAttributeDescription('validFrom'), assertionValue=pureldap.LDAPAssertionValue(timestamp)), ]), pureldap.LDAPFilter_or([ # no time pureldap.LDAPFilter_not(pureldap.LDAPFilter_present('validUntil')), # or still valid pureldap.LDAPFilter_greaterOrEqual(attributeDesc=pureldap.LDAPAttributeDescription('validUntil'), assertionValue=pureldap.LDAPAssertionValue(timestamp)), ]), ]), attributes=('1.1',)) def _gotEntries(entries): if not entries: return None assert len(entries)==1 #TODO e = entries[0] d = e.bind(request.auth) return d d.addCallback(_gotEntries) d.addCallbacks( callback=self._loopIfNone, callbackArgs=(services, baseEntry, request, controls, reply), errback=self._loopIfBindError, errbackArgs=(services, baseEntry, request, controls, reply)) return d def _loopIfNone(self, r, *a, **kw): if r is None: d = self._tryService(*a, **kw) return d else: return r def _loopIfBindError(self, fail, *a, **kw): fail.trap(ldaperrors.LDAPInvalidCredentials) d = self._tryService(*a, **kw) return d 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 return self.handleUnknown(request, controls, reply) else: d = self._whenConnected(self._startSearch, request, controls, reply) return d if __name__ == '__main__': """ Demonstration LDAP proxy; passes all requests to localhost:389. """ from twisted.internet import reactor, protocol from twisted.python import log import sys log.startLogging(sys.stderr) from ldaptor import config factory = protocol.ServerFactory() cfg = config.LDAPConfig(serviceLocationOverrides={ '': ('localhost', 389), }) factory.protocol = lambda : ServiceBindingProxy(config=cfg, services=[ 'svc1', 'svc2', 'svc3', ], fallback=True, ) reactor.listenTCP(10389, factory) reactor.run() ldaptor-0.0.43/ldaptor/protocols/ldap/ldapconnector.py0000644000175000017500000000646510344535643021205 0ustar janjanfrom twisted.internet import protocol, defer from ldaptor.protocols.ldap import distinguishedname try: from twisted.internet.utils import SRVConnector except ImportError: from twisted.names.srvconnect import SRVConnector class LDAPConnector(SRVConnector): def __init__(self, reactor, dn, factory, overrides=None): if not isinstance(dn, distinguishedname.DistinguishedName): dn = distinguishedname.DistinguishedName(stringValue=dn) if overrides is None: overrides={} self.override = self._findOverRide(dn, overrides) domain = dn.getDomainName() SRVConnector.__init__(self, reactor, 'ldap', domain, factory) def __getstate__(self): r={} r.update(self.__dict__) r['connector'] = None return r def _findOverRide(self, dn, overrides): while True: if overrides.has_key(dn): return overrides[dn] if dn == '': break dn = dn.up() return None def _isQueryNeeded(self): """Is there both need to do an SRV query.""" if self.override is None: return True assert not callable(self.override) overriddenHost, overriddenPort = self.override if overriddenHost is None: return True if overriddenPort is not None: return False return True def connect(self): if callable(self.override): self.override(self.factory) elif not self._isQueryNeeded(): self.factory.doStart() self.factory.startedConnecting(self) self._reallyConnect() else: SRVConnector.connect(self) def pickServer(self): if self.override is None: overriddenHost, overriddenPort = None, None else: overriddenHost, overriddenPort = self.override if (overriddenHost is not None and (overriddenPort is not None or self.domain is None)): host = overriddenHost port = overriddenPort else: host, port = SRVConnector.pickServer(self) if overriddenHost is not None: host = overriddenHost if overriddenPort is not None: port = overriddenPort try: port = int(port) except ValueError: pass assert host is not None if port is None: port = 389 return host, port class LDAPClientCreator(protocol.ClientCreator): def connect(self, dn, overrides=None): """Connect to remote host, return Deferred of resulting protocol instance.""" d = defer.Deferred() f = protocol._InstanceFactory(self.reactor, self.protocolClass(*self.args, **self.kwargs), d) c = LDAPConnector(self.reactor, dn, f, overrides=overrides) c.connect() return d def connectAnonymously(self, dn, overrides=None): """Connect to remote host and bind anonymously, return Deferred of resulting protocol instance.""" d = self.connect(dn, overrides=overrides) def _bind(proto): d=proto.bind() d.addCallback(lambda _: proto) return d d.addCallback(_bind) return d ldaptor-0.0.43/ldaptor/generate_password.py0000644000175000017500000000346110344535643017113 0ustar janjanfrom twisted.internet import process, protocol, defer from twisted.python import failure class PwgenException(Exception): pass class ReadPassword(protocol.ProcessProtocol): def __init__(self, deferred, count=1): self.deferred=deferred self.count=count self.stdout='' self.stderr='' def outReceived(self, data): self.stdout=self.stdout+data def errReceived(self, data): self.stderr=self.stderr+data def processEnded(self, reason): if self.stderr: self.deferred.errback(failure.Failure( PwgenException(reason, self.stderr))) elif self.stdout: lines=[x for x in self.stdout.split('\n') if x] if len(lines)!=self.count: self.deferred.errback(failure.Failure( PwgenException(reason, 'Wrong number of lines received.'))) self.deferred.callback(lines) else: self.deferred.errback(failure.Failure(PwgenException(reason, ''))) def generate(reactor, n=1): assert n>0 d=defer.Deferred() proto=ReadPassword(d, n) process.Process(reactor, 'pwgen', ('pwgen', '-cn1', '-N', '%d'%n), {}, None, proto) return d if __name__=='__main__': from twisted.internet import reactor import sys def say(passwords): for p in passwords: sys.stdout.write('%s\n' % p) return passwords def err(fail): fail.trap(PwgenException) sys.stderr.write('pwgen: %s\n' % fail.getErrorMessage()) # Could get more passwords in one fork, but this stresses it more # on purpose. l=[] for i in range(5): d=generate(reactor, 5) d.addCallbacks(say, err) l.append(d) dl=defer.DeferredList(l) dl.addBoth(lambda dummy: reactor.stop()) reactor.run() ldaptor-0.0.43/ldaptor/entry.py0000644000175000017500000001373410347777536014560 0ustar janjanimport sets, random, sha, base64 from zope.interface import implements from twisted.internet import defer from twisted.python.util import InsensitiveDict from ldaptor import interfaces, attributeset, delta from ldaptor.protocols.ldap import distinguishedname, ldif, ldaperrors def sshaDigest(passphrase, salt=None): if salt is None: salt = '' for i in range(8): salt += chr(random.randint(0, 255)) s = sha.sha() s.update(passphrase) s.update(salt) encoded = base64.encodestring(s.digest()+salt).rstrip() crypt = '{SSHA}' + encoded return crypt class BaseLDAPEntry(object): implements(interfaces.ILDAPEntry) dn = None def __init__(self, dn, attributes={}): """ Initialize the object. @param dn: Distinguished Name of the object, as a string. @param attributes: Attributes of the object. A dictionary of attribute types to list of attribute values. """ self._attributes=InsensitiveDict() self.dn = distinguishedname.DistinguishedName(dn) for k,vs in attributes.items(): if k not in self._attributes: self._attributes[k] = [] self._attributes[k].extend(vs) for k,vs in self._attributes.items(): self._attributes[k] = self.buildAttributeSet(k, vs) def buildAttributeSet(self, key, values): return attributeset.LDAPAttributeSet(key, values) def __getitem__(self, key): return self._attributes[key] def get(self, key, default=None): return self._attributes.get(key, default) def has_key(self, key): return key in self._attributes def __contains__(self, key): return self.has_key(key) def keys(self): return self._attributes.keys() def items(self): return self._attributes.items() def __str__(self): a=[] objectClasses = list(self.get('objectClass', [])) objectClasses.sort() a.append(('objectClass', objectClasses)) l=list(self.items()) l.sort() for key, values in l: if key.lower() != 'objectclass': vs = list(values) vs.sort() a.append((key, vs)) return ldif.asLDIF(self.dn, a) def __eq__(self, other): if not isinstance(other, BaseLDAPEntry): return 0 if self.dn != other.dn: return 0 my=self.keys() my.sort() its=other.keys() its.sort() if my!=its: return 0 for key in my: myAttr=self[key] itsAttr=other[key] if myAttr!=itsAttr: return 0 return 1 def __ne__(self, other): return not self==other def __len__(self): return len(self.keys()) def __nonzero__(self): return True def __repr__(self): x={} for key in self.keys(): x[key]=self[key] keys=self.keys() keys.sort() a=[] for key in keys: a.append('%s: %s' % (repr(key), repr(list(self[key])))) attributes=', '.join(a) return '%s(%s, {%s})' % ( self.__class__.__name__, repr(str(self.dn)), attributes) def diff(self, other): """ Compute differences between this and another LDAP entry. @param other: An LDAPEntry to compare to. @return: None if equal, otherwise a ModifyOp that would make this entry look like other. """ assert self.dn == other.dn if self == other: return None r = [] myKeys = sets.Set(self.keys()) otherKeys = sets.Set(other.keys()) addedKeys = list(otherKeys - myKeys) addedKeys.sort() # for reproducability only for added in addedKeys: r.append(delta.Add(added, other[added])) deletedKeys = list(myKeys - otherKeys) deletedKeys.sort() # for reproducability only for deleted in deletedKeys: r.append(delta.Delete(deleted, self[deleted])) sharedKeys = list(myKeys & otherKeys) sharedKeys.sort() # for reproducability only for shared in sharedKeys: addedValues = list(other[shared] - self[shared]) if addedValues: addedValues.sort() # for reproducability only r.append(delta.Add(shared, addedValues)) deletedValues = list(self[shared] - other[shared]) if deletedValues: deletedValues.sort() # for reproducability only r.append(delta.Delete(shared, deletedValues)) return delta.ModifyOp(dn=self.dn, modifications=r) def bind(self, password): return defer.maybeDeferred(self._bind, password) def _bind(self, password): for digest in self.get('userPassword', ()): if digest.startswith('{SSHA}'): raw = base64.decodestring(digest[len('{SSHA}'):]) salt = raw[20:] got = sshaDigest(password, salt) if got == digest: return self raise ldaperrors.LDAPInvalidCredentials def hasMember(self, dn): for memberDN in self.get('member', []): if memberDN == dn: return True return False def __hash__(self): return hash(self.dn) class EditableLDAPEntry(BaseLDAPEntry): implements(interfaces.IEditableLDAPEntry) def __setitem__(self, key, value): new=self.buildAttributeSet(key, value) self._attributes[key] = new def __delitem__(self, key): del self._attributes[key] def undo(self): raise NotImplementedError def commit(self): raise NotImplementedError def move(self, newDN): raise NotImplementedError def delete(self): raise NotImplementedError def setPassword(self, newPasswd, salt=None): crypt = sshaDigest(newPasswd, salt) self['userPassword'] = [crypt] ldaptor-0.0.43/ldaptor/md4.py0000644000175000017500000002130110344535643014054 0ustar janjan"""Pure-Python MD4 digest algorithm implementation.""" # http://www.geocities.com/rozmanov/python/ """ From: "Dmitry Rozmanov" To: "Tommi Virtanen" Subject: Re: About your md4.py Hi. Year, I am thinking of this, but could not find time for this. Thanks for the link. But why? Consider it as a GPL for now if it is important. Regards. ---Dmitry. ----- Original Message ----- From: "Tommi Virtanen" To: "Dmitry Rozmanov" Sent: Tuesday, August 27, 2002 9:17 PM Subject: About your md4.py > Hi. Could you consider adding a license > in your U32.py and md4.py files? Here's > a quick reference: > > http://zooko.com/license_quick_ref.html > > -- > :(){ :|:&};: """ """ From: "Dmitry Rozmanov" To: "Tommi Virtanen" Subject: Re: About your md4.py Ok. Let it be LGPL. Use libs, soon I will modify them and post to the site. Regards. ---Dmitry. ----- Original Message ----- From: "Tommi Virtanen" To: "Dmitry Rozmanov" Sent: Wednesday, August 28, 2002 9:21 AM Subject: Re: About your md4.py > On Wed, Aug 28, 2002 at 02:56:25AM +0400, Dmitry Rozmanov wrote: > > Year, I am thinking of this, but could not find time for > > this. Thanks for the link. > > > > But why? > > > > Consider it as a GPL for now if it is important. > > Please include that information in the files themselves; > it would really help. Otherwise, all I have is this > email to point to. > > Oh, and please reconsider the actual license. For example, > I have an LGPL'ed library I need md4 in. If you choose GPL, > my library couldn't use your md4.py. > > -- > :(){ :|:&};: """ # MD4 validation data md4_test= [ ('', 0x31d6cfe0d16ae931b73c59d7e0c089c0L), ("a", 0xbde52cb31de33e46245e05fbdbd6fb24L), ("abc", 0xa448017aaf21d8525fc10ae87aa6729dL), ("message digest", 0xd9130a8164549fe818874806e1c7014bL), ("abcdefghijklmnopqrstuvwxyz", 0xd79e1c308aa5bbcdeea8ed63df412da9L), ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", 0x043f8582f241db351ce627e153e7f0e4L), ("12345678901234567890123456789012345678901234567890123456789012345678901234567890", 0xe33b4ddc9c38f2199c3e7b164fcc0536L), ] from U32 import U32 class MD4Type: A = None B = None C = None D = None count, len1, len2 = None, None, None buf = [] def __init__(self, data=""): self.A = U32(0x67452301L) self.B = U32(0xefcdab89L) self.C = U32(0x98badcfeL) self.D = U32(0x10325476L) self.count, self.len1, self.len2 = U32(0L), U32(0L), U32(0L) self.buf = [0x00] * 64 self.update(data) def copy(self): dest = new() dest.len1 = self.len1 dest.len2 = self.len2 dest.A = self.A dest.B = self.B dest.C = self.C dest.D = self.D dest.count = self.count for i in range(self.count): dest.buf[i] = self.buf[i] return dest def update(self, str): buf = [] for i in str: buf.append(ord(i)) ilen = U32(len(buf)) #print (ilen) if (long(self.len1 + (ilen << 3)) < long(self.len1)): self.len2 = self.len2 + U32(1) self.len1 = self.len1 + (ilen << 3) self.len2 = self.len2 + (ilen >> 29) # print int(self.len1), int(self.len2) #print (self.len1), (self.len2) L = U32(0) bufpos = 0 while (long(ilen) > 0): if (64 - long(self.count)) < long(ilen): L = U32(64 - long(self.count)) else: L = ilen for i in range(int(L)): self.buf[i + int(self.count)] = buf[i + bufpos] self.count = self.count + L ilen = ilen - L bufpos = bufpos + int(L) #print self.count, L, ilen if (long(self.count) == 64L): self.count = U32(0L) X = [] i = 0 for j in range(16): X.append(U32(self.buf[i]) + (U32(self.buf[i+1]) << 8) + \ (U32(self.buf[i+2]) << 16) + (U32(self.buf[i+3]) << 24)) i = i + 4 A = self.A B = self.B C = self.C D = self.D A = f1(A,B,C,D, 0, 3, X) D = f1(D,A,B,C, 1, 7, X) C = f1(C,D,A,B, 2,11, X) B = f1(B,C,D,A, 3,19, X) A = f1(A,B,C,D, 4, 3, X) D = f1(D,A,B,C, 5, 7, X) C = f1(C,D,A,B, 6,11, X) B = f1(B,C,D,A, 7,19, X) A = f1(A,B,C,D, 8, 3, X) D = f1(D,A,B,C, 9, 7, X) C = f1(C,D,A,B,10,11, X) B = f1(B,C,D,A,11,19, X) A = f1(A,B,C,D,12, 3, X) D = f1(D,A,B,C,13, 7, X) C = f1(C,D,A,B,14,11, X) B = f1(B,C,D,A,15,19, X) A = f2(A,B,C,D, 0, 3, X) D = f2(D,A,B,C, 4, 5, X) C = f2(C,D,A,B, 8, 9, X) B = f2(B,C,D,A,12,13, X) A = f2(A,B,C,D, 1, 3, X) D = f2(D,A,B,C, 5, 5, X) C = f2(C,D,A,B, 9, 9, X) B = f2(B,C,D,A,13,13, X) A = f2(A,B,C,D, 2, 3, X) D = f2(D,A,B,C, 6, 5, X) C = f2(C,D,A,B,10, 9, X) B = f2(B,C,D,A,14,13, X) A = f2(A,B,C,D, 3, 3, X) D = f2(D,A,B,C, 7, 5, X) C = f2(C,D,A,B,11, 9, X) B = f2(B,C,D,A,15,13, X) A = f3(A,B,C,D, 0, 3, X) D = f3(D,A,B,C, 8, 9, X) C = f3(C,D,A,B, 4,11, X) B = f3(B,C,D,A,12,15, X) A = f3(A,B,C,D, 2, 3, X) D = f3(D,A,B,C,10, 9, X) C = f3(C,D,A,B, 6,11, X) B = f3(B,C,D,A,14,15, X) A = f3(A,B,C,D, 1, 3, X) D = f3(D,A,B,C, 9, 9, X) C = f3(C,D,A,B, 5,11, X) B = f3(B,C,D,A,13,15, X) A = f3(A,B,C,D, 3, 3, X) D = f3(D,A,B,C,11, 9, X) C = f3(C,D,A,B, 7,11, X) B = f3(B,C,D,A,15,15, X) self.A = self.A + A self.B = self.B + B self.C = self.C + C self.D = self.D + D #print self def digest(self): res = [0x00] * 16 s = [0x00] * 8 padding = [0x00] * 64 padding[0] = 0x80 padlen, oldlen1, oldlen2 = U32(0), U32(0), U32(0) temp = self.copy() oldlen1 = temp.len1 oldlen2 = temp.len2 if (56 <= long(self.count)): padlen = U32(56 - long(self.count) + 64) else: padlen = U32(56 - long(self.count)) #print int(padlen) temp.update(int_array2str(padding[:int(padlen)])) #print temp s[0]= (oldlen1) & U32(0xFF) s[1]=((oldlen1) >> 8) & U32(0xFF) s[2]=((oldlen1) >> 16) & U32(0xFF) s[3]=((oldlen1) >> 24) & U32(0xFF) s[4]= (oldlen2) & U32(0xFF) s[5]=((oldlen2) >> 8) & U32(0xFF) s[6]=((oldlen2) >> 16) & U32(0xFF) s[7]=((oldlen2) >> 24) & U32(0xFF) temp.update(int_array2str(s)) #print temp res[ 0]= temp.A & U32(0xFF) res[ 1]=(temp.A >> 8) & U32(0xFF) res[ 2]=(temp.A >> 16) & U32(0xFF) res[ 3]=(temp.A >> 24) & U32(0xFF) res[ 4]= temp.B & U32(0xFF) res[ 5]=(temp.B >> 8) & U32(0xFF) res[ 6]=(temp.B >> 16) & U32(0xFF) res[ 7]=(temp.B >> 24) & U32(0xFF) res[ 8]= temp.C & U32(0xFF) res[ 9]=(temp.C >> 8) & U32(0xFF) res[10]=(temp.C >> 16) & U32(0xFF) res[11]=(temp.C >> 24) & U32(0xFF) res[12]= temp.D & U32(0xFF) res[13]=(temp.D >> 8) & U32(0xFF) res[14]=(temp.D >> 16) & U32(0xFF) res[15]=(temp.D >> 24) & U32(0xFF) return int_array2str(res) def hexdigest(self): d=self.digest() return ''.join(map(lambda c: '%02x'%ord(c), d)) def F(x, y, z): return (((x) & (y)) | ((~x) & (z))) def G(x, y, z): return (((x) & (y)) | ((x) & (z)) | ((y) & (z))) def H(x, y, z): return ((x) ^ (y) ^ (z)) def ROL(x, n): return (((x) << n) | ((x) >> (32-n))) def f1(a, b, c, d, k, s, X): return ROL(a + F(b, c, d) + X[k], s) def f2(a, b, c, d, k, s, X): return ROL(a + G(b, c, d) + X[k] + U32(0x5a827999L), s) def f3(a, b, c, d, k, s, X): return ROL(a + H(b, c, d) + X[k] + U32(0x6ed9eba1L), s) def int_array2str(array): str = '' for i in array: str = str + chr(i) return str def md4(data=''): return MD4Type(data) def new(data=''): return MD4Type(data) ldaptor-0.0.43/ldaptor/numberalloc.py0000644000175000017500000000310110344535643015671 0ustar janjan"""Find an available uidNumber/gidNumber/other similar number.""" from ldaptor.protocols import pureldap class freeNumberGuesser: def __init__(self, makeAGuess, min=None, max=None): self.makeAGuess=makeAGuess self.min=min if self.min is None: self.min=0 self.max=max def startGuessing(self): d=self.makeAGuess(self.min) d.addCallback(self._nextGuess, self.min) return d def _nextGuess(self, found, lastGuess): if found: self.min=lastGuess else: self.max=lastGuess if self.max==self.min \ or self.max==self.min+1: return self.max max=self.max if max is None: max=self.min+1000 guess=(max+self.min)/2 d=self.makeAGuess(guess) d.addCallback(self._nextGuess, guess) return d class ldapGuesser: def __init__(self, ldapObject, numberType): self.numberType=numberType self.ldapObject=ldapObject def guess(self, num): d=self.ldapObject.search( filterObject=pureldap.LDAPFilter_equalityMatch( attributeDesc=pureldap.LDAPAttributeDescription(value=self.numberType), assertionValue=pureldap.LDAPAssertionValue(value=str(num))), sizeLimit=1) d.addCallback(lambda results: len(results)) return d def getFreeNumber(ldapObject, numberType, min=None, max=None): g=freeNumberGuesser(ldapGuesser(ldapObject, numberType).guess, min=min, max=max) return g.startGuessing() ldaptor-0.0.43/ldaptor/schema.py0000644000175000017500000004772410344535643014651 0ustar janjandef extractWord(text): if not text: return None l = text.split(None, 1) word = l[0] try: text = l[1] except IndexError: text = '' return word, text def peekWord(text): if not text: return None return text.split(None, 1)[0] class ASN1ParserThingie: def _to_list(self, text): """Split text into $-separated list.""" r=[] for x in text.split("$"): x = x.strip() assert x r.append(x) return tuple(r) def _strings_to_list(self, text): """Split ''-quoted strings into list.""" r=[] while text: text = text.lstrip() if not text: break assert text[0]=="'", "Text %s must start with a single quote."%repr(text) text=text[1:] end=text.index("'") r.append(text[:end]) text=text[end+1:] return tuple(r) def _str_list(self, l): s = ' '.join([self._str(x) for x in l]) if len(l) > 1: s = '( %s )' % s return s def _list(self, l): s = ' $ '.join([x for x in l]) if len(l) > 1: s = '( %s )' % s return s def _str(self, s): return "'%s'" % s class ObjectClassDescription(ASN1ParserThingie): """ ASN Syntax:: d = "0" / "1" / "2" / "3" / "4" / "5" / "6" / "7" / "8" / "9" numericstring = 1*d numericoid = numericstring *( "." numericstring ) space = 1*" " whsp = [ space ] descr = keystring qdescr = whsp "'" descr "'" whsp qdescrlist = [ qdescr *( qdescr ) ] ; object descriptors used as schema element names qdescrs = qdescr / ( whsp "(" qdescrlist ")" whsp ) dstring = 1*utf8 qdstring = whsp "'" dstring "'" whsp descr = keystring oid = descr / numericoid woid = whsp oid whsp ; set of oids of either form oids = woid / ( "(" oidlist ")" ) ObjectClassDescription = "(" whsp numericoid whsp ; ObjectClass identifier [ "NAME" qdescrs ] [ "DESC" qdstring ] [ "OBSOLETE" whsp ] [ "SUP" oids ] ; Superior ObjectClasses [ ( "ABSTRACT" / "STRUCTURAL" / "AUXILIARY" ) whsp ] ; default structural [ "MUST" oids ] ; AttributeTypes [ "MAY" oids ] ; AttributeTypes whsp ")" """ def __init__(self, text): self.oid=None self.name=None self.desc=None self.obsolete=0 self.sup=[] self.type=None self.must=[] self.may=[] if text is not None: self._parse(text) def _parse(self, text): assert text[0]=='(', "Text %s must be in parentheses."%repr(text) assert text[-1]==')', "Text %s must be in parentheses."%repr(text) text=text[1:-1] text = text.lstrip() # oid self.oid, text = extractWord(text) text = text.lstrip() if peekWord(text) == "NAME": text=text[len("NAME "):] text = text.lstrip() if text[0]=="'": text=text[1:] end=text.index("'") self.name=(text[:end],) text=text[end+1:] elif text[0]=="(": text=text[1:] text = text.lstrip() end=text.index(")") self.name=self._strings_to_list(text[:end]) text=text[end+1:] else: raise "TODO" text = text.lstrip() if peekWord(text) == "DESC": text=text[len("DESC "):] text = text.lstrip() assert text[0]=="'" text=text[1:] end=text.index("'") self.desc=text[:end] text=text[end+1:] text = text.lstrip() if peekWord(text) == "OBSOLETE": self.obsolete=1 text=text[len("OBSOLETE "):] text = text.lstrip() if peekWord(text) == "SUP": text=text[len("SUP "):] text = text.lstrip() if text[0]=="(": text=text[1:] text = text.lstrip() end=text.index(")") self.sup=self._to_list(text[:end]) text=text[end+1:] else: s, text = extractWord(text) self.sup=[s] text = text.lstrip() if peekWord(text) == "ABSTRACT": assert self.type is None self.type="ABSTRACT" text=text[len("ABSTRACT "):] text = text.lstrip() if peekWord(text) == "STRUCTURAL": assert self.type is None self.type="STRUCTURAL" text=text[len("STRUCTURAL "):] text = text.lstrip() if peekWord(text) == "AUXILIARY": assert self.type is None self.type="AUXILIARY" text=text[len("AUXILIARY "):] text = text.lstrip() if peekWord(text) == "MUST": text=text[len("MUST "):] text = text.lstrip() if text[0]=="(": text=text[1:] text = text.lstrip() end=text.index(")") self.must.extend(self._to_list(text[:end])) text=text[end+1:] else: s, text = extractWord(text) self.must.append(s) text = text.lstrip() if peekWord(text) == "MAY": text=text[len("MAY "):] text = text.lstrip() if text[0]=="(": text=text[1:] text = text.lstrip() end=text.index(")") self.may.extend(self._to_list(text[:end])) text=text[end+1:] else: s, text = extractWord(text) self.may.append(s) text = text.lstrip() assert text=="", "Text was not empty: %s"%repr(text) if not self.type: self.type="STRUCTURAL" assert self.oid for c in self.oid: assert c in "0123456789." assert self.name is None or self.name assert self.type in ("ABSTRACT", "STRUCTURAL", "AUXILIARY") def __repr__(self): nice = {} for k,v in self.__dict__.items(): nice[k]=repr(v) return ("<%s instance at 0x%x"%(self.__class__.__name__, id(self)) +(" oid=%(oid)s name=%(name)s desc=%(desc)s" +" obsolete=%(obsolete)s sup=%(sup)s type=%(type)s" +" must=%(must)s may=%(may)s>")%nice) def __str__(self): r=[] if self.name is not None: r.append('NAME %s' % self._str_list(self.name)) if self.desc is not None: r.append('DESC %s' % self._str(self.desc)) if self.obsolete: r.append('OBSOLETE') if self.sup: r.append('SUP %s' % self._list(self.sup)) r.append('%s' % self.type) if self.must: r.append('MUST %s' % self._list(self.must)) if self.may: r.append('MAY %s' % self._list(self.may)) return ('( %s ' % self.oid + '\n '.join(r) + ' )') def __lt__(self, other): if not isinstance(other, ObjectClassDescription): return NotImplemented if self.name is not None and other.name is not None: return self.name[0].upper() < other.name[0].upper() else: return self.oid < other.oid def __gt__(self, other): if not isinstance(other, ObjectClassDescription): return NotImplemented if self.name is not None and other.name is not None: return self.name[0].upper() > other.name[0].upper() else: return self.oid > other.oid def __le__(self, other): return self == other or self < other def __ge__(self, other): return self == other or self > other def __eq__(self, other): if not isinstance(other, ObjectClassDescription): return NotImplemented return (self.oid == other.oid and self.name == other.name and self.desc == other.desc and self.obsolete == other.obsolete and self.sup == other.sup and self.type == other.type and self.must == other.must and self.may == other.may) def __ne__(self, other): return not (self == other) class AttributeTypeDescription(ASN1ParserThingie): """ ASN Syntax:: AttributeTypeDescription = "(" whsp numericoid whsp ; AttributeType identifier [ "NAME" qdescrs ] ; name used in AttributeType [ "DESC" qdstring ] ; description [ "OBSOLETE" whsp ] [ "SUP" woid ] ; derived from this other AttributeType [ "EQUALITY" woid ; Matching Rule name [ "ORDERING" woid ; Matching Rule name [ "SUBSTR" woid ] ; Matching Rule name [ "SYNTAX" whsp noidlen whsp ] ; see section 4.3 [ "SINGLE-VALUE" whsp ] ; default multi-valued [ "COLLECTIVE" whsp ] ; default not collective [ "NO-USER-MODIFICATION" whsp ]; default user modifiable [ "USAGE" whsp AttributeUsage ]; default userApplications whsp ")" AttributeUsage = "userApplications" / "directoryOperation" / "distributedOperation" / ; DSA-shared "dSAOperation" ; DSA-specific, value depends on server noidlen = numericoid [ "{" len "}" ] len = numericstring """ def __init__(self, text): self.oid=None self.name=None self.desc=None self.obsolete=0 self.sup=None self.equality=None self.ordering=None self.substr=None self.syntax=None self.single_value=None self.collective=None self.no_user_modification=None self.usage=None if text is not None: self._parse(text) def _parse(self, text): assert text[0]=='(', "Text %s must be in parentheses."%repr(text) assert text[-1]==')', "Text %s must be in parentheses."%repr(text) text=text[1:-1] text = text.lstrip() # oid self.oid, text = extractWord(text) text = text.lstrip() if peekWord(text) == "NAME": text=text[len("NAME "):] text = text.lstrip() if text[0]=="'": text=text[1:] end=text.index("'") self.name=(text[:end],) text=text[end+1:] elif text[0]=="(": text=text[1:] text = text.lstrip() end=text.index(")") self.name=self._strings_to_list(text[:end]) text=text[end+1:] else: raise "TODO" text = text.lstrip() if peekWord(text) == "DESC": text=text[len("DESC "):] text = text.lstrip() assert text[0]=="'" text=text[1:] end=text.index("'") self.desc=text[:end] text=text[end+1:] text = text.lstrip() if peekWord(text) == "OBSOLETE": self.obsolete=1 text=text[len("OBSOLETE "):] text = text.lstrip() if peekWord(text) == "SUP": text=text[len("SUP "):] text = text.lstrip() self.sup, text = extractWord(text) text = text.lstrip() if peekWord(text) == "EQUALITY": text=text[len("EQUALITY "):] text = text.lstrip() self.equality, text = extractWord(text) text = text.lstrip() if peekWord(text) == "ORDERING": text=text[len("ORDERING "):] text = text.lstrip() self.ordering, text = extractWord(text) text = text.lstrip() if peekWord(text) == "SUBSTR": text=text[len("SUBSTR "):] text = text.lstrip() self.substr, text = extractWord(text) text = text.lstrip() if peekWord(text) == "SYNTAX": text=text[len("SYNTAX "):] text = text.lstrip() self.syntax, text = extractWord(text) text = text.lstrip() if peekWord(text) == "SINGLE-VALUE": assert self.single_value is None self.single_value=1 text=text[len("SINGLE-VALUE "):] text = text.lstrip() if peekWord(text) == "COLLECTIVE": assert self.collective is None self.collective=1 text=text[len("COLLECTIVE "):] text = text.lstrip() if peekWord(text) == "NO-USER-MODIFICATION": assert self.no_user_modification is None self.no_user_modification=1 text=text[len("NO-USER-MODIFICATION "):] text = text.lstrip() if peekWord(text) == "USAGE": assert self.usage is None text=text[len("USAGE "):] text = text.lstrip() self.usage, text = extractWord(text) text = text.lstrip() assert text=="", "Text was not empty: %s"%repr(text) if self.single_value is None: self.single_value=0 if self.collective is None: self.collective=0 if self.no_user_modification is None: self.no_user_modification=0 assert self.oid for c in self.oid: assert c in "0123456789." assert self.name is None or self.name assert self.usage is None or self.usage in ( "userApplications", "directoryOperation", "distributedOperation", "dSAOperation", ) def __repr__(self): nice = {} for k,v in self.__dict__.items(): nice[k]=repr(v) return ("<%s instance at 0x%x"%(self.__class__.__name__, id(self)) +(" oid=%(oid)s name=%(name)s desc=%(desc)s" +" obsolete=%(obsolete)s sup=%(sup)s" +" equality=%(equality)s ordering=%(ordering)s" +" substr=%(substr)s syntax=%(syntax)s" +" single_value=%(single_value)s" +" collective=%(collective)s" +" no_user_modification=%(no_user_modification)s" +" usage=%(usage)s>")%nice) def __str__(self): r=[] if self.name is not None: r.append('NAME %s' % self._str_list(self.name)) if self.desc is not None: r.append('DESC %s' % self._str(self.desc)) if self.obsolete: r.append('OBSOLETE') if self.sup is not None: r.append('SUP %s' % self.sup) if self.equality is not None: r.append('EQUALITY %s' % self.equality) if self.ordering is not None: r.append('ORDERING %s' % self.ordering) if self.substr is not None: r.append('SUBSTR %s' % self.substr) if self.syntax is not None: r.append('SYNTAX %s' % self.syntax) if self.single_value: r.append('SINGLE-VALUE') if self.collective: r.append('COLLECTIVE') if self.no_user_modification: r.append('NO-USER-MODIFICATION') if self.usage is not None: r.append('USAGE %s' % self.usage) return ('( %s ' % self.oid + '\n '.join(r) + ' )') class SyntaxDescription(ASN1ParserThingie): """ ASN Syntax:: SyntaxDescription = "(" whsp numericoid whsp [ "DESC" qdstring ] whsp ")" """ def __init__(self, text): self.oid=None self.desc=None assert text[0]=='(' assert text[-1]==')' text=text[1:-1] text = text.lstrip() # oid self.oid, text = extractWord(text) text = text.lstrip() if peekWord(text) == "DESC": text=text[len("DESC "):] text = text.lstrip() assert text[0]=="'" text=text[1:] end=text.index("'") self.desc=text[:end] text=text[end+1:] text = text.lstrip() if peekWord(text) == "X-BINARY-TRANSFER-REQUIRED": text=text[len("X-BINARY-TRANSFER-REQUIRED "):] text = text.lstrip() assert text[0]=="'" text=text[1:] end=text.index("'") self.desc=text[:end] text=text[end+1:] text = text.lstrip() if peekWord(text) == "X-NOT-HUMAN-READABLE": text=text[len("X-NOT-HUMAN-READABLE "):] text = text.lstrip() assert text[0]=="'" text=text[1:] end=text.index("'") self.desc=text[:end] text=text[end+1:] text = text.lstrip() assert text=="", "Text was not empty: %s"%repr(text) assert self.oid for c in self.oid: assert c in "0123456789." def __repr__(self): nice = {} for k,v in self.__dict__.items(): nice[k]=repr(v) return ("<%s instance at 0x%x"%(self.__class__.__name__, id(self)) +(" oid=%(oid)s desc=%(desc)s>")%nice) class MatchingRuleDescription(ASN1ParserThingie): """ ASN Syntax:: MatchingRuleDescription = "(" whsp numericoid whsp ; MatchingRule identifier [ "NAME" qdescrs ] [ "DESC" qdstring ] [ "OBSOLETE" whsp ] "SYNTAX" numericoid whsp ")" """ def __init__(self, text): self.oid=None self.name=None self.desc=None self.obsolete=None self.syntax=None assert text[0]=='(' assert text[-1]==')' text=text[1:-1] text = text.lstrip() # oid self.oid, text = extractWord(text) text = text.lstrip() if peekWord(text) == "NAME": text=text[len("NAME "):] text = text.lstrip() if text[0]=="'": text=text[1:] end=text.index("'") self.name=(text[:end],) text=text[end+1:] elif text[0]=="(": text=text[1:] text = text.lstrip() end=text.index(")") self.name=self._strings_to_list(text[:end]) text=text[end+1:] else: raise "TODO" text = text.lstrip() if peekWord(text) == "DESC": text=text[len("DESC "):] text = text.lstrip() assert text[0]=="'" text=text[1:] end=text.index("'") self.desc=text[:end] text=text[end+1:] text = text.lstrip() if peekWord(text) == "OBSOLETE": self.obsolete=1 text=text[len("OBSOLETE "):] text = text.lstrip() if peekWord(text) == "SYNTAX": text=text[len("SYNTAX "):] text = text.lstrip() self.syntax, text = extractWord(text) text = text.lstrip() assert text=="", "Text was not empty: %s"%repr(text) if self.obsolete is None: self.obsolete=0 assert self.oid for c in self.oid: assert c in "0123456789." assert self.syntax def __repr__(self): nice = {} for k,v in self.__dict__.items(): nice[k]=repr(v) return ("<%s instance at 0x%x"%(self.__class__.__name__, id(self)) +(" oid=%(oid)s desc=%(desc)s>")%nice) ldaptor-0.0.43/ldaptor/ldiftree.py0000644000175000017500000002762610347777536015222 0ustar janjan""" Manage LDAP data as a tree of LDIF files. """ import os, errno, sets from zope.interface import implements from twisted.internet import defer, error from twisted.python import failure from ldaptor import entry, interfaces, attributeset, entryhelpers from ldaptor.protocols.ldap import ldifprotocol, distinguishedname, ldaperrors from twisted.mail.maildir import _generateMaildirName as tempName class LDIFTreeEntryContainsMultipleEntries(Exception): """LDIFTree entry contains multiple LDIF entries.""" class LDIFTreeEntryContainsNoEntries(Exception): """LDIFTree entry does not contain a valid LDIF entry.""" class LDIFTreeNoSuchObject(Exception): # TODO combine with standard LDAP errors """LDIFTree does not contain such entry.""" class LDAPCannotRemoveRootError(ldaperrors.LDAPNamingViolation): """Cannot remove root of LDAP tree""" # TODO share with ldaptor.inmemory? class StoreParsedLDIF(ldifprotocol.LDIF): def __init__(self): self.done = False self.seen = [] def gotEntry(self, obj): self.seen.append(obj) def connectionLost(self, reason): self.done = True def get(path, dn): return defer.maybeDeferred(_get, path, dn) def _get(path, dn): dn = distinguishedname.DistinguishedName(dn) l = list(dn.split()) assert len(l) >= 1 l.reverse() parser = StoreParsedLDIF() entry = os.path.join(path, *['%s.dir'%rdn for rdn in l[:-1]]) entry = os.path.join(entry, '%s.ldif'%l[-1]) f = file(entry) while 1: data = f.read(8192) if not data: break parser.dataReceived(data) parser.connectionLost(failure.Failure(error.ConnectionDone)) assert parser.done entries = parser.seen if len(entries) == 0: raise LDIFTreeEntryContainsNoEntries elif len(entries) > 1: raise LDIFTreeEntryContainsMultipleEntries, entries else: return entries[0] def _putEntry(fileName, entry): """fileName is without extension.""" tmp = fileName + '.' + tempName() + '.tmp' f = file(tmp, 'w') f.write(str(entry)) f.close() os.rename(tmp, fileName+'.ldif') # TODO atomicity def _put(path, entry): l = list(entry.dn.split()) assert len(l) >= 1 l.reverse() entryRDN = l.pop() if l: grandParent = os.path.join(path, *['%s.dir'%rdn for rdn in l[:-1]]) parentEntry = os.path.join(grandParent, '%s.ldif' % l[-1]) parentDir = os.path.join(grandParent, '%s.dir' % l[-1]) if not os.path.exists(parentDir): if not os.path.exists(parentEntry): raise LDIFTreeNoSuchObject, entry.dn.up() try: os.mkdir(parentDir) except OSError, e: if e.errno == errno.EEXIST: # we lost a race to create the directory, safe to ignore pass else: raise else: parentDir = path return _putEntry(os.path.join(parentDir, '%s'%entryRDN), entry) def put(path, entry): return defer.execute(_put, path, entry) class LDIFTreeEntry(entry.EditableLDAPEntry, entryhelpers.DiffTreeMixin, entryhelpers.SubtreeFromChildrenMixin, entryhelpers.MatchMixin, entryhelpers.SearchByTreeWalkingMixin, ): implements(interfaces.IConnectedLDAPEntry) def __init__(self, path, dn=None, *a, **kw): if dn is None: dn = '' entry.BaseLDAPEntry.__init__(self, dn, *a, **kw) self.path = path if dn != '': #TODO DistinguishedName.__nonzero__ self._load() def _load(self): assert self.path.endswith('.dir') entryPath = '%s.ldif' % self.path[:-len('.dir')] parser = StoreParsedLDIF() try: f = file(entryPath) except IOError, e: if e.errno == errno.ENOENT: return else: raise while 1: data = f.read(8192) if not data: break parser.dataReceived(data) parser.connectionLost(failure.Failure(error.ConnectionDone)) assert parser.done entries = parser.seen if len(entries) == 0: raise LDIFTreeEntryContainsNoEntries elif len(entries) > 1: raise LDIFTreeEntryContainsMultipleEntries, entries else: # TODO ugliness and all of its friends for k,v in entries[0].items(): self._attributes[k] = attributeset.LDAPAttributeSet(k, v) def parent(self): # TODO add __nonzero__ to DistinguishedName if self.dn == '': # root return None else: parentPath, _ = os.path.split(self.path) return self.__class__(parentPath, self.dn.up()) def _sync_children(self): children = [] try: filenames = os.listdir(self.path) except OSError, e: if e.errno == errno.ENOENT: pass else: raise else: seen = sets.Set() for fn in filenames: base, ext = os.path.splitext(fn) if ext not in ['.dir', '.ldif']: continue if base in seen: continue seen.add(base) dn = distinguishedname.DistinguishedName( listOfRDNs=((distinguishedname.RelativeDistinguishedName(base),) + self.dn.split())) e = self.__class__(os.path.join(self.path, base + '.dir'), dn) children.append(e) return children def _children(self, callback=None): children = self._sync_children() if callback is None: return children else: for c in children: callback(c) return None def children(self, callback=None): return defer.maybeDeferred(self._children, callback=callback) def lookup(self, dn): dn = distinguishedname.DistinguishedName(dn) if not self.dn.contains(dn): return defer.fail(ldaperrors.LDAPNoSuchObject(dn)) if dn == self.dn: return defer.succeed(self) it = dn.split() me = self.dn.split() assert len(it) > len(me) assert ((len(me)==0) or (it[-len(me):] == me)) rdn = it[-len(me)-1] path = os.path.join(self.path, '%s.dir' % rdn) entry = os.path.join(self.path, '%s.ldif' % rdn) if not os.path.isdir(path) and not os.path.isfile(entry): return defer.fail(ldaperrors.LDAPNoSuchObject(dn)) else: childDN = distinguishedname.DistinguishedName(listOfRDNs=(rdn,)+me) c = self.__class__(path, childDN) return c.lookup(dn) def _addChild(self, rdn, attributes): rdn = distinguishedname.RelativeDistinguishedName(rdn) for c in self._sync_children(): if c.dn.split()[0] == rdn: raise ldaperrors.LDAPEntryAlreadyExists, c.dn dn = distinguishedname.DistinguishedName(listOfRDNs= (rdn,) +self.dn.split()) e = entry.BaseLDAPEntry(dn, attributes) if not os.path.exists(self.path): os.mkdir(self.path) fileName = os.path.join(self.path, '%s' % rdn) tmp = fileName + '.' + tempName() + '.tmp' f = file(tmp, 'w') f.write(str(e)) f.close() os.rename(tmp, fileName+'.ldif') # TODO atomicity dirName = os.path.join(self.path, '%s.dir' % rdn) e = self.__class__(dirName, dn) return e def addChild(self, rdn, attributes): d = self._addChild(rdn, attributes) return d def _delete(self): if self.dn == '': ##TODO DistinguishedName __nonzero__ raise LDAPCannotRemoveRootError if self._sync_children(): raise ldaperrors.LDAPNotAllowedOnNonLeaf( 'Cannot remove entry with children: %s' % self.dn) assert self.path.endswith('.dir') entryPath = '%s.ldif' % self.path[:-len('.dir')] os.remove(entryPath) return self def delete(self): return defer.maybeDeferred(self._delete) def _deleteChild(self, rdn): if not isinstance(rdn, distinguishedname.RelativeDistinguishedName): rdn = distinguishedname.RelativeDistinguishedName(stringValue=rdn) for c in self._sync_children(): if c.dn.split()[0] == rdn: return c.delete() raise ldaperrors.LDAPNoSuchObject, rdn def deleteChild(self, rdn): return defer.maybeDeferred(self._deleteChild, rdn) def __repr__(self): return '%s(%r, %r)' % (self.__class__.__name__, self.path, str(self.dn)) def __cmp__(self, other): if not isinstance(other, LDIFTreeEntry): return NotImplemented return cmp(self.dn, other.dn) def commit(self): assert self.path.endswith('.dir') entryPath = self.path[:-len('.dir')] return defer.maybeDeferred(_putEntry, entryPath, self) def move(self, newDN): return defer.maybeDeferred(self._move, newDN) def _move(self, newDN): if not isinstance(newDN, distinguishedname.DistinguishedName): newDN = distinguishedname.DistinguishedName(stringValue=newDN) if newDN.up() != self.dn.up(): # climb up the tree to root rootDN = self.dn rootPath = self.path while rootDN != '': rootDN = rootDN.up() rootPath = os.path.dirname(rootPath) root = self.__class__(path=rootPath, dn=rootDN) d = defer.maybeDeferred(root.lookup, newDN.up()) else: d = defer.succeed(None) d.addCallback(self._move2, newDN) return d def _move2(self, newParent, newDN): # remove old RDN attributes for attr in self.dn.split()[0].split(): self[attr.attributeType].remove(attr.value) # add new RDN attributes for attr in newDN.split()[0].split(): # TODO what if the key does not exist? self[attr.attributeType].add(attr.value) newRDN = newDN.split()[0] srcdir = os.path.dirname(self.path) if newParent is None: dstdir = srcdir else: dstdir = newParent.path newpath = os.path.join(dstdir, '%s.dir' % newRDN) try: os.rename(self.path, newpath) except OSError, e: if e.errno == errno.ENOENT: pass else: raise basename, ext = os.path.splitext(self.path) assert ext == '.dir' os.rename('%s.ldif' % basename, os.path.join(dstdir, '%s.ldif' % newRDN)) self.dn = newDN self.path = newpath return self.commit() if __name__ == '__main__': """ Demonstration LDAP server; serves an LDIFTree from given directory over LDAP on port 10389. """ from twisted.internet import reactor, protocol from twisted.python import log import sys log.startLogging(sys.stderr) from twisted.python import components from ldaptor.protocols.ldap import ldapserver path = sys.argv[1] db = LDIFTreeEntry(path) class LDAPServerFactory(protocol.ServerFactory): def __init__(self, root): self.root = root class MyLDAPServer(ldapserver.LDAPServer): debug = True components.registerAdapter(lambda x: x.root, LDAPServerFactory, interfaces.IConnectedLDAPEntry) factory = LDAPServerFactory(db) factory.protocol = MyLDAPServer reactor.listenTCP(10389, factory) reactor.run() ldaptor-0.0.43/ldaptor/usage.py0000644000175000017500000000607210344535643014504 0ustar janjanfrom twisted.python import usage, reflect from ldaptor.protocols import pureldap from ldaptor.protocols.ldap import distinguishedname class Options(usage.Options): optParameters = () def postOptions(self): postOpt = {} reflect.addMethodNamesToDict(self.__class__, postOpt, "postOptions_") for name in postOpt.keys(): method = getattr(self, 'postOptions_'+name) method() class Options_service_location: def opt_service_location(self, value): """Service location, in the form BASEDN:HOST[:PORT]""" if not self.opts.has_key('service-location'): self.opts['service-location']={} base, location = value.split(':', 1) try: dn = distinguishedname.DistinguishedName(base) except distinguishedname.InvalidRelativeDistinguishedName, e: raise usage.UsageError, str(e) if not location: raise usage.UsageError, "service-location must specify host" if ':' in location: host, port = location.split(':', 1) else: host, port = location, None if not host: host = None if not port: port = None self.opts['service-location'][dn] = (host, port) def postOptions_service_location(self): if not self.opts.has_key('service-location'): self.opts['service-location']={} class Options_base_optional: optParameters = ( ('base', None, None, "LDAP base dn"), ) class Options_base(Options_base_optional): def postOptions_base(self): # check that some things are given if self.opts['base'] is None: raise usage.UsageError, "%s must be given" % 'base' class Options_scope: optParameters = ( ('scope', None, 'sub', "LDAP search scope (one of base, one, sub)"), ) def postOptions_scope(self): synonyms = { 'base': 'baseObject', 'single': 'singleLevel', 'subtree': 'wholeSubtree', 'sub': 'wholeSubtree', } scope = self.opts['scope'] scope=synonyms.get(scope, scope) try: scope=getattr(pureldap, 'LDAP_SCOPE_'+scope) except AttributeError: raise usage.UsageError, "bad scope: %s" % scope self.opts['scope'] = scope class Options_bind: optParameters = ( ('binddn', None, None, "use Distinguished Name to bind to the directory"), ('bind-auth-fd', None, None, "read bind password from filedescriptor"), ) def postOptions_bind_auth_fd_numeric(self): val=self.opts['bind-auth-fd'] if val is not None: try: val = int(val) except ValueError: raise usage.UsageError, "%s value must be numeric" % 'bind-auth-fd' self.opts['bind-auth-fd'] = val class Options_bind_mandatory(Options_bind): def postOptions_bind_mandatory(self): if not self.opts['binddn']: raise usage.UsageError, "%s must be given" % 'binddn' ldaptor-0.0.43/ldaptor/apps/0000755000175000017500000000000010403234311013744 5ustar janjanldaptor-0.0.43/ldaptor/apps/__init__.py0000644000175000017500000000000007516345760016071 0ustar janjanldaptor-0.0.43/ldaptor/apps/webui/0000755000175000017500000000000010403234314015062 5ustar janjanldaptor-0.0.43/ldaptor/apps/webui/change_password.py0000644000175000017500000003630610402650176020622 0ustar janjanfrom zope.interface import implements from twisted.internet import reactor from twisted.internet import defer from webut.skin import iskin from ldaptor.protocols import pureldap from ldaptor.protocols.ldap import ldapsyntax, distinguishedname from ldaptor import generate_password, interfaces from ldaptor.apps.webui.uriquote import uriUnquote from ldaptor import weave from ldaptor.apps.webui.i18n import _ from ldaptor.apps.webui import i18n import os from nevow import rend, inevow, loaders, url, tags from formless import annotate, webform, iformless, configurable def getEntry(ctx, dn): user = ctx.locate(inevow.ISession).getLoggedInRoot().loggedIn e=ldapsyntax.LDAPEntry(client=user.client, dn=dn) return e def getEntryWithAttributes(ctx, dn, *attributes): e = getEntry(ctx, dn) d = e.fetch(*attributes) return d def getServiceName(ctx, dn): d = getEntryWithAttributes(ctx, dn, 'cn') def _cb(e): for cn in e.get('cn', []): return cn raise RuntimeError, \ _("Service password entry has no attribute cn: %r") % e d.addCallback(_cb) return d def checkPasswordTypos(newPassword, again): if newPassword != again: raise annotate.ValidateError( {}, formErrorMessage=_('Passwords do not match.')) class RemoveServicePassword(configurable.Configurable): def __init__(self, dn): super(RemoveServicePassword, self).__init__(None) self.dn = dn def getBindingNames(self, ctx): return ['remove'] def bind_remove(self, ctx): return annotate.MethodBinding( 'remove', annotate.Method(arguments=[ annotate.Argument('ctx', annotate.Context()), ], label=_('Remove')), action=_('Remove')) def remove(self, ctx): e = getEntry(ctx, self.dn) d = getServiceName(ctx, self.dn) def _delete(name, e): d = e.delete() d.addCallback(lambda _: name) return d d.addCallback(_delete, e) def _report(name): return _('Removed service %r') % name d.addCallback(_report) return d class SetServicePassword(configurable.Configurable): def __init__(self, dn): super(SetServicePassword, self).__init__(None) self.dn = dn def getBindingNames(self, ctx): return ['setServicePassword'] def bind_setServicePassword(self, ctx): return annotate.MethodBinding( 'setServicePassword', annotate.Method(arguments=[ annotate.Argument('ctx', annotate.Context()), annotate.Argument('newPassword', annotate.PasswordEntry(required=True, label=_('New password'))), annotate.Argument('again', annotate.PasswordEntry(required=True, label=_('Again'))), ], label=_('Set password')), action=_('Set password')) def _isPasswordAcceptable(self, ctx, newPassword, again): return checkPasswordTypos(newPassword, again) def setServicePassword(self, ctx, newPassword, again): d = defer.maybeDeferred(self._isPasswordAcceptable, ctx, newPassword, again) def _setPassword(ctx, dn, newPassword): e = getEntry(ctx, dn) d=defer.maybeDeferred(e.setPassword, newPasswd=newPassword) return d d.addCallback(lambda _: _setPassword(ctx, self.dn, newPassword)) def _getName(_, ctx): d = getServiceName(ctx, self.dn) return d d.addCallback(_getName, ctx) def _report(name): return _('Set password for service %r') % name d.addCallback(_report) return d class SetRandomServicePassword(configurable.Configurable): def __init__(self, dn): super(SetRandomServicePassword, self).__init__(None) self.dn = dn def getBindingNames(self, ctx): return ['generateRandom'] def bind_generateRandom(self, ctx): return annotate.MethodBinding( 'generateRandom', annotate.Method(arguments=[ annotate.Argument('ctx', annotate.Context()), ], label=_('Generate random')), action=_('Generate random')) def generateRandom(self, ctx): d=generate_password.generate(reactor) def _first(passwords): assert len(passwords)==1 return passwords[0] d.addCallback(_first) def _setPass(newPassword, ctx): e = getEntry(ctx, self.dn) d = e.setPassword(newPassword) def _getName(_, ctx): d = getServiceName(ctx, self.dn) return d d.addCallback(_getName, ctx) def _report(name, newPassword): return _('Service %r password set to %s') % (name, newPassword) d.addCallback(_report, newPassword) return d d.addCallback(_setPass, ctx) return d class AddService(configurable.Configurable): def __init__(self, dn): super(AddService, self).__init__(None) self.dn = dn def getBindingNames(self, ctx): return ['add'] def bind_add(self, ctx): return annotate.MethodBinding( 'add', annotate.Method(arguments=[ annotate.Argument('ctx', annotate.Context()), annotate.Argument('serviceName', annotate.String(required=True, label=_('Service name'))), annotate.Argument('newPassword', annotate.PasswordEntry(required=False, label=_('New password'), description=_("Leave empty to generate random password."))), annotate.Argument('again', annotate.PasswordEntry(required=False, label=_('Again'))), ], label=_('Add')), action=_('Add')) def add(self, ctx, serviceName, newPassword, again): if newPassword or again: checkPasswordTypos(newPassword, again) if not newPassword: return self._generate(ctx, serviceName) else: return self._add(ctx, newPassword, serviceName) return d def _cbSetPassword(self, ctx, newPassword, serviceName): e = getEntry(ctx, self.dn) rdn = distinguishedname.RelativeDistinguishedName( attributeTypesAndValues=[ distinguishedname.LDAPAttributeTypeAndValue( attributeType='cn', value=serviceName), distinguishedname.LDAPAttributeTypeAndValue( attributeType='owner', value=str(self.dn)) ]) d = e.addChild(rdn, { 'objectClass': ['serviceSecurityObject'], 'cn': [serviceName], 'owner': [str(self.dn)], 'userPassword': ['{crypt}!'], }) def _setPass(e, newPassword): d = e.setPassword(newPassword) return d d.addCallback(_setPass, newPassword) return d def _generate(self, ctx, serviceName): d=generate_password.generate(reactor) def _first(passwords): assert len(passwords)==1 return passwords[0] d.addCallback(_first) def _cb(newPassword, serviceName): d = self._cbSetPassword(ctx, newPassword, serviceName) d.addCallback(lambda dummy: _('Added service %r with password %s') % (serviceName, newPassword)) return d d.addCallback(_cb, serviceName) return d def _add(self, ctx, newPassword, serviceName): d = self._cbSetPassword(ctx, newPassword, serviceName) def _report(dummy, name): return _('Added service %r') % name d.addCallback(_report, serviceName) return d class ServicePasswordChangeMixin(object): def __init__(self, dn): super(ServicePasswordChangeMixin, self).__init__() self.dn = dn def listServicePasswordActions(self): l = [(int(pri), name) for x, pri, name in [name.split('_', 2) for name in dir(self) if name.startswith('servicePasswordAction_')]] l.sort() for pri, name in l: yield name def getServicePasswordAction(self, name): for attrName in dir(self): if not attrName.startswith('servicePasswordAction_'): continue x, pri, actionName = attrName.split('_', 2) if actionName == name: return getattr(self, attrName) return None def render_servicePasswords(self, ctx, data): docFactory = loaders.xmlfile( 'change_service_passwords.xhtml', templateDir=os.path.split(os.path.abspath(__file__))[0]) r = inevow.IQ(docFactory).onePattern('main') return r def render_hideIfNot(self, ctx, data): if data: return ctx.tag else: return tags.invisible() def data_servicePasswords(self, ctx, data): user = ctx.locate(inevow.ISession).getLoggedInRoot().loggedIn config = interfaces.ILDAPConfig(ctx) e=ldapsyntax.LDAPEntry(client=user.client, dn=config.getBaseDN()) d = e.search(filterObject=pureldap.LDAPFilter_and([ pureldap.LDAPFilter_equalityMatch(attributeDesc=pureldap.LDAPAttributeDescription('objectClass'), assertionValue=pureldap.LDAPAssertionValue('serviceSecurityObject')), pureldap.LDAPFilter_equalityMatch(attributeDesc=pureldap.LDAPAttributeDescription('owner'), assertionValue=pureldap.LDAPAssertionValue(str(self.dn))), pureldap.LDAPFilter_present('cn'), ]), attributes=['cn']) return d def render_form_service(self, ctx, data): # TODO error messages for one password change form display in # all of them. e = inevow.IData(ctx) for name in self.listServicePasswordActions(): yield webform.renderForms('service_%s_%s' % (name, e.dn))[ctx.tag()] def locateConfigurable(self, ctx, name): try: return super(ServicePasswordChangeMixin, self).locateConfigurable(ctx, name) except AttributeError: if name.startswith('service_'): pass else: raise rest = name[len('service_'):] l = rest.split('_', 1) if len(l) != 2: raise AttributeError, name c = self.getServicePasswordAction(l[0]) if c is None: raise AttributeError, name return iformless.IConfigurable(c(l[1])) render_zebra = weave.zebra() render_i18n = i18n.render() class ConfirmChange(ServicePasswordChangeMixin, rend.Page): implements(iskin.ISkinnable) title = _('Ldaptor Password Change Page') addSlash = True docFactory = loaders.xmlfile( 'change_password.xhtml', templateDir=os.path.split(os.path.abspath(__file__))[0]) def getBindingNames(self, ctx): return ['setPassword', 'generateRandom'] def bind_setPassword(self, ctx): return annotate.MethodBinding( 'setPassword', annotate.Method(arguments=[ annotate.Argument('ctx', annotate.Context()), annotate.Argument('newPassword', annotate.PasswordEntry(required=True, label=_('New password'))), annotate.Argument('again', annotate.PasswordEntry(required=True, label=_('Again'))), ], label=_('Set password')), action=_('Set password')) def bind_generateRandom(self, ctx): return annotate.MethodBinding( 'generateRandom', annotate.Method(arguments=[ annotate.Argument('ctx', annotate.Context()), ], label=_('Generate random')), action=_('Generate random')) servicePasswordAction_10_remove = RemoveServicePassword servicePasswordAction_20_set = SetServicePassword servicePasswordAction_30_random = SetRandomServicePassword def _setPassword(self, ctx, password): e = getEntry(ctx, self.dn) d=defer.maybeDeferred(e.setPassword, newPasswd=password) return d def setPassword(self, ctx, newPassword, again): d = defer.maybeDeferred(checkPasswordTypos, newPassword, again) d.addCallback(lambda dummy: self._setPassword(ctx, newPassword)) d.addCallback(lambda dummy: _('Password set.')) def eb(fail): return _("Failed: %s") % fail.getErrorMessage() d.addErrback(eb) return d def generateRandom(self, ctx): d=generate_password.generate(reactor) def _first(passwords): assert len(passwords)==1 return passwords[0] d.addCallback(_first) def _status(newPassword, ctx): d = self._setPassword(ctx, newPassword) d.addCallback(lambda dummy: _('Password set to %s') % newPassword) return d d.addCallback(_status, ctx) def eb(fail): return _("Failed: %s") % fail.getErrorMessage() d.addErrback(eb) return d def data_status(self, ctx, data): try: return ctx.locate(inevow.IStatusMessage) except KeyError: return '' def data_dn(self, ctx, data): return self.dn def render_form(self, ctx, data): return webform.renderForms() def render_passthrough(self, ctx, data): return ctx.tag.clear()[data] def data_header(self, ctx, data): u=url.URL.fromContext(ctx) u=u.parentdir().parentdir().clear() l=[] l.append(tags.a(href=u.sibling("search"))[_("Search")]) l.append(tags.a(href=u.sibling("add"))[_("add new entry")]) l.append(tags.a(href=u.sibling("edit").child(str(self.dn)))[_("edit")]) l.append(tags.a(href=u.sibling("delete").child(str(self.dn)))[_("delete")]) return l def render_add(self, ctx, data): return webform.renderForms('add') def configurable_add(self, ctx): return AddService(self.dn) render_i18n = i18n.render() def render_data(self, ctx, data): return ctx.tag.clear()[data] class GetDN(rend.Page): addSlash = True def child_(self, ctx): entry = inevow.ISession(ctx).getLoggedInRoot().loggedIn u = inevow.IRequest(ctx).URLPath() return u.child(str(entry.dn)) def childFactory(self, ctx, name): unquoted=uriUnquote(name) try: dn = distinguishedname.DistinguishedName(stringValue=unquoted) except distinguishedname.InvalidRelativeDistinguishedName, e: # TODO There's no way to throw a FormException at this stage. return None r=ConfirmChange(dn=dn) return r def getResource(): return GetDN() ldaptor-0.0.43/ldaptor/apps/webui/login.py0000644000175000017500000000240310402650176016552 0ustar janjanfrom zope.interface import implements import os from nevow import rend, loaders, guard, inevow, url from webut.skin import iskin from ldaptor.apps.webui import i18n from ldaptor.apps.webui.i18n import _ def getActionURL(current, history): action = current if len(history) == 1: action = action.here() else: for element in history[1:]: action = action.parentdir() action = action.child(guard.LOGIN_AVATAR) for element in history: action = action.child(element) return action class LoginPage(rend.Page): """The resource that is returned when you are not logged in""" implements(iskin.ISkinnable) title = _('Login') docFactory = loaders.xmlfile( 'login.xhtml', templateDir=os.path.split(os.path.abspath(__file__))[0]) def __init__(self, history): self.history = history super(LoginPage, self).__init__() def locateChild(self, request, segments): return LoginPage(self.history + list(segments)), [] def render_form(self, context, data): current = url.URL.fromContext(context) action = getActionURL(current, self.history) context.fillSlots('action-url', str(action)) return context.tag render_i18n = i18n.render() ldaptor-0.0.43/ldaptor/apps/webui/edit-really.xhtml0000644000175000017500000000167310402650176020371 0ustar janjan Title Goes Here

      Ldaptor Edit Page

      [ foo | ]

      ldaptor-0.0.43/ldaptor/apps/webui/edit.py0000644000175000017500000004364210402650176016401 0ustar janjanfrom zope.interface import implements from webut.skin import iskin from ldaptor.protocols.ldap import ldapsyntax from ldaptor.protocols.ldap import fetchschema from ldaptor.protocols.ldap import distinguishedname from ldaptor.apps.webui.uriquote import uriQuote, uriUnquote from ldaptor.apps.webui.i18n import _ from ldaptor.apps.webui import i18n import os from nevow import rend, loaders, inevow, url, tags from formless import iformless, configurable, annotate, webform from twisted.internet import defer from twisted.python import log class EditStatus(object): def __init__(self, entry, changes): super(EditStatus, self).__init__() self.entry = entry self.changes = changes multiLineAttributeTypes = { 'description'.upper(): 1, } def isAttributeTypeMultiLine(attributeType): for name in attributeType.name: if multiLineAttributeTypes.has_key(name.upper()): assert not attributeType.single_value return multiLineAttributeTypes[name.upper()] return 0 class EditForm(configurable.Configurable): nonEditableAttributes = { 'objectClass': 1, } def __init__(self, entry, attributeTypes, objectClasses): super(EditForm, self).__init__(None) self.entry=entry self.attributeTypes=attributeTypes self.objectClasses=objectClasses def getBindingNames(self, ctx): return ['edit'] def bind_edit(self, ctx): formFields=self._getFormFields() return annotate.MethodBinding( 'edit', annotate.Method(arguments=formFields, label=_('Edit')), action=_('Edit')) def _one_formfield(self, attr, values, required, result): if not self.nonEditableAttributes.get(attr): attrtype = self._get_attrtype(attr) if attrtype.uiHint_multiline: if attrtype.single_value: assert len(values)==1 for val in values: result.append(annotate.Argument( 'edit_'+attr, annotate.Text(label=attr, description=attrtype.desc or '', default=val, required=required, ))) result.append(annotate.Argument( 'old_'+attr, annotate.String(label=attr, description=attrtype.desc or '', default=val, required=required, hidden=True, ))) else: assert len(values)==1 # TODO handle multivalued multiline attributetypes for val in values: result.append(annotate.Argument( 'edit_'+attr, annotate.Text(label=attr, description=attrtype.desc or '', default=val, required=required, ))) result.append(annotate.Argument( 'old_'+attr, annotate.String(label=attr, description=attrtype.desc or '', default=val, required=required, hidden=True, ))) else: if attrtype.single_value: assert len(values)==1 for val in values: result.append(annotate.Argument( 'edit_'+attr, annotate.String(label=attr, description=attrtype.desc or '', default=val, required=required, ))) result.append(annotate.Argument( 'old_'+attr, annotate.String(label=attr, description=attrtype.desc or '', default=val, required=required, hidden=True, ))) else: # TODO maybe use a string field+button to add entries, # multiselection list+button to remove entries? values=map(str, values) result.append(annotate.Argument( 'edit_'+attr, annotate.Text(label=attr, description=attrtype.desc or '', default="\n".join(values), required=required, ))) if values: result.append(annotate.Argument( 'old_'+attr, annotate.String(label=attr, description=attrtype.desc or '', default="\n".join(values), required=required, hidden=True, ))) def _getFormFields(self): r=[] r.append(annotate.Argument('context', annotate.Context())) assert self.entry process = {} for k in self.entry.keys(): process[k.upper()]=k # TODO sort objectclasses somehow? objectClasses = list(self.entry[process["OBJECTCLASS"]]) objectClassesSeen = {} while objectClasses: objclassName = objectClasses.pop() if objectClassesSeen.has_key(objclassName): continue objectClassesSeen[objclassName]=1 objclass = None for o in self.objectClasses: for name in o.name: if objclassName.upper()==name.upper(): objclass = o assert objclass, "objectClass %s must have schema"%objclassName objectClasses.extend(objclass.sup or []) for attr_alias in objclass.must: found_one=0 real_attr = self._get_attrtype(str(attr_alias)) for attr in real_attr.name: if process.has_key(attr.upper()): found_one=1 if process[attr.upper()] is not None: self._one_formfield(attr, self.entry[process[attr.upper()]], required=True, result=r) for name in real_attr.name: process[name.upper()]=None if not found_one: log.msg("Object doesn't have required attribute %s:\n%s"%(attr, self.entry)) self._one_formfield(real_attr.name[0], [], required=True, result=r) for attr_alias in objclass.may: found_one=0 real_attr = self._get_attrtype(str(attr_alias)) for attr in real_attr.name: if process.has_key(attr.upper()): found_one=1 if process[attr.upper()] is not None: self._one_formfield(attr, self.entry[process[attr.upper()]], required=False, result=r) if not found_one: # a MAY attributetype not currently present in # object, but user is of course free to add it. attr=str(real_attr.name[0]) self._one_formfield(attr, ('',), required=False, result=r) for name in real_attr.name: process[name.upper()]=None assert [v is None for k,v in process.items()], "All attributes must be in objectClasses MUST or MAY: %s"%process return r def _get_attrtype(self, name): for a in self.attributeTypes: for cur in a.name: if name.upper() == cur.upper(): a.uiHint_multiline=isAttributeTypeMultiLine(a) return a raise RuntimeError, "attribute type %s not known"%name def _textarea_to_list(self, t): return filter(lambda x: x, [x.strip() for x in t.split("\n")]) def _prune_changes(self, old, new): """Prune non-changes when old and new state is known.""" if old is None: old = [] o={} n={} for x in old: n[x]=n.get(x, 0)+1 for x in new: o[x]=o.get(x, 0)+1 for k in o.keys(): while o[k]>0: try: old.remove(k) except ValueError: break o[k]-=1 for k in n.keys(): while n[k]>0: try: new.remove(k) except ValueError: break n[k]-=1 return old, new def edit(self, context, **kw): entry = context.locate(inevow.ISession).getLoggedInRoot().loggedIn user = entry.dn d = defer.succeed(None) changes = [] for k,v in kw.items(): if v is None: v = '' if k[:len("edit_")]=="edit_": old=kw.get("old_"+k[len("edit_"):]) attrtype = self._get_attrtype(k[len("edit_"):]) assert attrtype if attrtype.single_value or attrtype.uiHint_multiline: v=v.replace('\r\n', '\n') v=v.strip() v=[v] if v == ['']: v = [] if old is not None: old=old.replace('\r\n', '\n') old=old.strip() old=[old] else: v=self._textarea_to_list(v) if old is not None: old=self._textarea_to_list(old) o, v = self._prune_changes(old, v) if ((old is None and v) or (old is not None and (o or v))): attr=k[len("edit_"):] changes.append((attr, old, v)) #TODO if not changes: return EditStatus(self.entry, 'No changes!') changes_desc=tags.ul() newRDN = None for rdn in self.entry.dn.split()[0].split(): for attr,old,new in changes: if (rdn.attributeType == attr and old is not None and rdn.value in old): # Need to change the rdn if newRDN is None: newRDN = list(self.entry.dn.split()[0].split()) newRDN.remove(rdn) # Try to find a replacement RDN. Possibilities are # new values from the edit form and old values # currently in LDAP. possible = list(new) possible.extend(self.entry.get(rdn.attributeType, [])) for o in old: # Values to be removed are not acceptable as # new RDN. try: possible.remove(o) except ValueError: pass if not possible: raise ldapsyntax.CannotRemoveRDNError \ (rdn.attributeType, rdn.value) newRDN.append(distinguishedname.LDAPAttributeTypeAndValue(attributeType=attr, value=possible[0])) old.remove(rdn.value) try: new.remove(possible[0]) except ValueError: pass if newRDN is not None: newRDN = distinguishedname.RelativeDistinguishedName(newRDN) changes_desc[tags.li[ _("changing %s: changing RDN to say %s") \ %(repr(attr), newRDN)]] newDN = distinguishedname.DistinguishedName( (newRDN,)+self.entry.dn.split()[1:] ) def _move(_): return self.entry.move(newDN) d.addCallback(_move) def _redirect(r, ctx, newDN): request = inevow.IRequest(ctx) u = url.URL.fromContext(ctx).curdir() u = u.child(uriQuote(newDN)) request.setComponent(iformless.IRedirectAfterPost, u) return r d.addCallback(_redirect, context, newDN) for attr,old,new in changes: if new: if self.entry.has_key(attr): self.entry[attr].update(new) else: self.entry[attr]=new if old: for x in old: if x=='': continue self.entry[attr].remove(x) if old or new: l=tags.ul() changes_desc[tags.li[_("changing %s") % attr], l] if old: l[tags.li[_("remove "), ', '.join(map(repr, old))]] if new: l[tags.li[_("add "), ', '.join(map(repr, new))]] d.addCallback(lambda _: self.entry.commit()) d.addCallback(lambda e: EditStatus(e, changes_desc)) return d class ReallyEditPage(rend.Page): implements(iskin.ISkinnable) title = _('Ldaptor Edit Page') addSlash = True docFactory = loaders.xmlfile( 'edit-really.xhtml', templateDir=os.path.split(os.path.abspath(__file__))[0]) def __init__(self, entry, attributeTypes, objectClasses): super(ReallyEditPage, self).__init__() self.entry = entry self.attributeTypes = attributeTypes self.objectClasses = objectClasses def data_header(self, ctx, data): u=url.URL.fromContext(ctx) u=u.parentdir().parentdir().clear() l=[] l.append(tags.a(href=u.sibling("search"))[_("Search")]) l.append(tags.a(href=u.sibling("add"))[_("add new entry")]) return l def configurable_(self, context): a = EditForm(self.entry, self.attributeTypes, self.objectClasses) return a def render_form(self, context, data): return webform.renderForms() def render_passthrough(self, context, data): return context.tag.clear()[data] def render_status(self, context, data): try: obj = context.locate(inevow.IHand) except KeyError: return context.tag.clear() if not isinstance(obj, EditStatus): return context.tag.clear()[obj] u=url.URL.fromContext(context) u=u.parentdir().parentdir().clear() return context.tag.clear()[ _("Edited "), tags.a(href=u.parentdir() .child(obj.entry.dn) .child("search"))[obj.entry.dn], _(" successfully. "), # TODO share implementation with entryLinks '[', tags.a(href=u.sibling('move').child(uriQuote(obj.entry.dn)))[_('move')], '|', tags.a(href=u.sibling('delete').child(uriQuote(obj.entry.dn)))[_('delete')], '|', tags.a(href=u.sibling('change_password').child(uriQuote(obj.entry.dn)))[_('change password')], ']', tags.p[obj.changes], ] render_i18n = i18n.render() class EditPage(rend.Page): implements(iskin.ISkinnable) title = _('Ldaptor Edit Page') addSlash = True docFactory = loaders.xmlfile( 'edit.xhtml', templateDir=os.path.split(os.path.abspath(__file__))[0]) def render_url(self, ctx, data): u = url.URL.fromContext(ctx) return ctx.tag(href=u.parentdir().child('search')) def childFactory(self, context, name): dn = uriUnquote(name) userEntry = inevow.ISession(context).getLoggedInRoot().loggedIn e = ldapsyntax.LDAPEntryWithClient(dn=dn, client=userEntry.client) d = e.fetch() def _getSchema(entry): d = fetchschema.fetch(entry.client, entry.dn) def cb((attributeTypes, objectClasses), entry): return (entry, attributeTypes, objectClasses) d.addCallback(cb, entry) return d d.addCallback(_getSchema) def _createEditPage((entry, attributeTypes, objectClasses)): return ReallyEditPage(entry, attributeTypes, objectClasses) d.addCallback(_createEditPage) return d render_i18n = i18n.render() ldaptor-0.0.43/ldaptor/apps/webui/skin-default.html0000644000175000017500000000061310402650176020345 0ustar janjan ldaptor-0.0.43/ldaptor/apps/webui/search.xhtml0000644000175000017500000001127110402650176017416 0ustar janjan Title Goes Here

      Ldaptor Search Page

      ,

      [ foo | ]

      • [ | ]
        • None
        • :

      entries matched.

      Used filter

      Mass change passwords

      [ | ]
      • None
      • :
      ldaptor-0.0.43/ldaptor/apps/webui/basedn.xhtml0000644000175000017500000000116210402650176017403 0ustar janjan Title Goes Here

      Base DN

      Search form will be here.
      ldaptor-0.0.43/ldaptor/apps/webui/delete-nodn.xhtml0000644000175000017500000000134510402650176020350 0ustar janjan Title Goes Here

      Ldaptor Delete Page

      Missing DN to delete. You need to use the search page.

      ldaptor-0.0.43/ldaptor/apps/webui/iwebui.py0000644000175000017500000000021410277134704016730 0ustar janjanfrom twisted.python import components class ICurrentDN(components.Interface): """Marker interface for current DN for Ldaptor-webui.""" ldaptor-0.0.43/ldaptor/apps/webui/defskin.py0000644000175000017500000000324010402650176017065 0ustar janjanimport os from zope.interface import implements from webut.skin import iskin from nevow import rend, loaders, tags, util, inevow, static from formless import webform class DefaultSkin(rend.Page): implements(iskin.ISkin) docFactory = loaders.xmlfile( util.resource_filename('ldaptor.apps.webui', 'skin-default.html')) stylesheets = [ 'form.css', 'ldaptor.css', ] def locateChild(self, ctx, segments): if segments[0] == 'form.css': return webform.defaultCSS, segments[1:] if segments[0] == 'ldaptor.css': dirname = os.path.abspath(os.path.dirname(__file__)) return (static.File(os.path.join(dirname, 'ldaptor.css')), segments[1:]) else: return None, () def render_title(self, ctx, data): return ctx.tag.clear()[self.original.resource.title] def render_head(self, ctx, data): def links(l, path=None): for filename in l: href = filename if path is not None: href = path.child(href) yield tags.link(rel="stylesheet", type="text/css", href=href) ctx.tag.clear() stylesheets = getattr(self, 'stylesheets', None) if stylesheets is not None: ctx.tag[links(stylesheets, path=self.original.pathToFiles)] stylesheets = getattr(self.original.resource, 'stylesheets', None) if stylesheets is not None: ctx.tag[links(stylesheets)] return ctx.tag def render_content(self, ctx, data): return self.original.content ldaptor-0.0.43/ldaptor/apps/webui/delete.xhtml0000644000175000017500000000266710402650176017424 0ustar janjan Title Goes Here

      Ldaptor Delete Page

      [foo|]

      Status message goes here.

      Remove ?

      • None
      • :
      ldaptor-0.0.43/ldaptor/apps/webui/add.py0000644000175000017500000005044510402650176016203 0ustar janjanfrom zope.interface import implements from twisted.internet import defer from twisted.python import plugin from webut.skin import iskin from ldaptor.protocols.ldap import ldapsyntax, distinguishedname from ldaptor.protocols.ldap import fetchschema from ldaptor import numberalloc, interfaces from ldaptor.apps.webui import iwebui from ldaptor.apps.webui.uriquote import uriQuote, uriUnquote from ldaptor.apps.webui.i18n import _ from ldaptor.apps.webui import i18n import os from nevow import rend, inevow, loaders, url, tags from formless import annotate, webform, iformless, configurable def mapNameToObjectClass(objectClasses, name): name = name.upper() objclass = None for oc in objectClasses: for ocName in oc.name: if ocName.upper()==name: objclass = oc return objclass def mapNameToAttributeType(attributeTypes, name): name = name.upper() attrtype = None for at in attributeTypes: for atName in at.name: if atName.upper()==name: attrtype = at return attrtype class UnknownAttributeType(Exception): """LDAP Attribute type not known""" def __init__(self, name): Exception.__init__(self) self.name = name def __str__(self): return self.__doc__ + ': ' + repr(self.name) class AddOCForm(configurable.Configurable): def __init__(self, objectClasses): super(AddOCForm, self).__init__(None) structural = [] auxiliary = [] for oc in objectClasses: if oc.type == 'STRUCTURAL': structural.append(oc) elif oc.type == 'AUXILIARY': auxiliary.append(oc) structural.sort() auxiliary.sort() class KludgeNevowChoice(object): """ A kludge that allows using Choice with both Nevow 0.3 and newer. """ def __init__(self, oc): self.name = oc.name self.desc = oc.desc def __str__(self): """ For Choice in Nevow 0.4 and newer. Nevow 0.3 will use integer indexes. Actual stringification for display purposes happens in strObjectClass. """ return self.name[0] formFields = [ annotate.Argument('ctx', annotate.Context()), annotate.Argument('request', annotate.Request()), annotate.Argument('structuralObjectClass', annotate.Choice(label=_('Object type to create'), choices=[KludgeNevowChoice(x) for x in structural], stringify=strObjectClass)), ] for oc in auxiliary: formFields.append(annotate.Argument( 'auxiliary_%s' % oc.name[0], annotate.Boolean(label=oc.name[0], description=oc.desc or ''))) self.formFields = formFields def getBindingNames(self, ctx): return ['add'] def bind_add(self, ctx): return annotate.MethodBinding( 'add', annotate.Method(arguments=self.formFields, label=_('Add')), action=_('Add')) def add(self, ctx, request, structuralObjectClass, **kw): assert structuralObjectClass is not None structuralObjectClass = str(structuralObjectClass) auxiliaryObjectClasses = [] for k,v in kw.items(): assert k.startswith('auxiliary_') if k.startswith('auxiliary_'): k = k[len('auxiliary_'):] if v: auxiliaryObjectClasses.append(k) u = url.URL.fromContext(ctx) u = u.child('manual').child('+'.join([structuralObjectClass] + auxiliaryObjectClasses)) return u class AddForm(configurable.Configurable): def __init__(self, chosenObjectClasses, attributeTypes, objectClasses): super(AddForm, self).__init__(None) self.chosenObjectClasses=chosenObjectClasses self.nonUserEditableAttributeType_objectClass=[ oc.name[0] for oc in self.chosenObjectClasses] self.attributeTypes=attributeTypes self.objectClasses=objectClasses self.formFields=self._getFormFields() def _nonUserEditableAttributeType_getFreeNumber(self, attributeType, context): cfg = context.locate(interfaces.ILDAPConfig) entry = context.locate(inevow.ISession).getLoggedInRoot().loggedIn client = entry.client o=ldapsyntax.LDAPEntry(client=client, dn=cfg.getBaseDN()) d=numberalloc.getFreeNumber(ldapObject=o, numberType=attributeType, min=1000) d.addCallback(lambda x, a=attributeType: (a, [str(x)])) return d nonUserEditableAttributeType_uidNumber=_nonUserEditableAttributeType_getFreeNumber nonUserEditableAttributeType_gidNumber=_nonUserEditableAttributeType_getFreeNumber def _get_attrtype(self, name): for a in self.attributeTypes: for cur in a.name: if name.upper() == cur.upper(): a.uiHint_multiline=0 #TODO return a raise UnknownAttributeType, name def _one_formfield(self, attr, result, must=False): attrtype = self._get_attrtype(attr) name=attr if must: name=name+"*" if attrtype.uiHint_multiline: if attrtype.single_value: typed = annotate.Text(label=name, description=attrtype.desc or '', required=must) else: typed = annotate.Text(label=name, description=attrtype.desc or '', required=must) else: if attrtype.single_value: typed = annotate.String(label=name, description=attrtype.desc or '', required=must) else: # TODO maybe use a string field+button to add entries, # multiselection list+button to remove entries? typed = annotate.Text(label=name, description=attrtype.desc or '', required=must) result.append(annotate.Argument('add_'+attr, typed)) def _getFormFields(self): r=[] r.append(annotate.Argument('context', annotate.Context())) process = {} # TODO sort objectclasses somehow? objectClasses = list(self.chosenObjectClasses) objectClassesSeen = {} self.nonUserEditableAttributes = [] while objectClasses: objectClass = objectClasses.pop() objclassName = objectClass.name[0] if objectClassesSeen.has_key(objclassName): continue objectClassesSeen[objclassName]=1 for ocName in objectClass.sup or []: objclass = mapNameToObjectClass(self.objectClasses, ocName) assert objclass, "Objectclass %s must have schema" %objclassName objectClasses.append(objclass) for attr_alias in objectClass.must: real_attr = self._get_attrtype(str(attr_alias)) if hasattr(self, 'nonUserEditableAttributeType_'+real_attr.name[0]): self.nonUserEditableAttributes.append(real_attr.name[0]) else: for attr in real_attr.name: if not process.has_key(attr.upper()): process[attr.upper()]=0 if not process[attr.upper()]: self._one_formfield(attr, result=r, must=True) for name in real_attr.name: process[name.upper()]=1 for attr_alias in objectClass.may: real_attr = self._get_attrtype(str(attr_alias)) if hasattr(self, 'nonUserEditableAttributeType_'+real_attr.name[0]): self.nonUserEditableAttributes.append(real_attr.name[0]) else: for attr in real_attr.name: if not process.has_key(attr.upper()): process[attr.upper()]=0 if not process[attr.upper()]: self._one_formfield(attr, result=r) for name in real_attr.name: process[name.upper()]=1 assert [v==1 for k,v in process.items()], "TODO: %s"%process return r def getBindingNames(self, ctx): return ['add'] def bind_add(self, ctx): return annotate.MethodBinding( 'add', annotate.Method(arguments=self.formFields, label=_('Add')), action=_('Add')) def _textarea_to_list(self, t): return filter(lambda x: x, [x.strip() for x in t.split("\n")]) def _getDNAttr(self): attr_alias = self.chosenObjectClasses[0].must[0] attrType = mapNameToAttributeType(self.attributeTypes, attr_alias) assert attrType is not None dn_attribute = attrType.name[0] return dn_attribute def add(self, context, **kw): cfg = context.locate(interfaces.ILDAPConfig) dnAttr = self._getDNAttr() assert kw.has_key('add_'+dnAttr), 'Must have attribute dn %s points to.' % dnAttr assert kw['add_'+dnAttr], 'Attribute %s must have value.' % 'add_'+dnAttr # TODO ugly rdn=distinguishedname.RelativeDistinguishedName( attributeTypesAndValues=[ distinguishedname.LDAPAttributeTypeAndValue(attributeType=dnAttr, value=kw['add_'+dnAttr]), ]) #TODO verify changes = [] for k,v in kw.items(): if hasattr(self, "nonUserEditableAttributeType_"+k): raise "Can't set attribute %s when adding." % k elif k[:len("add_")]=="add_": if not v: continue attrtype = self._get_attrtype(k[len("add_"):]) assert attrtype if attrtype.single_value or attrtype.uiHint_multiline: v=[v] else: v=self._textarea_to_list(v) if v and [1 for x in v if x]: attr=k[len("add_"):] changes.append(defer.succeed((attr, v))) #TODO for attributeType in self.nonUserEditableAttributes: thing=getattr(self, 'nonUserEditableAttributeType_'+attributeType) if callable(thing): changes.append(thing(attributeType, context)) else: changes.append(defer.succeed((attributeType, thing))) dl=defer.DeferredList(changes, fireOnOneErrback=1) #dl.addErrback(lambda x: x[0]) # throw away index def _pruneSuccessFlags(l): r=[] for succeeded,result in l: assert succeeded r.append(result) return r dl.addCallback(_pruneSuccessFlags) dl.addCallback(self._process2, context, rdn, kw) return dl def _process2(self, changes, context, rdn, kw): cfg = context.locate(interfaces.ILDAPConfig) user = context.locate(inevow.ISession).getLoggedInRoot().loggedIn if not changes: return _("No changes!") #TODO changes_desc="" mod={} for attr,new in changes: if new: if attr not in mod: mod[attr]=[] mod[attr].extend(new) changes_desc=changes_desc+"
      adding %s: %s"%(repr(attr), ', '.join(map(repr, new))) if not mod: return _("No changes (2)!") #TODO e = ldapsyntax.LDAPEntryWithClient(client=user.client, dn=iwebui.ICurrentDN(context)) d = e.addChild(rdn, mod) #d.addCallback(lambda e: "Added %s successfully." % e.dn) d.addErrback(lambda reason: _("Failed: %s.") % reason.getErrorMessage()) return d class ReallyAddPage(rend.Page): implements(iskin.ISkinnable) title = _('Ldaptor Add Page') addSlash = True docFactory = loaders.xmlfile( 'add-really.xhtml', templateDir=os.path.split(os.path.abspath(__file__))[0]) def data_header(self, ctx, data): u=url.URL.fromContext(ctx) u=u.parentdir().parentdir().parentdir().clear() l=[] l.append(tags.a(href=u.sibling("search"))[_("Search")]) l.append(tags.a(href=u.sibling("add"))[_("add new entry")]) return l def render_form(self, context, data): return webform.renderForms() def render_passthrough(self, context, data): return context.tag.clear()[data] def render_status(self, context, data): try: obj = context.locate(inevow.IHand) except KeyError: return context.tag.clear() e = interfaces.ILDAPEntry(obj, None) if e is None: return context.tag.clear()[obj] u=url.URL.fromContext(context) u=u.parentdir().parentdir().parentdir().clear() return context.tag.clear()[ _("Added "), tags.a(href=u.parentdir().child(e.dn).child("search"))[e.dn], _(" successfully. "), # TODO share implementation with entryLinks '[', tags.a(href=u.sibling('edit').child(uriQuote(e.dn)))[_('edit')], '|', tags.a(href=u.sibling('move').child(uriQuote(e.dn)))[_('move')], '|', tags.a(href=u.sibling('delete').child(uriQuote(e.dn)))[_('delete')], '|', tags.a(href=u.sibling('change_password').child(uriQuote(e.dn)))[_('change password')], ']', ] render_i18n = i18n.render() class SmartObjectAddPage(ReallyAddPage): def __init__(self, smartObject): super(SmartObjectAddPage, self).__init__() self.smartObject = smartObject def configurable_(self, context): return self.smartObject def render_overview(self, ctx, data): return tags.invisible() class ManualAddPage(ReallyAddPage): def __init__(self, structuralObjectClass, auxiliaryObjectClasses, attributeTypes, objectClasses): super(ManualAddPage, self).__init__() self.structuralObjectClass = structuralObjectClass self.auxiliaryObjectClasses = auxiliaryObjectClasses self.attributeTypes = attributeTypes self.objectClasses = objectClasses def configurable_(self, context): a = AddForm(chosenObjectClasses=[self.structuralObjectClass] + self.auxiliaryObjectClasses, attributeTypes=self.attributeTypes, objectClasses=self.objectClasses) return a def render_overview(self, ctx, data): if self.auxiliaryObjectClasses: return ctx.tag.clear()[ _('Using objectclasses %s and %s.') % ( self.structuralObjectClass.name[0], ', '.join([oc.name[0] for oc in self.auxiliaryObjectClasses]), )] else: return ctx.tag.clear()[ _('Using objectclass %s.') % ( self.structuralObjectClass.name[0], )] def strObjectClass(oc): if oc.desc is not None: return '%s: %s' % (oc.name[0], oc.desc) else: return '%s' % (oc.name[0],) class ChooseSmartObject(object): def __init__(self, pluginNames): self.plugins = list(pluginNames) self.plugins.sort() def getBindingNames(self, ctx): return ['add'] def bind_add(self, ctx): return annotate.MethodBinding( 'add', annotate.Method(arguments=[ annotate.Argument('context', annotate.Context()), annotate.Argument('smartObjectClass', annotate.Choice(choicesAttribute='plugins')), ], label=_('Add')), action=_('Add')) def add(self, context, smartObjectClass): request = context.locate(inevow.IRequest) u = url.URL.fromContext(context) return u.child('smart').child(smartObjectClass) class AddPage(rend.Page): implements(iskin.ISkinnable) title = _('Ldaptor Add Page') addSlash = True docFactory = loaders.xmlfile( 'add.xhtml', templateDir=os.path.split(os.path.abspath(__file__))[0]) def __init__(self, attributeTypes, objectClasses): super(AddPage, self).__init__() self.attributeTypes = attributeTypes self.objectClasses = objectClasses def listPlugins(self): for plug in plugin.getPlugIns('ldaptor.apps.webui.smartObject'): yield plug.name def havePlugins(self): for plug in plugin.getPlugIns('ldaptor.apps.webui.smartObject'): return True return False def getPlugin(self, name): for plug in plugin.getPlugIns('ldaptor.apps.webui.smartObject'): if plug.name == name: return plug raise KeyError, name def data_header(self, ctx, data): u=url.URL.fromContext(ctx) u=u.parentdir().clear() l=[] l.append(tags.a(href=u.sibling("search"))[_("Search")]) return l def configurable_objectClass(self, context): return AddOCForm(self.objectClasses) def render_objectClassForm(self, context, data): return webform.renderForms('objectClass') def configurable_smartObject(self, context): return ChooseSmartObject(self.listPlugins()) def render_smartObjectForm(self, context, data): if self.havePlugins(): return webform.renderForms('smartObject') else: return context.tag.clear() def render_passthrough(self, context, data): return context.tag.clear()[data] def locateChild(self, request, segments): ret = super(AddPage, self).locateChild(request, segments) if ret != rend.NotFound: return ret if segments[0] == 'manual': if not segments[1:]: return rend.NotFound path=segments[1] unquoted=uriUnquote(path) objectClasses = unquoted.split('+') assert len(objectClasses) >= 1 structName=objectClasses[0] structuralObjectClass = mapNameToObjectClass(self.objectClasses, structName) assert structuralObjectClass is not None, \ "objectClass %s must have schema"%structName auxiliaryObjectClasses = [] for auxName in objectClasses[1:]: oc = mapNameToObjectClass(self.objectClasses, auxName) assert oc is not None, "objectClass %s must have schema"%oc auxiliaryObjectClasses.append(oc) r = ManualAddPage(structuralObjectClass=structuralObjectClass, auxiliaryObjectClasses=auxiliaryObjectClasses, attributeTypes=self.attributeTypes, objectClasses=self.objectClasses) return r, segments[2:] elif segments[0] == 'smart': if not segments[1:]: return rend.NotFound name = segments[1] if not name: return rend.NotFound plug = self.getPlugin(name) module = plug.load() add = module.add() r = SmartObjectAddPage(add) return r, segments[2:] else: return rend.NotFound render_i18n = i18n.render() def getResource(baseObject, request): entry = request.getSession().getLoggedInRoot().loggedIn client = entry.client d = fetchschema.fetch(client, baseObject) def cbAddPage(schema): attributeTypes, objectClasses = schema return AddPage(attributeTypes, objectClasses) d.addCallback(cbAddPage) return d ldaptor-0.0.43/ldaptor/apps/webui/__init__.py0000644000175000017500000000006007516345760017212 0ustar janjan""" The HTML/HTTP user interface to Ldaptor """ ldaptor-0.0.43/ldaptor/apps/webui/mass_change_password-really.xhtml0000644000175000017500000000357710402650176023643 0ustar janjan Title Goes Here

      Ldaptor Mass Password Change Page

      [ foo | ]

      ldaptor-0.0.43/ldaptor/apps/webui/add.xhtml0000644000175000017500000000166010402650176016702 0ustar janjan Title Goes Here

      Ldaptor Add Page

      [ foo | ]

      ldaptor-0.0.43/ldaptor/apps/webui/change_service_passwords.xhtml0000644000175000017500000000451010207114505023213 0ustar janjan
      Current service passwords
      Service Actions
      Service name here
      ldaptor-0.0.43/ldaptor/apps/webui/login.xhtml0000644000175000017500000000204010402650176017253 0ustar janjan Title Goes Here

      Login

      ldaptor-0.0.43/ldaptor/apps/webui/mass_change_password.xhtml0000644000175000017500000000136410402650176022345 0ustar janjan Title Goes Here

      Ldaptor Mass Password Change Page

      Missing filter to use. You need to use the search page.

      ldaptor-0.0.43/ldaptor/apps/webui/main.py0000644000175000017500000000316110402650176016370 0ustar janjanfrom zope.interface import implements from twisted.cred import portal, checkers, credentials from nevow import guard, inevow from webut.skin import skin from ldaptor.config import LDAPConfig from ldaptor.apps.webui import gadget, defskin from ldaptor.checkers import LDAPBindingChecker class TODOGetRidOfMeRealm: implements(portal.IRealm) def __init__(self, *args, **kwargs): self.args = args self.kwargs = kwargs def requestAvatar(self, avatarId, mind, *interfaces): if inevow.IResource not in interfaces: raise NotImplementedError, "no interface" if avatarId is checkers.ANONYMOUS: resource = gadget.LdaptorWebUIGadget(None, *self.args, **self.kwargs) resource.realm = self return (inevow.IResource, resource, lambda: None) else: resource = gadget.LdaptorWebUIGadget(avatarId, *self.args, **self.kwargs) resource.realm = self return (inevow.IResource, resource, lambda: None) def getResource(cfg=None, skinFactory=None): """Get a resource for the Ldaptor-webui app.""" if cfg is None: cfg = LDAPConfig() checker = LDAPBindingChecker(cfg) realm = TODOGetRidOfMeRealm(config=cfg) porta = portal.Portal(realm) porta.registerChecker(checkers.AllowAnonymousAccess(), credentials.IAnonymous) porta.registerChecker(checker) mainResource = guard.SessionWrapper(porta) if skinFactory is None: skinFactory = defskin.DefaultSkin return skin.Skinner(skinFactory, mainResource) ldaptor-0.0.43/ldaptor/apps/webui/mass_change_password.py0000644000175000017500000001242610402650176021642 0ustar janjanfrom zope.interface import implements from twisted.internet import defer from webut.skin import iskin from ldaptor.protocols.ldap import ldapsyntax from ldaptor import generate_password from ldaptor.apps.webui.uriquote import uriUnquote from twisted.internet import reactor from ldaptor.apps.webui.i18n import _ from ldaptor.apps.webui import i18n import os from nevow import rend, inevow, loaders, url, tags from formless import annotate, webform, configurable class MassPasswordChangeStatus(object): def __init__(self, deferlist): super(MassPasswordChangeStatus, self).__init__() self.deferlist = deferlist class MassPasswordChangeForm(configurable.Configurable): def __init__(self, ldapObjects): super(MassPasswordChangeForm, self).__init__(None) self.ldapObjects = {} for o in ldapObjects: assert o.dn not in self.ldapObjects self.ldapObjects[o.dn] = o self.formFields=self._getFormFields() def _getFormFields(self): r=[] r.append(annotate.Argument('request', annotate.Request())) for dn, e in self.ldapObjects.items(): r.append(annotate.Argument('dn_%s' % dn, annotate.Boolean(label=dn, description=e))) return r def getBindingNames(self, ctx): return ['generate'] def bind_generate(self, ctx): return annotate.MethodBinding( 'generatePasswords', annotate.Method(arguments=self.formFields, label=_('Generate passwords')), action=_('Generate passwords')) def generatePasswords(self, request, **kw): entries = [] for k,v in kw.items(): if not k.startswith('dn_'): continue k = k[len('dn_'):] if not v: continue assert k in self.ldapObjects entries.append(self.ldapObjects[k]) if not entries: return _('No passwords to change.') d=generate_password.generate(reactor, len(entries)) def _gotPasswords(passwords, entries): assert len(passwords)==len(entries) l=[] for entry, pwd in zip(entries, passwords): d=entry.setPassword(newPasswd=pwd) def _cb(entry, pwd): return (entry, pwd) d.addCallback(_cb, pwd) l.append(d) return defer.DeferredList(l, consumeErrors=True) d.addCallback(_gotPasswords, entries) d.addCallback(MassPasswordChangeStatus) return d class ReallyMassPasswordChangePage(rend.Page): implements(iskin.ISkinnable) title = _('Ldaptor Mass Password Change Page') addSlash = True docFactory = loaders.xmlfile( 'mass_change_password-really.xhtml', templateDir=os.path.split(os.path.abspath(__file__))[0]) def __init__(self, entries): super(ReallyMassPasswordChangePage, self).__init__() self.entries = entries def data_header(self, ctx, data): u=url.URL.fromContext(ctx) u=u.parentdir().parentdir().clear() l=[] l.append(tags.a(href=u.sibling("search"))[_("Search")]) l.append(tags.a(href=u.sibling("add"))[_("add new entry")]) return l def configurable_(self, context): request = context.locate(inevow.IRequest) return MassPasswordChangeForm(self.entries) def render_form(self, context, data): return webform.renderForms()[context.tag] def render_passthrough(self, context, data): return context.tag.clear()[data] def render_status(self, context, data): try: obj = context.locate(inevow.IHand) except KeyError: return context.tag.clear() if not isinstance(obj, MassPasswordChangeStatus): return context.tag.clear()[obj] dl = tags.dl(compact="compact") context.tag.clear()[dl] for success, x in obj.deferlist: if success: entry, pwd = x dl[tags.dt[entry.dn], tags.dd[pwd]] else: context.tag[_('Failed: '), x.getErrorMessage()] return context.tag render_i18n = i18n.render() class MassPasswordChangePage(rend.Page): implements(iskin.ISkinnable) title = _('Ldaptor Mass Password Change Page') addSlash = True docFactory = loaders.xmlfile( 'mass_change_password.xhtml', templateDir=os.path.split(os.path.abspath(__file__))[0]) def __init__(self, baseObject): super(MassPasswordChangePage, self).__init__() self.baseObject = baseObject def render_url(self, context, data): u = url.URL.fromContext(context) return context.tag(href=u.parentdir().child('search')) def childFactory(self, context, name): entry = inevow.ISession(context).getLoggedInRoot().loggedIn filt = uriUnquote(name) e=ldapsyntax.LDAPEntry(client=entry.client, dn=self.baseObject) d=e.search(filterText=filt, sizeLimit=20) d.addCallback(ReallyMassPasswordChangePage) return d render_i18n = i18n.render() ldaptor-0.0.43/ldaptor/apps/webui/move.xhtml0000644000175000017500000000134110402650176017114 0ustar janjan Title Goes Here

      Ldaptor Move Page

      Missing DN to move. You need to use the search page.

      ldaptor-0.0.43/ldaptor/apps/webui/add-really.xhtml0000644000175000017500000000203310402650176020163 0ustar janjan Title Goes Here

      Ldaptor Add Page

      [ foo | ]

      Overview of what is happening might be here.
      ldaptor-0.0.43/ldaptor/apps/webui/delete.py0000644000175000017500000001075210402650176016712 0ustar janjanfrom zope.interface import implements from webut.skin import iskin from ldaptor.protocols.ldap import ldapsyntax, distinguishedname from ldaptor.apps.webui.uriquote import uriUnquote from ldaptor.apps.webui.i18n import _ from ldaptor.apps.webui import i18n, iwebui from ldaptor import weave import os from nevow import rend, inevow, loaders, url, tags from formless import annotate, webform, iformless class ErrorWrapper: def __init__(self, value): self.value = value class ConfirmDelete(rend.Page): implements(iskin.ISkinnable) title = _('Ldaptor Delete Page') docFactory = loaders.xmlfile( 'delete.xhtml', templateDir=os.path.split(os.path.abspath(__file__))[0]) def __init__(self, dn): super(ConfirmDelete, self).__init__() self.dn = dn def getBindingNames(self, ctx): return ['delete'] def bind_delete(self, ctx): return annotate.MethodBinding( 'delete', annotate.Method(arguments=[ annotate.Argument('ctx', annotate.Context()), ], label=_('Confirm delete')), action=_('Delete')) def delete(self, ctx): request = inevow.IRequest(ctx) user = request.getSession().getLoggedInRoot().loggedIn e=ldapsyntax.LDAPEntry(client=user.client, dn=self.dn) d=e.delete() def cb(dummy): basedn = iwebui.ICurrentDN(ctx) while (basedn != '' and self.dn.contains(basedn)): basedn = basedn.up() u=url.URL.fromContext(ctx) u=u.parentdir().parentdir() if basedn != '': u=u.child(basedn).child('search') request.setComponent(iformless.IRedirectAfterPost, u) return _("Deleted %s.") % self.dn def eb(fail): return _("Failed: %s.") % fail.getErrorMessage() d.addCallbacks(cb, eb) return d def data_status(self, ctx, data): try: return ctx.locate(inevow.IStatusMessage) except KeyError: return None def render_if(self, context, data): r=context.tag.allPatterns(str(bool(data))) return context.tag.clear()[r] def data_entry(self, context, data): user = context.locate(inevow.ISession).getLoggedInRoot().loggedIn assert user entry = ldapsyntax.LDAPEntry(client=user.client, dn=self.dn) d = entry.fetch() d.addErrback(ErrorWrapper) return d def render_error_or_pass(self, context, data): if isinstance(data, ErrorWrapper): return context.tag.clear() \ [ tags.strong(style="color: red;") \ [ _('An error occurred: '), data.value.getErrorMessage(), ] ] else: return context.tag def data_dn(self, context, data): return self.dn def render_form(self, context, data): return webform.renderForms() def render_passthrough(self, context, data): return context.tag.clear()[data] def data_header(self, ctx, data): u=url.URL.fromContext(ctx).up().clear() l=[] l.append(tags.a(href=u.sibling("search"))[_("Search")]) l.append(tags.a(href=u.sibling("add"))[_("add new entry")]) l.append(tags.a(href=u.sibling("edit").child(str(self.dn)))[_("edit")]) return l def render_keyvalue(self, context, data): return weave.keyvalue(context, data) def render_keyvalue_item(self, context, data): return weave.keyvalue_item(context, data) render_i18n = i18n.render() class GetDN(rend.Page): implements(iskin.ISkinnable) title = _('Ldaptor Delete Page') addSlash = True docFactory = loaders.xmlfile( 'delete-nodn.xhtml', templateDir=os.path.split(os.path.abspath(__file__))[0]) def render_url(self, context, data): u = url.URL.fromContext(context) return context.tag(href=u.parentdir().child('search')) def childFactory(self, context, name): unquoted=uriUnquote(name) try: dn = distinguishedname.DistinguishedName(stringValue=unquoted) except distinguishedname.InvalidRelativeDistinguishedName, e: # TODO There's no way to throw a FormException at this stage. return None r=ConfirmDelete(dn=dn) return r render_i18n = i18n.render() def getResource(): return GetDN() ldaptor-0.0.43/ldaptor/apps/webui/uriquote.py0000644000175000017500000000030610344535643017325 0ustar janjanimport urllib def uriQuote(uri): uri=str(uri) for c in '%;/?:@&+$': uri=uri.replace(c, '%%%02x'%ord(c)) return uri def uriUnquote(q): q=str(q) return urllib.unquote(q) ldaptor-0.0.43/ldaptor/apps/webui/move.py0000644000175000017500000000236710402650176016421 0ustar janjanfrom zope.interface import implements import os from webut.skin import iskin from ldaptor.protocols.ldap import ldapsyntax from ldaptor.apps.webui.uriquote import uriUnquote from nevow import rend, loaders, url, inevow from ldaptor.apps.webui.i18n import _ from ldaptor.apps.webui import i18n from ldaptor.apps.webui.search import IMove class MovePage(rend.Page): implements(iskin.ISkinnable) title = _('Ldaptor Move Page') addSlash = True docFactory = loaders.xmlfile( 'move.xhtml', templateDir=os.path.split(os.path.abspath(__file__))[0]) def render_url(self, ctx, data): u = url.URL.fromContext(ctx) return ctx.tag(href=u.parentdir().child('search')) def childFactory(self, context, name): dn = uriUnquote(name) session = inevow.ISession(context) userEntry = session.getLoggedInRoot().loggedIn move = session.getComponent(IMove) if move is None: move = [] session.setComponent(IMove, move) e = ldapsyntax.LDAPEntryWithClient(dn=dn, client=userEntry.client) move.append(e) u = url.URL.fromContext(context).sibling('search') return u render_i18n = i18n.render() ldaptor-0.0.43/ldaptor/apps/webui/config.py0000644000175000017500000000133410344407651016714 0ustar janjanfrom ldaptor import config def _getSearchFields(): cfg = config.loadConfig() if not cfg.has_section('webui'): return for raw in cfg.options('webui'): if not raw: continue l=raw.split(None, 2) if l[0].lower() == 'search-field': pri, name = l[1:] pri = int(pri) yield (pri, name, raw) def getSearchFieldByName(name, vars): for pri, n, raw in _getSearchFields(): if n == name: cfg = config.loadConfig() val = cfg.get('webui', raw, raw=None, vars=vars) return val return None def getSearchFieldNames(): l = list(_getSearchFields()) l.sort() return [name for pri,name,raw in l] ldaptor-0.0.43/ldaptor/apps/webui/ldaptor.css0000644000175000017500000000210110402650176017242 0ustar janjan#moveEntries { width: 70%; border: 1px solid gray; position: relative; } #moveEntries li { height: 1.5em; } #moveEntries .moveForms { display: inline; margin-right: 0; position: absolute; right: 1em; } #moveEntries .moveForms form { display: inline; } #moveEntries .moveForms form input { font-size: 0.7em; } #ldaptorPage-mass-change-password .freeform-label { float: none; width: auto; } #ldaptorPage-mass-change-password .freeform-description { font-size: 80%; } input:focus { border-color: black; } fieldset, .emulate-fieldset { border-style: groove; border-width: 2px; border-color: #F7F7F8; padding: 0.5em; } .zebra-even { background-color: #edf3fe; } .zebra-odd { background-color: #ffffff; } .ldaptor-service-passwords form { padding: 0.5em; } .ldaptor-service-passwords table.emulate-fieldset { width: 100%; } .ldaptor-service-passwords table.emulate-fieldset thead tr th { text-align: left; padding-left: 0.5em; padding-right: 0.5em; } .ldaptor-service-passwords table.emulate-fieldset .ldaptor-svcpass-name { padding: 0.5em; } ldaptor-0.0.43/ldaptor/apps/webui/gadget.py0000644000175000017500000001145610402650176016705 0ustar janjanfrom zope.interface import implements from webut.skin import iskin from ldaptor.apps.webui import login, search, edit, add, delete, mass_change_password, change_password, move, iwebui from ldaptor.protocols.ldap import distinguishedname from ldaptor.apps.webui.uriquote import uriUnquote from ldaptor import interfaces from ldaptor.apps.webui.i18n import _ from ldaptor.apps.webui import i18n from nevow import rend, loaders, url, static, inevow from formless import annotate, webform, iformless import os class LdaptorWebUIGadget2(rend.Page): implements(iskin.ISkinnable) title = _('Ldaptor Web Interface') addSlash = True def __init__(self, baseObject): super(LdaptorWebUIGadget2, self).__init__() self.baseObject = baseObject def child_(self, context): return inevow.IRequest(context).URLPath().child('search') def child_search(self, context): return search.getSearchPage() def child_edit(self, context): if not inevow.ISession(context).getLoggedInRoot().loggedIn: return login.LoginPage([str(self.baseObject), 'edit']) return edit.EditPage() def child_move(self, context): if not inevow.ISession(context).getLoggedInRoot().loggedIn: return login.LoginPage([str(self.baseObject), 'move']) return move.MovePage() def child_add(self, context): if not inevow.ISession(context).getLoggedInRoot().loggedIn: return login.LoginPage([str(self.baseObject), 'add']) return add.getResource(baseObject=self.baseObject, request=inevow.IRequest(context)) def child_delete(self, context): if not inevow.ISession(context).getLoggedInRoot().loggedIn: return login.LoginPage([str(self.baseObject), 'delete']) return delete.getResource() def child_mass_change_password(self, context): if not inevow.ISession(context).getLoggedInRoot().loggedIn: return login.LoginPage([str(self.baseObject), 'mass_change_password']) return mass_change_password.MassPasswordChangePage( baseObject=self.baseObject) def child_change_password(self, context): if not inevow.ISession(context).getLoggedInRoot().loggedIn: return login.LoginPage([str(self.baseObject), 'change_password']) return change_password.getResource() class LDAPDN(annotate.String): def coerce(self, *a, **kw): val = super(LDAPDN, self).coerce(*a, **kw) try: dn = distinguishedname.DistinguishedName(stringValue=val) except distinguishedname.InvalidRelativeDistinguishedName, e: raise annotate.InputError, \ "%r is not a valid LDAP DN: %s" % (val, e) return dn class LdaptorWebUIGadget(rend.Page): implements(iskin.ISkinnable) title = _('Ldaptor Web Interface') addSlash = True docFactory = loaders.xmlfile( 'basedn.xhtml', templateDir=os.path.split(os.path.abspath(__file__))[0]) def __init__(self, loggedIn, config): super(LdaptorWebUIGadget, self).__init__() self.loggedIn = loggedIn self.config = config def getBindingNames(self, ctx): return ['go'] def bind_go(self, ctx): return annotate.MethodBinding( 'go', annotate.Method(arguments=[ annotate.Argument('ctx', annotate.Context()), annotate.Argument('baseDN', LDAPDN( label=_('Base DN'), description=_("The top-level LDAP DN you want" " to browse, e.g. dc=example,dc=com"))), ], label=_('Go')), action=_('Go')) def go(self, ctx, baseDN): u = url.URL.fromContext(ctx) u = u.child(str(baseDN)) return u def render_form(self, context, data): return webform.renderForms() def locateChild(self, ctx, segments): ret = super(LdaptorWebUIGadget, self).locateChild(ctx, segments) if ret != rend.NotFound: return ret path = segments[0] unquoted=uriUnquote(path) try: dn = distinguishedname.DistinguishedName(stringValue=unquoted) except distinguishedname.InvalidRelativeDistinguishedName, e: # TODO There's no way to throw a FormException at this stage. u = url.URL.fromContext(ctx) # TODO protect against guard bug, see # http://divmod.org/users/roundup.twistd/nevow/issue74 u = u.child('') # TODO freeform_post!configurableName!methodName u.add('basedn', path) return u, [] r=LdaptorWebUIGadget2(baseObject=dn) ctx.remember(self.config, interfaces.ILDAPConfig) ctx.remember(dn, iwebui.ICurrentDN) return r, segments[1:] render_i18n = i18n.render() ldaptor-0.0.43/ldaptor/apps/webui/i18n.py0000644000175000017500000000027610345073240016224 0ustar janjanfrom nevow.inevow import ILanguages from nevow.i18n import I18NConfig from nevow import i18n _ = i18n.Translator(domain='ldaptor-webui') def render(): return i18n.render(translator=_) ldaptor-0.0.43/ldaptor/apps/webui/edit.xhtml0000644000175000017500000000134110402650176017073 0ustar janjan Title Goes Here

      Ldaptor Edit Page

      Missing DN to edit. You need to use the search page.

      ldaptor-0.0.43/ldaptor/apps/webui/change_password.xhtml0000644000175000017500000000240610402650176021320 0ustar janjan Title Goes Here

      Ldaptor Password Change Page

      [foo|]

      About to set password for .

      Form goes here

      Service passwords

      ldaptor-0.0.43/ldaptor/apps/webui/search.py0000644000175000017500000003174610402650176016723 0ustar janjanfrom zope.interface import implements from twisted.internet import defer from twisted.python import components from webut.skin import iskin from ldaptor.protocols.ldap import ldapclient, ldapsyntax from ldaptor.protocols.ldap import distinguishedname, ldapconnector from ldaptor.protocols import pureldap from ldaptor import ldapfilter, interfaces from twisted.internet import reactor from ldaptor.apps.webui import config, iwebui from ldaptor.apps.webui.uriquote import uriQuote from ldaptor.apps.webui.i18n import _ from ldaptor.apps.webui import i18n from ldaptor import weave import os from nevow import rend, inevow, loaders, url, tags from formless import annotate, webform, iformless, configurable class IMove(components.Interface): """Entries being moved in the tree.""" pass class MoveItem(configurable.Configurable): def getBindingNames(self, ctx): return ['move', 'cancel'] def bind_move(self, ctx): return annotate.MethodBinding( 'move', annotate.Method(arguments=[ annotate.Argument('context', annotate.Context()), ], label=_('Move')), action=_('Move')) def bind_cancel(self, ctx): return annotate.MethodBinding( 'cancel', annotate.Method(arguments=[ annotate.Argument('context', annotate.Context()), ], label=_('Cancel')), action=_('Cancel')) def _remove(self, context): session = context.locate(inevow.ISession) move = session.getComponent(IMove) if move is None: return try: move.remove(self.original) except ValueError: pass def move(self, context): cfg = context.locate(interfaces.ILDAPConfig) newDN = distinguishedname.DistinguishedName( self.original.dn.split()[:1] + iwebui.ICurrentDN(context).split()) origDN = self.original.dn d = self.original.move(newDN) d.addCallback(lambda dummy: _('Moved %s to %s.') % (origDN, newDN)) def _cb(r, context): self._remove(context) return r d.addCallback(_cb, context) return d def cancel(self, context): self._remove(context) return _('Cancelled move of %s') % self.original.dn def strScope(scope): if scope == pureldap.LDAP_SCOPE_wholeSubtree: return _('whole subtree') elif scope == pureldap.LDAP_SCOPE_singleLevel: return _('single level') elif scope == pureldap.LDAP_SCOPE_baseObject: return _('baseobject') else: raise RuntimeError, 'scope is not known: %r' % scope class SearchForm(configurable.Configurable): implements(inevow.IContainer) filter = None def __init__(self): super(SearchForm, self).__init__(None) self.data = {} def getBindingNames(self, ctx): return ['search'] def bind_search(self, ctx): l = [] l.append(annotate.Argument('ctx', annotate.Context())) for field in config.getSearchFieldNames(): l.append(annotate.Argument('search_%s' % field, annotate.String(label=field))) l.append(annotate.Argument('searchfilter', annotate.String(label=_("Advanced search")))) l.append(annotate.Argument( 'scope', annotate.Choice(label=_("Search depth"), choices=[ pureldap.LDAP_SCOPE_wholeSubtree, pureldap.LDAP_SCOPE_singleLevel, pureldap.LDAP_SCOPE_baseObject, ], stringify=strScope, default=pureldap.LDAP_SCOPE_wholeSubtree))) return annotate.MethodBinding( name='search', action=_("Search"), typeValue=annotate.Method(arguments=l, label=_('Search'))) def search(self, ctx, scope, searchfilter, **kw): filt=[] for k,v in kw.items(): assert k.startswith('search_') if not k.startswith("search_"): continue k=k[len("search_"):] if v is None: continue v=v.strip() if v=='': continue # TODO escape ) in v # TODO handle unknown filter name right (old form open in browser etc) filter_ = config.getSearchFieldByName(k, vars={'input': v}) filt.append(ldapfilter.parseFilter(filter_)) if searchfilter: try: filt.append(ldapfilter.parseFilter(searchfilter)) except ldapfilter.InvalidLDAPFilter, e: raise annotate.ValidateError( {'searchfilter': str(e), }, partialForm=inevow.IRequest(ctx).args) if filt: if len(filt)==1: query=filt[0] else: query=pureldap.LDAPFilter_and(filt) else: query=pureldap.LDAPFilterMatchAll self.data.update(kw) # annotate.Choice in nevow 0.3 maps choices to a list, and # passes indexes to this list to client. annotate.Choice in # 0.4pre converts choice to string and back with callbacks, # defaulting to str, and leaving the value as string. We # can't use the 0.4pre mechanism as long as we need 0.3 # compatibility, so work around that by explicitly making sure # scope is an integer. scope = int(scope) self.data['scope'] = scope self.data['searchfilter'] = searchfilter self.filter = query return self def child(self, context, name): fn = getattr(self, 'child_%s' % name, None) if fn is None: return None else: return fn(context) def child_filter(self, context): return self.filter.asText() def child_results(self, context): assert self.filter is not None cfg = context.locate(interfaces.ILDAPConfig) c=ldapconnector.LDAPClientCreator(reactor, ldapclient.LDAPClient) curDN = iwebui.ICurrentDN(context) d=c.connectAnonymously(curDN, cfg.getServiceLocationOverrides()) def _search(proto, dn, searchFilter, scope): baseEntry = ldapsyntax.LDAPEntry(client=proto, dn=dn) d=baseEntry.search(filterObject=searchFilter, scope=scope, sizeLimit=20, sizeLimitIsNonFatal=True) def _cb(result, proto): proto.unbind() return result d.addBoth(_cb, proto) return d d.addCallback(_search, curDN, self.filter, self.data['scope']) return d def child_base(self, context): cfg = context.locate(interfaces.ILDAPConfig) c=ldapconnector.LDAPClientCreator(reactor, ldapclient.LDAPClient) d=c.connectAnonymously(iwebui.ICurrentDN(context), cfg.getServiceLocationOverrides()) def _search(proto, base): baseEntry = ldapsyntax.LDAPEntry(client=proto, dn=base) d=baseEntry.search(scope=pureldap.LDAP_SCOPE_baseObject, sizeLimit=1) def _cb(result, proto): proto.unbind() return result d.addBoth(_cb, proto) return d d.addCallback(_search, iwebui.ICurrentDN(context)) def _first(results, dn): assert len(results)==1, \ "Expected one result, not %r" % results return {'dn': dn, 'attributes': results[0], } d.addCallback(_first, iwebui.ICurrentDN(context)) return d def __nonzero__(self): return self.filter is not None def getSearchForm(ctx): try: hand = ctx.locate(inevow.IHand) except KeyError: pass else: if isinstance(hand, SearchForm): return hand return SearchForm() class SearchPage(rend.Page): implements(iskin.ISkinnable) title = _('Ldaptor Search Page') addSlash = True docFactory = loaders.xmlfile( 'search.xhtml', templateDir=os.path.split(os.path.abspath(__file__))[0]) def render_form(self, ctx, data): conf = getSearchForm(ctx) formDefaults = ctx.locate(iformless.IFormDefaults) methodDefaults = formDefaults.getAllDefaults('search') for k,v in conf.data.items(): if v is not None: methodDefaults[k] = str(v) return webform.renderForms() def render_keyvalue(self, context, data): return weave.keyvalue(context, data) def render_keyvalue_item(self, context, data): return weave.keyvalue_item(context, data) def render_passthrough(self, context, data): return context.tag.clear()[data] def data_status(self, context, data): try: obj = context.locate(inevow.IStatusMessage) except KeyError: return '' if isinstance(obj, SearchForm): return '' else: return obj def render_data(self, ctx, data): return ctx.tag.clear()[data] def render_if(self, context, data): r=context.tag.allPatterns(str(bool(data))) return context.tag.clear()[r] def data_search(self, ctx, data): return getSearchForm(ctx) def data_header(self, ctx, data): u=url.URL.fromContext(ctx) u=u.parentdir().clear() l=[] l.append(tags.a(href=u.sibling("add"))[_("add new entry")]) return l def data_navilink(self, context, data): cfg = context.locate(interfaces.ILDAPConfig) dn = iwebui.ICurrentDN(context) r=[] while dn!=distinguishedname.DistinguishedName(stringValue=''): #TODO and while inside base? firstPart=dn.split()[0] r.append(('../../%s' % uriQuote(str(dn)), str(firstPart))) dn=dn.up() return r def render_link(self, context, (url, desc)): context.fillSlots('url', url) context.fillSlots('description', desc) return context.tag def render_linkedDN(self, ctx, data): dn = data cfg = ctx.locate(interfaces.ILDAPConfig) baseDN = iwebui.ICurrentDN(ctx) ctx.tag.clear() while (dn!=baseDN and dn!=distinguishedname.DistinguishedName(stringValue='')): firstPart=dn.split()[0] u = url.here.parentdir().parentdir().child(dn) segments = inevow.ICurrentSegments(ctx) if segments[-1] == '': u = u.child(segments[-2]).child(segments[-1]) else: u = u.child(segments[-1]) for segment in inevow.IRemainingSegments(ctx): u = u.child(segment) ctx.tag[tags.a(href=u)[str(firstPart)], ','] dn=dn.up() ctx.tag['%s\n' % str(dn)] return ctx.tag def render_entryLinks(self, ctx, data): u = url.URL.fromContext(ctx).parentdir().clear() l = [ (u.sibling('edit').child(uriQuote(data)), _('edit')), (u.sibling('move').child(uriQuote(data)), _('move')), (u.sibling('delete').child(uriQuote(data)), _('delete')), (u.sibling('change_password').child(uriQuote(data)), _('change password')), ] return self.render_sequence(ctx, l) def render_listLen(self, context, data): if data is None: length = 0 else: length = len(data) return context.tag.clear()[length] def render_mass_change_password(self, ctx, data): u = url.URL.fromContext(ctx) u = u.parentdir().sibling("mass_change_password") u = u.child(uriQuote(data)) return ctx.tag(href=u) def data_move(self, context, data): session = context.locate(inevow.ISession) if not session.getLoggedInRoot().loggedIn: return [] move = session.getComponent(IMove) if move is None: return [] return move def locateConfigurable(self, context, name): if name == '': return getSearchForm(context) elif name.startswith('move_'): dn = name[len('move_'):] session = context.locate(inevow.ISession) move = session.getComponent(IMove) if move is not None: for entry in move: if entry.dn == dn: return iformless.IConfigurable(MoveItem(entry)) raise KeyError, name def render_move(self, context, data): return webform.renderForms('move_%s' % data.dn)[context.tag] render_i18n = i18n.render() def getSearchPage(): r = SearchPage() return r ldaptor-0.0.43/ldaptor/checkers.py0000644000175000017500000000575510345073240015165 0ustar janjanfrom zope.interface import implements from twisted.cred import checkers, credentials, error from twisted.internet import reactor from twisted.python import failure from ldaptor import ldapfilter, config from ldaptor.protocols.ldap import ldapconnector, ldapclient, ldapsyntax, ldaperrors def makeFilter(name, template=None): filter=None try: filter=ldapfilter.parseFilter(name) except ldapfilter.InvalidLDAPFilter: try: filter=ldapfilter.parseFilter('('+name+')') except ldapfilter.InvalidLDAPFilter: if template is not None: try: filter=ldapfilter.parseFilter(template % {'name':name}) except ldapfilter.InvalidLDAPFilter: pass return filter class LDAPBindingChecker: """ The avatarID returned is an LDAPEntry. """ implements(checkers.ICredentialsChecker) credentialInterfaces = (credentials.IUsernamePassword,) def __init__(self, cfg): self.config = cfg def _valid(self, result, entry): matchedDN, serverSaslCreds = result return entry def _found(self, results, credentials): if not results: return failure.Failure(error.UnauthorizedLogin('TODO 1')) assert len(results)==1 entry = results[0] d = entry.client.bind(str(entry.dn), credentials.password) d.addCallback(self._valid, entry) return d def _connected(self, client, filt, credentials): base = ldapsyntax.LDAPEntry(client, self.config.getIdentityBaseDN()) d = base.search(filterObject=filt, sizeLimit=1, attributes=[''], # TODO no attributes ) d.addCallback(self._found, credentials) return d def requestAvatarId(self, credentials): try: baseDN = self.config.getIdentityBaseDN() except config.MissingBaseDNError, e: return failure.Failure(error.UnauthorizedLogin("Disabled due configuration error: %s." % e)) if not credentials.username: return failure.Failure(error.UnauthorizedLogin("I don't support anonymous")) filtText = self.config.getIdentitySearch(credentials.username) try: filt = ldapfilter.parseFilter(filtText) except ldapfilter.InvalidLDAPFilter: return failure.Failure(error.UnauthorizedLogin("Couldn't create filter")) c = ldapconnector.LDAPClientCreator(reactor, ldapclient.LDAPClient) d = c.connect(baseDN, self.config.getServiceLocationOverrides()) d.addCallback(self._connected, filt, credentials) def _err(reason): reason.trap(ldaperrors.LDAPInvalidCredentials, # this happens with slapd 2.1.30 when binding # with DN but no password ldaperrors.LDAPUnwillingToPerform) return failure.Failure(error.UnauthorizedLogin()) d.addErrback(_err) return d ldaptor-0.0.43/ldaptor/insensitive.py0000644000175000017500000000331610104536552015730 0ustar janjanclass InsensitiveString(str): """A str subclass that performs all matching without regard to case.""" def __eq__(self, other): if isinstance(other, basestring): return self.lower() == other.lower() else: return super(InsensitiveString, self).__eq__(other) def __ne__(self, other): if isinstance(other, basestring): return self.lower() != other.lower() else: return super(InsensitiveString, self).__ne__(self, other) def __ge__(self, other): if isinstance(other, basestring): return self.lower() >= other.lower() else: return super(InsensitiveString, self).__ge__(self, other) def __gt__(self, other): if isinstance(other, basestring): return self.lower() > other.lower() else: return super(InsensitiveString, self).__gt__(self, other) def __le__(self, other): if isinstance(other, basestring): return self.lower() <= other.lower() else: return super(InsensitiveString, self).__le__(self, other) def __lt__(self, other): if isinstance(other, basestring): return self.lower() < other.lower() else: return super(InsensitiveString, self).__lt__(self, other) def __hash__(self): return hash(self.lower()) def __contains__(self, other): if isinstance(other, basestring): return other.lower() in self.lower() else: return super(InsensitiveString, self).__contains__(self, other) def __getslice__(self, *a, **kw): r = super(InsensitiveString, self).__getslice__(*a, **kw) return self.__class__(r) ldaptor-0.0.43/ldaptor/interfaces.py0000644000175000017500000002341710344535643015525 0ustar janjanfrom twisted.python import components class ILDAPEntry(components.Interface): """ Pythonic API for LDAP object access and modification. >>> o=LDAPEntry(client=ldapclient.LDAPClient(), ... dn='cn=foo,dc=example,dc=com', ... attributes={'anAttribute': ['itsValue', 'secondValue'], ... 'onemore': ['aValue'], ... }) >>> o LDAPEntry(dn='cn=foo,dc=example,dc=com', attributes={'anAttribute': ['itsValue', 'secondValue'], 'onemore': ['aValue']}) """ def __getitem__(self, key): """ Get all values of an attribute. >>> o=LDAPEntry(client=ldapclient.LDAPClient(), ... dn='cn=foo,dc=example,dc=com', ... attributes={'anAttribute': ['itsValue']}) >>> o['anAttribute'] ['itsValue'] """ def get(self, key, default=None): """ Get all values of an attribute. >>> o=LDAPEntry(client=ldapclient.LDAPClient(), ... dn='cn=foo,dc=example,dc=com', ... attributes={'anAttribute': ['itsValue']}) >>> o.get('anAttribute') ['itsValue'] >>> o.get('foo') >>> o.get('foo', []) [] """ def has_key(self, key): """TODO""" def __contains__(self, key): """TODO""" def keys(self): """TODO""" def items(self): """TODO""" def __str__(self): """ Stringify as LDIF. >>> o=LDAPEntry(client=ldapclient.LDAPClient(), ... dn='cn=foo,dc=example,dc=com', ... attributes={'anAttribute': ['itsValue', 'secondValue'], ... 'onemore': ['aValue'], ... }) >>> # must use rstrip or doctests won't like it due to the empty line >>> # you can just say "print o" >>> print str(o).rstrip() dn: cn=foo,dc=example,dc=com anAttribute: itsValue anAttribute: secondValue onemore: aValue """ def __eq__(self, other): """ Comparison. Only equality is supported. >>> client=ldapclient.LDAPClient() >>> a=LDAPEntry(client=client, ... dn='dc=example,dc=com') >>> b=LDAPEntry(client=client, ... dn='dc=example,dc=com') >>> a==b 1 >>> c=LDAPEntry(client=ldapclient.LDAPClient(), ... dn='ou=different,dc=example,dc=com') >>> a==c 0 Comparison does not consider the client of the object. >>> anotherClient=ldapclient.LDAPClient() >>> d=LDAPEntry(client=anotherClient, ... dn='dc=example,dc=com') >>> a==d 1 """ def __ne__(self, other): """ Inequality comparison. See L{__eq__}. """ def __len__(self): """TODO""" def __nonzero__(self): """Always return True""" def bind(self, password): """ Try to authenticate with given secret. @return: Deferred ILDAPEntry (that is, self). @raise ldaperrors.LDAPInvalidCredentials: password was incorrect. """ class IEditableLDAPEntry(components.Interface): """Interface definition for editable LDAP entries.""" def __setitem__(self, key, value): """ Set values of an attribute. Please use lists. Do not modify the lists in place, that's not supported _yet_. >>> o=LDAPEntry(client=ldapclient.LDAPClient(), ... dn='cn=foo,dc=example,dc=com', ... attributes={'anAttribute': ['itsValue']}) >>> o['anAttribute']=['foo', 'bar'] >>> o['anAttribute'] ['bar', 'foo'] """ def __delitem__(self, key): """ Delete all values of an attribute. >>> o=LDAPEntry(client=ldapclient.LDAPClient(), ... dn='cn=foo,dc=example,dc=com', ... attributes={ ... 'anAttribute': ['itsValue', 'secondValue'], ... 'another': ['moreValues'], ... }) >>> del o['anAttribute'] >>> o LDAPEntry(dn='cn=foo,dc=example,dc=com', attributes={'another': ['moreValues']}) """ def undo(self): """ Forget all pending changes. """ def commit(self): """ Send all pending changes to the LDAP server. @returns: a Deferred that tells you whether the operation succeeded or not. (TODO specify how) """ def move(self, newDN): """ Move the object to a new DN. @param newDN: the new DistinguishedName @return: A Deferred that will complete when the move is done. """ def delete(self): """ Delete this object from the LDAP server. @return: A Deferred that will complete when the delete is done. """ def setPassword(self, newPasswd): """ Set all applicable passwords for this object. @param newPasswd: A string containing the new password. @return: A Deferred that will complete when the operation is done. """ class IConnectedLDAPEntry(components.Interface): """Interface definition for LDAP entries that are part of a bigger whole.""" def namingContext(self): """ Return an LDAPEntry for the naming context that contains this object. """ def fetch(self, *attributes): """ Fetch the attributes of this object from the server. @param attributes: Attributes to fetch. If none, fetch all attributes. Fetched attributes are overwritten, and if fetching all attributes, attributes that are not on the server are removed. @return: A Deferred that will complete when the operation is done. """ def search(self, filterText=None, filterObject=None, attributes=(), scope=None, derefAliases=None, sizeLimit=0, timeLimit=0, typesOnly=0, callback=None): """ Perform an LDAP search with this object as the base. @param filterText: LDAP search filter as a string. @param filterObject: LDAP search filter as LDAPFilter. Note if both filterText and filterObject are given, they are combined with AND. If neither is given, the search is made with a filter that matches everything. @param attributes: List of attributes to retrieve for the result objects. An empty list and means all. @param scope: Whether to recurse into subtrees. @param derefAliases: Whether to deref LDAP aliases. TODO write better documentation. @param sizeLimit: At most how many entries to return. 0 means unlimited. @param timeLimit: At most how long to use for processing the search request. 0 means unlimited. @param typesOnly: Whether to return attribute types only, or also values. @param callback: Callback function to call for each resulting LDAPEntry. None means gather the results into a list and give that to the Deferred returned from here. @return: A Deferred that will complete when the search is done. The Deferred gives None if callback was given and a list of the search results if callback is not given or is None. """ def children(self, callback=None): """ List the direct children of this entry. Try to avoid using .search(), as this will be used later to implement .search() on LDAP backends. @param callback: Callback function to call for each resulting LDAPEntry. None means gather the results into a list and give that to the Deferred returned from here. @return: A Deferred that will complete when the list is over. The Deferred gives None if callback was given and a list of the children if callback is not given or is None. """ def subtree(self, callback=None): """ List the subtree rooted at this entry, including this entry. Try to avoid using .search(), as this will be used later to implement .search() on LDAP backends. @param callback: Callback function to call for each resulting LDAPEntry. None means gather the results into a list and give that to the Deferred returned from here. @return: A Deferred that will complete when the list is over. The Deferred gives None if callback was given and a list of the children if callback is not given or is None. """ def lookup(self, dn): """ Lookup the referred to by dn. @return: A Deferred returning an ILDAPEntry, or failing with e.g. LDAPNoSuchObject. """ def match(self, filter): """ Does entry match filter. @param filter: An LDAPFilter (e.g. LDAPFilter_present, LDAPFilter_equalityMatch etc. TODO provide an interface or superclass for filters.) @return: Boolean. """ class ILDAPConfig(components.Interface): """Generic LDAP configuration retrieval.""" def getBaseDN(self): """ Get the LDAP base DN, as a DistinguishedName. Raises ldaptor.config.MissingBaseDNError if configuration does not specify a base DN. """ def getServiceLocationOverrides(self): """ Get the LDAP service location overrides, as a mapping of DistinguishedName to (host, port) tuples. """ def copy(self, baseDN=None, serviceLocationOverrides=None): """ Make a copy of this configuration, overriding certain aspects of it. """ def getIdentityBaseDN(self): """TODO""" def getIdentitySearch(self, name): """TODO""" ldaptor-0.0.43/ldaptor/delta.py0000644000175000017500000001606010352535753014470 0ustar janjan""" Changes to the content of one single LDAP entry. (This means these do not belong here: adding or deleting of entries, changing of location in tree) """ from ldaptor import attributeset from ldaptor.protocols import pureldap, pureber from ldaptor.protocols.ldap import ldif, distinguishedname class Modification(attributeset.LDAPAttributeSet): def patch(self, entry): raise NotImplementedError _LDAP_OP = None def asLDAP(self): if self._LDAP_OP is None: raise NotImplementedError("%s.asLDAP not implemented" % self.__class__.__name__) return str(pureber.BERSequence([ pureber.BEREnumerated(self._LDAP_OP), pureber.BERSequence([ pureldap.LDAPAttributeDescription(self.key), pureber.BERSet(map(pureldap.LDAPString, list(self))), ]), ])) def __eq__(self, other): if not isinstance(other, self.__class__): return False return super(Modification, self).__eq__(other) class Add(Modification): _LDAP_OP = 0 def patch(self, entry): if self.key in entry: entry[self.key].update(self) else: entry[self.key] = self def asLDIF(self): r=[] values = list(self) values.sort() r.append(ldif.attributeAsLDIF('add', self.key)) for v in values: r.append(ldif.attributeAsLDIF(self.key, v)) r.append('-\n') return ''.join(r) class Delete(Modification): _LDAP_OP = 1 def patch(self, entry): if not self: del entry[self.key] else: for v in self: entry[self.key].remove(v) def asLDIF(self): r=[] values = list(self) values.sort() r.append(ldif.attributeAsLDIF('delete', self.key)) for v in values: r.append(ldif.attributeAsLDIF(self.key, v)) r.append('-\n') return ''.join(r) class Replace(Modification): _LDAP_OP = 2 def patch(self, entry): if self: entry[self.key] = self else: try: del entry[self.key] except KeyError: pass def asLDIF(self): r=[] values = list(self) values.sort() r.append(ldif.attributeAsLDIF('replace', self.key)) for v in values: r.append(ldif.attributeAsLDIF(self.key, v)) r.append('-\n') return ''.join(r) class Operation(object): def patch(self, root): """ Find the correct entry in IConnectedLDAPEntry and patch it. @param root: IConnectedLDAPEntry that is at the root of the subtree the patch applies to. @returns: Deferred with None or failure. """ raise NotImplementedError class ModifyOp(Operation): def __init__(self, dn, modifications=[]): if not isinstance(dn, distinguishedname.DistinguishedName): dn=distinguishedname.DistinguishedName(stringValue=dn) self.dn = dn self.modifications = modifications[:] def asLDIF(self): r = [] r.append(ldif.attributeAsLDIF('dn', str(self.dn))) r.append(ldif.attributeAsLDIF('changetype', 'modify')) for m in self.modifications: r.append(m.asLDIF()) r.append("\n") return ''.join(r) def asLDAP(self): return pureldap.LDAPModifyRequest( object=str(self.dn), modification=[x.asLDAP() for x in self.modifications]) def _getClassFromOp(class_, op): for mod in [Add, Delete, Replace]: if op == mod._LDAP_OP: return mod return None _getClassFromOp = classmethod(_getClassFromOp) def fromLDAP(class_, request): if not isinstance(request, pureldap.LDAPModifyRequest): raise RuntimeError("%s.fromLDAP needs an LDAPModifyRequest" % class_.__name__) dn = request.object result = [] for op, mods in request.modification: op = op.value klass = class_._getClassFromOp(op) if klass is None: raise RuntimeError("Unknown LDAP op number %r in %s.fromLDAP" % (op, class_.__name__)) key, vals = mods key = key.value vals = [x.value for x in vals] m = klass(key, vals) result.append(m) return class_(dn, result) fromLDAP = classmethod(fromLDAP) def patch(self, root): d = root.lookup(self.dn) def gotEntry(entry, modifications): for mod in self.modifications: mod.patch(entry) return entry d.addCallback(gotEntry, self.modifications) return d def __repr__(self): return (self.__class__.__name__ + '(' + 'dn=%r' % str(self.dn) + ', ' + 'modifications=%r' % self.modifications + ')') def __eq__(self, other): if not isinstance(other, self.__class__): return 0 if self.dn != other.dn: return 0 if self.modifications != other.modifications: return 0 return 1 def __ne__(self, other): return not self==other class AddOp(Operation): def __init__(self, entry): self.entry = entry def asLDIF(self): l = str(self.entry).splitlines() assert l[0].startswith('dn:') l[1:1] = [ldif.attributeAsLDIF('changetype', 'add').rstrip('\n')] return ''.join([x+'\n' for x in l]) def patch(self, root): d = root.lookup(self.entry.dn.up()) def gotParent(parent, entry): parent.addChild(entry.dn.split()[0], entry) d.addCallback(gotParent, self.entry) return d def __repr__(self): return (self.__class__.__name__ + '(' + '%r' % self.entry + ')') def __eq__(self, other): if not isinstance(other, self.__class__): return False if self.entry != other.entry: return False return True def __ne__(self, other): return not self==other class DeleteOp(Operation): def __init__(self, dn): self.dn = dn def asLDIF(self): r = [] r.append(ldif.attributeAsLDIF('dn', str(self.dn))) r.append(ldif.attributeAsLDIF('changetype', 'delete')) r.append("\n") return ''.join(r) def patch(self, root): d = root.lookup(self.dn) def gotEntry(entry): return entry.delete() d.addCallback(gotEntry) return d def __repr__(self): return (self.__class__.__name__ + '(' + '%r' % self.dn + ')') def __eq__(self, other): if not isinstance(other, self.__class__): return False if self.dn != other.dn: return False return True def __ne__(self, other): return not self==other ldaptor-0.0.43/ldaptor/config.py0000644000175000017500000001143210345073240014630 0ustar janjanimport os.path import ConfigParser from zope.interface import implements from ldaptor import interfaces from ldaptor.insensitive import InsensitiveString from ldaptor.protocols.ldap import distinguishedname class MissingBaseDNError(Exception): """Configuration must specify a base DN""" def __str__(self): return self.__doc__ class LDAPConfig(object): implements(interfaces.ILDAPConfig) baseDN = None identityBaseDN = None identitySearch = None def __init__(self, baseDN=None, serviceLocationOverrides=None, identityBaseDN=None, identitySearch=None): if baseDN is not None: baseDN = distinguishedname.DistinguishedName(baseDN) self.baseDN = baseDN self.serviceLocationOverrides = {} if serviceLocationOverrides is not None: for k,v in serviceLocationOverrides.items(): dn = distinguishedname.DistinguishedName(k) self.serviceLocationOverrides[dn]=v if identityBaseDN is not None: identityBaseDN = distinguishedname.DistinguishedName(identityBaseDN) self.identityBaseDN = identityBaseDN if identitySearch is not None: self.identitySearch = identitySearch def getBaseDN(self): if self.baseDN is not None: return self.baseDN cfg = loadConfig() try: return cfg.get('ldap', 'base') except (ConfigParser.NoOptionError, ConfigParser.NoSectionError): raise MissingBaseDNError def getServiceLocationOverrides(self): r = self._loadServiceLocationOverrides() r.update(self.serviceLocationOverrides) return r def _loadServiceLocationOverrides(self): serviceLocationOverride = {} cfg = loadConfig() for section in cfg.sections(): if section.lower().startswith('service-location '): base = section[len('service-location '):].strip() host = None if cfg.has_option(section, 'host'): host = cfg.get(section, 'host') if not host: host = None port = None if cfg.has_option(section, 'port'): port = cfg.get(section, 'port') if not port: port = None dn = distinguishedname.DistinguishedName(stringValue=base) serviceLocationOverride[dn]=(host, port) return serviceLocationOverride def copy(self, **kw): if 'baseDN' not in kw: kw['baseDN'] = self.baseDN if 'serviceLocationOverrides' not in kw: kw['serviceLocationOverrides'] = self.serviceLocationOverrides if 'identityBaseDN' not in kw: kw['identityBaseDN'] = self.identityBaseDN if 'identitySearch' not in kw: kw['identitySearch'] = self.identitySearch r = self.__class__(**kw) return r def getIdentityBaseDN(self): if self.identityBaseDN is not None: return self.identityBaseDN cfg = loadConfig() try: return cfg.get('authentication', 'identity-base') except (ConfigParser.NoOptionError, ConfigParser.NoSectionError): return self.getBaseDN() def getIdentitySearch(self, name): data = { 'name': name, } if self.identitySearch is not None: f = self.identitySearch % data else: cfg = loadConfig() try: f=cfg.get('authentication', 'identity-search', vars=data) except (ConfigParser.NoOptionError, ConfigParser.NoSectionError): f='(|(cn=%(name)s)(uid=%(name)s))' % data return f DEFAULTS = { 'samba': { 'use-lmhash': 'no', }, } CONFIG_FILES = [ '/etc/ldaptor/global.cfg', os.path.expanduser('~/.ldaptor/global.cfg'), ] __config = None def loadConfig(configFiles=None, reload=False): """ Load configuration file. """ global __config if __config is None or reload: x = ConfigParser.SafeConfigParser() x.optionxform = InsensitiveString for section, options in DEFAULTS.items(): x.add_section(section) for option, value in options.items(): x.set(section, option, value) if configFiles is None: configFiles = CONFIG_FILES x.read(configFiles) __config = x return __config def useLMhash(): """ Read configuration file if necessary and return whether to use LanMan hashes or not. """ cfg = loadConfig() return cfg.getboolean('samba', 'use-lmhash') ldaptor-0.0.43/ldaptor/U32.py0000644000175000017500000000732210344535643013750 0ustar janjan"""Utility library for handling 32-bit unsigner integers.""" # http://www.geocities.com/rozmanov/python/ """ From: "Dmitry Rozmanov" To: "Tommi Virtanen" Subject: Re: About your md4.py Hi. Year, I am thinking of this, but could not find time for this. Thanks for the link. But why? Consider it as a GPL for now if it is important. Regards. ---Dmitry. ----- Original Message ----- From: "Tommi Virtanen" To: "Dmitry Rozmanov" Sent: Tuesday, August 27, 2002 9:17 PM Subject: About your md4.py > Hi. Could you consider adding a license > in your U32.py and md4.py files? Here's > a quick reference: > > http://zooko.com/license_quick_ref.html > > -- > :(){ :|:&};: """ """ From: "Dmitry Rozmanov" To: "Tommi Virtanen" Subject: Re: About your md4.py Ok. Let it be LGPL. Use libs, soon I will modify them and post to the site. Regards. ---Dmitry. ----- Original Message ----- From: "Tommi Virtanen" To: "Dmitry Rozmanov" Sent: Wednesday, August 28, 2002 9:21 AM Subject: Re: About your md4.py > On Wed, Aug 28, 2002 at 02:56:25AM +0400, Dmitry Rozmanov wrote: > > Year, I am thinking of this, but could not find time for > > this. Thanks for the link. > > > > But why? > > > > Consider it as a GPL for now if it is important. > > Please include that information in the files themselves; > it would really help. Otherwise, all I have is this > email to point to. > > Oh, and please reconsider the actual license. For example, > I have an LGPL'ed library I need md4 in. If you choose GPL, > my library couldn't use your md4.py. > > -- > :(){ :|:&};: """ C = 0x1000000000L def norm(n): return n & 0xFFFFFFFFL class U32: v = 0L def __init__(self, value = 0): self.v = C + norm(abs(long(value))) def set(self, value = 0): self.v = C + norm(abs(long(value))) def __repr__(self): return hex(norm(self.v)) def __long__(self): return long(norm(self.v)) def __int__(self): return int(norm(self.v)) def __chr__(self): return chr(norm(self.v)) def __add__(self, b): r = U32() r.v = C + norm(self.v + b.v) return r def __sub__(self, b): r = U32() if self.v < b.v: r.v = C + norm(0x100000000L - (b.v - self.v)) else: r.v = C + norm(self.v - b.v) return r def __mul__(self, b): r = U32() r.v = C + norm(self.v * b.v) return r def __div__(self, b): r = U32() r.v = C + (norm(self.v) / norm(b.v)) return r def __mod__(self, b): r = U32() r.v = C + (norm(self.v) % norm(b.v)) return r def __neg__(self): return U32(self.v) def __pos__(self): return U32(self.v) def __abs__(self): return U32(self.v) def __invert__(self): r = U32() r.v = C + norm(~self.v) return r def __lshift__(self, b): r = U32() r.v = C + norm(self.v << b) return r def __rshift__(self, b): r = U32() r.v = C + (norm(self.v) >> b) return r def __and__(self, b): r = U32() r.v = C + norm(self.v & b.v) return r def __or__(self, b): r = U32() r.v = C + norm(self.v | b.v) return r def __xor__(self, b): r = U32() r.v = C + norm(self.v ^ b.v) return r def __not__(self): return U32(not norm(self.v)) def truth(self): return norm(self.v) def __cmp__(self, b): if norm(self.v) > norm(b.v): return 1 elif norm(self.v) < norm(b.v): return -1 else: return 0 def __nonzero__(self): return norm(self.v) ldaptor-0.0.43/bin/0000755000175000017500000000000010403234316012111 5ustar janjanldaptor-0.0.43/bin/ldaptor-ldap2passwd0000755000175000017500000000510310344540144015730 0ustar janjan#!/usr/bin/python import sys from ldaptor.protocols.ldap import ldapclient, ldif, distinguishedname, ldapconnector, ldapsyntax from ldaptor.protocols import pureber, pureldap from ldaptor import usage, ldapfilter, config from twisted.internet import protocol, reactor, defer def _cbSearch(obj): a={} for attr, vals in obj.items(): attr=str(attr) assert not a.has_key(attr) a[attr]=map(str, vals) print ':'.join(( a['uid'][0], 'x', a['uidNumber'][0], a['gidNumber'][0], a.get('gecos', a.get('cn', ['']))[0], a['homeDirectory'][0], a.get('loginShell', [''])[0], )) def search(client, baseDN, filt): e=ldapsyntax.LDAPEntry(client=client, dn=baseDN) d = e.search(filterObject=filt, attributes=['uid', 'uidNumber', 'gidNumber', 'gecos', 'cn', 'homeDirectory', 'loginShell', ], callback=_cbSearch) return d exitStatus=0 def error(fail): print >>sys.stderr, 'fail:', fail.getErrorMessage() global exitStatus exitStatus=1 def main(cfg, filter_text): try: baseDN = cfg.getBaseDN() except config.MissingBaseDNError, e: print >>sys.stderr, "%s: %s." % (sys.argv[0], e) sys.exit(1) filt = ldapfilter.parseFilter('(objectClass=posixAccount)') if filter_text is not None: filt = pureldap.LDAPFilter_and([ filt, ldapfilter.parseFilter(filter_text)]) c = ldapconnector.LDAPClientCreator(reactor, ldapclient.LDAPClient) d = c.connectAnonymously(dn=baseDN, overrides=cfg.getServiceLocationOverrides()) d.addCallback(search, baseDN, filt) d.addErrback(error) d.addBoth(lambda x: reactor.stop()) reactor.run() sys.exit(exitStatus) class MyOptions(usage.Options, usage.Options_service_location, usage.Options_base_optional): """LDAPtor command line search utility""" def parseArgs(self, filter=None): self.opts['filter'] = filter if __name__ == "__main__": try: opts = MyOptions() opts.parseOptions() except usage.UsageError, ue: sys.stderr.write('%s: %s\n' % (sys.argv[0], ue)) sys.exit(1) cfg = config.LDAPConfig(baseDN=opts['base'], serviceLocationOverrides=opts['service-location']) main(cfg, opts['filter']) ldaptor-0.0.43/bin/ldaptor-fetchschema0000755000175000017500000000343310344540144015762 0ustar janjan#!/usr/bin/python import sys from ldaptor.protocols.ldap import ldapclient, ldapconnector, fetchschema from ldaptor import usage, config from twisted.internet import reactor def _printResults(x): attributeTypes, objectClasses = x something = False for at in attributeTypes: print 'attributetype', at something = True if something: print for oc in objectClasses: print 'objectclass', oc exitStatus=0 def error(fail): print >>sys.stderr, 'fail:', fail.getErrorMessage() global exitStatus exitStatus=1 def main(cfg): try: baseDN = cfg.getBaseDN() except config.MissingBaseDNError, e: print >>sys.stderr, "%s: %s." % (sys.argv[0], e) sys.exit(1) c = ldapconnector.LDAPClientCreator(reactor, ldapclient.LDAPClient) d= c.connectAnonymously(dn=baseDN, overrides=cfg.getServiceLocationOverrides()) def _cbBound(client, baseDN): d=fetchschema.fetch(client, baseDN) return d d.addCallback(_cbBound, baseDN) d.addCallback(_printResults) d.addErrback(error) d.addBoth(lambda x: reactor.stop()) reactor.run() sys.exit(exitStatus) class MyOptions(usage.Options, usage.Options_service_location, usage.Options_base_optional): """LDAPtor command line schema fetching utility""" if __name__ == "__main__": from twisted.python import log log.startLogging(sys.stderr, setStdout=0) try: opts = MyOptions() opts.parseOptions() except usage.UsageError, ue: sys.stderr.write('%s: %s\n' % (sys.argv[0], ue)) sys.exit(1) cfg = config.LDAPConfig(baseDN=opts['base'], serviceLocationOverrides=opts['service-location']) main(cfg) ldaptor-0.0.43/bin/ldaptor-ldifdiff0000755000175000017500000000263410344540144015261 0ustar janjan#!/usr/bin/python import sys from ldaptor.protocols.ldap import ldif from ldaptor import usage, inmemory from twisted.internet import protocol, reactor, defer exitStatus=0 def error(fail): print >>sys.stderr, 'fail:', fail.getErrorMessage() global exitStatus exitStatus=1 def output(result, outputFile): outputFile.write(ldif.header()) for op in result: outputFile.write(op.asLDIF()) def main(filename1, filename2, outputFile): def _open(filename): f = open(filename) d = inmemory.fromLDIFFile(f) return d d = _open(filename1) def _gotDB1(db1, filename2): d = _open(filename2) d.addCallback(lambda db2: db1.diffTree(db2)) return d d.addCallback(_gotDB1, filename2) d.addCallback(output, outputFile) d.addErrback(error) d.addBoth(lambda x: reactor.stop()) reactor.run() sys.exit(exitStatus) class MyOptions(usage.Options, usage.Options_service_location, usage.Options_bind): """LDAPtor object rename utility""" def parseArgs(self, file1, file2): self.opts['file1'] = file1 self.opts['file2'] = file2 if __name__ == "__main__": try: config = MyOptions() config.parseOptions() except usage.UsageError, ue: sys.stderr.write('%s: %s\n' % (sys.argv[0], ue)) sys.exit(1) main(config.opts['file1'], config.opts['file2'], sys.stdout) ldaptor-0.0.43/bin/ldaptor-ldifpatch0000755000175000017500000000246110344540144015446 0ustar janjan#!/usr/bin/python import sys from ldaptor.protocols.ldap import ldif, ldifdelta from ldaptor import usage, inmemory from twisted.internet import protocol, reactor, defer exitStatus=0 def error(fail): print >>sys.stderr, 'fail:', fail.getErrorMessage() global exitStatus exitStatus=1 def output(tree, outputFile): outputFile.write(ldif.header()) def _write(node): outputFile.write(str(node)) tree.subtree(callback=_write) def main(dataFile, patchFile, outputFile): d = inmemory.fromLDIFFile(dataFile) def _gotDB(db, patchFile): patches = ldifdelta.fromLDIFFile(patchFile) # find the right entry to patch for p in patches: p.patch(db) return db d.addCallback(_gotDB, patchFile) d.addCallback(output, outputFile) d.addErrback(error) d.addBoth(lambda x: reactor.stop()) reactor.run() sys.exit(exitStatus) class MyOptions(usage.Options): """LDAPtor LDIF patching utility""" def parseArgs(self, data): self['data'] = data if __name__ == "__main__": try: config = MyOptions() config.parseOptions() except usage.UsageError, ue: sys.stderr.write('%s: %s\n' % (sys.argv[0], ue)) sys.exit(1) data = open(config['data']) main(data, sys.stdin, sys.stdout) ldaptor-0.0.43/bin/ldaptor-webui0000755000175000017500000000235610344540144014626 0ustar janjan#!/usr/bin/python from ldaptor.apps import webui from ldaptor.apps.webui import main from ldaptor import usage from ldaptor.checkers import LDAPBindingChecker from ldaptor.protocols.ldap.distinguishedname import DistinguishedName from twisted.internet import reactor from twisted.python import log from nevow import appserver class MyOptions(usage.Options): """LDAPtor Web User Interface""" def __init__(self, *args, **kwargs): usage.Options.__init__(self, *args, **kwargs) optParameters = ( ('http-port', None, '38980', # think "ldap and http" "listen on this port"), ) def postOptions_httpport(self): try: val = int(self.opts['http-port']) except ValueError: raise usage.UsageError, "%s value must be numeric" % 'http-port' self.opts['http-port'] = val if __name__ == '__main__': import sys try: opts = MyOptions() opts.parseOptions() except usage.UsageError, ue: sys.stderr.write('%s: %s\n' % (sys.argv[0], ue)) sys.exit(1) log.startLogging(sys.stderr, setStdout=0) resource = main.getResource() sit = appserver.NevowSite(resource) reactor.listenTCP(opts['http-port'], sit) reactor.run() ldaptor-0.0.43/bin/ldaptor-ldap2pdns0000755000175000017500000002137110344540144015400 0ustar janjan#!/usr/bin/python import sys from twisted.internet.defer import succeed, fail from twisted.internet import defer, reactor from twisted.internet.stdio import StandardIO from twisted.protocols.basic import LineReceiver from twisted.internet.protocol import ClientFactory from twisted.python.failure import Failure from twisted.python import log from ldaptor.protocols.ldap import ldapclient, distinguishedname, ldapconnector from ldaptor.protocols.ldap.ldapsyntax import LDAPEntry from ldaptor import usage, config from cStringIO import StringIO class ExitSentinel: pass class TooMuchQueued(Exception): """Too many requests queued already waiting for server""" def __str__(self): return self.__doc__ class PdnsPipeProtocol(LineReceiver): delimiter = '\n' state = 'start' ldapEntry = None ldapEntryFetchInProgress = False MAX_WAIT_QUEUE = 10 def __init__(self, ldapEntryFactory, dnsDomain): self.work=[] self.ldapEntryFactory = ldapEntryFactory self.waitingForLdapEntry = [] self.dnsDomain = dnsDomain def _cbLdapEntry(self, e): self.ldapEntryFetchInProgress = False assert self.ldapEntry is None self.ldapEntry = e while self.waitingForLdapEntry: d = self.waitingForLdapEntry.pop(0) d.callback(e) def _ebLdapEntry(self, reason): self.ldapEntryFetchInProgress = False waiters, self.waitingForLdapEntry = self.waitingForLdapEntry, [] for w in waiters: w.errback(reason) return reason def getLdapEntry(self): if self.ldapEntry is not None: return defer.succeed(self.ldapEntry) else: if len(self.waitingForLdapEntry) > self.MAX_WAIT_QUEUE: raise TooMuchQueued d = defer.Deferred() self.waitingForLdapEntry.append(d) if not self.ldapEntryFetchInProgress: self.ldapEntryFetchInProgress = True fetch = self.ldapEntryFactory(self) fetch.addCallback(self._cbLdapEntry) fetch.addErrback(self._ebLdapEntry) fetch.addErrback(log.err) return d def _doWork(self): while self.work: if isinstance(self.work[0][0], defer.Deferred): # end of done items, stop and wait for completions break else: done=self.work.pop(0) if done == [ExitSentinel]: # that's it, I'm outtahere assert not self.work reactor.stop() else: for line in done: self.sendLine(line) sys.stdout.flush() def completed(self, result, who): who[:]=result self._doWork() def failed(self, result, who): io = StringIO() result.printTraceback(file=io) who[:]=['LOG\t%s' % line for line in io.getvalue().splitlines()] \ + ['FAIL'] self._doWork() def do_start_HELO(self, rest): if rest=='1': self.state = 'main' return succeed(['OK\t%s' % sys.argv[0]]) else: return succeed(['FAIL']) def _gotA(self, results, qname, qclass, qtype, ident): r=[] for o in results: for ip in o.get('ipHostNumber', ()): r.append('\t'.join(('DATA', qname, qclass, qtype, '3600', ident, ip))) r.append('END') return r def question_A(self, qname,qclass,ident,ipAddress): ident='-1' if not qname.endswith('.'+self.dnsDomain): return succeed(['END']) cn=qname[:-len('.'+self.dnsDomain)] d = self.getLdapEntry() def _cb(e): d=e.search(filterText='(&(cn=%s)(ipHostNumber=*))'%cn, attributes=['ipHostNumber']) return d d.addCallback(_cb) d.addCallback(self._gotA, qname, qclass, 'A', ident) return d def question_ANY(self, qname,qclass,ident,ipAddress): if qname.endswith('.in-addr.arpa'): return self.question_PTR(qname,qclass,ident,ipAddress) else: return self.question_A(qname,qclass,ident,ipAddress) def _gotPTR(self, results, qname, qclass, qtype, ident): r=[] for o in results: for cn in o.get('cn', ()): r.append('\t'.join(('DATA', qname, qclass, qtype, '3600', ident, cn+'.'+self.dnsDomain+'.'))) r.append('END') return r def question_PTR(self, qname,qclass,ident,ipAddress): ident='-1' if not qname.endswith('.in-addr.arpa'): return succeed(['END']) octets=qname[:-len('.in-addr.arpa')].split('.') if len(octets)!=4: return succeed(['END']) octets.reverse() ip='.'.join(octets) d = self.getLdapEntry() def _cb(e): d=e.search(filterText='(ipHostNumber=%s)'%ip, attributes=['cn']) return d d.addCallback(_cb) d.addCallback(self._gotPTR, qname, qclass, 'PTR', ident) return d def do_main_Q(self, rest): try: qname,qclass,qtype,ident,ipAddress=rest.split('\t', 4) except ValueError: return succeed(['LOG\tInvalid question: %s' % repr(rest), 'END']) if qclass!='IN': return succeed(['LOG\tInvalid qclass: %s' % repr(qclass), 'END']) q=getattr(self, 'question_'+qtype, None) if q: return q(qname,qclass,ident,ipAddress) else: return succeed(['END']) def do_main_AXFR(self, rest): return succeed(['LOG\tRefusing AXFR', 'END']) def do_main_PING(self, rest): #TODO it's undocumented what I should be saying return succeed(['END']) def lineReceived(self, line): try: try: type,rest=line.split('\t', 1) except ValueError: type=line rest='' f=getattr(self, 'do_'+self.state+'_'+type, None) if f: d=f(rest) else: d=succeed(['LOG\tUnknown command %s in state %s' % (repr(type), self.state), 'END']) except: f=Failure() d=fail(f) l=[d] self.work.append(l) d.addCallback(self.completed, l) d.addErrback(self.failed, l) def connectionLost(self, reason=None): self.work.append([ExitSentinel]) self._doWork() def lostLDAPClient(self, client): assert self.ldapEntry is not None assert self.ldapEntry.client is client self.ldapEntry = None exitStatus = 0 def error(fail): print >>sys.stderr, 'fail:', fail.getErrorMessage() global exitStatus exitStatus=1 class MyLDAPClient(ldapclient.LDAPClient): def __init__(self, pdnsProto): ldapclient.LDAPClient.__init__(self) self.pdnsProto = pdnsProto def connectionLost(self, reason): ldapclient.LDAPClient.connectionLost(self, reason) self.pdnsProto.lostLDAPClient(self) def main(cfg, dnsDomain): try: baseDN = cfg.getBaseDN() except config.MissingBaseDNError, e: print >>sys.stderr, "%s: %s." % (sys.argv[0], e) sys.exit(1) def ldapEntryFactory(pdnsProto): c = ldapconnector.LDAPClientCreator( reactor, MyLDAPClient, pdnsProto=pdnsProto) d = c.connectAnonymously(dn=baseDN, overrides=cfg.getServiceLocationOverrides()) def _cb(client, baseDN): e = LDAPEntry(client=client, dn=baseDN) return e d.addCallback(_cb, baseDN) return d pdnsPipeProtocol = PdnsPipeProtocol(ldapEntryFactory, dnsDomain) reactor.addReader(StandardIO(pdnsPipeProtocol)) reactor.run() sys.exit(exitStatus) class MyOptions(usage.Options, usage.Options_service_location, usage.Options_base_optional): """LDAPtor PDNS pipe backend""" optParameters = ( ('dns-domain', None, 'example.com', "DNS domain name"), ) if __name__ == "__main__": try: opts = MyOptions() opts.parseOptions() except usage.UsageError, ue: sys.stderr.write('%s: %s\n' % (sys.argv[0], ue)) sys.exit(1) from twisted.python import log log.startLogging(sys.stderr, setStdout=0) cfg = config.LDAPConfig(baseDN=opts['base'], serviceLocationOverrides=opts['service-location']) main(cfg, opts['dns-domain']) ldaptor-0.0.43/bin/ldaptor-ldap2maradns0000755000175000017500000001431210344540144016056 0ustar janjan#!/usr/bin/python from ldaptor.protocols.ldap import distinguishedname, ldapconnector, ldapsyntax, ldapclient from ldaptor.protocols import pureber, pureldap from ldaptor import usage, ldapfilter, config, dns import sys from twisted.internet import protocol, defer, reactor def printIPAddress(name, ip): print 'A'+name+'.%|86400|'+ip def printPTR(name, ip): octets = ip.split('.') octets.reverse() octets.append('in-addr.arpa.') print 'P'+('.'.join(octets))+'|86400|'+name+'.%' class HostIPAddress: def __init__(self, host, ipAddress): self.host=host self.ipAddress=ipAddress def printZone(self, domain): print '# '+self.host.dn printIPAddress(self.host.name+'.'+domain, self.ipAddress) printPTR(self.host.name+'.'+domain, self.ipAddress) def __repr__(self): return (self.__class__.__name__ +'(' +'host=%r, ' % self.host.name +'ipAddress=%s' % repr(self.ipAddress) +')') class Host: def __init__(self, dn, name, ipAddresses): self.dn=dn self.name=name self.ipAddresses=[HostIPAddress(self, ip) for ip in ipAddresses] def __repr__(self): return (self.__class__.__name__ +'(' +'dn=%s, ' % repr(self.dn) +'name=%s, ' % repr(self.name) +'ipAddresses=%s' % repr(self.ipAddresses) +')') class Net: def __init__(self, dn, name, address, mask): self.dn=dn self.name=name self.address=address self.mask=mask def isInNet(self, ipAddress): net = dns.aton(self.address) mask = dns.aton(self.mask) ip = dns.aton(ipAddress) if ip&mask == net: return 1 return 0 def printZone(self): print '#'+self.dn printIPAddress(self.name, self.address) printPTR(self.name, self.address) ip = dns.aton(self.address) mask = dns.aton(self.mask) ipmask = dns.ntoa(mask) broadcast = dns.ntoa(ip|~mask) printIPAddress('netmask.'+self.name, ipmask) printIPAddress('broadcast.'+self.name, broadcast) printPTR('broadcast.'+self.name, broadcast) def __repr__(self): return (self.__class__.__name__ +'(' +'dn=%s, ' % repr(self.dn) +'name=%s, ' % repr(self.name) +'address=%s, ' % repr(self.address) +'mask=%s' % repr(self.mask) +')') exitStatus=0 def error(fail): print >>sys.stderr, 'fail:', str(fail) #.getErrorMessage() global exitStatus exitStatus=1 def only(e, attrName): assert len(e[attrName])==1, \ "object %s attribute %r has multiple values: %s" \ % (e.dn, attrName, e[attrName]) for val in e[attrName]: return val def getNets(e, filter): filt=pureldap.LDAPFilter_and(value=( pureldap.LDAPFilter_present('cn'), pureldap.LDAPFilter_present('ipNetworkNumber'), pureldap.LDAPFilter_present('ipNetmaskNumber'), )) if filter: filt = pureldap.LDAPFilter_and(value=(filter, filt)) d = e.search(filterObject=filt, attributes=['cn', 'ipNetworkNumber', 'ipNetmaskNumber', ]) def _cbGotNets(nets): r = [] for e in nets: net = Net(str(e.dn), str(only(e, 'cn')), str(only(e, 'ipNetworkNumber')), str(only(e, 'ipNetmaskNumber'))) net.printZone() r.append(net) return r d.addCallback(_cbGotNets) return d def getHosts(nets, e, filter): filt=pureldap.LDAPFilter_equalityMatch(attributeDesc=pureldap.LDAPAttributeDescription('objectClass'), assertionValue=pureber.BEROctetString('ipHost')) if filter: filt = pureldap.LDAPFilter_and(value=(filter, filt)) def _cbGotHost(e): host = Host(str(e.dn), str(only(e, 'cn')), map(str, e['ipHostNumber'])) for hostIP in host.ipAddresses: parent=None for net in nets: if net.isInNet(hostIP.ipAddress): parent=net break if parent: hostIP.printZone(parent.name) else: sys.stderr.write("IP address %s is in no net, discarding.\n" % hostIP) d = e.search(filterObject=filt, attributes=['ipHostNumber', 'cn'], callback=_cbGotHost) return d def cbConnected(client, cfg, filter): e = ldapsyntax.LDAPEntryWithClient(client, cfg.getBaseDN()) d = getNets(e, filter) d.addCallback(getHosts, e, filter) def unbind(r, e): e.client.unbind() return r d.addCallback(unbind, e) return d def main(cfg, filter_text): from twisted.python import log log.startLogging(sys.stderr, setStdout=0) try: baseDN = cfg.getBaseDN() except config.MissingBaseDNError, e: print >>sys.stderr, "%s: %s." % (sys.argv[0], e) sys.exit(1) if filter_text is not None: filt = ldapfilter.parseFilter(filter_text) else: filt = None c=ldapconnector.LDAPClientCreator(reactor, ldapclient.LDAPClient) d = c.connectAnonymously( baseDN, overrides=cfg.getServiceLocationOverrides()) d.addCallback(cbConnected, cfg, filt) d.addErrback(error) d.addBoth(lambda x: reactor.stop()) reactor.run() sys.exit(exitStatus) class MyOptions(usage.Options, usage.Options_service_location, usage.Options_base_optional): """LDAPtor maradns zone file exporter""" def parseArgs(self, filter=None): self.opts['filter'] = filter if __name__ == "__main__": import sys try: opts = MyOptions() opts.parseOptions() except usage.UsageError, ue: sys.stderr.write('%s: %s\n' % (sys.argv[0], ue)) sys.exit(1) cfg = config.LDAPConfig(baseDN=opts['base'], serviceLocationOverrides=opts['service-location']) main(cfg, opts['filter']) ldaptor-0.0.43/bin/ldaptor-ldap2dhcpconf0000755000175000017500000003267510344540144016231 0ustar janjan#!/usr/bin/python #host fantasia { # dhcp-client-identifier # hardware ethernet 08:00:07:26:c0:a5; # fixed-address fantasia.fugue.com; #} #subnet 1.2.3.0 netmask 255.255.255.0 { # option routers 1.2.3.4; # range 1.2.3.100 1.2.3.200; # option domain-name "foo.bar.example.com"; #} #shared-network "foo" { #} from ldaptor.protocols.ldap import ldapclient, ldapconnector, ldapsyntax from ldaptor.protocols import pureber, pureldap from ldaptor import usage, ldapfilter, config from twisted.internet import reactor from socket import inet_aton, inet_ntoa import sets def my_aton_octets(ip): s=inet_aton(ip) octets=map(None, s) n=0L for o in octets: n=n<<8 n+=ord(o) return n def my_aton_numbits(num): n=0L while num>0: n>>=1 n |= 2**31 num-=1 return n def my_aton(ip): try: i=int(ip) except ValueError: return my_aton_octets(ip) else: return my_aton_numbits(i) def my_ntoa(n): s=( chr((n>>24)&0xFF) + chr((n>>16)&0xFF) + chr((n>>8)&0xFF) + chr(n&0xFF) ) ip=inet_ntoa(s) return ip class HostIPAddress: def __init__(self, host, ipAddress): self.host=host self.ipAddress=ipAddress def printDHCP(self, domain, prefix=''): def output(): yield '# %s' % self.host.dn yield 'host %s.%s {' % (self.host.name, domain) for mac in self.host.macAddresses: yield '\thardware ethernet %s;' % mac yield '\tfixed-address %s;' % self.ipAddress if self.host.bootFile is not None: # TODO quote bootFile yield '\tfilename "%s";' % self.host.bootFile yield '}' print '\n'.join([prefix+line for line in output()]) def __repr__(self): return (self.__class__.__name__ +'(' +'host=%s, ' % id(self.host) +'ipAddress=%s' % repr(self.ipAddress) +')') class Group: def __init__(self, dn, bootFile=None): self.dn = dn self.bootFile = bootFile self.hosts = sets.Set() def addHost(self, host): if host.group is not None: print >>sys.stderr, ( 'Host %s is in two groups: %r and %r' % (host.dn, host.group, self)) else: host.group = self self.hosts.add(host) def printDHCP(self, domain, addrs, prefix=''): addresses = sets.Set([addr for host in self.hosts for addr in host.ipAddresses]) addresses.intersection_update(addrs) addrs.difference_update(addresses) if addresses: print prefix+'# '+str(self.dn) print prefix+'group {' if self.bootFile is not None: # TODO quote bootFile print prefix+'\tfilename "%s";' % self.bootFile for addr in addresses: addr.printDHCP(domain, prefix=prefix+'\t') print prefix+'}' class Host: group = None def __init__(self, dn, name, ipAddresses, macAddresses=(), bootFile=None): self.dn=dn self.name=name self.ipAddresses=[HostIPAddress(self, ip) for ip in ipAddresses] self.macAddresses=macAddresses self.bootFile = bootFile def __repr__(self): return (self.__class__.__name__ +'(' +'dn=%s, ' % repr(self.dn) +'name=%s, ' % repr(self.name) +'ipAddresses=%s, ' % repr(self.ipAddresses) +'macAddresses=%s, ' % repr(self.macAddresses) +'bootFile=%s' % repr(self.bootFile) +')') class Net: def __init__(self, dn, name, address, mask, routers=(), dhcpRanges=(), winsServers=(), domainNameServers=(), ): self.dn=dn self.name=name self.address=address self.mask=mask self.routers=routers self.dhcpRanges=dhcpRanges self.winsServers=winsServers self.domainNameServers=domainNameServers self.hosts=[] def isInNet(self, ipAddress): net = my_aton(self.address) mask = my_aton(self.mask) ip = my_aton(ipAddress) if ip&mask == net: return 1 return 0 def addHost(self, host): assert self.isInNet(host.ipAddress) self.hosts.append(host) def printDHCP(self, domain, prefix=''): nm = self.mask nm = my_aton(nm) nm = my_ntoa(nm) r = ['# %s' % self.dn, 'subnet %s netmask %s {' % (self.address, nm), '\toption domain-name "%s.%s";' % (self.name, domain)] if self.routers: r.append('\toption routers %s;' % (', '.join(self.routers))) for dhcpRange in self.dhcpRanges: r.append('\trange %s;' % dhcpRange) if self.winsServers: r.append('\toption netbios-name-servers %s;' % (', '.join(self.winsServers))) if self.domainNameServers: r.append('\toption domain-name-servers %s;' % (', '.join(self.domainNameServers))) r.append('}') print '\n'.join([prefix+line for line in r]) addrs = sets.Set() for addr in self.hosts: addrs.add(addr) for addr in self.hosts: g = addr.host.group if g is not None: g.printDHCP(self.name+'.'+domain, addrs, prefix=prefix) while addrs: addr = addrs.pop() addr.printDHCP(self.name+'.'+domain, prefix=prefix) def __repr__(self): return (self.__class__.__name__ +'(' +'dn=%s, ' % repr(self.dn) +'name=%s, ' % repr(self.name) +'address=%s, ' % repr(self.address) +'mask=%s' % repr(self.mask) +')') class SharedNet: def __init__(self, name): self.name=name self.nets=[] def addNet(self, net): self.nets.append(net) def printDHCP(self, domain): print 'shared-network "%s" {' % self.name for net in self.nets: net.printDHCP(domain, prefix='\t') print '}' print def _cbGetGroups(entries, hosts): dnToHost = {} for host in hosts: assert host.dn not in dnToHost dnToHost[host.dn] = host for e in entries: group = Group(dn=e.dn, bootFile=only(e, 'bootFile', None)) for member in e.get('member', []): host = dnToHost.get(member, None) if host is not None: group.addHost(host) return hosts def getGroups(hosts, e, filter): """Add group info to hosts.""" def buildFilter(hosts): for host in hosts: f = pureldap.LDAPFilter_equalityMatch( attributeDesc=pureldap.LDAPAttributeDescription('member'), assertionValue=pureber.BEROctetString(str(host.dn))) yield f filt=pureldap.LDAPFilter_and(value=( # the only reason we do groups is for the bootFile, # so require one to be present pureldap.LDAPFilter_present('bootFile'), pureldap.LDAPFilter_or(value=list(buildFilter(hosts))), )) if filter: filt = pureldap.LDAPFilter_and(value=(filter, filt)) d = e.search(filterObject=filt, attributes=['member', 'bootFile']) d.addCallback(_cbGetGroups, hosts) return d def haveHosts(hosts, e, filt, nets, sharedNets, dnsDomain): d = getGroups(hosts, e, filt) d.addCallback(haveGroups, nets, sharedNets, dnsDomain) return d def haveGroups(hosts, nets, sharedNets, dnsDomain): for host in hosts: for hostIP in host.ipAddresses: parent=None for net in nets + reduce(lambda x,y: x+y, [x.nets for x in sharedNets.values()], []): if net.isInNet(hostIP.ipAddress): parent=net break if parent: parent.addHost(hostIP) else: sys.stderr.write("IP address %s is in no net, discarding.\n" % hostIP) for net in sharedNets.values(): net.printDHCP(dnsDomain) for net in nets: net.printDHCP(dnsDomain) class _NO_DEFAULT(object): pass def only(e, attr, default=_NO_DEFAULT): val = e.get(attr, _NO_DEFAULT) if val is _NO_DEFAULT: if default is not _NO_DEFAULT: return default else: raise RuntimeError("object %s does not have attribute %r." % (e.dn, attr)) else: if len(val)!=1: raise RuntimeError("object %s attribute %r has multiple values: %s" % (e.dn, attr, val)) for item in val: return item def _cbGetHosts(entries): hosts = [] for e in entries: cn = only(e, 'cn') hosts.append(Host(str(e.dn), str(cn), map(str, e['ipHostNumber']), map(str, e.get('macAddress', ())), bootFile=only(e, 'bootFile', default=None), )) return hosts def getHosts(e, filter): filt=pureldap.LDAPFilter_and(value=( pureldap.LDAPFilter_present('cn'), pureldap.LDAPFilter_present('ipHostNumber'), )) if filter: filt = pureldap.LDAPFilter_and(value=(filter, filt)) d = e.search(filterObject=filt, attributes=['cn', 'ipHostNumber', 'macAddress', 'bootFile', ]) d.addCallback(_cbGetHosts) return d def haveNets(data, e, baseDN, filt, dnsDomain): nets, sharedNets = data d = getHosts(e, filt) d.addCallback(haveHosts, e, filt, nets, sharedNets, dnsDomain) return d def _cbGetNets(entries): sharedNetworks = {} nets = [] for e in entries: cn=only(e, 'cn') ipNetworkNumber=only(e, 'ipNetworkNumber') ipNetmaskNumber=only(e, 'ipNetmaskNumber') net = Net(e.dn, cn, ipNetworkNumber, ipNetmaskNumber, routers=e.get('router', ()), dhcpRanges=e.get('dhcpRange', ()), winsServers=e.get('winsServer', ()), domainNameServers=e.get('domainNameServer', ()), ) if e.has_key('sharedNetworkName'): name = only(e, 'sharedNetworkName') if not sharedNetworks.has_key(name): sharedNetworks[name]=SharedNet(name) sharedNetworks[name].addNet(net) else: nets.append(net) return (nets, sharedNetworks) def getNets(e, filter): filt=pureldap.LDAPFilter_and(value=( pureldap.LDAPFilter_present('cn'), pureldap.LDAPFilter_present('ipNetworkNumber'), pureldap.LDAPFilter_present('ipNetmaskNumber'), )) if filter: filt = pureldap.LDAPFilter_and(value=(filter, filt)) d = e.search(filterObject=filt, attributes=['cn', 'ipNetworkNumber', 'ipNetmaskNumber', 'router', 'dhcpRange', 'winsServer', 'domainNameServer', 'sharedNetworkName']) d.addCallback(_cbGetNets) return d def search(client, baseDN, filter, dnsDomain): e=ldapsyntax.LDAPEntry(client=client, dn=baseDN) d = getNets(e, filter) d.addCallback(haveNets, e, baseDN, filter, dnsDomain) return d exitStatus=0 def error(fail): print >>sys.stderr, 'fail:', fail.getErrorMessage() global exitStatus exitStatus=1 def main(cfg, filter_text, dnsDomain): try: baseDN = cfg.getBaseDN() except config.MissingBaseDNError, e: print >>sys.stderr, "%s: %s." % (sys.argv[0], e) sys.exit(1) from twisted.python import log log.startLogging(sys.stderr, setStdout=0) if filter_text is not None: filt = ldapfilter.parseFilter(filter_text) else: filt = None c = ldapconnector.LDAPClientCreator(reactor, ldapclient.LDAPClient) d = c.connectAnonymously(dn=baseDN, overrides=cfg.getServiceLocationOverrides()) d.addCallback(search, baseDN, filt, dnsDomain) d.addErrback(error) d.addBoth(lambda x: reactor.stop()) reactor.run() sys.exit(exitStatus) class MyOptions(usage.Options, usage.Options_service_location, usage.Options_base_optional): """LDAPtor dhcpd config file exporter""" optParameters = ( ('dns-domain', None, 'example.com', "DNS domain name"), ) def parseArgs(self, filter=None): self.opts['filter'] = filter if __name__ == "__main__": import sys try: opts = MyOptions() opts.parseOptions() except usage.UsageError, ue: sys.stderr.write('%s: %s\n' % (sys.argv[0], ue)) sys.exit(1) cfg = config.LDAPConfig(baseDN=opts['base'], serviceLocationOverrides=opts['service-location']) main(cfg, opts['filter'], opts['dns-domain'], ) ldaptor-0.0.43/bin/ldaptor-rename0000755000175000017500000000454010344540144014757 0ustar janjan#!/usr/bin/python import sys, os, getpass from ldaptor.protocols.ldap import ldapclient, ldif, ldapsyntax, distinguishedname, ldapconnector from ldaptor.protocols import pureber, pureldap from ldaptor import usage, config from twisted.internet import protocol, reactor, defer def move(client, fromDN, toDN): e = ldapsyntax.LDAPEntry(client=client, dn=fromDN) d = e.move(toDN) return d exitStatus=0 def error(fail): print >>sys.stderr, 'fail:', fail.getErrorMessage() global exitStatus exitStatus=1 def main(cfg, fromDN, toDN, binddn, bindPassword): fromDN = distinguishedname.DistinguishedName(stringValue=fromDN) toDN = distinguishedname.DistinguishedName(stringValue=toDN) c = ldapconnector.LDAPClientCreator(reactor, ldapclient.LDAPClient) d = c.connect(dn=fromDN, overrides=cfg.getServiceLocationOverrides()) def _bind(proto, binddn, bindPassword): if binddn: pwd = bindPassword if pwd is None: pwd = getpass.getpass('Password for %s: ' % binddn) d=proto.bind(binddn, pwd) else: d=proto.bind() d.addCallback(lambda _: proto) return d d.addCallback(_bind, binddn, bindPassword) d.addCallback(move, fromDN, toDN) d.addErrback(error) d.addBoth(lambda x: reactor.stop()) reactor.run() sys.exit(exitStatus) class MyOptions(usage.Options, usage.Options_service_location, usage.Options_bind): """LDAPtor object rename utility""" def parseArgs(self, fromDN, toDN): self.opts['from'] = fromDN self.opts['to'] = toDN if __name__ == "__main__": try: opts = MyOptions() opts.parseOptions() except usage.UsageError, ue: sys.stderr.write('%s: %s\n' % (sys.argv[0], ue)) sys.exit(1) from twisted.python import log log.startLogging(sys.stderr, setStdout=0) cfg = config.LDAPConfig(serviceLocationOverrides=opts['service-location']) bindPassword=None if opts['bind-auth-fd']: f=os.fdopen(opts['bind-auth-fd']) bindPassword=f.readline() assert bindPassword[-1]=='\n' bindPassword=bindPassword[:-1] f.close() main(cfg, opts['from'], opts['to'], opts['binddn'], bindPassword) ldaptor-0.0.43/bin/ldaptor-passwd0000755000175000017500000001041310344540144015005 0ustar janjan#!/usr/bin/python import sys, getpass from ldaptor.protocols.ldap import ldapclient, distinguishedname, ldapconnector, ldapsyntax from ldaptor.protocols import pureber, pureldap from ldaptor import usage, generate_password from twisted.internet import defer, reactor, protocol class PasswdClient(ldapclient.LDAPClient): def connectionMade(self): if self.factory.binddn: pwd = self.factory.bindPassword if pwd is None: pwd = getpass.getpass('Password for %s: ' \ % self.factory.binddn) d=self.bind(self.factory.binddn, pwd) else: d=self.bind() d.addCallbacks(self._handle_bind_success, self._handle_bind_fail) def _report_ldap_error(self, fail): print >>sys.stderr, "fail:", fail.getErrorMessage() global exitStatus exitStatus=1 return fail def getPassword(self, dn): if not self.factory.generatePasswords: pwd=getpass.getpass('NEW Password for %s: ' % dn) return defer.succeed((pwd,)) else: return generate_password.generate(reactor) def _handle_bind_success(self, x): l=[] for dn in self.factory.dnlist: d=self.getPassword(dn) d.addCallbacks(callback=self._got_password, callbackArgs=(dn,), errback=self._report_pwgen_error) l.append(d) dl=defer.DeferredList(l) dl.addBoth(lambda x, f=self.transport.loseConnection: f()) dl.addErrback(defer.logError) dl.addBoth(lambda x, f=reactor.stop: f()) return x def _handle_bind_fail(self, fail): self._report_ldap_error(fail) self.transport.loseConnection() reactor.stop() def _report_new_password(self, dummy, dn, password): if self.factory.generatePasswords: print dn, password def _got_password(self, password, dn): assert len(password)==1 password=password[0] o=ldapsyntax.LDAPEntry(client=self, dn=dn) d=o.setPassword(newPasswd=password) d.addCallbacks(callback=self._report_new_password, callbackArgs=(dn, password), errback=self._report_ldap_error) return d def _report_pwgen_error(self, fail): fail.trap(PwgenException) print >>sys.stderr, 'pwgen:', fail.getErrorMessage() return fail class PasswdClientFactory(protocol.ClientFactory): protocol = PasswdClient def __init__(self, binddn, bindPassword=None, dnlist=(), generatePasswords=0): self.binddn = binddn self.bindPassword = bindPassword self.dnlist=dnlist self.generatePasswords=generatePasswords exitStatus=0 class MyOptions(usage.Options, usage.Options_service_location, usage.Options_bind_mandatory): """LDAPtor command line password change utility""" synopsis = "Usage: %s --binddn=DN [OPTION..] [DN..]" % sys.argv[0] optFlags = [ ('generate', None, 'Generate random passwords'), ] def parseArgs(self, *dnlist): if not dnlist: dnlist=(self.opts['binddn'],) self.opts['dnlist'] = dnlist if __name__ == "__main__": import sys, os from twisted.python import log log.startLogging(sys.stderr, setStdout=0) try: config = MyOptions() config.parseOptions() except usage.UsageError, ue: print >>sys.stderr, '%s:'%sys.argv[0], ue sys.exit(1) bindPassword=None if config.opts['bind-auth-fd']: f=os.fdopen(config.opts['bind-auth-fd']) bindPassword=f.readline() assert bindPassword[-1]=='\n' bindPassword=bindPassword[:-1] f.close() s=PasswdClientFactory(dnlist=config.opts['dnlist'], binddn=config.opts['binddn'], bindPassword=bindPassword, generatePasswords=config.opts['generate'], ) dn = distinguishedname.DistinguishedName(stringValue=config.opts['binddn']) c=ldapconnector.LDAPConnector(reactor, dn, s, overrides=config.opts['service-location']) c.connect() reactor.run() sys.exit(exitStatus) ldaptor-0.0.43/bin/ldaptor-namingcontexts0000755000175000017500000000360410344540144016551 0ustar janjan#!/usr/bin/python import sys from ldaptor.protocols.ldap import ldapclient, ldapsyntax from ldaptor.protocols import pureber, pureldap from ldaptor import ldapfilter from twisted.internet.defer import Deferred, DeferredList from twisted.internet import protocol, reactor class Search(ldapclient.LDAPClient): def connectionMade(self): d=self.bind() d.addCallback(self._handle_bind_success) def _printResults(self, result, host): for context in result['namingContexts']: print '%s\t%s' % (host, context) def _handle_bind_success(self, x): matchedDN, serverSaslCreds = x o=ldapsyntax.LDAPEntry(client=self, dn='') d=o.search(filterText='(objectClass=*)', scope=pureldap.LDAP_SCOPE_baseObject, attributes=['namingContexts'], callback=(lambda x: self._printResults(x, self.factory.server))) d.chainDeferred(self.factory.deferred) class SearchFactory(protocol.ClientFactory): protocol = Search def __init__(self, server, deferred): self.server=server self.deferred=deferred def clientConnectionFailed(self, connector, reason): self.deferred.errback(None) exitStatus = 0 def errback(data): print "ERROR:", data.getErrorMessage() global exitStatus exitStatus=1 def main(servers): l = [] for server in servers: d=Deferred() l.append(d) s=SearchFactory(server, d) reactor.connectTCP(server, 389, s) d.addErrback(errback) dl = DeferredList(l) dl.addBoth(lambda x: reactor.stop()) reactor.run() sys.exit(exitStatus) if __name__ == "__main__": if not sys.argv[1:]: print >>sys.stderr, '%s: usage:' % sys.argv[0] print >>sys.stderr, ' %s HOST..' % sys.argv[0] else: main(sys.argv[1:]) ldaptor-0.0.43/bin/ldaptor-search0000755000175000017500000000361510344540144014757 0ustar janjan#!/usr/bin/python import sys from ldaptor.protocols.ldap import ldapclient, ldif, ldapsyntax, ldapconnector from ldaptor.protocols import pureber, pureldap from ldaptor import usage, ldapfilter, config from twisted.internet import protocol, reactor, defer def printResults(o): sys.stdout.write(str(o)) def search(client, baseDN, filter_text, attributes): o=ldapsyntax.LDAPEntry(client=client, dn=baseDN) d=o.search(filterText=filter_text, attributes=attributes, callback=printResults) return d exitStatus=0 def error(fail): print >>sys.stderr, 'fail:', fail.getErrorMessage() global exitStatus exitStatus=1 def main(cfg, filter_text, attributes): try: baseDN = cfg.getBaseDN() except config.MissingBaseDNError, e: print >>sys.stderr, "%s: %s." % (sys.argv[0], e) sys.exit(1) c = ldapconnector.LDAPClientCreator(reactor, ldapclient.LDAPClient) d = c.connectAnonymously(dn=baseDN, overrides=cfg.getServiceLocationOverrides()) d.addCallback(search, baseDN, filter_text, attributes) d.addErrback(error) d.addBoth(lambda x: reactor.stop()) reactor.run() sys.exit(exitStatus) class MyOptions(usage.Options, usage.Options_service_location, usage.Options_base_optional): """LDAPtor command line search utility""" def parseArgs(self, filter, *attributes): self.opts['filter'] = filter self.opts['attributes'] = attributes if __name__ == "__main__": try: opts = MyOptions() opts.parseOptions() except usage.UsageError, ue: sys.stderr.write('%s: %s\n' % (sys.argv[0], ue)) sys.exit(1) cfg = config.LDAPConfig(baseDN=opts['base'], serviceLocationOverrides=opts['service-location']) main(cfg, opts['filter'], opts['attributes']) ldaptor-0.0.43/bin/ldaptor-find-server0000755000175000017500000000307010344540144015731 0ustar janjan#!/usr/bin/python import sys from ldaptor.protocols.ldap.distinguishedname import DistinguishedName from twisted.internet.defer import DeferredList from twisted.internet import reactor from twisted.protocols import dns from twisted.names import client def printAnswer(answers, dn): for rrlist in answers: for rr in rrlist: assert isinstance(rr.payload, dns.Record_SRV) print '%s\tpri=%d weight=%d %s:%d' % (dn, rr.payload.priority, rr.payload.weight, rr.payload.target, rr.payload.port, ) return answers exitStatus = 0 def errback(data): print "ERROR:", data.getErrorMessage() global exitStatus exitStatus=1 def main(dns): l = [] resolver = client.Resolver('/etc/resolv.conf') for dnString in dns: dn=DistinguishedName(stringValue=dnString) domain=dn.getDomainName() d = resolver.lookupService('_ldap._tcp.%s' % domain) l.append(d) d.addCallback(printAnswer, dnString) d.addErrback(errback) dl = DeferredList(l) dl.addBoth(lambda dummy: reactor.callLater(0, reactor.stop)) reactor.run() sys.exit(exitStatus) if __name__ == "__main__": if not sys.argv[1:]: print >>sys.stderr, '%s: usage:' % sys.argv[0] print >>sys.stderr, ' %s DN..' % sys.argv[0] else: main(sys.argv[1:]) ldaptor-0.0.43/bin/ldaptor-getfreenumber0000755000175000017500000000331210344540144016336 0ustar janjan#!/usr/bin/python import sys from ldaptor.protocols.ldap import ldapclient, ldapsyntax, ldapconnector from ldaptor import usage, numberalloc, config from twisted.internet import reactor exitStatus=0 def error(fail): print fail print >>sys.stderr, 'fail:', fail.getErrorMessage() global exitStatus exitStatus=1 def main(cfg): try: baseDN = cfg.getBaseDN() except config.MissingBaseDNError, e: print >>sys.stderr, "%s: %s." % (sys.argv[0], e) sys.exit(1) c = ldapconnector.LDAPClientCreator(reactor, ldapclient.LDAPClient) d= c.connectAnonymously(dn=baseDN, overrides=cfg.getServiceLocationOverrides()) def _cbBound(client, baseDN): o=ldapsyntax.LDAPEntry(client=client, dn=baseDN) return numberalloc.getFreeNumber(o, 'uidNumber', min=1000) d.addCallback(_cbBound, baseDN) d.addCallback(lambda num: sys.stdout.write('%s\n' % repr(num))) d.addErrback(error) d.addBoth(lambda x: reactor.stop()) reactor.run() sys.exit(exitStatus) class MyOptions(usage.Options, usage.Options_service_location, usage.Options_base_optional): """LDAPtor command line search utility""" if __name__ == "__main__": from twisted.python import log log.startLogging(sys.stderr, setStdout=0) try: opts = MyOptions() opts.parseOptions() except usage.UsageError, ue: sys.stderr.write('%s: %s\n' % (sys.argv[0], ue)) sys.exit(1) cfg = config.LDAPConfig( baseDN=opts['base'], serviceLocationOverrides=opts['service-location']) main(cfg) ldaptor-0.0.43/bin/ldaptor-ldap2dnszones0000755000175000017500000002435710344540144016306 0ustar janjan#!/usr/bin/python from ldaptor.protocols.ldap import distinguishedname, ldapconnector, ldapsyntax, ldapclient from ldaptor.protocols import pureber, pureldap from ldaptor import usage, ldapfilter, config, dns import sys, os, socket from twisted.internet import protocol, defer, reactor def formatIPAddress(name, ip): return '%s\tIN A\t%s\n' % (name, ip) def formatPTR(name, ip): octets = ip.split('.') octets.reverse() octets.append('in-addr.arpa.') return '%s\tIN PTR\t%s.\n' % ('.'.join(octets), name) class HostIPAddress: def __init__(self, host, ipAddress): self.host=host self.ipAddress=ipAddress def getForward(self, domain): return (('; %s\n' % self.host.dn) + formatIPAddress(self.host.name+'.'+domain, self.ipAddress)) def getReverse(self, domain): return (('; %s\n' % self.host.dn) + formatPTR(self.host.name+'.'+domain, self.ipAddress)) def __repr__(self): return (self.__class__.__name__ +'(' +'host=%r, ' % self.host.name +'ipAddress=%s' % repr(self.ipAddress) +')') class Host: def __init__(self, dn, name, ipAddresses): self.dn=dn self.name=name self.ipAddresses=[HostIPAddress(self, ip) for ip in ipAddresses] def __repr__(self): return (self.__class__.__name__ +'(' +'dn=%s, ' % repr(self.dn) +'name=%s, ' % repr(self.name) +'ipAddresses=%s' % repr(self.ipAddresses) +')') class Net: reverseZone = None def __init__(self, dn, name, address, mask): self.dn=dn self.name=name self.address=address self.mask=mask def isInNet(self, ipAddress): try: net = dns.aton(self.address) mask = dns.aton(self.mask) ip = dns.aton(ipAddress) except socket.error: # no need to log here, higher levels will log a warning # when they see the address is in no net return False if ip&mask == net: return True return False def getForward(self): ip = dns.aton(self.address) mask = dns.aton(self.mask) ipmask = dns.ntoa(mask) broadcast = dns.ntoa(ip|~mask) return (('; %s\n' % self.dn) + formatIPAddress(self.name, self.address) + formatIPAddress('netmask.'+self.name, ipmask) + formatIPAddress('broadcast.'+self.name, broadcast)) def getReverse(self, domain): ip = dns.aton(self.address) mask = dns.aton(self.mask) broadcast = dns.ntoa(ip|~mask) return (('; %s\n' % self.dn) + formatPTR(self.name+'.'+domain, self.address) + formatPTR('broadcast.'+self.name+'.'+domain, broadcast)) def __repr__(self): return (self.__class__.__name__ +'(' +'dn=%s, ' % repr(self.dn) +'name=%s, ' % repr(self.name) +'address=%s, ' % repr(self.address) +'mask=%s' % repr(self.mask) +')') exitStatus=0 def error(fail): print >>sys.stderr, 'fail:', str(fail) #.getErrorMessage() global exitStatus exitStatus=1 def only(e, attrName): assert len(e[attrName])==1, \ "object %s attribute %r has multiple values: %s" \ % (e.dn, attrName, e[attrName]) for val in e[attrName]: return val def getNets(e, domain, forward, reverse, filter): filt=pureldap.LDAPFilter_and(value=( pureldap.LDAPFilter_present('cn'), pureldap.LDAPFilter_present('ipNetworkNumber'), pureldap.LDAPFilter_present('ipNetmaskNumber'), )) if filter: filt = pureldap.LDAPFilter_and(value=(filter, filt)) d = e.search(filterObject=filt, attributes=['cn', 'ipNetworkNumber', 'ipNetmaskNumber', ]) def _cbGotNets(nets, forward, reverse): r = [] for e in nets: net = Net(str(e.dn), str(only(e, 'cn')), str(only(e, 'ipNetworkNumber')), str(only(e, 'ipNetmaskNumber'))) print >>forward, net.getForward() for data in reverse: ip = dns.aton(net.address) if ip & data['netmask'] == data['address']: if 'file' not in data: data['tempname'] = '%s.%d.tmp' % (data['filename'], os.getpid()) data['file'] = open(data['tempname'], 'w') print >>data['file'], net.getReverse(domain) net.reverseZone = data r.append(net) return r d.addCallback(_cbGotNets, forward, reverse) return d def getHosts(nets, e, domain, forward, reverse, filter): filt=pureldap.LDAPFilter_equalityMatch(attributeDesc=pureldap.LDAPAttributeDescription('objectClass'), assertionValue=pureber.BEROctetString('ipHost')) if filter: filt = pureldap.LDAPFilter_and(value=(filter, filt)) def _cbGotHost(e): host = Host(str(e.dn), str(only(e, 'cn')), map(str, e['ipHostNumber'])) for hostIP in host.ipAddresses: parent=None for net in nets: if net.isInNet(hostIP.ipAddress): parent=net break if parent: print >>forward, hostIP.getForward(parent.name) if parent.reverseZone: print >>parent.reverseZone['file'], hostIP.getReverse(parent.name + '.' + domain) else: print >>sys.stderr, "Not writing PTR for %s." % hostIP else: sys.stderr.write("IP address %s is in no net, discarding.\n" % hostIP) d = e.search(filterObject=filt, attributes=['ipHostNumber', 'cn'], callback=_cbGotHost) return d def cbConnected(client, cfg, domain, forward, reverse, filter): e = ldapsyntax.LDAPEntryWithClient(client, cfg.getBaseDN()) d = getNets(e, domain, forward, reverse, filter) d.addCallback(getHosts, e, domain, forward, reverse, filter) def unbind(r, e): e.client.unbind() return r d.addCallback(unbind, e) return d def filesOk(result, forward, forwardTmp, forwardFile, reverse): forwardFile.close() os.rename(forwardTmp, forward) for data in reverse: if 'file' in data: data['file'].close() del data['file'] if 'tempname' in data: os.rename(data['tempname'], data['filename']) del data['tempname'] return result def filesAbort(reason, forward, forwardTmp, forwardFile, reverse): forwardFile.close() os.unlink(forwardTmp) for data in reverse: if 'file' in data: data['file'].close() if 'tempname' in data: os.unlink(data['tempname']) return reason def main(cfg, domain, forward, reverse, filter_text): from twisted.python import log log.startLogging(sys.stderr, setStdout=0) try: baseDN = cfg.getBaseDN() except config.MissingBaseDNError, e: print >>sys.stderr, "%s: %s." % (sys.argv[0], e) sys.exit(1) if filter_text is not None: filt = ldapfilter.parseFilter(filter_text) else: filt = None forwardTmp = '%s.%d.tmp' % (forward, os.getpid()) forwardFile = file(forwardTmp, 'w') print >>forwardFile, '$ORIGIN\t%s.' % domain print >>forwardFile c=ldapconnector.LDAPClientCreator(reactor, ldapclient.LDAPClient) d = c.connectAnonymously( baseDN, overrides=cfg.getServiceLocationOverrides()) d.addCallback(cbConnected, cfg, domain, forwardFile, reverse, filt) d.addCallbacks(callback=filesOk, callbackArgs=(forward, forwardTmp, forwardFile, reverse), errback=filesAbort, errbackArgs=(forward, forwardTmp, forwardFile, reverse)) d.addErrback(error) d.addBoth(lambda x: reactor.stop()) reactor.run() sys.exit(exitStatus) class MyOptions(usage.Options, usage.Options_service_location, usage.Options_base_optional): """LDAPtor DNS zone file exporter""" synopsis = "Usage: %s [OPTION..] DOMAIN OUTPUTFILE [FILTER]" % sys.argv[0] def opt_reverse(self, net_file): """Write out reverse zone, in the form ADDRESS/NETMASK:FILE""" if ':' not in net_file: raise usage.UsageError('--reverse= value must contain semicolon') addr_nm, filename = net_file.split(':', 1) if '/' not in addr_nm: raise usage.UsageError('--reverse= value must have netmask') addressString, netmaskString = addr_nm.split('/', 1) try: address = dns.aton(addressString) except socket.error, e: raise usage.UsageError('--reverse= address is invalid: %s' % e) try: netmask = dns.aton(netmaskString) except socket.error, e: raise usage.UsageError('--reverse= netmask is invalid: %s' % e) self.opts.setdefault('reverse', []).append({ 'address': address, 'netmask': netmask, 'filename': filename, }) def parseArgs(self, domain, forward, filter=None): self.opts['domain'] = domain self.opts['forward'] = forward self.opts['filter'] = filter if __name__ == "__main__": import sys try: opts = MyOptions() opts.parseOptions() except usage.UsageError, ue: sys.stderr.write('%s: %s\n' % (sys.argv[0], ue)) sys.exit(1) cfg = config.LDAPConfig(baseDN=opts['base'], serviceLocationOverrides=opts['service-location']) main(cfg, opts['domain'], opts['forward'], opts['reverse'], opts['filter']) ldaptor-0.0.43/test-ldapserver.tac0000644000175000017500000000177010374156152015173 0ustar janjan# -*- python -*- import shutil from twisted.application import service, internet from twisted.internet import protocol from twisted.python import components from twisted.trial import util from ldaptor import ldiftree, interfaces from ldaptor.protocols.ldap import ldapserver DBPATH = 'ldaptor/test/ldif/webtests' TMPDBPATH = '%s.tmp' % DBPATH shutil.rmtree(TMPDBPATH, ignore_errors=True) shutil.copytree(DBPATH, TMPDBPATH) db = ldiftree.LDIFTreeEntry(TMPDBPATH) class LDAPServerFactory(protocol.ServerFactory): protocol = ldapserver.LDAPServer def __init__(self, root): self.root = root ldapserver.LDAPServer.debug = True components.registerAdapter(lambda x: x.root, LDAPServerFactory, interfaces.IConnectedLDAPEntry) application = service.Application("ldaptor-server") myService = service.IServiceCollection(application) factory = LDAPServerFactory(db) myServer = internet.TCPServer(38942, factory) myServer.setServiceParent(myService) ldaptor-0.0.43/doc/0000755000175000017500000000000010403234271012106 5ustar janjanldaptor-0.0.43/doc/interfaces.dia0000644000175000017500000000254310067304565014726 0ustar janjan‹í›]o£8†ïû+½Í8˜o&“Žfº;Ú•ºÚ•fæ:r‚K¼ã@dœ¦éÅþöµN>B€ÐÕ¶DjÔÀáãóœ—@>||\Q峘DáT…@S."Ÿ„ÁTýþíË;WýxsõÁ'è½ø Z)âˆ0–Ÿ¦ê’óõûñx»Ýº‹ db<þQŠÆÂh¬Þ\)Ê¡q$·e[çŒÌ7+!Zá©:G‹‹6¡¯¦V™Ý"¢Sª×÷ÉKgnÆG~Nø^£ÏF?ª]kâåym\¯1Ë»]­£˜¾[L*üÈ÷›Ì*FapsýɼN‡”mØû*he¾B, a1Ž˜šN„\]ןç¡yˆyÿ!hÿ!Xÿ!H<[GŒ3Dx1Ì<Š(Fa‰³ n'^ *–Ì©T`{ï÷„ó¨fü÷ˆÆç$nþY=M+1`Ä?]ˆG^¶ÄçËÙcOÓ•zßõäýÄdNqÙèIÈ/æ~w÷ù³“ÐxxÇÕz«Ä½ïb [ÍqlˆãševlSái™™ëf=owîĤ›rÍ7 AѳÌýç}ÛU²œç`¢ùßxÁ³ônI¼ˆ”wÊ-6ò g!$ñ§êŸÚñìäó¾ø Ó³ŽökÈA3Ÿo‘UÎçó¼o&€âÿ݆%þs€íhV»p˜âÕl±°Øß/“O F1§g`Ø2î—˜K^íß¶©»-­×% ØÁ¹¬êMaúóÒNÓÚ…¸'”žâX˜6 /£í¬DýÖKÆsóÅù8ܬ²Q€Qi[j4”¬gˈ‘§(䈞L.§‡$âdq éÆzUüþÇ`àWŽ8.a ìÊ@]Y="PºŸè&€ÖÈnŸðkŸÈ9ì“ôˆ>Øvn^™8~äjjËÙT1.¹l=²Ë]áÞýòé¯_CÎvº'ÔdÕ â”E”Û•{´"t'†ÂXU.&Õ›ú¦X²äˆÍGPX^¥ÍÏíä˜#¥%¨Í‘…~X+mBkê3×t›@âŠ5W¸üüíû<3J©bîýzgö;#¨õ éb@`Œ ì™þíS9ÿÆ@ÿ×Bÿ[JDÅø¿8þ=)óD¥ø ü¿þ0úÅâ`Y½ó¿C.çñßäÿ«i_1Õ44€K7hÝ:À‹uó=ë!ÿGzÿÚ¿GôÜÜÿýË7†ñ@ýËË~Qàð/|úÐGÌÔÿŒŸfw¢ÒKÐou¿÷ lͪ٦g¸}^˜Àq WÊÛ¶œ‰Œly¦Œl™nËÈsü4K²i•c™tV' ì:ƒŸâ§eJiŸ›Éu7¹uVº¯Q« ýb,ÚVG=|þ¨•ëÅapúÞ¬Õ5DíÝß3#,¢0KXÔ[\hNÏ{”¥¨Ó êâ|Ap¸5`°FúÉý2³An Øh„ ³«ÜH?'?*¹¹:úÕÉÍÕ¿ôVVi:ldaptor-0.0.43/doc/ldap-intro.xml0000644000175000017500000003755010403040300014677 0ustar janjan ]> Introduction to LDAP Tommi Virtanen <tv@debian.org> tv@debian.org 2003 Tommi Virtanen. Foreword This text is intended as a quick introduction to the interesting bits of the LDAP protocol, and should be useful whether you are managing an LDAP server, programming something using an LDAP library, or writing an LDAP library yourself. I welcome any feedback you might have. LDAP Presents a Distributed Tree of Information Probably the nicest way to get a mental model of LDAP information is to think of a tree with elements both in leaf and non-leaf nodes. Parts of the tree may reside at different LDAP servers.
      Tree-like, distributed nature of data stored in LDAP
      An organization normally uses their DNS domain name as the root entry for their local LDAP tree. For example, example.com is free to use dc=example,dc=com. The dc stands for domainComponent. An alternative is to identify the organization via geographical location, as in o=Example Inc., c=US, but this is cumbersome as it requires registration to avoid name conflicts. The o stands for organization, c for country. You will also encounter ou, short for organizational unit. Each node of the tree is called an "LDAP entry", and can contain multiple attributes in the form of attributeType=value pairs, for example surname=Wiesel. One attributeType may appear multiple times, in effect having multiple values. One or more of the attributes are chosen as a Relative Distinguished NameRelative Distinguished NameRDN, and will be used to identify the node based on its parent. This means the RDN must be unique among the children of its parent. Listing all the RDNs, separated by commas, from the node to the root, gives us the Distinguished NameDistinguished NameDN of the entry. The RDN of the entry for Jack E. Wiesel is cn=Jack E. Wiesel. The DN is cn=Jack E. Wiesel,ou=Sales,ou=People,dc=example,dc=com. The cn is short for common name. The RDN of the entry for John Doe consist of two attributes, gn=John and sn=Doe, joined with a plus sign to form gn=John+sn=Doe. gn is short for given name (first name), sn for surname (last name).
      Objectclasses and Schemas A special attributeType of objectClass lists all the objectclasses the LDAP entry manifests. An object class basically lists what attribute types an entry must have, and what optional attribute types it may have. For example, telephone directory entries must have a name and a telephone number, and may have a fax number and street address. objectClass can have multiple values, allowing the same entry to describe e.g. information about a person both for a telephone directory and for UNIX shell login. An LDAP schema is a part of the configuration of the LDAP server, containing two things: definitions of attribute types and definitions of objectclasses. It is normally stored as ASCII text, but can e.g. be requested from the server over an LDAP connection. An attribute type definition commonly contains a global identifier for the attribute type (a list of period-separated integers), a list of names for the attribute type, a free-form description and a reference to another attribute type this definition inherits from. It may also contain information about what sort of data the attribute values may contain, how to compare and sort them, how to find substrings in the value, whether the attribute type can have multiple values, etc. An example attributeType definition attributetype ( 2.5.4.4 NAME ( 'sn' 'surname' ) DESC 'RFC2256: last (family) name(s) for which the entity is known by' SUP name ) An object class definition also commonly contains a global identifier, name, description and inheritance information. It also commonly lists the attribute types entries having this object class must have, and additional attribute types they may have. An entry cannot have attribute types that are not listed as a MUST or MAY by one of the entrys object classes or their parents. An example objectClass definition objectclass ( 2.5.6.6 NAME 'person' DESC 'RFC2256: a person' SUP top STRUCTURAL MUST ( sn $ cn ) MAY ( userPassword $ telephoneNumber $ seeAlso $ description ) ) There are a lot of pre-existing schemas, standardized in various RFCs. Also, anyone can create their own schemas. The only things you need are access to the LDAP server configuration, and a number reserved for you, which can be achieved by filling a web form. Object-oriented look at LDAP entries If you look at LDAP entries from the viewpoint of a programmer accustomed with object oriented programming, you will see a lot of similarities, but also some striking differences: &ldapentry_vs_oo; Writing Things Down: LDIF There is a standardized way of writing down, in plain text, the contents of LDAP directories, individual entries and even add, delete and modify operations. This format is known as LDIF (LDAP Data Interchange Format)LDIFLDAP Data Interchange Format, and it is defined in RFC2849. The rough format of LDIF is this: there is a paragraph per entry, where paragraphs are separated by blank lines. Each paragraph contains lines in the format keyword: value. Entries start by listing the keyword dn, and their DN, and then list all the attributes and values the entry has. Lines starting with space are appended to the previous line. The whole file starts with the keyword version and value 1. The actual format is more complex, but this tutorial should allow you to read and write normal LDIF files fluently. A simple LDAP file with two entries. version: 1 dn: cn=Barbara Jensen, ou=Product Development, dc=airius, dc=com objectclass: top objectclass: person objectclass: organizationalPerson cn: Barbara Jensen cn: Barbara J Jensen cn: Babs Jensen sn: Jensen uid: bjensen telephonenumber: +1 408 555 1212 description: A big sailing fan. dn: cn=Bjorn Jensen, ou=Accounting, dc=airius, dc=com objectclass: top objectclass: person objectclass: organizationalPerson cn: Bjorn Jensen sn: Jensen telephonenumber: +1 408 555 1212 A file containing an entry with a folded attribute value, from RFC2849. version: 1 dn:cn=Barbara Jensen, ou=Product Development, dc=airius, dc=com objectclass:top objectclass:person objectclass:organizationalPerson cn:Barbara Jensen cn:Barbara J Jensen cn:Babs Jensen sn:Jensen uid:bjensen telephonenumber:+1 408 555 1212 description:Babs is a big sailing fan, and travels extensively in sea rch of perfect sailing conditions. title:Product Manager, Rod and Reel Division Searches and Search Filters The most common LDAP operation is a search, and LDAP is purposefully designed for environments where searches are many times more common than modify operations. In general, LDAP servers index the entries and can effectively search for matches against a reasonably complex criteria among thousands of entries. An LDAP search takes the following information as input: Once again, we are skipping some details for understandability. &search_inputs; Of these, the search filter is clearly the most interesting one. As with LDIF, search filters have a standardized plain text representation, even though they are not transmitted as plain text in the actual protocol. A search filter is basically a combination of tests an entry must fulfill in order to match the filter. They are always written inside parentheses. A simple example would be (cn=John Smith) but the filters can also match against presence, prefix, suffix, substring, rough equality, etc. Multiple matches can be combined freely with and, or and not operators, which are represented by &, | and !, respectively. For example, to match only objects that have objectClass person, where the full name contains the letters a and b in either order, and who don't have a telephone number listed, we could use the filter Yes, once again we are skipping details for understandability. See RFC2254 for more. (&(objectClass=person)(!(telephoneNumber=*))(|(cn=*a*b*)(cn=*b*a*)))
      Visualizing an LDAP search filter
      Phases of an LDAP Protocol Chat An average LDAP protocol chat consists of three stages: Opening the connection Doing one or more searches Closing the connection At the first stage, opening a connection, an LDAP client opens a TCP connection to the LDAP server, either as plain text, encrypted by TLS or starting with plaintext and switching to use TLS with STARTTLS. The client authenticates itself and/or the user, providing any necessary authentication information. This is called bindingbinding. Normally, the connection is not really authenticated, but left as anonymous; the bind message is sent with no user or password information.
      Beginning of an LDAP protocol chat
      Next, the client sends a search request, containing the base DN for the search, the filter that entries must fulfill to match, and some extra settings discussed above. The server replies by sending search result entries back, one message per matching entry. If no entry matched or there was an error before the search could even start, the server might not send any entries. Finally, the server sends a message indicating the search is done, and includes information on whether the search was completely successfully, or the error encountered.
      A sample LDAP search operation
      Note that the client could have sent another search request without waiting for the first search to complete. The order of results from the different search, or when they are completed, is in no way guaranteed.
      Multiple search operations pipelined
      One important detail we have skimmed over so far is how the LDAP client knows what message the server is replying to. Earlier we avoided this topic just by doing only one thing at a time, but now we have two searches getting their result entries interleaved. Clearly, there must be a mechanism to separate which entries belong to which search request. And exactly such a mechanism exists; each message sent by the client contains a number identifying the request, and the server replies by including the same number in the reply. Now, all the client needs to do is remember which numbers are still in use, and not reuse those. It can internally maintain search state based on these numbers, and process result entries based on them. The client can reuse a number when it is known that no more server replies will be sent using that number; for example, the search done message gives this guarantee. Finally, when the client longer wants to talk to the server, it sends a message effectively saying "good bye". This message is known as unbind. This only means that the state of connection is the same as when connected, before the first bind; that is, it unauthenticates the current user. If the client really wants to close the connection, it will then close the TCP socket.
      End of an LDAP protocol chat
      Please understand that these were just examples, and in reality protocol chats are often more complicated. For example, one could connect some other protocol servers, say a web servers, authentication mechanism to actually act as an LDAP client, that tries to bind as the user authenticating himself to the web server, with the password given by the user. If this service had no other interest in the contents of LDAP, it would probably immediately after the bind close the connection. But opening and closing TCP connections repeatedly is slow; it is quite likely the authentication mechanism would be changed to keep a single TCP connection alive, and just do repeated binds over the same connection.
      ldaptor-0.0.43/doc/chat-search-pipeline.dia0000644000175000017500000000275610052211034016554 0ustar janjan‹í\[Ú8}Ÿ_¥¯ÔĤS¦šYiµ­úÐö™Ä€·!Fކ—ýíë\f€\ $q´ìiF"8ßçËwαŸ¿¼¬}í™ð²`ªC`è \æÑ`9Õýüó£­y¸ûìQüIþ-9^kòŽ ŒÞMõ•›OÃán·þ>Ä‚qàÓ-Éðìûx( õ‡;M;àa£kéU,§ó­ Z€×dªÏ±û{ÉÙ6ðô¤UÚÎe>ãÚ3ö§ú‡EüÒ‡i˜áIœ3±7xIæœàßå¡ ùrœ:¡7„gî7,¤²‰ØorMJâDÿÚ¤­BÙ(X>|x}Hº”^8Ä*êhi±Æ|Iƒ|97~2Ø¡×y¸>Å\} _} ®> gÆÇTäÓÌó ’L‚oIý<¡‹}Y2ç†ëG_P!Ø…þ/°V@rù =×"qÉ©wˆ'-J¢ì¨'V³EÓ•Dß+ŠþLC:÷IQïi Z ¿o'|vub6>ÜÞ°’h¥tïÙÄ"Öõt¿ÜR„Êì´MI¤UÚlxiÖ³íªNLr)#¾q ï OÃ?dWK8eœ£Y`ó¿‰+Òáý8ð0÷´ÚOò"ôÃ6Bn"¨7Õ¿§ó“‘Œ&©/7AöVEÎ`¬ìˆó|Q|>ÏÆæ²û8Xúä5þØÖ}”Æ®™GDC¿PÑ™6eKúI»Œú—hÿ€•±5 DQ¢èº¶ÀkêïepêZ(öQ%È%Mný‹øÏDPìõ=Xº\öáÀ€°›%9-® V'É)ÁœÙUF³DR¾—Á𝠶ëtÖ*f)¤2 Ð_Ÿ`S:€À±X*)!Îaç>Mæô´ðþh¡ ÐzjPI ¨)5 U3C”""†$UÏ ïòUÖÓB}Zøõí«d„ïñ¥N0oP!éFÁº7=@µÄ'ë™Ëx÷ÛK>¶òã9XX“&Ï3ëdó¿J–øT¥§Ìö)ÓœXðFšPi–/á„EåžÃY¥¿ Ç/¡ÀB]üä}¿ñ¹U£ßóÔ„/ gØô9‡®s_Ž\Å+¶›%CU9Ö[_Ðß| Õ7†ßHâ%)ØŽšŸMk ö¬hFN¬{ˆX`T/™Ë‚`F/îû5#*ØAFþ]•‡»“^y¸ûRÚþélMldaptor-0.0.43/doc/chat-bind.dia0000644000175000017500000000263510052211034014414 0ustar janjan‹í[KoÛ8¾çWÊÕ¥E½Õ4)vXì¡EmÏ-Ñ2·2iPt_ö·/õH½[íFZÀÌhä|ßpFɇO›D{Ä<%ŒÞ뺆iÈ"Bã{ýû·?ßùúLJ›Aï忘£&Ÿ iöí^_ ±}?Ÿï÷{R$ ÙÏÿAI‚æRh®?ÜhÚK([+W‘œ,wkmð½¾DᘳôBª” Y¸öˆ’{ýv•ôy©f^ÑsB÷ÅxÉ1úÑ­ÚŸ è£z‹y]ífËR"EÄaÛéГýÿB¦”J¥n³o —Ê…£®6G;ˆ â1¡M;ro’b#Là›¦ù¼—›Xª7‘¨7ÁÕ› ébË¸àˆˆ¦™%c F´°$ø÷·“†(‘)s*Ø_ûŠÁ^ñ…’ôœŠåŸè¹‰1'Ñi V$:´ìI$Ö‹'EÛUh?(ÒþHR²Lp›÷„ŠÑÔÆQ_?œ̆B['ÝG>v°s9ÝÇ;áô•4«ÊthZ—bó×v½.wîÆKµâ››HÐóRýïDz«•\2΋]`Ë¿q(Êð¾ D#Ä#íö ? ýx—Ýë_ŒêþÔ#’Ú$õ56hË~fQ0ó€S¸É]ʗ˺n.ÝG4Nð³~øÎ]fÆïiGd¡¿’Ñ5™.‚ÎKzE®Vý;jÿ €u±5£¢ÍP¶®­Ð†$颩®¥âe‚<ÒâÑ¿pòˆ ÑË„½Üƒ5&ñºÕ‡#Àf¤š\g$X#U‚9q«4Œa†dùŽé·Ÿ¦»M¹kgZi¥. œÑߟàP:€ pf8*)!·a€à®4L´ðöh¡%Ñ&jPI æPj0TÍ ™‰Œ S/¼A^hfÙD ýiáûçO’¾äK-œ` ¾.Ì B:0Ê‹‚sgfön p‚7‹qÚœ ŽK®>74ã9ް¼!Î7ÀYŸd¼æ•,ÿHˆÊD™ãS¦å9ðFšPiv&/æ˜eé^ÃÉJŽŸR„:ýÅ÷éâ󫢨˜Ó§'|Iº@¡ tz9r?¬Ù~Q,¦ªllv‰ Ûdx ç_ ?ã4E1n¹ÚûEÏtf€®ëzJ[F׳}sfhø–}M`Ж–]À=-‡ŒÒ¦QO([.—Yy–‚æx×´g*Μ•{2¸€´U¿V4_êø+™"OJžžçÈÓ;ÏF°Ì×´Q힢­Êj!˜LhG׎?•ça4N«åaX>ìU6šoˆjÁOd…BÛ0è Ç`q1ô•âÏ03žçÜh€0MG9ð¡u eþôó† ¶=ÑsõTºd'¹ ¯¯GûÅŠ…»Óõ©ú 9K•Àp›,(ãb½Çic‹Ž‡Ú_yÊvê”çžc¤Ðó1”÷âN«Jîˆô×9šr¦ÔΦàq8m`8êÇSJçSÖ[N}Å\æàÔÖŽþžÏÓljšMM³©i65ͦ¦ÙÔ(±7ønX%•}q1ð)úb˜Ý ³>ÔPÞ7#ë–ŠžØ¶Š®ØË¶qê‹O¶®Pe_ UöÅPe_ öÅ®º¾¸{2ï3™wcÉêÉ|MâmËwÜb2oØÒr àU&ó(»&óRж—TØ”Uw¡JÒ9Aïê‘÷&*nA!d}ŽW1Ö¯"Ò¯BèW& .RAÃtW͘óˆÑ¸Ð”Š%;]O2¡‘ì2ûª‚O/ý1LS~àúi”´©@qøuô;N÷Ä-‰†RžÃi:{xÑt»ŠÒWšJ “p±º«ã´³âWÝ_mÜoNWì Eiæ~ê1›ÙÇ›û`NYr ›mË4”4+ņ‡îzU®í)Uœo®"¢+&Êâÿµq»½²K‹óÍ]àãÿ²IZVïï/¿ön{¿ñ)ëoBòC8õ7¶oMµ2² iõvîÍ‚¿v [oàWëºk)šÊ«E yá4"¶.öÀC–}G0²íqåÓ²ˆÍ&\Ļ޽“ ååçc·R¯^H¥ì ƒYÚ\8öN+¼Â2µƒ$e/5~oÇy帳%W!£hJˆ½Ðù"bH*ià¤=ƨɳñ8­Sœï=Òy­äõÑ8é÷’t•9ŠSfÑKà ývp;ÍSÓDòÔ”lÆÚì"{€ „m5MÛy††š"É;A,=Ê>ÌøóCq0Ñ¥£„yõ:tÀ…–*šKg ;‹s›òëÎAÎÀÖ …'×¥ šÈ¶?:N'#kkp)r][CCCCCCE0´UÁÐ`Ã<ÂÏÈcä°î€áéÕi‡–ÚÕ¿8,sÀµtíZ|d;$@ˆ@ˆ@ˆ@ˆ@ˆ@ˆ]$w9ª˜¸]èÌî’:H¦ÃµïÜ,¿ ½ ^5ujÂÆþ/·"I^äI^äI^gHò²’¼ì $y¹ê“yXûlΧóò<|ýóyØÒ³±>|̆/G0! Ïz6"$#² „l d!Ù(¢§¼ë%Ä,رFDì!¢ŸjÔ]„—£¿øœ¥3ù«÷“ìèàe:_`!ÛZZZZZZì‚}åmĜֺ˜“­•œ(iñÖÈ·Á14ã¢B•Úà¢, ÅÑ*¾J›à`:ßGÌDf6J1+o7Kôb"ÎcŠ^±ŠÔ8$­Œ„¸ýE#–€_é|) ! ! ! ! ! ah¨¼“¶§ Ý b"©P?zzÙðT~{_t¸\doõçÒù4³;[EŠ•ßðºÁÎu¤Erbþž©ÏÍß$¡u!i]­¥È7 ´‘8øÆü&^9«Xa+¬b…–…U¬-V±Ú «X]}«XyDà#Ó]#Aþý¶à€;ÇøøsðË`½Á/CË‚_né—½ ì.•÷ŠÏ^^¢5ÑÇÚLçä9ú§sªÔ.æs–£Ï“ _Æ®eJÇE>LéÀ”LéÀ”LéÀ”Ngå ä_7@ÒÀ¹u‘•épí;©Ï•ĈõÆoêjÕ(åì—ºu[‰øû£@ù’F˜ÕèÄ z-{%Ñ#W!zä_lVǹ (0sµL€ÛH ò€€.S.ø2Ý~dí”Í9À3s@Ë~hæÀÆÅ Ã½ è°.vð“Øá¡+£·Äêð€:€:€:€: e:º¢|1êð®Ž:òï!JNø¼Á –w„ÜÁ'ˆ\¦ì;æ !Œu‰\_彟1µr‹_ J¾qK¶©Ÿ¥9Ó÷ô µIôu>zšï$}ž/dóþôBç Øu‚ Ÿ Èñ…_Èñ…_Èñ…ß.b\Dyçç|71ñ-3çD# o9ytËÖ;©V­P­„¹?ÄäT$`U6„j T¡Õœ!TSÍS9.Vs±ÌZ¢¼Ó®i ŸäY'ŽN,½c™Ûbß™æ9\rmÅšÅ8gÓï Þl8xgðÎç÷ÎÄTðÎĺ˜w&W— B<äŽúj²Ai— ÒZ®•ˆ|Þ7÷ŠxÈØ{OI#ï$'V°G&€‡ÀkA˾ûÄ’*K%±„(ïëZúS­Éùîz'9Ó\‡,½oT«Øå·“#§FŽÎ¼ÜŸ,aTLf7ÿ óÅ]ïGöÄ"¾Èæ+aÒºëIëuT 6˜ƒäH>äH>äuˆTÞ‰8ŸÅÑ9ÍE6ùɦ\ýéÉ 5j•Ÿ¬¿ï"E9ˆG¿ðYüÏ$ýÈø˜ÎYÑA&±!QXXXXX±ÊŠÅ˜¸¿)~ÈO èüþæÿQæÝfNåldaptor-0.0.43/doc/search-inputs.xml0000644000175000017500000000123210020230417015404 0ustar janjan The base DN for the search A search filter, specifying criteria an entry must fulfill to match. Scope of the search, either look at the base DN only, only look one level below it, or look at the whole subtree rooted at the base DN. Size limit of at most how many matching entries to return. Attributes to return, or none for all attributes the matching entries happen to have. ldaptor-0.0.43/doc/ldapfilter-as-tree.dia0000644000175000017500000000335010052211034016242 0ustar janjan‹í\[›8~Ÿ_ÁRiªŒƒsiš©Ú•ª}¨Ú‡í>GNð$l s©VûÛצ ` \œéì2RË„1çø˜Ïçû|0yûîaïIw$ŠÝÀ_Ê(²DüMà¸þv)ÿùõãµ%¿»¹zë¸ø ý·ð^¢Wø1û´”wI¾™Ïïïï÷ã$ˆ€ç@Læÿ`ÏÃsÚh.ß\IÒ©'˜ËÏâ$‰Üõ!!’÷d)¯ñæÛ6 ¾#g­òv›À "é{KùÕmú#Ïs3ó‚Û!Þ’uDð·zÓ ý±í>¦C•Íîà vi“ä1¬4©±Ãþ?i“·Ši#{óê½þ*ëR~âh‹×ÑZ'ÉG[ׯú¡cãe¡KUÕ§qèîb-Þ…'ÞE$Þ…¯Â J"ì&U7ë ðö3OIt ýýÄìQÈ4…û[¿u“$héÿ-öâsÈNÿ˜=]gâ6ræ‰XhQcåÞu’Ýê¡i¸€úXfÿQ˜ý;7v×áEàúÉàûýdþqóå;”fäãå‘Y«MùŽEAÝSþöà:$nZ±M¥]ÞlÞ6êåvçLvªDÀ© ?’(7ÿáH½R~ƒó¬s2 Áú/²IòðþH°ïàÈ‘®¥O®O䣔 BÂu–ò¥8>刨5šþ*?PdÍ 0Q9êjÞ¨s°^—íG4ìo=’ù0m© ¤n v\@LÚ@1Ì£èæxøþŠøNJÇ«XfF¿žø‡ý&,w€7K›Œ²pè Ñ{WæîÓ_¤Eà ½ùI@o>Teéøç¥¬UBä\ Ÿ.6Š+UØ—º”Îzb¿’‡„ƒX8±&PU4³*±Ì‡Z<¹C=gH†¡%—ÚÔIŒT”Ú•ôkzm ‡:½ø Ï;/Ýâ½ë=Òþ`?–¥8yÌ1™]ú;ñîHânð)»÷`GÜíŽÛ‡S·†9)íL°õqT¤È†µ‘¢ sDEèÖßþÝ#4Uå#w¦.Ö‘x·ôPChêðô`Šç3C·u•ñ™M4EXŠ‚RBCô(˜ÐªrÛ —Ïg°ŸéµÃg5€Õ†V®ÀØ*ÚÖRfÒ#E¬®*†ÍkÒ£pÄjíLöËG¬Ú±zíµûÑ î7;%±‚`õ¡€½¦+ç¾·ê,´2h†€ƒÃO?wÄ#ûÕ&ˆüjep¤€Riµ¡Ô“N1€5ÀxE •ËJ@ëg=Þ÷+N=¸½8Ö-K°Ñ_EØqq}…jLû!vœ¢,®XV~åžÍáß<ÇËÎØÀŸ´üØZ>e`ÿ·”<|&%ßÎ3h(Ï@c4Ãе¥H×÷3óÌ€€Î¡ èh♉gšxfã/_¯_ã×»ŒÍ.®¹UšD”‰a.Å0Æ`†Q3 <2 ºèÃL óì ƒ)ÇL 3:ÃhÃ\˜aÌ¡ £‹%íÈ/ðü¢ ¥s¢—‰^Zè%¡·:Ü>ù|دI´œxFÀ3ï‰f.ûÐÛ^*c»œL€tÁõ2+u£› ÒlÅkë)RÃF‚"òBlX…¿¸'‰Æ€½\HÄ“ÄvØÚƒa‹²Ç{ba«!3ƒ­©²•¸m-‡­&¶œ¤ýK‡m§-ÆóÀ*#ì1Jï©Ø=F¦žæZÝFù£²HAâ7•¢«×/¯zí®×v¼š‚ñZ·cvð–YkÅnï6- h*=.¨7 Ùê´_ö¸v( mÚ)ÛiÑÀª aanŧ`ënh–xŸ¾?RIjÛ[0Õ4ÁIë3¥ÿKK•dÁO«Y]3&[X€-Š¨ìæ“_¶90níŠS^ÒºàÌáŠsÀKkõu¬ñ߇¼õÜpµ "÷;MØéÝÅ>J°4*ØUµŠÆI³ÒÒÎ~ó4ô”Ú(³?ÝY  O¬ÔK‚*·6Ëì÷þ~ù’ U8t‘%ñð+Þ‡‹ZÑHH竈Á:¢O?¸Z¢]MôqUU粟?ž¼8S`ôqW'2Új“Ír†/5:$™ì̉Þè¢?>Ü ¤£?ŒËèts9{¿-tÑ%ô‡!^  èI~Lò£:ùÑHòƒæø™,[³µ HCcoÉU§îX‰ÍJë^éÇ—$D~™DÈEDH 8'!òLBäKÄKEÆ©¢ ¨çJD×á -÷[¦bBñzdX€SAdR$?¹"1ÇIÀ°R`+†.^’°Mâ™?Qéåä¢ òyQò÷$JÄ‹’|Nªd4U’}L¿ÉëæªðU_7Wÿß?(âSldaptor-0.0.43/doc/chat-unbind.dia0000644000175000017500000000254510052211034014757 0ustar janjan‹í[[sÚ8~ϯð8¯TØßš’ÎîÎììC;}hûÌ[mÄH"„—ýí+_Rb| ¶Qf»13a‚8œ£#Ÿï“Îgøðñq›˜ Âè´e˜†,"4^˜ß¿ýùÎ7?Þß|ˆz¯þb޶†úé«…¹‘r÷~:= 9 $ Ù§ÿ $ASe45ïo 㹃I”Ž£HJNV{‰ жxa®Pø#ælO#3·*ìB–0n< daÞ®³‡9-ÜLK~Z|ïPŒW£Í®-õ‚>®w˜Ÿ»Ýî˜ ÊDw“?éó3›ÂJ(#ßßþ6¿Í§T œ|ÕM´1ˆÜ"Z£Ö&ÉBø´ÝC¬ô‡Hô‡àúC±Ü1.9"²fÅX‚Í#I¾Çýãˆ%ªdÚR±û{_)Ù ó_£D\’@>ü=]‘sµ±dÑàå@"¹Y>jZ®ÜûQ“÷"È*Áu³'T^Íýñ:îϯNÆÆ§¬†Ü[#ÝG>v°Óîã=‰°x¡ÌÊ6 ž6…Ùô¥U?·»taò¡³Í7 ‘ #æ…ûßOÛ®Q\à‚qž­[ýCY¤÷U"!ïŒoøQš§c„:Dha~±Êësž‘ò¦¨¯²@;ö³Š‚‰œóŒ«|Ñä|µ:÷ÍÕôüäß¾s—†ñ{Æ‘iê/Tô™MAg[zÉîl÷oØû[ÖÄ֌ʺ@鸱F[’Õ|¦!ä1­uIóþ…“,Iˆžl÷l0‰7µs81 üaAÊÅuAõ R&˜–S¥e ¤¶ï˜nqý•Ãt¿-VíÂ(µôÑDÓúûÓ=”l88:)!‹a஌´ðöh¡¦ÐFjÐI p(5@`ëf†4DJ y¨‘Þ /T«l¤…þ´ðýó'Å_²¡N˜ >.Llt`çnü ìÝ@ào—!ã´ª ^#—Ì}&7Tó9IXÞÉWÀy®d<æ•,ÿHˆÊH™×§Ì™ç(xØÿ3Ò´5fcñbŽYZîç`hÝé;áøQH$õùÏ_Ÿ_ÅÖÄÏ<=áKÄ…’éJöXŽ~eI«´4{ëºÒWÌU ŽéÕoÑA0ÊJ£¬4ÊJ£¬4ÊJ£¬t•ÆØ|6Ì7%}q.Ûä}±ž Ó>ÔÒÞW3k°é«mµ÷Äé—ì}± æc_ÜÚºB}1ÔÙC}1ÔØ»úúâfQÝ® ¦*×Pª‚®7fÌ,z¹¬n¥Q}:žvq°’a“®®ŒlÂ:û¡YTŸkÕ¨ÕsAà¨+§=N=³Êź\‡¯‚¼à:È úJ¼#Ï™øÀÏ0§‚ùžcÁW\)±&Àõξ mÅÿ…oe¥k£–Ñ‚¯‡¹`ææ1—¿Î~ÌtSúµÓýÍ¿Oà$á<ldaptor-0.0.43/doc/overlay.js.patch0000644000175000017500000000123310020230417015213 0ustar janjan--- /usr/share/sgml/docbook/custom/slides/3.0b1/browser/overlay.js Tue Jun 18 19:03:26 2002 +++ bk-for-sg-engine/browser/overlay.js Tue Dec 31 11:31:48 2002 @@ -53,8 +53,10 @@ overlayLy = window.innerHeight; overlayLx = window.innerWidth; overlayH = document.overlayDiv.clip.height; - overlayW = body.clip.width; // document.overlayDiv.clip.width; - contentH = body.clip.height; + //overlayW = body.clip.width; // document.overlayDiv.clip.width; + overlayW = document.overlayDiv.clip.width; + //contentH = body.clip.height; + contentH = document.overlayDiv.clip.height; } else if (overlayNS6) { var odiv = document.getElementById('overlayDiv'); ldaptor-0.0.43/doc/examples.webui/0000755000175000017500000000000010403234256015041 5ustar janjanldaptor-0.0.43/doc/examples.webui/global.cfg0000644000175000017500000000072510104536552016770 0ustar janjan[ldap] base = dc=example,dc=com [Service-Location dc=example,dc=com] host = localhost port = 10389 [authentication] identity-base = ou=People,dc=example,dc=com identity-search = (|(cn=%(name)s)(uid=%(name)s)(mail=%(name)s)(mail=%(name)s@*)) [webui] search-field 1 Name = (|(cn=%(input)s)(uid=%(input)s)(mail=%(input)s)(mail=%(input)s@*)(ou=%(input)s)) search-field 2 Phone = (telephoneNumber=%(input)s) search-field 3 Location = (|(location=%(input)s)(l=%(input)s)) ldaptor-0.0.43/doc/examples.webui/webui.tac0000644000175000017500000000063410104536552016652 0ustar janjan# -*- python -*- from twisted.application import service, internet from nevow import appserver from ldaptor import config from ldaptor.apps.webui import main application = service.Application("ldaptor-webui") myService = service.IServiceCollection(application) resource = main.getResource() site = appserver.NevowSite(resource) myServer = internet.TCPServer(38980, site) myServer.setServiceParent(myService) ldaptor-0.0.43/doc/examples.webui/ldaptor-twisted-web.py0000644000175000017500000000064210104536552021320 0ustar janjan# -*- python -*- """ If you are using moshez's experimental twisted-web Debian package, try copying this file as /etc/twisted-web/local.d/ldaptor.py editing to suite (the example assumes your LDAP server is running on localhost, and serves the DN dc=example,dc=com), and browsing http://localhost/ldap/ """ from ldaptor.apps.webui import main resource = main.getResource() default.putChild('ldap', resource) ldaptor-0.0.43/doc/slides-driver.xsl0000644000175000017500000002244410052211034015410 0ustar janjan 1 1 yes graphics browser . slide-style.css 1 <xsl:value-of select="title"/>