2
# -*- coding: utf-8 -*-
4
import ldap, ldap.modlist, re, ldif, sys, random
5
from crypt import crypt
6
from exceptions import *
7
from configuration import Configuration
9
# This is just for debug
10
ldifwriter = ldif.LDIFWriter(sys.stdout)
14
"""Generate a random string to be used as salt
18
possible_letters = [ ".", "/" ]
19
ff = [ "a", "b", "c", "d", "e", "f", "g", "h", "i",
20
"j", "k", "h", "l", "m", "n", "o", "p", "q",
21
"r", "s", "t", "u", "v", "w", "x", "y", "z" ]
22
possible_letters.extend(ff)
23
possible_letters.extend(map(lambda x : x.capitalize(), ff))
24
for letter in range(0,length):
25
r += random.choice(possible_letters)
32
def __init__(self, ldap_output = None):
34
# Create an internatl dictionary that represent
39
# If ldap_output is a UserModel object use __init__()
40
# as a copy constructor
41
if isinstance(ldap_output, UserModel):
42
dn = ldap_output.get_dn()
43
dictionary = ldap_output.to_ldif()
45
# Copy internal objects
46
self.__ldif = dict(dictionary)
49
# Give reference to originating object away
52
# If ldap_output is passed to UserModel and is not
53
# a UserModel istance we can assume
54
# to build an object representing a real LdapObject
55
if ldap_output is not None:
56
self.__dn = ldap_output[0]
57
self.__ldif = dict(ldap_output[1])
59
# If you didn't set a name we can set it
61
if not ldap_output[1].has_key("givenName"):
62
self.set_given_name("")
64
# If that is not the case we shall give the object
65
# a Class that usually represent users in Ldap databases
67
self.__ldif['objectClass'] = ["inetOrgPerson",
71
"organizationalPerson",
74
def __getitem__(self, item):
75
"""Deprecated method to get item from the
76
internal dictionary"""
77
return self.__ldif[item]
79
def __setitem__(self, item, value):
80
"""Deprecated method to set item in the
81
internal dictionary"""
82
self.__ldif[item] = value
85
"""Return the distinguished name of the object.
86
Need some work to do the right thing."""
90
"""Set Distinguished name of the Ldap object"""
94
"""Return the uid number of the user, or 0
96
if self.__ldif.has_key("uidNumber"):
97
return self.__ldif['uidNumber'][0]
101
def set_uid(self, uid):
102
"""Set uid of the user. Setting it to zero
103
means to autodetermine the first free uid when
105
self.__ldif['uidNumber'] = [str(uid)]
107
def get_username(self):
108
"""Return username of the user"""
109
return self.__ldif['uid'][0]
111
def set_username(self, username):
112
"""Set username of the user"""
113
self.__ldif['uid'] = [str(username)]
116
"""Return gid number. You shall use the
117
method ldap_protocol.group_from_gid to
118
get the group_name"""
119
return self.__ldif['gidNumber'][0]
121
def set_gid(self, gid):
122
"""Set the gid of the user"""
123
self.__ldif['gidNumber'] = [str(gid)]
125
def get_given_name(self):
126
"""Get the given name (i.e. the name)
128
return self.__ldif['givenName'][0]
131
"""Return a complete name of the user, such
133
return self.__ldif['gecos'][0]
135
def set_given_name(self, given_name):
136
"""Set the given name (i.e. the name) of the
138
self.__ldif['givenName'] = [str(given_name)]
139
self.__ldif['cn'] = [str(given_name)]
141
# Gecos shall be in the form name + " " + surname, but
142
# if surname is not set, set only the name
143
if self.__ldif.has_key("sn"):
144
self.__ldif['gecos'] = [str(self.__ldif['givenName'][0] + " " +
145
self.__ldif['sn'][0]).strip()]
147
self.__ldif['gecos'] = [str(given_name)]
149
def get_surname(self):
150
"""Return surname of the user"""
151
return self.__ldif['sn'][0]
153
def set_surname(self, sn):
154
"""Set surname of the user"""
155
self.__ldif['sn'] = [str(sn)]
157
# Set gecos, including name if it is available
158
if self.__ldif.has_key("givenName"):
159
self.__ldif['gecos'] = [str(self.__ldif['givenName'][0] + " " +
160
self.__ldif['sn'][0]).strip()]
162
self.__ldif['gecos'] = [str(sn)]
165
"""Returns home directory of the user"""
166
return self.__ldif['homeDirectory'][0]
168
def set_home(self, home):
169
"""Set home directory of the user"""
170
self.__ldif['homeDirectory'] = [str(home)]
173
"""Return the shell of the user"""
174
return self.__ldif['loginShell'][0]
176
def set_shell(self, shell):
177
"""Set the shell of the user"""
178
self.__ldif['loginShell'] = [str(shell)]
180
def set_password(self, password, crypt_strategy = "CRYPT"):
181
"""Set userPassword field of the user. Only CRYPT
182
is supported at the moment being"""
183
if (crypt_strategy == "CRYPT"):
184
salt = "$1$%s$" % random_string()
185
value = "{CRYPT}" + crypt(password, salt)
186
self.__ldif['userPassword'] = [value]
188
raise LumUnsupportedError('$s crypt strategy is not supported')
191
return "%s a.k.a %s %s" % (self.get_uid(),
192
self.get_given_name(),
195
"""Return a dictionary to be passed to ldap
197
return dict(self.__ldif)
202
def __init__(self, uri, bind_dn, password, base_dn, users_ou, groups_ou):
204
Create a connection to the specified uri
209
self.__bind_dn = bind_dn
210
self.__base_dn = base_dn
211
self.__password = password
212
self.__users_ou = users_ou
213
self.__groups_ou = groups_ou
215
self.__ldap = ldap.initialize(uri)
217
# Bind to the database with the provided credentials
219
self.__ldap.simple_bind_s(bind_dn, password)
221
raise LumError('Error connecting to the server, check your credentials')
223
# Check if there are missing ou and add them
224
if not self.is_present(self.__users_ou):
225
print "Adding missing OrganizationalUnit: %s" % self.__users_ou
226
self.add_ou(self.__users_ou)
228
if not self.is_present(self.__groups_ou):
229
print "Adding missing OrganizationalUnit: %s" % self.__groups_ou
230
self.add_ou(self.__groups_ou)
232
def add_user(self, user, modify = False):
234
if result_type == ldap.RES_SEARCH_RESULT:
236
Add a user to the LDAP database
238
users_ou = self.__users_ou
239
if not self.is_present(users_ou):
240
raise LumError("users organizational unit not present!")
242
# This means that we have to autodetermine the first free uid
243
if (int(user.get_uid()) == 0):
244
user.set_uid(self.next_free_uid())
247
dn = "uid=%s,%s" % (user['uid'][0], users_ou)
249
self.__ldap.add_s(dn, ldap.modlist.addModlist(user.to_ldif()))
251
def modify_user(self, old_user, new_user):
252
"""Modify existing user (i.e. replace it)"""
253
users_ou = self.__users_ou
254
if not self.is_present(users_ou):
255
raise LumError("users organizational unit not present!")
258
new_dn = "uid=%s,%s" % (new_user.get_username(), users_ou)
259
old_dn = "uid=%s,%s" % (old_user.get_username(), users_ou)
261
self.__ldap.modify_s(old_dn, ldap.modlist.modifyModlist(old_user.to_ldif(), new_user.to_ldif()))
263
def add_group(self, group_name):
264
"""Add a new group, autodetermining gid."""
265
groups_ou = self.__groups_ou
267
dn = "cn=%s,%s" % (group_name, groups_ou)
270
'cn': [str(group_name)],
271
'gidNumber': [str(self.next_free_gid())],
272
'objectClass': ['posixGroup', 'top'],
275
self.__ldap.add_s(dn, ldap.modlist.addModlist(group_ldif))
277
def next_free_uid(self):
278
"""Determine next free uid"""
279
users = self.get_users()
280
uids = map(lambda x : int(x['uidNumber'][0]), users)
283
return (max(uids) + 1)
285
def next_free_gid(self):
286
"""Determine next free gid"""
287
groups_ou = self.__groups_ou
288
groups = self.__ldap.search_s(groups_ou, ldap.SCOPE_ONELEVEL, "cn=*")
293
gids = map(lambda x : int(x[1]['gidNumber'][0]), groups)
296
return (max(gids) + 1)
298
def delete_user(self, user):
300
Delete an user given the uid or the UserModel
302
self.__ldap.delete_s(user)
304
def change_password(self, uid, password):
305
"""Change password of selected user. If called when
306
user has no password it will set the passord, making
309
# Get the user from LDAP
310
user = self.get_user(uid)
312
# Create a copy of the old user and change
314
new_user = UserModel(user)
315
new_user.set_password(password)
318
self.modify_user(user, new_user)
321
def is_user_present(self, username):
323
Test if user is present
325
return self.is_present ("uid=%s" % username)
327
def gid_from_group(self, group_name):
328
"""Return gid of the given group"""
329
if self.__ldap is not None:
330
groups = self.__ldap.search_s(self.__base_dn,
331
ldap.SCOPE_SUBTREE, "cn=%s" % group_name)
335
return groups[0][1]['gidNumber'][0]
339
def group_from_gid(self, gid):
340
"""Return group name"""
341
if self.__ldap is not None:
342
group = self.__ldap.search_s(self.__groups_ou,
343
ldap.SCOPE_ONELEVEL, "gidNumber=%d" % int(gid))
346
return group[0][1]['cn'][0]
348
def is_present(self, ob):
350
Test if object is present in database
353
ob = ob.split(",")[0]
354
res = self.__ldap.search_s(self.__base_dn, ldap.SCOPE_SUBTREE, ob)
359
def add_ou(self, ou):
361
Add an organizationalUnit to the database.
362
Usage example: add_ou("People")
365
ldif['objectClass'] = ['organizationalUnit',
367
ldif['ou'] = [re.findall(r"ou=(\w+),", ou)[0]]
368
self.__ldap.add_s(ou, ldap.modlist.addModlist(ldif))
370
def get_user(self, uid):
371
users = self.__ldap.search_s(self.__users_ou, ldap.SCOPE_ONELEVEL, "uid=%s" % uid)
373
return UserModel(users[0])
375
raise LumUserNotFoundError("User %s not found in LDAP" % uid)
377
def get_users(self, key = None):
379
Get user that match the given key or all users if
382
msgid = self.__ldap.search(self.__users_ou, ldap.SCOPE_ONELEVEL, "uid=*")
383
return UserIterator(self.__ldap, msgid)
385
def get_groups(self, key = None):
388
groups = map(lambda x : x[1], self.__ldap.search_s(self.__groups_ou, ldap.SCOPE_ONELEVEL, "cn=%s" % key))
391
ret[group['gidNumber'][0]] = group['cn'][0]
394
class UserIterator():
395
"""UserIterator permits to extract ldap query
396
result while the ldap_module is still searching,
397
reducing search time"""
399
def __init__(self, ldap_connection, msgid):
400
self.__ldap = ldap_connection
407
result_type, data = self.__ldap.result(msgid = self.__msgid, all = 0)
409
return UserModel(user)
410
if result_type == ldap.RES_SEARCH_RESULT: