1 from zope.interface import implements
2 from twisted.internet import defer, error
3 from twisted.python.failure import Failure
4 from ldaptor import interfaces, entry, entryhelpers
5 from ldaptor.protocols.ldap import distinguishedname, ldaperrors, ldifprotocol
6
8 """Cannot remove root of LDAP tree"""
9
10 -class ReadOnlyInMemoryLDAPEntry(entry.EditableLDAPEntry,
11 entryhelpers.DiffTreeMixin,
12 entryhelpers.SubtreeFromChildrenMixin,
13 entryhelpers.MatchMixin,
14 entryhelpers.SearchByTreeWalkingMixin,
15 ):
16 implements(interfaces.IConnectedLDAPEntry)
17
18 - def __init__(self, *a, **kw):
19 entry.BaseLDAPEntry.__init__(self, *a, **kw)
20 self._parent = None
21 self._children = []
22
25
26 - def children(self, callback=None):
27 if callback is None:
28 return defer.succeed(self._children[:])
29 else:
30 for c in self._children:
31 callback(c)
32 return defer.succeed(None)
33
34 - def _lookup(self, dn):
35 if not self.dn.contains(dn):
36 raise ldaperrors.LDAPNoSuchObject(dn)
37 if dn == self.dn:
38 return defer.succeed(self)
39
40 for c in self._children:
41 if c.dn.contains(dn):
42 return c.lookup(dn)
43
44 raise ldaperrors.LDAPNoSuchObject(dn)
45
46 - def lookup(self, dn):
47 return defer.maybeDeferred(self._lookup, dn)
48
49 - def fetch(self, *attributes):
50 return defer.succeed(self)
51
52 - def addChild(self, rdn, attributes):
53 """TODO ugly API. Returns the created entry."""
54 rdn = distinguishedname.RelativeDistinguishedName(rdn)
55 for c in self._children:
56 if c.dn.split()[0] == rdn:
57 raise ldaperrors.LDAPEntryAlreadyExists, c.dn
58 dn = distinguishedname.DistinguishedName(listOfRDNs=
59 (rdn,)
60 +self.dn.split())
61 e = ReadOnlyInMemoryLDAPEntry(dn, attributes)
62 e._parent = self
63 self._children.append(e)
64 return e
65
67 if self._parent is None:
68 raise LDAPCannotRemoveRootError
69 if self._children:
70 raise ldaperrors.LDAPNotAllowedOnNonLeaf, self.dn
71 return self._parent.deleteChild(self.dn.split()[0])
72
74 return defer.maybeDeferred(self._delete)
75
76 - def _deleteChild(self, rdn):
84
85 - def deleteChild(self, rdn):
86 return defer.maybeDeferred(self._deleteChild, rdn)
87
88 - def _move(self, newDN):
89 if not isinstance(newDN, distinguishedname.DistinguishedName):
90 newDN = distinguishedname.DistinguishedName(stringValue=newDN)
91 if newDN.up() != self.dn.up():
92
93 root = self
94 while root._parent is not None:
95 root = root._parent
96 d = defer.maybeDeferred(root.lookup, newDN.up())
97 else:
98 d = defer.succeed(None)
99 d.addCallback(self._move2, newDN)
100 return d
101
102 - def _move2(self, newParent, newDN):
103 if newParent is not None:
104 newParent._children.append(self)
105 self._parent._children.remove(self)
106
107 for attr in self.dn.split()[0].split():
108 self[attr.attributeType].remove(attr.value)
109
110 for attr in newDN.split()[0].split():
111
112 self[attr.attributeType].add(attr.value)
113 self.dn = newDN
114 return self
115
116 - def move(self, newDN):
117 return defer.maybeDeferred(self._move, newDN)
118
120 return defer.succeed(self)
121
123
124 """
125 Receive LDIF data and gather results into an ReadOnlyInMemoryLDAPEntry.
126
127 You can override lookupFailed and addFailed to provide smarter
128 error handling. They are called as Deferred errbacks; returning
129 the reason causes error to pass onward and abort the whole
130 operation. Returning None from lookupFailed skips that entry, but
131 continues loading.
132
133 When the full LDIF data has been read, the completed Deferred will
134 trigger.
135 """
136
138 self.db = None
139 self._deferred = defer.Deferred()
140 self.completed = defer.Deferred()
141
142 - def _addEntry(self, db, entry):
143 d = db.lookup(entry.dn.up())
144 d.addErrback(self.lookupFailed, entry)
145
146 def _add(parent, entry):
147 if parent is not None:
148 parent.addChild(rdn=entry.dn.split()[0],
149 attributes=entry)
150 d.addCallback(_add, entry)
151 d.addErrback(self.addFailed, entry)
152
153 def _passDB(_, db):
154 return db
155 d.addCallback(_passDB, db)
156 return d
157
158 - def gotEntry(self, entry):
159 if self.db is None:
160
161 self.db = ReadOnlyInMemoryLDAPEntry(
162 dn=entry.dn,
163 attributes=entry)
164 self._deferred.callback(self.db)
165 else:
166 self._deferred.addCallback(self._addEntry, entry)
167
170
173
175 super(InMemoryLDIFProtocol, self).connectionLost(reason)
176 if not reason.check(error.ConnectionDone):
177 self._deferred.addCallback(lambda db: reason)
178 else:
179 self._deferred.chainDeferred(self.completed)
180
181 del self._deferred
182
195