First modifications for upcoming migration to OAR2.5 and senslab prod LDAP.
[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
186             attrs = {}
187             attrs['objectClass'] = ["top", "person", "inetOrgPerson",\
188                                      "organizationalPerson", "posixAccount",\
189                                      "shadowAccount", "systemQuotas",\
190                                      "ldapPublicKey"]
191             
192             attrs['givenName'] = str(record['first_name']).lower(),capitalize()
193             attrs['sn'] = str(record['last_name']).lower().capitalize()
194             attrs['cn'] = attrs['givenName'] + ' ' + attrs['sn']
195             attrs['gecos'] = attrs['givenName'] + ' ' + attrs['sn']
196             attrs['uid'] = self.generate_login(record)   
197                         
198             attrs['quota'] = '/dev/vdb:2000000:2500000:0:0'
199             attrs['homeDirectory'] = '/senslab/users/' + attrs['uid']
200             attrs['loginShell'] = '/senslab/users/.ssh/welcome.sh'
201             attrs['gidNumber'] = '2000' 
202             attrs['uidNumber'] = str(self.find_max_uidNumber())
203             attrs['mail'] = record['mail'].lower()
204             attrs['sshPublicKey'] = record['sshpkey']  #To be filled by N. Turro
205             attrs['description'] = 'SFA USER FROM OUTSIDE SENSLAB'
206             #TODO  TO BE FILLED 
207             attrs['userPassword']= ""
208             
209             return attrs
210         
211         def ldapAdd(self, record = None) :
212             #SFA users are added from here
213             #They get a description attribute which others don't have.
214             
215             #Bind to the server
216             result = self.conn.connect()
217             
218             if(result['bool']):
219                 
220                 # A dict to help build the "body" of the object
221                 attrs = self.make_ldap_attributes_from_record(record)
222                 
223                 print >>sys.stderr, "\r\n \r\n \t LDAP.PY \t\t  ldapAdd  attrs %s " %(attrs)
224                 # The dn of our new entry/object
225                 dn = 'uid=' +attrs['uid'] +","+self.baseDN 
226  
227                 try:
228                         ldif = modlist.addModlist(attrs)
229                         print " \r\n \r\n LDAPapi.PY add attrs %s \r\n  ldif %s  " %(attrs,ldif) 
230                         self.conn.ldapserv.add_s(dn,ldif)
231                         logger.info("Adding user %s login %s in LDAP" \
232                                                 %attrs['cn'], %attrs['uid'] )
233                 except ldap.LDAPError, e:
234                     logger.log_exc("LDAP Add Error %s" %e)
235                     return {'bool' : False, 'message' : e }
236             
237                 self.conn.close()
238                 return {'bool': True}  
239             else: 
240                 return result
241
242          
243          
244         def ldapDelete(self, record_filter): 
245             #Find uid of the  person 
246             person = self.ldapFindHrn(record_filter)
247            
248             if person:
249                 dn = 'uid=' + person['uid'] + "," +self.baseDN 
250             else:
251                 return {'bool': False}
252             
253             #Connect and bind   
254             result =  self.conn.connect()
255             if(result['bool']):
256                 try:
257                     self.conn.ldapserv.delete_s(dn)
258                     return {'bool': True}
259                 
260                 except ldap.LDAPError, e:
261                     logger.log_exc("LDAP Delete Error %s" %e)
262                     print>>sys.stderr, "\r\n LDAP.PY \tldapDelete error : %s" %(e)  
263                     return {'bool': False}
264                     
265                     
266         def ldapModify(self, record_filter, new_attributes):
267             """
268             Gets the record from one user based on record_filter 
269             and changes the attributes according to the specified new_attributes.
270             Does not use this if we need to modify the uid. Use a ModRDN 
271             #operation instead ( modify relative DN )
272             """
273             
274             person = self.ldapFindHrn(record_filter,[] )
275             if person:
276                 # The dn of our existing entry/object
277                 dn  = 'uid=' + person['uid'] + "," +self.baseDN 
278             else:
279                 return
280             
281             if new_attributes:
282                 old = {}
283                 for k in new_attributes:
284                     old[k] =  person[k]
285                     
286                 ldif = modlist.modifyModlist(old,new_attributes)
287                 
288                 # Connect and bind/authenticate    
289                 result = self.conn.connect(bind) 
290                 if (result['bool']): 
291                     try:
292                         self.conn.ldapserver.modify_s(dn,ldif)
293                         self.close()
294                     except ldap.LDAPError, e:
295                         return {'bool' : False, 'message' : e }
296                 return {'bool': True}  
297                 
298                 
299                 
300         #TODO Handle OR filtering in the ldap query when 
301         #dealing with a list of records instead of doing a for loop in GetPersons   
302         def make_ldap_filters_from_record(self, record=None):
303             
304             req_ldapdict = {}
305             if record :
306                 if 'first_name' in record  and 'last_name' in record:
307                     req_ldapdict['cn'] = str(record['first_name'])+" "+str(record['last_name'])
308                 if 'email' in record :
309                     req_ldapdict['mail'] = record['email']
310                 if 'hrn' in record :
311                     splited_hrn = record['hrn'].split(".")
312                     if splited_hrn[0] != self.authname :
313                             print >>sys.stderr,"i know nothing about",record['hrn'], " my authname is ", self.authname, " not ", splited_hrn[0]
314                     login=splited_hrn[1]
315                     req_ldapdict['uid'] = login
316                 
317                 req_ldap=''
318                 print >>sys.stderr, "\r\n \r\n \t LDAP.PY \t\t   make_ldap_filters_from_record record %s req_ldapdict %s" %(record,req_ldapdict)
319                 for k in req_ldapdict:
320                     req_ldap += '('+str(k)+'='+str(req_ldapdict[k])+')'
321                 if  len(req_ldapdict.keys()) >1 :
322                     req_ldap = req_ldap[:0]+"(&"+req_ldap[0:]
323                     size = len(req_ldap)
324                     req_ldap= req_ldap[:(size-1)] +')'+ req_ldap[(size-1):]
325             else:
326                 req_ldap = "(cn=*)"
327             
328             return req_ldap
329
330             
331             
332         #Returns one matching entry 
333         def ldapSearch (self, req_ldap = None, return_fields = None ):
334             
335             self.conn.connect(bind = False)
336             req_ldap = self.make_ldap_filters_from_record(record) 
337             return_fields = []
338             if expected_fields == None : 
339                return_fields = ['mail','givenName', 'sn', 'uid','sshPublicKey']
340             else : 
341                 return_fields = expected_fields
342             print >>sys.stderr, "\r\n \r\n \t LDAP.PY \t\t ldapSearch  req_ldap %s return_fields %s " %(req_ldap,return_fields)
343             
344             try:
345                 msg_id = self.conn.ldapserv.search(
346                                             self.baseDN,ldap.SCOPE_SUBTREE,\
347                                             req_ldap,return_fields)     
348                 #Get all the results matching the search from ldap in one shot (1 value)
349                 result_type, result_data = self.conn.ldapserv.result(msg_id,1)
350                 #results = []
351                 print >>sys.stderr, "\r\n \r\n \t LDAP.PY \t\t ldapSearch  result_data %s" %(result_data) 
352                 
353                 return result_data
354                 #if result_data is None:
355                     #return None
356                 ##Asked for a specific user
357                 #if record :
358                     #ldapentry = result_data[0][1]
359                     #print >>sys.stderr, "\r\n \r\n \t LDAP.PY \t\t ldapSearch  ldapentry %s" %(ldapentry) 
360                     #tmpname = ldapentry['uid'][0]
361    
362                     #tmpemail = ldapentry['mail'][0]
363                     #if ldapentry['mail'][0] == "unknown":
364                         #tmpemail = None
365                         
366                     #try:
367                         #hrn = record['hrn']
368                         #parent_hrn = get_authority(hrn)
369                         #peer_authority = None
370                         #if parent_hrn is not self.authname:
371                             #peer_authority = parent_hrn
372                             
373
374                                     
375                         #results=  {    
376                                     #'type': 'user',
377                                     #'pkey': ldapentry['sshPublicKey'][0],
378                                     ##'uid': ldapentry[1]['uid'][0],
379                                     #'uid': tmpname ,
380                                     #'email':tmpemail,
381                                     ##'email': ldapentry[1]['mail'][0],
382                                     #'first_name': ldapentry['givenName'][0],
383                                     #'last_name': ldapentry['sn'][0],
384                                     ##'phone': 'none',
385                                     #'serial': 'none',
386                                     #'authority': parent_hrn,
387                                     #'peer_authority': peer_authority,
388                                     #'pointer' : -1,
389                                     #'hrn': hrn,
390                                     #}
391                     #except KeyError:
392                         #print >>sys.stderr, "\r\n \r\n LDAPapi \t ldapSearch KEyError results %s" %(results)
393                         #pass 
394                 #else:
395                 ##Asked for all users in ldap
396                     #results = []
397                     #for ldapentry in result_data:
398                         #print>>sys.stderr,"\r\n\t\t LDAP.py ldapentry  name : %s " %(ldapentry[1]['uid'][0])
399                         #tmpname = ldapentry[1]['uid'][0]
400                         #hrn=self.authname+"."+ tmpname
401                         
402                         #tmpemail = ldapentry[1]['mail'][0]
403                         #if ldapentry[1]['mail'][0] == "unknown":
404                             #tmpemail = None
405
406                 
407                         #parent_hrn = get_authority(hrn)
408                         #parent_auth_info = self.senslabauth.get_auth_info(parent_hrn)
409                         #try:
410                             #results.append(  { 
411                                     #'type': 'user',
412                                     #'pkey': ldapentry[1]['sshPublicKey'][0],
413                                     ##'uid': ldapentry[1]['uid'][0],
414                                     #'uid': tmpname ,
415                                     #'email':tmpemail,
416                                     ##'email': ldapentry[1]['mail'][0],
417                                     #'first_name': ldapentry[1]['givenName'][0],
418                                     #'last_name': ldapentry[1]['sn'][0],
419     ##                          'phone': 'none',
420                                     #'serial': 'none',
421                                     #'authority': self.authname,
422                                     #'peer_authority': '',
423                                     #'pointer' : -1,
424                                     #'hrn': hrn,
425                                     #} ) 
426                         #except KeyError:
427                             #pass
428                 #return results
429
430             
431             except  ldap.LDAPError,e :
432                 print >>sys.stderr, "ERROR LDAP %s" %(e)
433                
434         
435
436         def ldapFindHrn(self,record = None, expected_fields = None):
437             self.conn.connect(bind = False)
438             req_ldap = self.make_ldap_filters_from_record(record) 
439             return_fields = []
440             if expected_fields == None : 
441                return_fields = ['mail','givenName', 'sn', 'uid','sshPublicKey']
442             else : 
443                 return_fields = expected_fields
444                 
445             result_data = self.ldapSearch(req_ldap,  return_fields )
446                
447             if result_data is None:
448                     return None
449                 #Asked for a specific user
450                 if record :
451                     ldapentry = result_data[0][1]
452                     print >>sys.stderr, "\r\n \r\n \t LDAP.PY \t\t ldapSearch  ldapentry %s" %(ldapentry) 
453                     tmpname = ldapentry['uid'][0]
454    
455                     tmpemail = ldapentry['mail'][0]
456                     if ldapentry['mail'][0] == "unknown":
457                         tmpemail = None
458                         
459                     try:
460                         hrn = record['hrn']
461                         parent_hrn = get_authority(hrn)
462                         peer_authority = None
463                         if parent_hrn is not self.authname:
464                             peer_authority = parent_hrn
465                             
466
467                                     
468                         results=  {     
469                                     'type': 'user',
470                                     'pkey': ldapentry['sshPublicKey'][0],
471                                     #'uid': ldapentry[1]['uid'][0],
472                                     'uid': tmpname ,
473                                     'email':tmpemail,
474                                     #'email': ldapentry[1]['mail'][0],
475                                     'first_name': ldapentry['givenName'][0],
476                                     'last_name': ldapentry['sn'][0],
477                                     #'phone': 'none',
478                                     'serial': 'none',
479                                     'authority': parent_hrn,
480                                     'peer_authority': peer_authority,
481                                     'pointer' : -1,
482                                     'hrn': hrn,
483                                     }
484                     except KeyError:
485                         print >>sys.stderr, "\r\n \r\n LDAPapi \t ldapSearch KEyError results %s" %(results)
486                         pass 
487                 else:
488                 #Asked for all users in ldap
489                     results = []
490                     for ldapentry in result_data:
491                         print>>sys.stderr,"\r\n\t\t LDAP.py ldapentry  name : %s " %(ldapentry[1]['uid'][0])
492                         tmpname = ldapentry[1]['uid'][0]
493                         hrn=self.authname+"."+ tmpname
494                         
495                         tmpemail = ldapentry[1]['mail'][0]
496                         if ldapentry[1]['mail'][0] == "unknown":
497                             tmpemail = None
498
499                 
500                         parent_hrn = get_authority(hrn)
501                         parent_auth_info = self.senslabauth.get_auth_info(parent_hrn)
502                         try:
503                             results.append(  {  
504                                     'type': 'user',
505                                     'pkey': ldapentry[1]['sshPublicKey'][0],
506                                     #'uid': ldapentry[1]['uid'][0],
507                                     'uid': tmpname ,
508                                     'email':tmpemail,
509                                     #'email': ldapentry[1]['mail'][0],
510                                     'first_name': ldapentry[1]['givenName'][0],
511                                     'last_name': ldapentry[1]['sn'][0],
512     #                           'phone': 'none',
513                                     'serial': 'none',
514                                     'authority': self.authname,
515                                     'peer_authority': '',
516                                     'pointer' : -1,
517                                     'hrn': hrn,
518                                     } ) 
519                         except KeyError:
520                             pass
521                 return results   
522                 
523         #def ldapFindHrn(self, record_filter = None):        
524         ##def ldapFindHrn(self, record_filter = None, columns=None):
525
526                 #results = [] 
527                 #self.conn.connect(bind = False)
528                 ##self.connect()
529                 #if 'authority' in record_filter:
530                 ## ask for authority
531                         #if record_filter['authority']==self.authname:
532                                 ## which is SFA_REGISTRY_ROOT_AUTH
533                                 ## request all records which are under our authority, ie all ldap entries
534                                 #ldapfilter="cn=*"
535                         #else:
536                                 ##which is NOT SFA_REGISTRY_ROOT_AUTH
537                                 #return []
538                 #else :
539                         #if not 'hrn' in record_filter:
540                                 #print >>sys.stderr,"find : don't know how to handle filter ",record_filter
541                                 #return []
542                         #else:
543                                 #hrns=[]
544                                 #h=record_filter['hrn']
545                                 #if  isinstance(h,list):
546                                         #hrns=h
547                                 #else : 
548                                         #hrns.append(h)
549         
550                                 #ldapfilter="(|"
551                                 #for hrn in hrns:
552                                         #splited_hrn=hrn.split(".")
553                                         #if splited_hrn[0] != self.authname :
554                                                 #print >>sys.stderr,"i know nothing about",hrn, " my authname is ", self.authname, " not ", splited_hrn[0]
555                                         #else :
556                                                 #login=splited_hrn[1]
557                                                 #ldapfilter+="(uid="
558                                                 #ldapfilter+=login
559                                                 #ldapfilter+=")"
560                                 #ldapfilter+=")"
561         
562                 #rindex=self.conn.ldapserv.search(self.baseDN,ldap.SCOPE_SUBTREE,ldapfilter, ['mail','givenName', 'sn', 'uid','sshPublicKey'])
563                 ##rindex=self.ldapserv.search(self.baseDN,ldap.SCOPE_SUBTREE,ldapfilter, ['mail','givenName', 'sn', 'uid','sshPublicKey'])
564                 #ldapresponse=self.conn.ldapserv.result(rindex,1)
565                 #for ldapentry in ldapresponse[1]:
566                          
567                         #tmpname = ldapentry[1]['uid'][0]
568                         
569                         #if ldapentry[1]['uid'][0] == "savakian":
570                             #tmpname = 'avakian'
571
572                         #hrn=self.authname+"."+ tmpname
573                         
574                         #tmpemail = ldapentry[1]['mail'][0]
575                         #if ldapentry[1]['mail'][0] == "unknown":
576                             #tmpemail = None
577 ##                      uuid=create_uuid() 
578                 
579 ##                      RSA_KEY_STRING=ldapentry[1]['sshPublicKey'][0]
580                 
581 ##                      pkey=convert_public_key(RSA_KEY_STRING)
582                 
583 ##                      gid=self.senslabauth.create_gid("urn:publicid:IDN+"+self.authname+"+user+"+ldapentry[1]['uid'][0], uuid, pkey, CA=False)
584                 
585                         #parent_hrn = get_authority(hrn)
586                         #parent_auth_info = self.senslabauth.get_auth_info(parent_hrn)
587
588                         #results.append(  {     
589                                 #'type': 'user',
590                                 #'pkey': ldapentry[1]['sshPublicKey'][0],
591                                 ##'uid': ldapentry[1]['uid'][0],
592                                 #'uid': tmpname ,
593                                 #'email':tmpemail,
594                                 ##'email': ldapentry[1]['mail'][0],
595                                 #'first_name': ldapentry[1]['givenName'][0],
596                                 #'last_name': ldapentry[1]['sn'][0],
597 ##                              'phone': 'none',
598                                 #'serial': 'none',
599                                 #'authority': self.authname,
600                                 #'peer_authority': '',
601                                 #'pointer' : -1,
602                                 #'hrn': hrn,
603                                 #} )
604                 #return results