1 import random, base64
2 from zope.interface import implements
3 from twisted.internet import defer
4 from twisted.python.util import InsensitiveDict
5 from ldaptor import interfaces, attributeset, delta
6 from ldaptor.protocols.ldap import distinguishedname, ldif, ldaperrors
7
8 try:
9 from hashlib import sha1
10 except ImportError:
11 from sha import sha as sha1
12
13
15 if salt is None:
16 salt = ''
17 for i in range(8):
18 salt += chr(random.randint(0, 255))
19
20 s = sha1()
21 s.update(passphrase)
22 s.update(salt)
23 encoded = base64.encodestring(s.digest()+salt).rstrip()
24 crypt = '{SSHA}' + encoded
25 return crypt
26
27 -class BaseLDAPEntry(object):
28 implements(interfaces.ILDAPEntry)
29 dn = None
30
31 - def __init__(self, dn, attributes={}):
32 """
33
34 Initialize the object.
35
36 @param dn: Distinguished Name of the object, as a string.
37
38 @param attributes: Attributes of the object. A dictionary of
39 attribute types to list of attribute values.
40
41 """
42 self._attributes=InsensitiveDict()
43 self.dn = distinguishedname.DistinguishedName(dn)
44
45 for k,vs in attributes.items():
46 if k not in self._attributes:
47 self._attributes[k] = []
48 self._attributes[k].extend(vs)
49
50 for k,vs in self._attributes.items():
51 self._attributes[k] = self.buildAttributeSet(k, vs)
52
53 - def buildAttributeSet(self, key, values):
55
56 - def __getitem__(self, key):
57 return self._attributes[key]
58
59 - def get(self, key, default=None):
60 return self._attributes.get(key, default)
61
62 - def has_key(self, key):
63 return key in self._attributes
64
65 - def __contains__(self, key):
66 return self.has_key(key)
67
69 a = []
70 if self.get('objectClass'):
71 a.append('objectClass')
72 l=list(self._attributes.keys())
73 l.sort()
74 for key in l:
75 if key.lower() != 'objectclass':
76 a.append(key)
77 return a
78
80 a=[]
81 objectClasses = list(self.get('objectClass', []))
82 objectClasses.sort()
83 if objectClasses:
84 a.append(('objectClass', objectClasses))
85
86 l=list(self._attributes.items())
87 l.sort()
88 for key, values in l:
89 if key.lower() != 'objectclass':
90 vs = list(values)
91 vs.sort()
92 a.append((key, vs))
93
94 return a
95
97 a=[]
98
99 objectClasses = list(self.get('objectClass', []))
100 objectClasses.sort()
101 a.append(('objectClass', objectClasses))
102
103 l=list(self.items())
104 l.sort()
105 for key, values in l:
106 if key.lower() != 'objectclass':
107 vs = list(values)
108 vs.sort()
109 a.append((key, vs))
110 return ldif.asLDIF(self.dn, a)
111
112 - def __eq__(self, other):
113 if not isinstance(other, BaseLDAPEntry):
114 return 0
115 if self.dn != other.dn:
116 return 0
117
118 my=self.keys()
119 my.sort()
120 its=other.keys()
121 its.sort()
122 if my!=its:
123 return 0
124 for key in my:
125 myAttr=self[key]
126 itsAttr=other[key]
127 if myAttr!=itsAttr:
128 return 0
129 return 1
130
131 - def __ne__(self, other):
132 return not self==other
133
135 return len(self.keys())
136
137 - def __nonzero__(self):
139
140 - def __repr__(self):
141 x={}
142 for key in self.keys():
143 x[key]=self[key]
144 keys=self.keys()
145 keys.sort()
146 a=[]
147 for key in keys:
148 a.append('%s: %s' % (repr(key), repr(list(self[key]))))
149 attributes=', '.join(a)
150 return '%s(%s, {%s})' % (
151 self.__class__.__name__,
152 repr(str(self.dn)),
153 attributes)
154
155 - def diff(self, other):
156 """
157 Compute differences between this and another LDAP entry.
158
159 @param other: An LDAPEntry to compare to.
160
161 @return: None if equal, otherwise a ModifyOp that would make
162 this entry look like other.
163 """
164 assert self.dn == other.dn
165 if self == other:
166 return None
167
168 r = []
169
170 myKeys = set(self.keys())
171 otherKeys = set(other.keys())
172
173 addedKeys = list(otherKeys - myKeys)
174 addedKeys.sort()
175 for added in addedKeys:
176 r.append(delta.Add(added, other[added]))
177
178 deletedKeys = list(myKeys - otherKeys)
179 deletedKeys.sort()
180 for deleted in deletedKeys:
181 r.append(delta.Delete(deleted, self[deleted]))
182
183 sharedKeys = list(myKeys & otherKeys)
184 sharedKeys.sort()
185 for shared in sharedKeys:
186
187 addedValues = list(other[shared] - self[shared])
188 if addedValues:
189 addedValues.sort()
190 r.append(delta.Add(shared, addedValues))
191
192 deletedValues = list(self[shared] - other[shared])
193 if deletedValues:
194 deletedValues.sort()
195 r.append(delta.Delete(shared, deletedValues))
196
197 return delta.ModifyOp(dn=self.dn, modifications=r)
198
199 - def bind(self, password):
200 return defer.maybeDeferred(self._bind, password)
201
202 - def _bind(self, password):
203 for digest in self.get('userPassword', ()):
204 if digest.startswith('{SSHA}'):
205 raw = base64.decodestring(digest[len('{SSHA}'):])
206 salt = raw[20:]
207 got = sshaDigest(password, salt)
208 if got == digest:
209 return self
210 raise ldaperrors.LDAPInvalidCredentials
211
212 - def hasMember(self, dn):
213 for memberDN in self.get('member', []):
214 if memberDN == dn:
215 return True
216 return False
217
218 - def __hash__(self):
220
221 -class EditableLDAPEntry(BaseLDAPEntry):
222 implements(interfaces.IEditableLDAPEntry)
223
224 - def __setitem__(self, key, value):
225 new=self.buildAttributeSet(key, value)
226 self._attributes[key] = new
227
228 - def __delitem__(self, key):
229 del self._attributes[key]
230
232 raise NotImplementedError
233
235 raise NotImplementedError
236
237 - def move(self, newDN):
238 raise NotImplementedError
239
241 raise NotImplementedError
242
243 - def setPassword(self, newPasswd, salt=None):
244 crypt = sshaDigest(newPasswd, salt)
245 self['userPassword'] = [crypt]
246