2 This API is adapted for OpenLDAP.
3 The file contains all LDAP classes and methods needed to:
4 - Load the LDAP connection configuration file (login, address..) with
6 - Connect to LDAP with ldap_co
7 - Create a unique LDAP login and password for a user based on his email or
8 last name and first name with LoginPassword.
9 - Manage entries in LDAP using SFA records with LDAPapi
10 (Search, Add, Delete, Modify)
14 from passlib.hash import ldap_salted_sha1 as lssha
16 from sfa.util.xrn import get_authority
17 from sfa.util.sfalogging import logger
18 from sfa.util.config import Config
21 import ldap.modlist as modlist
28 Ldap configuration class loads the configuration file and sets the
29 ldap IP address, password, people dn, web dn, group dn. All these settings
30 were defined in a separate file ldap_config.py to avoid sharing them in
31 the SFA git as it contains sensible information.
34 def __init__(self, config_file='/etc/sfa/ldap_config.py'):
35 """Loads configuration from file /etc/sfa/ldap_config.py and set the
36 parameters for connection to LDAP.
41 execfile(config_file, self.__dict__)
43 self.config_file = config_file
44 # path to configuration data
45 self.config_path = os.path.dirname(config_file)
47 raise IOError, "Could not find or load the configuration file: %s" \
52 """ Set admin login and server configuration variables."""
55 """Fetch LdapConfig attributes (Ldap server connection parameters and
56 defines port , version and subtree scope.
59 #Iotlab PROD LDAP parameters
61 ldap_config = LdapConfig()
62 self.config = ldap_config
63 self.ldapHost = ldap_config.LDAP_IP_ADDRESS
64 self.ldapPeopleDN = ldap_config.LDAP_PEOPLE_DN
65 self.ldapGroupDN = ldap_config.LDAP_GROUP_DN
66 self.ldapAdminDN = ldap_config.LDAP_WEB_DN
67 self.ldapAdminPassword = ldap_config.LDAP_WEB_PASSWORD
68 self.ldapPort = ldap.PORT
69 self.ldapVersion = ldap.VERSION3
70 self.ldapSearchScope = ldap.SCOPE_SUBTREE
72 def connect(self, bind=True):
73 """Enables connection to the LDAP server.
75 :param bind: Set the bind parameter to True if a bind is needed
76 (for add/modify/delete operations). Set to False otherwise.
78 :returns: dictionary with status of the connection. True if Successful,
79 False if not and in this case the error
80 message( {'bool', 'message'} ).
85 self.ldapserv = ldap.open(self.ldapHost)
86 except ldap.LDAPError, error:
87 return {'bool': False, 'message': error}
89 # Bind with authentification
99 :returns: dictionary with the bind status. True if Successful,
100 False if not and in this case the error message({'bool','message'})
105 # Opens a connection after a call to ldap.open in connect:
106 self.ldapserv = ldap.initialize("ldap://" + self.ldapHost)
108 # Bind/authenticate with a user with apropriate
109 #rights to add objects
110 self.ldapserv.simple_bind_s(self.ldapAdminDN,
111 self.ldapAdminPassword)
113 except ldap.LDAPError, error:
114 return {'bool': False, 'message': error}
116 return {'bool': True}
119 """ Close the LDAP connection.
121 Can throw an exception if the unbinding fails.
122 :returns: dictionary with the bind status if fails.
123 False if not and in this case the error message({'bool','message'})
128 self.ldapserv.unbind_s()
129 except ldap.LDAPError, error:
130 return {'bool': False, 'message': error}
133 class LoginPassword():
136 Class to handle login and password generation, using custom login generation
143 Sets password and login maximum length, and defines the characters that
144 can be found in a random generated password.
147 self.login_max_length = 8
148 self.length_password = 8
149 self.chars_password = ['!', '$', '(',')', '*', '+', ',', '-', '.',
150 '0', '1', '2', '3', '4', '5', '6', '7', '8',
151 '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
152 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q',
153 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
154 '_', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
155 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
156 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
160 def clean_user_names(record):
163 Removes special characters such as '-', '_' , '[', ']' and ' ' from the
164 first name and last name.
166 :param record: user's record
168 :returns: lower_first_name and lower_last_name if they were found
169 in the user's record. Return None, none otherwise.
170 :rtype: string, string or None, None.
173 if 'first_name' in record and 'last_name' in record:
174 #Remove all special characters from first_name/last name
175 lower_first_name = record['first_name'].replace('-', '')\
176 .replace('_', '').replace('[', '')\
177 .replace(']', '').replace(' ', '')\
179 lower_last_name = record['last_name'].replace('-', '')\
180 .replace('_', '').replace('[', '')\
181 .replace(']', '').replace(' ', '')\
183 return lower_first_name, lower_last_name
188 def extract_name_from_email(record):
191 When there is no valid first name and last name in the record,
192 the email is used to generate the login. Here, we assume the email
193 is firstname.lastname@something.smthg. The first name and last names
194 are extracted from the email, special charcaters are removed and
195 they are changed into lower case.
197 :param record: user's data
199 :returns: the first name and last name taken from the user's email.
200 lower_first_name, lower_last_name.
201 :rtype: string, string
205 email = record['email']
206 email = email.split('@')[0].lower()
207 lower_first_name = None
208 lower_last_name = None
209 #Assume there is first name and last name in email
210 #if there is a separator
211 separator_list = ['.', '_', '-']
212 for sep in separator_list:
214 mail = email.split(sep)
215 lower_first_name = mail[0]
216 lower_last_name = mail[1]
219 #Otherwise just take the part before the @ as the
220 #lower_first_name and lower_last_name
221 if lower_first_name is None:
222 lower_first_name = email
223 lower_last_name = email
225 return lower_first_name, lower_last_name
227 def get_user_firstname_lastname(self, record):
230 Get the user first name and last name from the information we have in
233 :param record: user's information
235 :returns: the user's first name and last name.
237 .. seealso:: clean_user_names
238 .. seealso:: extract_name_from_email
241 lower_first_name, lower_last_name = self.clean_user_names(record)
243 #No first name and last name check email
244 if lower_first_name is None and lower_last_name is None:
246 lower_first_name, lower_last_name = \
247 self.extract_name_from_email(record)
249 return lower_first_name, lower_last_name
251 def choose_sets_chars_for_login(self, lower_first_name, lower_last_name):
254 Algorithm to select sets of characters from the first name and last
255 name, depending on the lenght of the last name and the maximum login
256 length which in our case is set to 8 characters.
258 :param lower_first_name: user's first name in lower case.
259 :param lower_last_name: usr's last name in lower case.
260 :returns: user's login
264 length_last_name = len(lower_last_name)
265 self.login_max_length = 8
267 #Try generating a unique login based on first name and last name
269 if length_last_name >= self.login_max_length:
270 login = lower_last_name[0:self.login_max_length]
272 logger.debug("login : %s index : %s" % (login, index))
273 elif length_last_name >= 4:
274 login = lower_last_name
276 logger.debug("login : %s index : %s" % (login, index))
277 elif length_last_name == 3:
278 login = lower_first_name[0:1] + lower_last_name
280 logger.debug("login : %s index : %s" % (login, index))
281 elif length_last_name == 2:
282 if len(lower_first_name) >= 2:
283 login = lower_first_name[0:2] + lower_last_name
285 logger.debug("login : %s index : %s" % (login, index))
287 logger.error("LoginException : \
288 Generation login error with \
289 minimum four characters")
292 logger.error("LDAP LdapGenerateUniqueLogin failed : \
293 impossible to generate unique login for %s %s"
294 % (lower_first_name, lower_last_name))
297 def generate_password(self):
300 Generate a password upon adding a new user in LDAP Directory
301 (8 characters length). The generated password is composed of characters
302 from the chars_password list.
304 :returns: the randomly generated password
310 length = len(self.chars_password)
311 for index in range(self.length_password):
312 char_index = random.randint(0, length - 1)
313 password += self.chars_password[char_index]
318 def encrypt_password(password):
321 Use passlib library to make a RFC2307 LDAP encrypted password salt size
322 is 8, use sha-1 algorithm.
324 :param password: password not encrypted.
325 :type password: string
326 :returns: Returns encrypted password.
330 #Keep consistency with Java Iotlab's LDAP API
331 #RFC2307SSHAPasswordEncryptor so set the salt size to 8 bytes
332 return lssha.encrypt(password, salt_size=8)
336 """Defines functions to insert and search entries in the LDAP.
340 logger.setLevelDebug()
345 self.login_pwd = LoginPassword()
346 self.authname = config.SFA_REGISTRY_ROOT_AUTH
347 self.conn = ldap_co()
348 self.ldapUserQuotaNFS = self.conn.config.LDAP_USER_QUOTA_NFS
349 self.ldapUserUidNumberMin = self.conn.config.LDAP_USER_UID_NUMBER_MIN
350 self.ldapUserGidNumber = self.conn.config.LDAP_USER_GID_NUMBER
351 self.ldapUserHomePath = self.conn.config.LDAP_USER_HOME_PATH
352 self.baseDN = self.conn.ldapPeopleDN
353 self.ldapShell = '/bin/bash'
356 def LdapGenerateUniqueLogin(self, record):
359 Generate login for adding a new user in LDAP Directory
360 (four characters minimum length). Get proper last name and
361 first name so that the user's login can be generated.
363 :param record: Record must contain first_name and last_name.
365 :returns: the generated login for the user described with record if the
366 login generation is successful, None if it fails.
367 :rtype: string or None
370 #For compatibility with other ldap func
371 if 'mail' in record and 'email' not in record:
372 record['email'] = record['mail']
374 lower_first_name, lower_last_name = \
375 self.login_pwd.get_user_firstname_lastname(record)
377 index, login = self.login_pwd.choose_sets_chars_for_login(
378 lower_first_name, lower_last_name)
380 login_filter = '(uid=' + login + ')'
383 #Check if login already in use
385 while (len(self.LdapSearch(login_filter, get_attrs)) is not 0):
389 logger.error("LoginException : Generation login error \
390 with minimum four characters")
394 lower_first_name[0:index] + \
396 self.login_pwd.login_max_length
398 login_filter = '(uid=' + login + ')'
400 print "lower_first_name - lower_last_name too short"
402 logger.debug("LDAP.API \t LdapGenerateUniqueLogin login %s"
406 except ldap.LDAPError, error:
407 logger.log_exc("LDAP LdapGenerateUniqueLogin Error %s" % (error))
410 def find_max_uidNumber(self):
411 """Find the LDAP max uidNumber (POSIX uid attribute).
413 Used when adding a new user in LDAP Directory
415 :returns: max uidNumber + 1
419 #First, get all the users in the LDAP
420 get_attrs = "(uidNumber=*)"
421 login_filter = ['uidNumber']
423 result_data = self.LdapSearch(get_attrs, login_filter)
424 #It there is no user in LDAP yet, First LDAP user
425 if result_data == []:
426 max_uidnumber = self.ldapUserUidNumberMin
427 #Otherwise, get the highest uidNumber
429 uidNumberList = [int(r[1]['uidNumber'][0])for r in result_data]
430 logger.debug("LDAPapi.py \tfind_max_uidNumber \
431 uidNumberList %s " % (uidNumberList))
432 max_uidnumber = max(uidNumberList) + 1
434 return str(max_uidnumber)
437 def get_ssh_pkey(self, record):
438 """TODO ; Get ssh public key from sfa record
439 To be filled by N. Turro ? or using GID pl way?
445 #TODO Handle OR filtering in the ldap query when
446 #dealing with a list of records instead of doing a for loop in GetPersons
447 def make_ldap_filters_from_record(record=None):
448 """Helper function to make LDAP filter requests out of SFA records.
450 :param record: user's sfa record. Should contain first_name,last_name,
451 email or mail, and if the record is enabled or not. If the dict
452 record does not have all of these, must at least contain the user's
455 :returns: LDAP request
462 if 'first_name' in record and 'last_name' in record:
463 if record['first_name'] != record['last_name']:
464 req_ldapdict['cn'] = str(record['first_name'])+" "\
465 + str(record['last_name'])
466 if 'email' in record:
467 req_ldapdict['mail'] = record['email']
469 req_ldapdict['mail'] = record['mail']
470 if 'enabled' in record:
471 if record['enabled'] is True:
472 req_ldapdict['shadowExpire'] = '-1'
474 req_ldapdict['shadowExpire'] = '0'
476 #Hrn should not be part of the filter because the hrn
477 #presented by a certificate of a SFA user not imported in
478 #Iotlab does not include the iotlab login in it
479 #Plus, the SFA user may already have an account with iotlab
480 #using another login.
482 logger.debug("\r\n \t LDAP.PY make_ldap_filters_from_record \
483 record %s req_ldapdict %s"
484 % (record, req_ldapdict))
486 for k in req_ldapdict:
487 req_ldap += '(' + str(k) + '=' + str(req_ldapdict[k]) + ')'
488 if len(req_ldapdict.keys()) >1 :
489 req_ldap = req_ldap[:0]+"(&"+req_ldap[0:]
491 req_ldap = req_ldap[:(size-1)] + ')' + req_ldap[(size-1):]
497 def make_ldap_attributes_from_record(self, record):
500 When adding a new user to Iotlab's LDAP, creates an attributes
501 dictionnary from the SFA record understandable by LDAP. Generates the
502 user's LDAP login.User is automatically validated (account enabled)
503 and described as a SFA USER FROM OUTSIDE IOTLAB.
505 :param record: must contain the following keys and values:
506 first_name, last_name, mail, pkey (ssh key).
508 :returns: dictionary of attributes using LDAP data structure model.
514 attrs['objectClass'] = ["top", "person", "inetOrgPerson",
515 "organizationalPerson", "posixAccount",
516 "shadowAccount", "systemQuotas",
519 attrs['uid'] = self.LdapGenerateUniqueLogin(record)
521 attrs['givenName'] = str(record['first_name']).lower().capitalize()
522 attrs['sn'] = str(record['last_name']).lower().capitalize()
523 attrs['cn'] = attrs['givenName'] + ' ' + attrs['sn']
524 attrs['gecos'] = attrs['givenName'] + ' ' + attrs['sn']
527 attrs['givenName'] = attrs['uid']
528 attrs['sn'] = attrs['uid']
529 attrs['cn'] = attrs['uid']
530 attrs['gecos'] = attrs['uid']
532 attrs['quota'] = self.ldapUserQuotaNFS
533 attrs['homeDirectory'] = self.ldapUserHomePath + attrs['uid']
534 attrs['loginShell'] = self.ldapShell
535 attrs['gidNumber'] = self.ldapUserGidNumber
536 attrs['uidNumber'] = self.find_max_uidNumber()
537 attrs['mail'] = record['mail'].lower()
539 attrs['sshPublicKey'] = record['pkey']
541 attrs['sshPublicKey'] = self.get_ssh_pkey(record)
544 #Password is automatically generated because SFA user don't go
545 #through the Iotlab website used to register new users,
546 #There is no place in SFA where users can enter such information
548 #If the user wants to set his own password , he must go to the Iotlab
550 password = self.login_pwd.generate_password()
551 attrs['userPassword'] = self.login_pwd.encrypt_password(password)
553 #Account automatically validated (no mail request to admins)
554 #Set to 0 to disable the account, -1 to enable it,
555 attrs['shadowExpire'] = '-1'
557 #Motivation field in Iotlab
558 attrs['description'] = 'SFA USER FROM OUTSIDE SENSLAB'
560 attrs['ou'] = 'SFA' #Optional: organizational unit
561 #No info about those here:
562 attrs['l'] = 'To be defined'#Optional: Locality.
563 attrs['st'] = 'To be defined' #Optional: state or province (country).
569 def LdapAddUser(self, record) :
570 """Add SFA user to LDAP if it is not in LDAP yet.
572 :param record: dictionnary with the user's data.
573 :returns: a dictionary with the status (Fail= False, Success= True)
574 and the uid of the newly added user if successful, or the error
575 meassage it is not. Dict has keys bool and message in case of
576 failure, and bool uid in case of success.
579 .. seealso:: make_ldap_filters_from_record
582 logger.debug(" \r\n \t LDAP LdapAddUser \r\n\r\n ================\r\n ")
583 user_ldap_attrs = self.make_ldap_attributes_from_record(record)
585 #Check if user already in LDAP wih email, first name and last name
586 filter_by = self.make_ldap_filters_from_record(user_ldap_attrs)
587 user_exist = self.LdapSearch(filter_by)
589 logger.warning(" \r\n \t LDAP LdapAddUser user %s %s \
590 already exists" % (user_ldap_attrs['sn'],
591 user_ldap_attrs['mail']))
592 return {'bool': False}
595 result = self.conn.connect()
599 # A dict to help build the "body" of the object
600 logger.debug(" \r\n \t LDAP LdapAddUser attrs %s "
603 # The dn of our new entry/object
604 dn = 'uid=' + user_ldap_attrs['uid'] + "," + self.baseDN
607 ldif = modlist.addModlist(user_ldap_attrs)
608 logger.debug("LDAPapi.py add attrs %s \r\n ldif %s"
609 % (user_ldap_attrs, ldif))
610 self.conn.ldapserv.add_s(dn, ldif)
612 logger.info("Adding user %s login %s in LDAP"
613 % (user_ldap_attrs['cn'], user_ldap_attrs['uid']))
614 except ldap.LDAPError, error:
615 logger.log_exc("LDAP Add Error %s" % error)
616 return {'bool': False, 'message': error}
619 return {'bool': True, 'uid': user_ldap_attrs['uid']}
623 def LdapDelete(self, person_dn):
624 """Deletes a person in LDAP. Uses the dn of the user.
626 :param person_dn: user's ldap dn.
627 :type person_dn: string
628 :returns: dictionary with bool True if successful, bool False
629 and the error if not.
634 result = self.conn.connect()
637 self.conn.ldapserv.delete_s(person_dn)
639 return {'bool': True}
641 except ldap.LDAPError, error:
642 logger.log_exc("LDAP Delete Error %s" % error)
643 return {'bool': False, 'message': error}
645 def LdapDeleteUser(self, record_filter):
646 """Deletes a SFA person in LDAP, based on the user's hrn.
648 :param record_filter: Filter to find the user to be deleted. Must
649 contain at least the user's email.
650 :type record_filter: dict
651 :returns: dict with bool True if successful, bool False and error
655 .. seealso:: LdapFindUser docstring for more info on record filter.
656 .. seealso:: LdapDelete for user deletion
659 #Find uid of the person
660 person = self.LdapFindUser(record_filter, [])
661 logger.debug("LDAPapi.py \t LdapDeleteUser record %s person %s"
662 % (record_filter, person))
665 dn = 'uid=' + person['uid'] + "," + self.baseDN
667 return {'bool': False}
669 result = self.LdapDelete(dn)
672 def LdapModify(self, dn, old_attributes_dict, new_attributes_dict):
673 """ Modifies a LDAP entry, replaces user's old attributes with
676 :param dn: user's absolute name in the LDAP hierarchy.
677 :param old_attributes_dict: old user's attributes. Keys must match
678 the ones used in the LDAP model.
679 :param new_attributes_dict: new user's attributes. Keys must match
680 the ones used in the LDAP model.
682 :type old_attributes_dict: dict
683 :type new_attributes_dict: dict
684 :returns: dict bool True if Successful, bool False if not.
689 ldif = modlist.modifyModlist(old_attributes_dict, new_attributes_dict)
690 # Connect and bind/authenticate
691 result = self.conn.connect()
694 self.conn.ldapserv.modify_s(dn, ldif)
696 return {'bool': True}
697 except ldap.LDAPError, error:
698 logger.log_exc("LDAP LdapModify Error %s" % error)
699 return {'bool': False}
702 def LdapModifyUser(self, user_record, new_attributes_dict):
705 Gets the record from one user based on the user sfa recordand changes
706 the attributes according to the specified new_attributes. Do not use
707 this if we need to modify the uid. Use a ModRDN operation instead
708 ( modify relative DN ).
710 :param user_record: sfa user record.
711 :param new_attributes_dict: new user attributes, keys must be the
712 same as the LDAP model.
713 :type user_record: dict
714 :type new_attributes_dict: dict
715 :returns: bool True if successful, bool False if not.
718 .. seealso:: make_ldap_filters_from_record for info on what is mandatory
720 .. seealso:: make_ldap_attributes_from_record for the LDAP objectclass.
723 if user_record is None:
724 logger.error("LDAP \t LdapModifyUser Need user record ")
725 return {'bool': False}
727 #Get all the attributes of the user_uid_login
728 #person = self.LdapFindUser(record_filter,[])
729 req_ldap = self.make_ldap_filters_from_record(user_record)
730 person_list = self.LdapSearch(req_ldap, [])
731 logger.debug("LDAPapi.py \t LdapModifyUser person_list : %s"
734 if person_list and len(person_list) > 1:
735 logger.error("LDAP \t LdapModifyUser Too many users returned")
736 return {'bool': False}
737 if person_list is None:
738 logger.error("LDAP \t LdapModifyUser User %s doesn't exist "
740 return {'bool': False}
742 # The dn of our existing entry/object
743 #One result only from ldapSearch
744 person = person_list[0][1]
745 dn = 'uid=' + person['uid'][0] + "," + self.baseDN
747 if new_attributes_dict:
749 for k in new_attributes_dict:
754 logger.debug(" LDAPapi.py \t LdapModifyUser new_attributes %s"
755 % (new_attributes_dict))
756 result = self.LdapModify(dn, old, new_attributes_dict)
759 logger.error("LDAP \t LdapModifyUser No new attributes given. ")
760 return {'bool': False}
763 def LdapMarkUserAsDeleted(self, record):
766 Sets shadowExpire to 0, disabling the user in LDAP. Calls LdapModifyUser
767 to change the shadowExpire of the user.
769 :param record: the record of the user who has to be disabled.
770 Should contain first_name,last_name, email or mail, and if the
771 record is enabled or not. If the dict record does not have all of
772 these, must at least contain the user's email.
774 :returns: {bool: True} if successful or {bool: False} if not
777 .. seealso:: LdapModifyUser, make_ldap_attributes_from_record
782 new_attrs['shadowExpire'] = '0'
783 logger.debug(" LDAPapi.py \t LdapMarkUserAsDeleted ")
784 ret = self.LdapModifyUser(record, new_attrs)
787 def LdapResetPassword(self, record):
788 """Resets password for the user whose record is the parameter and
789 changes the corresponding entry in the LDAP.
791 :param record: user's sfa record whose Ldap password must be reset.
792 Should contain first_name,last_name,
793 email or mail, and if the record is enabled or not. If the dict
794 record does not have all of these, must at least contain the user's
797 :returns: return value of LdapModifyUser. True if successful, False
800 .. seealso:: LdapModifyUser, make_ldap_attributes_from_record
803 password = self.login_pwd.generate_password()
805 attrs['userPassword'] = self.login_pwd.encrypt_password(password)
806 logger.debug("LDAP LdapResetPassword encrypt_password %s"
807 % (attrs['userPassword']))
808 result = self.LdapModifyUser(record, attrs)
812 def LdapSearch(self, req_ldap=None, expected_fields=None):
814 Used to search directly in LDAP, by using ldap filters and return
815 fields. When req_ldap is None, returns all the entries in the LDAP.
817 :param req_ldap: ldap style request, with appropriate filters,
819 :param expected_fields: Fields in the user ldap entry that has to be
820 returned. If None is provided, will return 'mail', 'givenName',
821 'sn', 'uid', 'sshPublicKey', 'shadowExpire'.
822 :type req_ldap: string
823 :type expected_fields: list
825 .. seealso:: make_ldap_filters_from_record for req_ldap format.
828 result = self.conn.connect(bind=False)
831 return_fields_list = []
832 if expected_fields is None:
833 return_fields_list = ['mail', 'givenName', 'sn', 'uid',
834 'sshPublicKey', 'shadowExpire']
836 return_fields_list = expected_fields
837 #No specifc request specified, get the whole LDAP
841 logger.debug("LDAP.PY \t LdapSearch req_ldap %s \
842 return_fields_list %s" \
843 %(req_ldap, return_fields_list))
846 msg_id = self.conn.ldapserv.search(
847 self.baseDN, ldap.SCOPE_SUBTREE,
848 req_ldap, return_fields_list)
849 #Get all the results matching the search from ldap in one
851 result_type, result_data = \
852 self.conn.ldapserv.result(msg_id, 1)
856 logger.debug("LDAP.PY \t LdapSearch result_data %s"
861 except ldap.LDAPError, error:
862 logger.log_exc("LDAP LdapSearch Error %s" % error)
866 logger.error("LDAP.PY \t Connection Failed")
869 def _process_ldap_info_for_all_users(self, result_data):
870 """Process the data of all enabled users in LDAP.
872 :param result_data: Contains information of all enabled users in LDAP
873 and is coming from LdapSearch.
874 :param result_data: list
876 .. seealso:: LdapSearch
880 logger.debug(" LDAP.py _process_ldap_info_for_all_users result_data %s "
882 for ldapentry in result_data:
883 logger.debug(" LDAP.py _process_ldap_info_for_all_users \
884 ldapentry name : %s " % (ldapentry[1]['uid'][0]))
885 tmpname = ldapentry[1]['uid'][0]
886 hrn = self.authname + "." + tmpname
888 tmpemail = ldapentry[1]['mail'][0]
889 if ldapentry[1]['mail'][0] == "unknown":
895 'pkey': ldapentry[1]['sshPublicKey'][0],
896 #'uid': ldapentry[1]['uid'][0],
899 #'email': ldapentry[1]['mail'][0],
900 'first_name': ldapentry[1]['givenName'][0],
901 'last_name': ldapentry[1]['sn'][0],
904 'authority': self.authname,
905 'peer_authority': '',
909 except KeyError, error:
910 logger.log_exc("LDAPapi.PY \t LdapFindUser EXCEPTION %s"
916 def _process_ldap_info_for_one_user(self, record, result_data):
919 Put the user's ldap data into shape. Only deals with one user
920 record and one user data from ldap.
922 :param record: user record
923 :param result_data: Raw ldap data coming from LdapSearch
924 :returns: user's data dict with 'type','pkey','uid', 'email',
925 'first_name' 'last_name''serial''authority''peer_authority'
928 :type result_data: list
932 #One entry only in the ldap data because we used a filter
933 #to find one user only
934 ldapentry = result_data[0][1]
935 logger.debug("LDAP.PY \t LdapFindUser ldapentry %s" % (ldapentry))
936 tmpname = ldapentry['uid'][0]
938 tmpemail = ldapentry['mail'][0]
939 if ldapentry['mail'][0] == "unknown":
943 peer_authority = None
946 parent_hrn = get_authority(hrn)
947 if parent_hrn != self.authname:
948 peer_authority = parent_hrn
949 #In case the user was not imported from Iotlab LDAP
950 #but from another federated site, has an account in
951 #iotlab but currently using his hrn from federated site
952 #then the login is different from the one found in its hrn
953 if tmpname != hrn.split('.')[1]:
960 'pkey': ldapentry['sshPublicKey'],
961 #'uid': ldapentry[1]['uid'][0],
964 #'email': ldapentry[1]['mail'][0],
965 'first_name': ldapentry['givenName'][0],
966 'last_name': ldapentry['sn'][0],
969 'authority': parent_hrn,
970 'peer_authority': peer_authority,
976 def LdapFindUser(self, record=None, is_user_enabled=None,
977 expected_fields=None):
980 Search a SFA user with a hrn. User should be already registered
983 :param record: sfa user's record. Should contain first_name,last_name,
984 email or mail. If no record is provided, returns all the users found
987 :param is_user_enabled: is the user's iotlab account already valid.
988 :type is_user_enabled: Boolean.
989 :returns: LDAP entries from ldap matching the filter provided. Returns
990 a single entry if one filter has been given and a list of
997 custom_record['enabled'] = is_user_enabled
999 custom_record.update(record)
1001 req_ldap = self.make_ldap_filters_from_record(custom_record)
1002 return_fields_list = []
1003 if expected_fields is None:
1004 return_fields_list = ['mail', 'givenName', 'sn', 'uid',
1007 return_fields_list = expected_fields
1009 result_data = self.LdapSearch(req_ldap, return_fields_list)
1010 logger.debug("LDAP.PY \t LdapFindUser result_data %s" % (result_data))
1012 if len(result_data) == 0:
1014 #Asked for a specific user
1015 if record is not None:
1016 results = self._process_ldap_info_for_one_user(record, result_data)
1019 #Asked for all users in ldap
1020 results = self._process_ldap_info_for_all_users(result_data)