Ldap cleanup.
[sfa.git] / sfa / senslab / LDAPapi.py
1
2 import string
3 from sfa.util.xrn import Xrn,get_authority 
4 import ldap
5 from sfa.util.config import Config
6 #from sfa.trust.gid import *
7 from sfa.trust.hierarchy import Hierarchy
8 #from sfa.trust.auth import *
9 from sfa.trust.certificate import *
10 import ldap.modlist as modlist
11 from sfa.util.sfalogging import logger
12
13 class ldap_co:
14     """ Set admin login and server configuration variables."""
15     def __init__(self):
16         
17         self.login = 'cn=admin,dc=senslab,dc=info'
18         self.passwd = 'sfa'  
19         self.server_ip = "192.168.0.251"
20
21     
22     def connect(self, bind = True):
23         """Enables connection to the LDAP server.
24         Set the bind parameter to True if a bind is needed
25         (for add/modify/delete operations).
26         Set to False otherwise.
27         
28         """
29         try:
30             self.ldapserv = ldap.open(self.server_ip)
31         except ldap.LDAPError, e:
32             return {'bool' : False, 'message' : e }
33         
34         # Bind with authentification
35         if(bind): 
36             return self.bind()
37         
38         else:     
39             return {'bool': True}
40     
41     
42     def bind(self):
43         """ Binding method. """
44         try:
45             # Opens a connection after a call to ldap.open in connect:
46             self.ldapserv = ldap.initialize("ldap://" + self.server_ip )
47                 
48             # Bind/authenticate with a user with apropriate rights to add objects
49             self.ldapserv.simple_bind_s(self.login, self.passwd)
50
51         except ldap.LDAPError, e:
52             return {'bool' : False, 'message' : e }
53
54         return {'bool': True}
55     
56     def close(self):
57         """ Close the LDAP connection """
58         try:
59             self.ldapserv.unbind_s()
60         except ldap.LDAPError, e:
61             return {'bool' : False, 'message' : e }
62             
63         
64 class LDAPapi :
65         def __init__(self):
66             
67                 #SFA related config
68                 self.senslabauth=Hierarchy()
69                 config=Config()
70                 self.authname=config.SFA_REGISTRY_ROOT_AUTH
71                 #authinfo=self.senslabauth.get_auth_info(self.authname)
72         
73
74                 #self.auth=Auth()
75                 #gid=authinfo.get_gid_object()
76                 #self.ldapdictlist = ['type',
77                                 #'pkey',
78                                 #'uid',
79                                 #'serial',
80                                 #'authority',
81                                 #'peer_authority',
82                                 #'pointer' ,
83                                 #'hrn']
84                 self.baseDN = "ou=people,dc=senslab,dc=info"
85                 self.conn =  ldap_co()    
86                           
87         
88         def generate_login(self, record):
89             """Generate login for adding a new user in LDAP Directory 
90             (four characters minimum length)
91             Record contains first name and last name.
92             
93             """ 
94             #Remove all special characters from first_name/last name
95             lower_first_name = record['first_name'].replace('-','')\
96                                             .replace('_','').replace('[','')\
97                                             .replace(']','').replace(' ','')\
98                                             .lower()
99             lower_last_name = record['last_name'].replace('-','')\
100                                             .replace('_','').replace('[','')\
101                                             .replace(']','').replace(' ','')\
102                                             .lower()  
103             length_last_name = len(lower_last_name)
104             login_max_length = 8
105             
106             #Try generating a unique login based on first name and last name
107             getAttrs = ['uid']
108             if length_last_name >= login_max_length :
109                 login = lower_last_name[0:login_max_length]
110                 index = 0;
111                 logger.debug("login : %s index : %s" %login %index);
112             elif length_last_name >= 4 :
113                 login = lower_last_name
114                 index = 0
115                 logger.debug("login : %s index : %s" %login %index);
116             elif length_last_name == 3 :
117                 login = lower_first_name[0:1] + lower_last_name
118                 index = 1
119                 logger.debug("login : %s index : %s" %login %index);
120             elif length_last_name == 2:
121                 if len ( lower_first_name) >=2:
122                     login = lower_first_name[0:2] + lower_last_name
123                     index = 2
124                     logger.debug("login : %s index : %s" %login %index);
125                 else:
126                     logger.error("LoginException : \
127                                 Generation login error with \
128                                 minimum four characters")
129                 
130                     
131             else :
132                 logger.error("LDAP generate_login failed : \
133                                 impossible to generate unique login for %s %s" \
134                                 %lower_first_name %lower_last_name)
135                 
136             filter = '(uid='+ login+ ')'
137             try :
138                 #Check if login already in use
139                 while (self.ldapSearch(filter, getAttrs) is not [] ):
140                 
141                     index += 1
142                     if index >= 9:
143                         logger.error("LoginException : Generation login error \
144                                         with minimum four characters")
145                     else:
146                         try:
147                             login = lower_first_name[0,index] + \
148                                         lower_last_name[0,login_max_length-index]
149                             filter = '(uid='+ login+ ')'
150                         except KeyError:
151                             print "lower_first_name - lower_last_name too short"
152                 return login
153                         
154             except  ldap.LDAPError,e :
155                 logger.log_exc("LDAP generate_login Error %s" %e)
156                 #print >>sys.stderr, "ERROR LDAP %s" %(e)   
157             
158             
159         def find_max_uidNumber(self):
160                 
161             """Find the LDAP max uidNumber (POSIX uid attribute) .
162             Used when adding a new user in LDAP Directory 
163             returns integer max uidNumber + 1
164             
165             """
166             #Get all the users in the LDAP
167             ldapUserUidNumberMin = 2000 
168
169             getAttrs = "(uidNumber=*)"
170             filter = ['uidNumber']
171
172             result_data = self.ldapSearch(getAttrs, filter) 
173             #First LDAP user
174             if result_data == []:
175                 max_uidnumber = ldapUserUidNumberMin
176             #Get the highest uidNumber
177             else:
178                 uidNumberList = [r[1]['uidNumber'] for r in result_data ]
179                 max_uidnumber = max(uidNumberList) + 1
180                 
181             return max_uidnumber
182                        
183            
184         def make_ldap_attributes_from_record(self, record):
185             """When addind a new user to LDAP, creates an attributes dictionnary
186             from the SFA record.
187             
188             """
189
190             attrs = {}
191             attrs['objectClass'] = ["top", "person", "inetOrgPerson",\
192                                      "organizationalPerson", "posixAccount",\
193                                      "shadowAccount", "systemQuotas",\
194                                      "ldapPublicKey"]
195             
196             attrs['givenName'] = str(record['first_name']).lower(),capitalize()
197             attrs['sn'] = str(record['last_name']).lower().capitalize()
198             attrs['cn'] = attrs['givenName'] + ' ' + attrs['sn']
199             attrs['gecos'] = attrs['givenName'] + ' ' + attrs['sn']
200             attrs['uid'] = self.generate_login(record)   
201                         
202             attrs['quota'] = '/dev/vdb:2000000:2500000:0:0'
203             attrs['homeDirectory'] = '/senslab/users/' + attrs['uid']
204             attrs['loginShell'] = '/senslab/users/.ssh/welcome.sh'
205             attrs['gidNumber'] = '2000' 
206             attrs['uidNumber'] = str(self.find_max_uidNumber())
207             attrs['mail'] = record['mail'].lower()
208             attrs['sshPublicKey'] = record['sshpkey']  #To be filled by N. Turro
209             attrs['description'] = 'SFA USER FROM OUTSIDE SENSLAB'
210             #TODO  TO BE FILLED 
211             attrs['userPassword']= ""
212             
213             return attrs
214         
215         def ldapAdd(self, record = None) :
216             """Add SFA user to LDAP """
217            
218             user_ldap_attrs = self.make_ldap_attributes_from_record(record)
219             #Bind to the server
220             result = self.conn.connect()
221             
222             if(result['bool']):
223                 
224                 # A dict to help build the "body" of the object
225                 
226                 logger.debug(" \r\n \t LDAP ldapAdd attrs %s " %user_ldap_attrs)
227
228                 # The dn of our new entry/object
229                 dn = 'uid=' + user_ldap_attrs['uid'] + "," + self.baseDN 
230  
231                 try:
232                     ldif = modlist.addModlist(user_ldap_attrs)
233                     logger.debug("\r\n \tLDAPapi.PY add attrs %s \r\n  ldif %s"\
234                                  %(user_ldap_attrs,ldif) )
235                     self.conn.ldapserv.add_s(dn,ldif)
236                     
237                     logger.info("Adding user %s login %s in LDAP" \
238                             %user_ldap_attrs['cn'] %user_ldap_attrs['uid'])
239                             
240                             
241                 except ldap.LDAPError, e:
242                     logger.log_exc("LDAP Add Error %s" %e)
243                     return {'bool' : False, 'message' : e }
244             
245                 self.conn.close()
246                 return {'bool': True}  
247             else: 
248                 return result
249
250          
251         def ldapDelete(self, person_dn):
252             """
253             Deletes a person in LDAP. Uses the dn of the user.
254             """
255             #Connect and bind   
256             result =  self.conn.connect()
257             if(result['bool']):
258                 try:
259                     self.conn.ldapserv.delete_s(person_dn)
260                     self.conn.close()
261                     return {'bool': True}
262                 
263                 except ldap.LDAPError, e:
264                     logger.log_exc("LDAP Delete Error %s" %e)
265                     return {'bool': False}
266             
267         
268         def ldapDeleteHrn(self, record_filter): 
269             """
270             Deletes a SFA person in LDAP, based on the user's hrn.
271             """
272             #Find uid of the  person 
273             person = self.ldapFindHrn(record_filter)
274            
275             if person:
276                 dn = 'uid=' + person['uid'] + "," +self.baseDN 
277             else:
278                 return {'bool': False}
279             
280             result = self.ldapDelete(dn)
281             return result
282             
283                     
284                     
285         def ldapModify(self, record_filter, new_attributes):
286             """
287             Gets the record from one user based on record_filter 
288             and changes the attributes according to the specified new_attributes.
289             Does not use this if we need to modify the uid. Use a ModRDN 
290             #operation instead ( modify relative DN )
291             """
292             
293             person = self.ldapFindHrn(record_filter,[] )
294             if person:
295                 # The dn of our existing entry/object
296                 dn  = 'uid=' + person['uid'] + "," +self.baseDN 
297             else:
298                 return
299             
300             if new_attributes:
301                 old = {}
302                 for k in new_attributes:
303                     old[k] =  person[k]
304                     
305                 ldif = modlist.modifyModlist(old,new_attributes)
306                 
307                 # Connect and bind/authenticate    
308                 result = self.conn.connect(bind) 
309                 if (result['bool']): 
310                     try:
311                         self.conn.ldapserver.modify_s(dn,ldif)
312                         self.conn.close()
313                     except ldap.LDAPError, e:
314                         logger.log_exc("LDAP ldapModify Error %s" %e)
315                         return {'bool' : False }
316                 
317                 return {'bool': True}  
318                 
319                 
320                 
321         #TODO Handle OR filtering in the ldap query when 
322         #dealing with a list of records instead of doing a for loop in GetPersons   
323         def make_ldap_filters_from_record(self, record=None):
324             """
325             Helper function to make LDAP filter requests out of SFA records.
326             """
327             req_ldapdict = {}
328             if record :
329                 if 'first_name' in record  and 'last_name' in record:
330                     req_ldapdict['cn'] = str(record['first_name'])+" "\
331                                             + str(record['last_name'])
332                 if 'email' in record :
333                     req_ldapdict['mail'] = record['email']
334                 if 'hrn' in record :
335                     splited_hrn = record['hrn'].split(".")
336                     if splited_hrn[0] != self.authname :
337                         logger.warning(" \r\n LDAP.PY \
338                             make_ldap_filters_from_record I know nothing \
339                             about %s my authname is %s not %s" \
340                             %(record['hrn'], self.authname, splited_hrn[0]) )
341                             
342                     login=splited_hrn[1]
343                     req_ldapdict['uid'] = login
344                 
345                 req_ldap=''
346                 logger.debug("\r\n \t LDAP.PY make_ldap_filters_from_record \
347                                     record %s req_ldapdict %s" \
348                                     %(record, req_ldapdict))
349                
350                 for k in req_ldapdict:
351                     req_ldap += '('+str(k)+'='+str(req_ldapdict[k])+')'
352                 if  len(req_ldapdict.keys()) >1 :
353                     req_ldap = req_ldap[:0]+"(&"+req_ldap[0:]
354                     size = len(req_ldap)
355                     req_ldap= req_ldap[:(size-1)] +')'+ req_ldap[(size-1):]
356             else:
357                 req_ldap = "(cn=*)"
358             
359             return req_ldap
360
361             
362             
363
364         def ldapSearch (self, req_ldap = None, expected_fields = None ):
365             """
366             Used to search directly in LDAP, by using ldap filters and
367             return fields. 
368             When req_ldap is None, returns all the entries in the LDAP.
369             """
370             result = self.conn.connect(bind = False)
371             if (result['bool']) :
372                 
373                 return_fields = []
374                 if expected_fields == None : 
375                     return_fields = ['mail','givenName', 'sn', 'uid','sshPublicKey']
376                 else : 
377                     return_fields = expected_fields
378                     
379                 logger.debug("LDAP.PY \t ldapSearch  req_ldap %s \
380                                 return_fields %s" %(req_ldap,return_fields))
381     
382                 try:
383                     msg_id = self.conn.ldapserv.search(
384                                                 self.baseDN,ldap.SCOPE_SUBTREE,\
385                                                 req_ldap,return_fields)     
386                     #Get all the results matching the search from ldap in one 
387                     #shot (1 value)
388                     result_type, result_data = \
389                                          self.conn.ldapserv.result(msg_id,1)
390
391                     self.conn.close()
392
393                     logger.debug("LDAP.PY \t ldapSearch  result_data %s"\
394                                 %(result_data))
395     
396                     return result_data
397                 
398                 except  ldap.LDAPError,e :
399                     logger.log_exc("LDAP ldapSearch Error %s" %e)
400                     return []
401                 
402                 else:
403                     logger.error("LDAP.PY \t Connection Failed" )
404                     return 
405                
406
407         def ldapFindHrn(self,record = None, expected_fields = None):
408             """
409             Search a SFA user with a hrn. User should be already registered 
410             in Senslab LDAP. 
411             Returns one matching entry 
412             """   
413
414             req_ldap = self.make_ldap_filters_from_record(record) 
415             return_fields = []
416             if expected_fields == None : 
417                return_fields = ['mail','givenName', 'sn', 'uid','sshPublicKey']
418             else : 
419                 return_fields = expected_fields
420                 
421             result_data = self.ldapSearch(req_ldap,  return_fields )
422                
423             if result_data is None:
424                     return None
425             #Asked for a specific user
426             if record :
427                 ldapentry = result_data[0][1]
428                 logger.debug("LDAP.PY \t ldapFindHrn ldapentry %s" %(ldapentry))
429                 tmpname = ldapentry['uid'][0]
430
431                 tmpemail = ldapentry['mail'][0]
432                 if ldapentry['mail'][0] == "unknown":
433                     tmpemail = None
434                     
435                 try:
436                     hrn = record['hrn']
437                     parent_hrn = get_authority(hrn)
438                     peer_authority = None
439                     if parent_hrn is not self.authname:
440                         peer_authority = parent_hrn
441                         
442
443                                 
444                     results=  { 
445                                 'type': 'user',
446                                 'pkey': ldapentry['sshPublicKey'][0],
447                                 #'uid': ldapentry[1]['uid'][0],
448                                 'uid': tmpname ,
449                                 'email':tmpemail,
450                                 #'email': ldapentry[1]['mail'][0],
451                                 'first_name': ldapentry['givenName'][0],
452                                 'last_name': ldapentry['sn'][0],
453                                 #'phone': 'none',
454                                 'serial': 'none',
455                                 'authority': parent_hrn,
456                                 'peer_authority': peer_authority,
457                                 'pointer' : -1,
458                                 'hrn': hrn,
459                                 }
460                 except KeyError:
461                     lorrer.log_exc("LDAPapi \t ldapSearch KEyError results %s" \
462                                    %(results) )
463                     pass 
464             else:
465             #Asked for all users in ldap
466                 results = []
467                 for ldapentry in result_data:
468                     logger.debug(" LDAP.py ldapFindHrn ldapentry name : %s " \
469                                  %(ldapentry[1]['uid'][0]))
470                     tmpname = ldapentry[1]['uid'][0]
471                     hrn=self.authname+"."+ tmpname
472                     
473                     tmpemail = ldapentry[1]['mail'][0]
474                     if ldapentry[1]['mail'][0] == "unknown":
475                         tmpemail = None
476
477             
478                     parent_hrn = get_authority(hrn)
479                     parent_auth_info = self.senslabauth.get_auth_info(parent_hrn)
480                     try:
481                         results.append(  {      
482                                 'type': 'user',
483                                 'pkey': ldapentry[1]['sshPublicKey'][0],
484                                 #'uid': ldapentry[1]['uid'][0],
485                                 'uid': tmpname ,
486                                 'email':tmpemail,
487                                 #'email': ldapentry[1]['mail'][0],
488                                 'first_name': ldapentry[1]['givenName'][0],
489                                 'last_name': ldapentry[1]['sn'][0],
490                                 #'phone': 'none',
491                                 'serial': 'none',
492                                 'authority': self.authname,
493                                 'peer_authority': '',
494                                 'pointer' : -1,
495                                 'hrn': hrn,
496                                 } ) 
497                     except KeyError:
498                         pass
499             return results   
500