X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=sfa%2Fiotlab%2FLDAPapi.py;h=254f994250c0241cea2f5113587ea52abae553cf;hb=a18ad5b1dbc2a1dd346784105c89a0714086ebc4;hp=98a72ec8a1ab2a725b16ca88f07354d191f29601;hpb=efa9ed860628dba98c63b7722f472d922fbe8a87;p=sfa.git diff --git a/sfa/iotlab/LDAPapi.py b/sfa/iotlab/LDAPapi.py index 98a72ec8..254f9942 100644 --- a/sfa/iotlab/LDAPapi.py +++ b/sfa/iotlab/LDAPapi.py @@ -1,19 +1,41 @@ +""" +This API is adapted for OpenLDAP. +The file contains all LDAP classes and methods needed to: + - Load the LDAP connection configuration file (login, address..) with + LdapConfig + - Connect to LDAP with ldap_co + - Create a unique LDAP login and password for a user based on his email or + last name and first name with LoginPassword. + - Manage entries in LDAP using SFA records with LDAPapi + (Search, Add, Delete, Modify) + +""" import random from passlib.hash import ldap_salted_sha1 as lssha + from sfa.util.xrn import get_authority -import ldap +from sfa.util.sfalogging import logger from sfa.util.config import Config - +import ldap import ldap.modlist as modlist -from sfa.util.sfalogging import logger -import os.path -#API for OpenLDAP +import os.path class LdapConfig(): - def __init__(self, config_file = '/etc/sfa/ldap_config.py'): + """ + Ldap configuration class loads the configuration file and sets the + ldap IP address, password, people dn, web dn, group dn. All these settings + were defined in a separate file ldap_config.py to avoid sharing them in + the SFA git as it contains sensible information. + + """ + def __init__(self, config_file='/etc/sfa/ldap_config.py'): + """Loads configuration from file /etc/sfa/ldap_config.py and set the + parameters for connection to LDAP. + + """ try: execfile(config_file, self.__dict__) @@ -23,13 +45,17 @@ class LdapConfig(): self.config_path = os.path.dirname(config_file) except IOError: raise IOError, "Could not find or load the configuration file: %s" \ - % config_file + % config_file class ldap_co: """ Set admin login and server configuration variables.""" def __init__(self): + """Fetch LdapConfig attributes (Ldap server connection parameters and + defines port , version and subtree scope. + + """ #Iotlab PROD LDAP parameters self.ldapserv = None ldap_config = LdapConfig() @@ -39,24 +65,20 @@ class ldap_co: self.ldapGroupDN = ldap_config.LDAP_GROUP_DN self.ldapAdminDN = ldap_config.LDAP_WEB_DN self.ldapAdminPassword = ldap_config.LDAP_WEB_PASSWORD - - self.ldapPort = ldap.PORT - self.ldapVersion = ldap.VERSION3 + self.ldapVersion = ldap.VERSION3 self.ldapSearchScope = ldap.SCOPE_SUBTREE - def connect(self, bind=True): - """ - Enables connection to the LDAP server. - :param bind : Set the bind parameter to True if a bind is needed - (for add/modify/delete operations). - Set to False otherwise. - - :type bind : boolean - :return: dictionary with status of the connection. True if Successful, - False if not and in this case the error message( {'bool', 'message'} ) - :rtype:dict + """Enables connection to the LDAP server. + + :param bind: Set the bind parameter to True if a bind is needed + (for add/modify/delete operations). Set to False otherwise. + :type bind: boolean + :returns: dictionary with status of the connection. True if Successful, + False if not and in this case the error + message( {'bool', 'message'} ). + :rtype: dict """ try: @@ -73,8 +95,9 @@ class ldap_co: def bind(self): """ Binding method. - :return: dictionary with the bind status. True if Successful, - False if not and in this case the error message( {'bool', 'message'} ) + + :returns: dictionary with the bind status. True if Successful, + False if not and in this case the error message({'bool','message'}) :rtype: dict """ @@ -84,8 +107,8 @@ class ldap_co: # Bind/authenticate with a user with apropriate #rights to add objects - self.ldapserv.simple_bind_s(self.ldapAdminDN, \ - self.ldapAdminPassword) + self.ldapserv.simple_bind_s(self.ldapAdminDN, + self.ldapAdminPassword) except ldap.LDAPError, error: return {'bool': False, 'message': error} @@ -96,6 +119,9 @@ class ldap_co: """ Close the LDAP connection. Can throw an exception if the unbinding fails. + :returns: dictionary with the bind status if fails. + False if not and in this case the error message({'bool','message'}) + :rtype: dict or None """ try: @@ -114,8 +140,8 @@ class LoginPassword(): def __init__(self): """ - Sets password and login maximum length, and defines the characters - that can be found in a random generated password. + Sets password and login maximum length, and defines the characters that + can be found in a random generated password. """ self.login_max_length = 8 @@ -134,13 +160,13 @@ class LoginPassword(): def clean_user_names(record): """ - Removes special characters such as - '-', '_' , '[', ']' and ' ' from the first name and last name. + Removes special characters such as '-', '_' , '[', ']' and ' ' from the + first name and last name. :param record: user's record - :type record:dict - :return: lower_first_name and lower_last_name if they were found - in the user's record. Return None, none otherwise. + :type record: dict + :returns: lower_first_name and lower_last_name if they were found + in the user's record. Return None, none otherwise. :rtype: string, string or None, None. """ @@ -161,16 +187,19 @@ class LoginPassword(): @staticmethod def extract_name_from_email(record): """ + When there is no valid first name and last name in the record, the email is used to generate the login. Here, we assume the email is firstname.lastname@something.smthg. The first name and last names are extracted from the email, special charcaters are removed and they are changed into lower case. + :param record: user's data - :type record:dict - :return: the first name and last name taken from the user's email. - lower_first_name, lower_last_name. + :type record: dict + :returns: the first name and last name taken from the user's email. + lower_first_name, lower_last_name. :rtype: string, string + """ email = record['email'] @@ -196,13 +225,18 @@ class LoginPassword(): return lower_first_name, lower_last_name def get_user_firstname_lastname(self, record): - """Get the user first name and last name from the information - we have in the record. + """ + + Get the user first name and last name from the information we have in + the record. + :param record: user's information :type record: dict - :return: the user's first name and last name. - ..seealso: clean_user_names - ..seealso: extract_name_from_email + :returns: the user's first name and last name. + + .. seealso:: clean_user_names + .. seealso:: extract_name_from_email + """ lower_first_name, lower_last_name = self.clean_user_names(record) @@ -210,62 +244,64 @@ class LoginPassword(): if lower_first_name is None and lower_last_name is None: lower_first_name, lower_last_name = \ - self.extract_name_from_email(record) + self.extract_name_from_email(record) return lower_first_name, lower_last_name - def choose_sets_chars_for_login(self, lower_first_name, lower_last_name): """ - Algorithm to select sets of characters from the first name and - last name, depending on the lenght of the last name and the - maximum login length which in our case is set to 8 charachetrs. + + Algorithm to select sets of characters from the first name and last + name, depending on the lenght of the last name and the maximum login + length which in our case is set to 8 characters. + :param lower_first_name: user's first name in lower case. :param lower_last_name: usr's last name in lower case. - :return: user's login - :rtype:string + :returns: user's login + :rtype: string + """ length_last_name = len(lower_last_name) self.login_max_length = 8 #Try generating a unique login based on first name and last name - if length_last_name >= self.login_max_length : + if length_last_name >= self.login_max_length: login = lower_last_name[0:self.login_max_length] index = 0 - logger.debug("login : %s index : %s" %(login, index)) - elif length_last_name >= 4 : + logger.debug("login : %s index : %s" % (login, index)) + elif length_last_name >= 4: login = lower_last_name index = 0 - logger.debug("login : %s index : %s" %(login, index)) - elif length_last_name == 3 : + logger.debug("login : %s index : %s" % (login, index)) + elif length_last_name == 3: login = lower_first_name[0:1] + lower_last_name index = 1 - logger.debug("login : %s index : %s" %(login, index)) + logger.debug("login : %s index : %s" % (login, index)) elif length_last_name == 2: - if len ( lower_first_name) >=2: + if len(lower_first_name) >= 2: login = lower_first_name[0:2] + lower_last_name index = 2 - logger.debug("login : %s index : %s" %(login, index)) + logger.debug("login : %s index : %s" % (login, index)) else: logger.error("LoginException : \ Generation login error with \ minimum four characters") - else : + else: logger.error("LDAP LdapGenerateUniqueLogin failed : \ - impossible to generate unique login for %s %s" \ - %(lower_first_name,lower_last_name)) + impossible to generate unique login for %s %s" + % (lower_first_name, lower_last_name)) return index, login - - def generate_password(self): + """ - """Generate a password upon adding a new user in LDAP Directory - (8 characters length). The generated password is composed of characters - from the charsPassword list - :return: the randomly generated password + Generate a password upon adding a new user in LDAP Directory + (8 characters length). The generated password is composed of characters + from the chars_password list. + + :returns: the randomly generated password :rtype: string """ @@ -273,27 +309,33 @@ class LoginPassword(): length = len(self.chars_password) for index in range(self.length_password): - char_index = random.randint(0, length-1) + char_index = random.randint(0, length - 1) password += self.chars_password[char_index] return password @staticmethod def encrypt_password(password): - """ Use passlib library to make a RFC2307 LDAP encrypted password - salt size = 8, use sha-1 algorithm. + """ + + Use passlib library to make a RFC2307 LDAP encrypted password salt size + is 8, use sha-1 algorithm. + :param password: password not encrypted. :type password: string - :return: Returns encrypted password. - :rtype:string + :returns: Returns encrypted password. + :rtype: string + """ #Keep consistency with Java Iotlab's LDAP API #RFC2307SSHAPasswordEncryptor so set the salt size to 8 bytes - return lssha.encrypt(password, salt_size = 8) + return lssha.encrypt(password, salt_size=8) +class LDAPapi: + """Defines functions to insert and search entries in the LDAP. -class LDAPapi : + """ def __init__(self): logger.setLevelDebug() @@ -302,24 +344,17 @@ class LDAPapi : config = Config() self.login_pwd = LoginPassword() self.authname = config.SFA_REGISTRY_ROOT_AUTH - self.conn = ldap_co() self.ldapUserQuotaNFS = self.conn.config.LDAP_USER_QUOTA_NFS self.ldapUserUidNumberMin = self.conn.config.LDAP_USER_UID_NUMBER_MIN self.ldapUserGidNumber = self.conn.config.LDAP_USER_GID_NUMBER self.ldapUserHomePath = self.conn.config.LDAP_USER_HOME_PATH - self.baseDN = self.conn.ldapPeopleDN - - - self.ldapShell = '/bin/bash' - def LdapGenerateUniqueLogin(self, record): """ -<<<<<<< HEAD:sfa/iotlab/LDAPapi.py Generate login for adding a new user in LDAP Directory (four characters minimum length). Get proper last name and @@ -327,8 +362,8 @@ class LDAPapi : :param record: Record must contain first_name and last_name. :param record: dict - :return: the generated login for the user described with record if the - login generation is successful, None if it fails. + :returns: the generated login for the user described with record if the + login generation is successful, None if it fails. :rtype: string or None """ @@ -337,18 +372,17 @@ class LDAPapi : record['email'] = record['mail'] lower_first_name, lower_last_name = \ - self.login_pwd.get_user_firstname_lastname(record) + self.login_pwd.get_user_firstname_lastname(record) + index, login = self.login_pwd.choose_sets_chars_for_login( + lower_first_name, lower_last_name) - index, login = self.login_pwd.choose_sets_chars_for_login( \ - lower_first_name, \ - lower_last_name) login_filter = '(uid=' + login + ')' get_attrs = ['uid'] - try : + try: #Check if login already in use - while (len(self.LdapSearch(login_filter, get_attrs)) is not 0 ): + while (len(self.LdapSearch(login_filter, get_attrs)) is not 0): index += 1 if index >= 9: @@ -357,26 +391,30 @@ class LDAPapi : else: try: login = \ - lower_first_name[0:index] + \ - lower_last_name[0:self.login_pwd.login_max_length-index] - login_filter = '(uid='+ login+ ')' + lower_first_name[0:index] + \ + lower_last_name[0: + self.login_pwd.login_max_length + - index] + login_filter = '(uid=' + login + ')' except KeyError: print "lower_first_name - lower_last_name too short" - logger.debug("LDAP.API \t LdapGenerateUniqueLogin login %s"%(login)) + logger.debug("LDAP.API \t LdapGenerateUniqueLogin login %s" + % (login)) return login - except ldap.LDAPError, error : - logger.log_exc("LDAP LdapGenerateUniqueLogin Error %s" %error) + except ldap.LDAPError, error: + logger.log_exc("LDAP LdapGenerateUniqueLogin Error %s" % (error)) return None - def find_max_uidNumber(self): + """Find the LDAP max uidNumber (POSIX uid attribute). - """Find the LDAP max uidNumber (POSIX uid attribute) . Used when adding a new user in LDAP Directory - :return: max uidNumber + 1 - :rtype:string + + :returns: max uidNumber + 1 + :rtype: string + """ #First, get all the users in the LDAP get_attrs = "(uidNumber=*)" @@ -388,10 +426,9 @@ class LDAPapi : max_uidnumber = self.ldapUserUidNumberMin #Otherwise, get the highest uidNumber else: - - uidNumberList = [int(r[1]['uidNumber'][0])for r in result_data ] + uidNumberList = [int(r[1]['uidNumber'][0])for r in result_data] logger.debug("LDAPapi.py \tfind_max_uidNumber \ - uidNumberList %s " %(uidNumberList)) + uidNumberList %s " % (uidNumberList)) max_uidnumber = max(uidNumberList) + 1 return str(max_uidnumber) @@ -407,29 +444,31 @@ class LDAPapi : @staticmethod #TODO Handle OR filtering in the ldap query when #dealing with a list of records instead of doing a for loop in GetPersons - def make_ldap_filters_from_record( record=None): - """ - Helper function to make LDAP filter requests out of SFA records. + def make_ldap_filters_from_record(record=None): + """Helper function to make LDAP filter requests out of SFA records. + :param record: user's sfa record. Should contain first_name,last_name, - email or mail, and if the record is enabled or not. If the dict - record does not have all of these, must at least contain the user's - email. + email or mail, and if the record is enabled or not. If the dict + record does not have all of these, must at least contain the user's + email. :type record: dict - :return: LDAP request + :returns: LDAP request :rtype: string + """ req_ldap = '' req_ldapdict = {} if record : - if 'first_name' in record and 'last_name' in record: - req_ldapdict['cn'] = str(record['first_name'])+" "\ - + str(record['last_name']) - if 'email' in record : + if 'first_name' in record and 'last_name' in record: + if record['first_name'] != record['last_name']: + req_ldapdict['cn'] = str(record['first_name'])+" "\ + + str(record['last_name']) + if 'email' in record: req_ldapdict['mail'] = record['email'] if 'mail' in record: req_ldapdict['mail'] = record['mail'] if 'enabled' in record: - if record['enabled'] == True : + if record['enabled'] is True: req_ldapdict['shadowExpire'] = '-1' else: req_ldapdict['shadowExpire'] = '0' @@ -440,45 +479,42 @@ class LDAPapi : #Plus, the SFA user may already have an account with iotlab #using another login. - - logger.debug("\r\n \t LDAP.PY make_ldap_filters_from_record \ - record %s req_ldapdict %s" \ - %(record, req_ldapdict)) + record %s req_ldapdict %s" + % (record, req_ldapdict)) for k in req_ldapdict: - req_ldap += '('+ str(k)+ '=' + str(req_ldapdict[k]) + ')' - if len(req_ldapdict.keys()) >1 : + req_ldap += '(' + str(k) + '=' + str(req_ldapdict[k]) + ')' + if len(req_ldapdict.keys()) >1 : req_ldap = req_ldap[:0]+"(&"+req_ldap[0:] size = len(req_ldap) - req_ldap = req_ldap[:(size-1)] +')'+ req_ldap[(size-1):] + req_ldap = req_ldap[:(size-1)] + ')' + req_ldap[(size-1):] else: req_ldap = "(cn=*)" return req_ldap def make_ldap_attributes_from_record(self, record): - """When adding a new user to Iotlab's LDAP, creates an attributes - dictionnary from the SFA record understandable by LDAP. - Generates the user's LDAP login. - User is automatically validated (account enabled) and described - as a SFA USER FROM OUTSIDE SENSLAB'. + """ + + When adding a new user to Iotlab's LDAP, creates an attributes + dictionnary from the SFA record understandable by LDAP. Generates the + user's LDAP login.User is automatically validated (account enabled) + and described as a SFA USER FROM OUTSIDE IOTLAB. + :param record: must contain the following keys and values: - first_name, last_name, mail, pkey (ssh key). + first_name, last_name, mail, pkey (ssh key). :type record: dict - - :return: dictionary of attributes using LDAP data structure - model. + :returns: dictionary of attributes using LDAP data structure model. :rtype: dict """ attrs = {} - attrs['objectClass'] = ["top", "person", "inetOrgPerson", \ - "organizationalPerson", "posixAccount", \ - "shadowAccount", "systemQuotas", \ - "ldapPublicKey"] - + attrs['objectClass'] = ["top", "person", "inetOrgPerson", + "organizationalPerson", "posixAccount", + "shadowAccount", "systemQuotas", + "ldapPublicKey"] attrs['uid'] = self.LdapGenerateUniqueLogin(record) try: @@ -493,7 +529,6 @@ class LDAPapi : attrs['cn'] = attrs['uid'] attrs['gecos'] = attrs['uid'] - attrs['quota'] = self.ldapUserQuotaNFS attrs['homeDirectory'] = self.ldapUserHomePath + attrs['uid'] attrs['loginShell'] = self.ldapShell @@ -533,28 +568,27 @@ class LDAPapi : def LdapAddUser(self, record) : """Add SFA user to LDAP if it is not in LDAP yet. - :param record: dictionnary with the user's data. - :return: a dictionary with the status (Fail= False, Success= True) - and the uid of the newly added user if successful, or the error - meassage it is not. Dict has keys bool and message in case of failure, - and bool uid in case of success. + :param record: dictionnary with the user's data. + :returns: a dictionary with the status (Fail= False, Success= True) + and the uid of the newly added user if successful, or the error + meassage it is not. Dict has keys bool and message in case of + failure, and bool uid in case of success. :rtype: dict - ..seealso: make_ldap_filters_from_record + .. seealso:: make_ldap_filters_from_record """ logger.debug(" \r\n \t LDAP LdapAddUser \r\n\r\n ================\r\n ") user_ldap_attrs = self.make_ldap_attributes_from_record(record) - #Check if user already in LDAP wih email, first name and last name filter_by = self.make_ldap_filters_from_record(user_ldap_attrs) user_exist = self.LdapSearch(filter_by) if user_exist: logger.warning(" \r\n \t LDAP LdapAddUser user %s %s \ - already exists" %(user_ldap_attrs['sn'], \ - user_ldap_attrs['mail'])) + already exists" % (user_ldap_attrs['sn'], + user_ldap_attrs['mail'])) return {'bool': False} #Bind to the server @@ -563,40 +597,38 @@ class LDAPapi : if(result['bool']): # A dict to help build the "body" of the object - - logger.debug(" \r\n \t LDAP LdapAddUser attrs %s " %user_ldap_attrs) + logger.debug(" \r\n \t LDAP LdapAddUser attrs %s " + % user_ldap_attrs) # The dn of our new entry/object dn = 'uid=' + user_ldap_attrs['uid'] + "," + self.baseDN try: ldif = modlist.addModlist(user_ldap_attrs) - logger.debug("LDAPapi.py add attrs %s \r\n ldif %s"\ - %(user_ldap_attrs, ldif) ) + logger.debug("LDAPapi.py add attrs %s \r\n ldif %s" + % (user_ldap_attrs, ldif)) self.conn.ldapserv.add_s(dn, ldif) - logger.info("Adding user %s login %s in LDAP" \ - %(user_ldap_attrs['cn'] , user_ldap_attrs['uid'])) - - + logger.info("Adding user %s login %s in LDAP" + % (user_ldap_attrs['cn'], user_ldap_attrs['uid'])) except ldap.LDAPError, error: - logger.log_exc("LDAP Add Error %s" %error) + logger.log_exc("LDAP Add Error %s" % error) return {'bool': False, 'message': error} self.conn.close() - return {'bool': True, 'uid':user_ldap_attrs['uid']} + return {'bool': True, 'uid': user_ldap_attrs['uid']} else: return result - def LdapDelete(self, person_dn): - """ - Deletes a person in LDAP. Uses the dn of the user. + """Deletes a person in LDAP. Uses the dn of the user. + :param person_dn: user's ldap dn. :type person_dn: string - :return: dictionary with bool True if successful, bool False - and the error if not. - :rtype:dict + :returns: dictionary with bool True if successful, bool False + and the error if not. + :rtype: dict + """ #Connect and bind result = self.conn.connect() @@ -607,26 +639,27 @@ class LDAPapi : return {'bool': True} except ldap.LDAPError, error: - logger.log_exc("LDAP Delete Error %s" %error) + logger.log_exc("LDAP Delete Error %s" % error) return {'bool': False, 'message': error} - def LdapDeleteUser(self, record_filter): - """ - Deletes a SFA person in LDAP, based on the user's hrn. + """Deletes a SFA person in LDAP, based on the user's hrn. + :param record_filter: Filter to find the user to be deleted. Must - contain at least the user's email. + contain at least the user's email. :type record_filter: dict - :return: dict with bool True if successful, bool False and error message - otherwise - :rtype:dict - ..seealso: LdapFindUser docstring for more info on record filter. - ..seealso: LdapDelete for user deletion + :returns: dict with bool True if successful, bool False and error + message otherwise. + :rtype: dict + + .. seealso:: LdapFindUser docstring for more info on record filter. + .. seealso:: LdapDelete for user deletion + """ #Find uid of the person person = self.LdapFindUser(record_filter, []) - logger.debug("LDAPapi.py \t LdapDeleteUser record %s person %s" \ - %(record_filter, person)) + logger.debug("LDAPapi.py \t LdapDeleteUser record %s person %s" + % (record_filter, person)) if person: dn = 'uid=' + person['uid'] + "," + self.baseDN @@ -636,20 +669,21 @@ class LDAPapi : result = self.LdapDelete(dn) return result - def LdapModify(self, dn, old_attributes_dict, new_attributes_dict): """ Modifies a LDAP entry, replaces user's old attributes with the new ones given. + :param dn: user's absolute name in the LDAP hierarchy. :param old_attributes_dict: old user's attributes. Keys must match - the ones used in the LDAP model. + the ones used in the LDAP model. :param new_attributes_dict: new user's attributes. Keys must match - the ones used in the LDAP model. + the ones used in the LDAP model. :type dn: string :type old_attributes_dict: dict :type new_attributes_dict: dict - :return: dict bool True if Successful, bool False if not. - :rtype:dict + :returns: dict bool True if Successful, bool False if not. + :rtype: dict + """ ldif = modlist.modifyModlist(old_attributes_dict, new_attributes_dict) @@ -659,28 +693,32 @@ class LDAPapi : try: self.conn.ldapserv.modify_s(dn, ldif) self.conn.close() - return {'bool' : True } + return {'bool': True} except ldap.LDAPError, error: - logger.log_exc("LDAP LdapModify Error %s" %error) - return {'bool' : False } + logger.log_exc("LDAP LdapModify Error %s" % error) + return {'bool': False} def LdapModifyUser(self, user_record, new_attributes_dict): """ - Gets the record from one user based on the user sfa record - and changes the attributes according to the specified new_attributes. - Do not use this if we need to modify the uid. Use a ModRDN - #operation instead ( modify relative DN ) + + Gets the record from one user based on the user sfa recordand changes + the attributes according to the specified new_attributes. Do not use + this if we need to modify the uid. Use a ModRDN operation instead + ( modify relative DN ). + :param user_record: sfa user record. :param new_attributes_dict: new user attributes, keys must be the - same as the LDAP model. + same as the LDAP model. :type user_record: dict :type new_attributes_dict: dict - :return: bool True if successful, bool False if not. + :returns: bool True if successful, bool False if not. :rtype: dict - ..seealso: make_ldap_filters_from_record for info on what is mandatory - in the user_record. - ..seealso: make_ldap_attributes_from_record for the LDAP objectclass. + + .. seealso:: make_ldap_filters_from_record for info on what is mandatory + in the user_record. + .. seealso:: make_ldap_attributes_from_record for the LDAP objectclass. + """ if user_record is None: logger.error("LDAP \t LdapModifyUser Need user record ") @@ -690,30 +728,31 @@ class LDAPapi : #person = self.LdapFindUser(record_filter,[]) req_ldap = self.make_ldap_filters_from_record(user_record) person_list = self.LdapSearch(req_ldap, []) - logger.debug("LDAPapi.py \t LdapModifyUser person_list : %s" \ - %(person_list)) - if person_list and len(person_list) > 1 : + logger.debug("LDAPapi.py \t LdapModifyUser person_list : %s" + % (person_list)) + + if person_list and len(person_list) > 1: logger.error("LDAP \t LdapModifyUser Too many users returned") return {'bool': False} - if person_list is None : - logger.error("LDAP \t LdapModifyUser User %s doesn't exist "\ - %(user_record)) + if person_list is None: + logger.error("LDAP \t LdapModifyUser User %s doesn't exist " + % (user_record)) return {'bool': False} # The dn of our existing entry/object #One result only from ldapSearch person = person_list[0][1] - dn = 'uid=' + person['uid'][0] + "," + self.baseDN + dn = 'uid=' + person['uid'][0] + "," + self.baseDN if new_attributes_dict: old = {} for k in new_attributes_dict: if k not in person: - old[k] = '' - else : + old[k] = '' + else: old[k] = person[k] - logger.debug(" LDAPapi.py \t LdapModifyUser new_attributes %s"\ - %( new_attributes_dict)) + logger.debug(" LDAPapi.py \t LdapModifyUser new_attributes %s" + % (new_attributes_dict)) result = self.LdapModify(dn, old, new_attributes_dict) return result else: @@ -721,17 +760,21 @@ class LDAPapi : return {'bool': False} - - def LdapMarkUserAsDeleted(self, record): """ - Sets shadowExpire to 0, disabling the user in LDAP. - Calls LdapModifyUser to change the shadowExpire of the user. + + Sets shadowExpire to 0, disabling the user in LDAP. Calls LdapModifyUser + to change the shadowExpire of the user. + :param record: the record of the user who has to be disabled. + Should contain first_name,last_name, email or mail, and if the + record is enabled or not. If the dict record does not have all of + these, must at least contain the user's email. :type record: dict - :return: bool True if successful or bool False if not - :rtype:dict - ..seealso: LdapModifyUser + :returns: {bool: True} if successful or {bool: False} if not + :rtype: dict + + .. seealso:: LdapModifyUser, make_ldap_attributes_from_record """ new_attrs = {} @@ -741,40 +784,58 @@ class LDAPapi : ret = self.LdapModifyUser(record, new_attrs) return ret - def LdapResetPassword(self, record): - """ - Resets password for the user whose record is the parameter and changes - the corresponding entry in the LDAP. + """Resets password for the user whose record is the parameter and + changes the corresponding entry in the LDAP. + + :param record: user's sfa record whose Ldap password must be reset. + Should contain first_name,last_name, + email or mail, and if the record is enabled or not. If the dict + record does not have all of these, must at least contain the user's + email. + :type record: dict + :returns: return value of LdapModifyUser. True if successful, False + otherwise. + + .. seealso:: LdapModifyUser, make_ldap_attributes_from_record """ password = self.login_pwd.generate_password() attrs = {} attrs['userPassword'] = self.login_pwd.encrypt_password(password) - logger.debug("LDAP LdapResetPassword encrypt_password %s"\ - %(attrs['userPassword'])) + logger.debug("LDAP LdapResetPassword encrypt_password %s" + % (attrs['userPassword'])) result = self.LdapModifyUser(record, attrs) return result - def LdapSearch (self, req_ldap = None, expected_fields = None ): + def LdapSearch(self, req_ldap=None, expected_fields=None): """ - Used to search directly in LDAP, by using ldap filters and - return fields. - When req_ldap is None, returns all the entries in the LDAP. + Used to search directly in LDAP, by using ldap filters and return + fields. When req_ldap is None, returns all the entries in the LDAP. + + :param req_ldap: ldap style request, with appropriate filters, + example: (cn=*). + :param expected_fields: Fields in the user ldap entry that has to be + returned. If None is provided, will return 'mail', 'givenName', + 'sn', 'uid', 'sshPublicKey', 'shadowExpire'. + :type req_ldap: string + :type expected_fields: list + + .. seealso:: make_ldap_filters_from_record for req_ldap format. """ - result = self.conn.connect(bind = False) - if (result['bool']) : + result = self.conn.connect(bind=False) + if (result['bool']): return_fields_list = [] - if expected_fields == None : - return_fields_list = ['mail', 'givenName', 'sn', 'uid', \ - 'sshPublicKey', 'shadowExpire'] - else : + if expected_fields is None: + return_fields_list = ['mail', 'givenName', 'sn', 'uid', + 'sshPublicKey', 'shadowExpire'] + else: return_fields_list = expected_fields #No specifc request specified, get the whole LDAP - if req_ldap == None: + if req_ldap is None: req_ldap = '(cn=*)' logger.debug("LDAP.PY \t LdapSearch req_ldap %s \ @@ -783,43 +844,44 @@ class LDAPapi : try: msg_id = self.conn.ldapserv.search( - self.baseDN,ldap.SCOPE_SUBTREE,\ - req_ldap, return_fields_list) + self.baseDN, ldap.SCOPE_SUBTREE, + req_ldap, return_fields_list) #Get all the results matching the search from ldap in one #shot (1 value) result_type, result_data = \ - self.conn.ldapserv.result(msg_id, 1) + self.conn.ldapserv.result(msg_id, 1) self.conn.close() - logger.debug("LDAP.PY \t LdapSearch result_data %s"\ - %(result_data)) + logger.debug("LDAP.PY \t LdapSearch result_data %s" + % (result_data)) return result_data - except ldap.LDAPError, error : - logger.log_exc("LDAP LdapSearch Error %s" %error) + except ldap.LDAPError, error: + logger.log_exc("LDAP LdapSearch Error %s" % error) return [] else: - logger.error("LDAP.PY \t Connection Failed" ) + logger.error("LDAP.PY \t Connection Failed") return - def _process_ldap_info_for_all_users(self, result_data): - """ - Process the data of all enabled users in LDAP. + """Process the data of all enabled users in LDAP. + :param result_data: Contains information of all enabled users in LDAP - and is coming from LdapSearch. + and is coming from LdapSearch. :param result_data: list - ..seealso: LdapSearch + + .. seealso:: LdapSearch + """ results = [] - logger.debug(" LDAP.py _process_ldap_info_for_all_users result_data %s " \ - %(result_data)) + logger.debug(" LDAP.py _process_ldap_info_for_all_users result_data %s " + % (result_data)) for ldapentry in result_data: - logger.debug(" LDAP.py _process_ldap_info_for_all_users ldapentry name : %s " \ - %(ldapentry[1]['uid'][0])) + logger.debug(" LDAP.py _process_ldap_info_for_all_users \ + ldapentry name : %s " % (ldapentry[1]['uid'][0])) tmpname = ldapentry[1]['uid'][0] hrn = self.authname + "." + tmpname @@ -827,48 +889,50 @@ class LDAPapi : if ldapentry[1]['mail'][0] == "unknown": tmpemail = None - try: - results.append( { - 'type': 'user', - 'pkey': ldapentry[1]['sshPublicKey'][0], - #'uid': ldapentry[1]['uid'][0], - 'uid': tmpname , - 'email':tmpemail, - #'email': ldapentry[1]['mail'][0], - 'first_name': ldapentry[1]['givenName'][0], - 'last_name': ldapentry[1]['sn'][0], - #'phone': 'none', - 'serial': 'none', - 'authority': self.authname, - 'peer_authority': '', - 'pointer' : -1, - 'hrn': hrn, - } ) + results.append({ + 'type': 'user', + 'pkey': ldapentry[1]['sshPublicKey'][0], + #'uid': ldapentry[1]['uid'][0], + 'uid': tmpname , + 'email':tmpemail, + #'email': ldapentry[1]['mail'][0], + 'first_name': ldapentry[1]['givenName'][0], + 'last_name': ldapentry[1]['sn'][0], + #'phone': 'none', + 'serial': 'none', + 'authority': self.authname, + 'peer_authority': '', + 'pointer': -1, + 'hrn': hrn, + }) except KeyError, error: - logger.log_exc("LDAPapi.PY \t LdapFindUser EXCEPTION %s" \ - %(error)) + logger.log_exc("LDAPapi.PY \t LdapFindUser EXCEPTION %s" + % (error)) return return results def _process_ldap_info_for_one_user(self, record, result_data): """ + Put the user's ldap data into shape. Only deals with one user record and one user data from ldap. + :param record: user record :param result_data: Raw ldap data coming from LdapSearch - :return: user's data dict with 'type','pkey','uid', 'email', - 'first_name' 'last_name''serial''authority''peer_authority' - 'pointer''hrn' + :returns: user's data dict with 'type','pkey','uid', 'email', + 'first_name' 'last_name''serial''authority''peer_authority' + 'pointer''hrn' :type record: dict :type result_data: list :rtype :dict + """ #One entry only in the ldap data because we used a filter #to find one user only ldapentry = result_data[0][1] - logger.debug("LDAP.PY \t LdapFindUser ldapentry %s" %(ldapentry)) + logger.debug("LDAP.PY \t LdapFindUser ldapentry %s" % (ldapentry)) tmpname = ldapentry['uid'][0] tmpemail = ldapentry['mail'][0] @@ -891,42 +955,42 @@ class LDAPapi : else: hrn = None - - - results = { - 'type': 'user', - 'pkey': ldapentry['sshPublicKey'], - #'uid': ldapentry[1]['uid'][0], - 'uid': tmpname , - 'email':tmpemail, - #'email': ldapentry[1]['mail'][0], - 'first_name': ldapentry['givenName'][0], - 'last_name': ldapentry['sn'][0], - #'phone': 'none', - 'serial': 'none', - 'authority': parent_hrn, - 'peer_authority': peer_authority, - 'pointer' : -1, - 'hrn': hrn, + results = { + 'type': 'user', + 'pkey': ldapentry['sshPublicKey'], + #'uid': ldapentry[1]['uid'][0], + 'uid': tmpname, + 'email': tmpemail, + #'email': ldapentry[1]['mail'][0], + 'first_name': ldapentry['givenName'][0], + 'last_name': ldapentry['sn'][0], + #'phone': 'none', + 'serial': 'none', + 'authority': parent_hrn, + 'peer_authority': peer_authority, + 'pointer': -1, + 'hrn': hrn, } return results - def LdapFindUser(self, record=None, is_user_enabled=None, expected_fields=None): """ + Search a SFA user with a hrn. User should be already registered in Iotlab LDAP. + :param record: sfa user's record. Should contain first_name,last_name, - email or mail. If no record is provided, returns all the users found - in LDAP. + email or mail. If no record is provided, returns all the users found + in LDAP. :type record: dict :param is_user_enabled: is the user's iotlab account already valid. :type is_user_enabled: Boolean. - :return: LDAP entries from ldap matching the filter provided. Returns - a single entry if one filter has been given and a list of - entries otherwise. + :returns: LDAP entries from ldap matching the filter provided. Returns + a single entry if one filter has been given and a list of + entries otherwise. :rtype: dict or list + """ custom_record = {} if is_user_enabled: @@ -934,19 +998,18 @@ class LDAPapi : if record: custom_record.update(record) - req_ldap = self.make_ldap_filters_from_record(custom_record) return_fields_list = [] - if expected_fields == None : - return_fields_list = ['mail', 'givenName', 'sn', 'uid', \ - 'sshPublicKey'] - else : + if expected_fields is None: + return_fields_list = ['mail', 'givenName', 'sn', 'uid', + 'sshPublicKey'] + else: return_fields_list = expected_fields - result_data = self.LdapSearch(req_ldap, return_fields_list ) - logger.debug("LDAP.PY \t LdapFindUser result_data %s" %(result_data)) + result_data = self.LdapSearch(req_ldap, return_fields_list) + logger.debug("LDAP.PY \t LdapFindUser result_data %s" % (result_data)) - if len(result_data) is 0: + if len(result_data) == 0: return None #Asked for a specific user if record is not None: @@ -955,5 +1018,4 @@ class LDAPapi : else: #Asked for all users in ldap results = self._process_ldap_info_for_all_users(result_data) - return results - + return results \ No newline at end of file