Ticket #1038: LDAPAuthentication.py.patch
File LDAPAuthentication.py.patch, 14.6 KB (added by makub, 3 years ago) |
---|
-
MaKaC/authentication/
old new 35 35 o: Example Inc. 36 36 postalAddress: Example Inc., Some City, Some Country 37 37 38 and groups listing their members by DNs, like:38 and groups in the OpenLDAP/SLAPD format listing their members by DNs, like: 39 39 40 40 dn: cn=somegroup,ou=groups,dc=example,dc=com 41 41 objectClass: groupOfNames … … 45 45 member: uid=bob,ou=people,dc=example,dc=com 46 46 description: Just a group of people ... 47 47 48 Adjust it to your needs if your LDAP structure is different. 48 or groups in ActiveDirectory format marked by 'memberof' attribute. 49 50 Adjust it to your needs if your LDAP structure is different, 51 preferably by changing the extractUserDataFromLdapData() method. 49 52 50 53 See indico.conf for information about customization options. 51 54 """ … … 54 57 try: 55 58 import ldap 56 59 import ldap.filter 60 import re 57 61 except: 58 62 pass 59 63 … … 67 71 RETRIEVED_FIELDS = ['uid', 'cn', 'mail', 'o', 'ou', 'company', 'givenName', 68 72 'sn', 'postalAddress', 'userPrincipalName'] 69 73 74 def extractUserDataFromLdapData(ret): 75 """extracts user data from a LDAP record as a dictionary, edit to modify for your needs""" 76 udata= {} 77 udata["login"] = ret['uid'] 78 udata["email"] = ret['mail'] 79 udata["name"]= ret.get('givenName', '') 80 udata["surName"]= ret.get('sn', '') 81 udata["organisation"] = ret.get('o','') 82 udata['address'] = fromLDAPmultiline(ret['postalAddress']) if 'postalAddress' in ret else '' 83 Logger.get('auth.ldap').debug("extractUserDataFromLdapData(): %s " % udata) 84 return udata 70 85 71 86 class LDAPAuthenticator(Authenthicator): 72 87 idxName = "LDAPIdentities" … … 88 103 return None 89 104 90 105 106 91 107 class LDAPIdentity(PIdentity): 92 108 93 109 def __str__(self): … … 98 114 """ 99 115 id is MaKaC.user.LoginInfo instance, self.user is Avatar 100 116 """ 101 102 Logger.get('auth.ldap').info("authenticate(%s)" % id.getLogin())117 log = Logger.get('auth.ldap') 118 log.info("authenticate(%s)" % id.getLogin()) 103 119 data = LDAPChecker().check(id.getLogin(), id.getPassword()) 104 120 if data: 105 121 if self.getLogin() == id.getLogin(): 106 122 # modify Avatar with the up-to-date info from LDAP 107 123 av = self.user 108 109 124 av.clearAuthenticatorPersonalData() 110 111 if 'postalAddress' in data: 112 postalAddress = fromLDAPmultiline(data['postalAddress']) 113 if av.getAddress() != postalAddress: 114 av.setAddress(postalAddress) 115 116 if 'sn' in data: 117 surname = data['sn'] 118 av.setAuthenticatorPersonalData('surName', surname) 119 if surname and av.getSurName() != surname and av.isFieldSynced('surName'): 120 av.setSurName(surname, reindex=True) 121 122 if 'givenName' in data: 123 firstName = data['givenName'] 125 udata = extractUserDataFromLdapData(data) 126 if 'name' in udata: 127 firstName = udata['name'] 124 128 av.setAuthenticatorPersonalData('firstName', firstName) 125 129 if firstName and av.getName() != firstName and av.isFieldSynced('firstName'): 126 130 av.setName(firstName, reindex=True) 127 128 if 'o' in data: 129 org = data.get('o', '') 130 else: 131 org = data.get('company', '') 132 133 av.setAuthenticatorPersonalData('affiliation', org) 134 if org.strip() != '' and org != av.getOrganisation() and av.isFieldSynced('affiliation'): 135 av.setOrganisation(org, reindex=True) 136 137 mail = data.get('mail', '') 138 139 if mail.strip() != '' and mail != av.getEmail(): 140 av.setEmail(mail, reindex=True) 141 131 log.info('updated name for user '+id.getLogin()+' to '+firstName) 132 if 'surName' in udata: 133 surname = udata['surName'] 134 av.setAuthenticatorPersonalData('surName', surname) 135 if surname and av.getSurName() != surname and av.isFieldSynced('surName'): 136 av.setSurName(surname, reindex=True) 137 log.info('updated surName for user '+id.getLogin()+' to '+surname) 138 if 'organisation' in udata: 139 org = udata['organisation'] 140 av.setAuthenticatorPersonalData('affiliation', org) 141 if org.strip() != '' and org != av.getOrganisation() and av.isFieldSynced('affiliation'): 142 av.setOrganisation(org, reindex=True) 143 log.info('updated organisation for user '+id.getLogin()+' to '+org) 144 if 'email' in udata: 145 mail = udata['email'] 146 if mail.strip() != '' and mail != av.getEmail(): 147 av.setEmail(mail, reindex=True) 148 log.info('updated email for user '+id.getLogin()+' to '+mail) 149 if 'address' in udata: 150 address = udata['address'] 151 if address != av.getAddress(): 152 av.setFieldSynced('address',True) 153 av.setAddress(address) 154 log.info('updated address for user '+id.getLogin()+' to '+address) 142 155 return self.user 143 156 else: 144 157 return None … … 147 160 def getAuthenticatorTag(self): 148 161 return LDAPAuthenticator.getId() 149 162 150 151 163 def objectAttributes(dn, result_data, attributeNames): 152 164 """ 153 165 adds selected attributes … … 193 205 self.ldapGroupsFilter, self.ldapGroupsDN = \ 194 206 ldapConfig.get('groupDNQuery') 195 207 self.ldapAccessCredentials = ldapConfig.get('accessCredentials') 196 self.ldapMembershipQuery = ldapConfig.get('membershipQuery')197 208 self.ldapUseTLS = ldapConfig.get('useTLS') 209 self.groupStyle = ldapConfig.get('groupStyle') 198 210 199 211 def login(self): 200 212 try: … … 269 281 270 282 for dn, data in res: 271 283 if dn: 284 Logger.get('auth.ldap').debug('lookupUser(%s) successful'%uid) 272 285 return objectAttributes(dn, data, RETRIEVED_FIELDS) 273 286 return None 274 287 … … 303 316 gfilter = self.ldapGroupsFilter.format(star + name + star) 304 317 else: 305 318 return [] 319 Logger.get('auth.ldap').debug('findGroups(%s) '%name) 306 320 res = self.l.search_s(self.ldapGroupsDN, ldap.SCOPE_SUBTREE, gfilter) 307 321 groupDicts = [] 308 322 for dn, data in res: … … 313 327 314 328 def userInGroup(self, login, name): 315 329 """ 316 Finds uids of users referenced by the member attribute 317 of the group LDAP object 330 Retursn whether a user is in a group. Depends on groupStyle (SLAPD/ActiveDirectory) 318 331 """ 319 query = self.ldapMembershipQuery.format(self._findDNOfGroup(name)) 320 res = self.l.search_s(self._findDNOfUser(login), ldap.SCOPE_BASE, query) 321 332 Logger.get('auth.ldap').debug('userInGroup(%s,%s) '%(login,name)) 333 # In ActiveDirectory users have multivalued attribute 'memberof' with list of groups 334 # In SLAPD groups have multivalues attribute 'member' with list of users 335 if self.groupStyle=='ActiveDirectory': 336 query = 'memberof={0}'.format(self._findDNOfGroup(name)) 337 res = self.l.search_s(self._findDNOfUser(login), ldap.SCOPE_BASE, query) 338 elif self.groupStyle=='SLAPD': 339 query = 'member={0}'.format(self._findDNOfUser(login)) 340 res = self.l.search_s(self._findDNOfGroup(name), ldap.SCOPE_BASE, query) 341 else: 342 raise Exception("Unknown LDAP group style, choices are: SLAPD or ActiveDirectory") 322 343 return res != [] 323 344 345 def findGroupMemberUids(self,name): 346 """ 347 Finds uids of users in a group. Depends on groupStyle (SLAPD/ActiveDirectory) 348 """ 349 Logger.get('auth.ldap').debug('findGroupMemberUids(%s) '%name) 350 # In ActiveDirectory users have multivalued attribute 'memberof' with list of groups 351 # In SLAPD groups have multivalues attribute 'member' with list of users 352 if self.groupStyle=='ActiveDirectory': 353 #!not tested, I have not ActiveDirectory instance to try test it 354 #search for users with attribute memberof=groupdn 355 memberUids = [] 356 query = 'memberof={0}'.format(self._findDNOfGroup(name)) 357 res = self.l.search_s(self.ldapPeopleDN, ldap.SCOPE_SUBTREE,query) 358 for dn, data in res: 359 if dn: 360 memberUids.append( data['uid'] ) 361 return memberUids 362 elif self.groupStyle=='SLAPD': 363 #read member attibute values from the group object 364 members = None 365 res = self.l.search_s(self._findDNOfGroup(name), ldap.SCOPE_BASE) 366 for dn, data in res: 367 if dn: 368 members = data['member'] 369 if not members: 370 return [] 371 memberUids = [] 372 for memberDN in members: 373 m = re.search('uid=([^,]*),',memberDN) 374 if m: 375 uid = m.group(1) 376 memberUids.append( uid ) 377 Logger.get('auth.ldap').debug('findGroupMemberUids(%s) returns %s'%(name,memberUids)) 378 return memberUids 379 else: 380 raise Exception("Unknown LDAP group style, choices are: SLAPD or ActiveDirectory") 381 324 382 325 383 class LDAPChecker(object): 326 384 def check(self, userName, password): 385 if not password or not password.strip(): 386 Logger.get('auth.ldap').info("Username: %s - empty password" % userName) 387 return None 327 388 try: 328 389 ret = {} 329 390 ldapc = LDAPConnector() 330 391 ldapc.openAsUser(userName, password) 331 392 ret = ldapc.lookupUser(userName) 332 393 ldapc.close() 333 Logger.get('auth.ldap').debug("Username: %s checked: %s" % \ 334 (userName, ret)) 394 Logger.get('auth.ldap').debug("Username: %s checked: %s" % (userName, ret)) 395 if not ret : 396 return None 397 #LDAP search is case-insensitive, we want case-sensitive match 398 if ret.get('uid')!=userName : 399 Logger.get('auth.ldap').info('user %s invalid case %s' % (userName,ret.get('uid'))) 400 return None 335 401 return ret 336 402 except ldap.INVALID_CREDENTIALS: 337 Logger.get('auth.ldap').exception( 338 "Username: %s - invalid credentials" % userName) 403 Logger.get('auth.ldap').info("Username: %s - invalid credentials" % userName) 339 404 return None 340 405 341 406 … … 353 418 class LDAPUserCreator(object): 354 419 355 420 def create(self, li): 356 Logger.get('auth.ldap'). info("create '%s'" % li.getLogin())421 Logger.get('auth.ldap').debug("create '%s'" % li.getLogin()) 357 422 # first, check if authentication is OK 358 423 data = LDAPChecker().check(li.getLogin(), li.getPassword()) 359 424 if not data: … … 367 432 # User doesn't exist, create it 368 433 try: 369 434 av = user.Avatar() 370 name = data.get('cn') 371 av.setName(name.split()[0]) 372 av.setSurName(name.split()[-1]) 373 av.setOrganisation(data.get('o', "")) 374 av.setEmail(data['mail']) 375 if 'postalAddress' in data: 376 av.setAddress(fromLDAPmultiline(data.get('postalAddress'))) 377 #av.setTelephone(data.get('telephonenumber',"")) 435 udata = extractUserDataFromLdapData(data) 436 av.setName(udata['name']) 437 av.setSurName(udata['surName']) 438 av.setOrganisation(udata['organisation']) 439 av.setEmail(udata['email']) 440 av.setAddress(udata['address']) 378 441 ah.add(av) 379 442 av.activateAccount() 443 Logger.get('auth.ldap').info("created '%s'" % li.getLogin()) 380 444 except KeyError: 381 445 raise MaKaCError("LDAP account does not contain the mandatory" 382 446 "data to create an Indico account.") 383 447 else: 384 448 # user founded 449 Logger.get('auth.ldap').info("found user '%s'" % li.getLogin()) 385 450 av = userList[0] 386 451 #now create the nice identity for the user 387 452 na = LDAPAuthenticator() … … 390 455 return av 391 456 392 457 393 # for MaKaC.externUsers 458 394 459 def dictToAv(ret): 460 """converts user data obtained from LDAP to the structure expected by Avatar""" 395 461 av = {} 396 av["email"] = [ret['mail']] 397 av["name"] = ret.get('givenName', '') 398 av["surName"] = ret.get('sn', '') 399 400 if 'o' in ret: 401 av["organisation"] = [ret.get('o', '')] 402 else: 403 av["organisation"] = [ret.get('company', '')] 404 405 if 'postalAddress' in ret: 406 av['address'] = [fromLDAPmultiline(ret['postalAddress'])] 407 408 av["login"] = ret.get('uid') if 'uid' in ret else ret['userPrincipalName'] 409 av["id"] = 'LDAP:' + av["login"] 462 udata=extractUserDataFromLdapData(ret) 463 av["login"] = udata["login"] 464 av["email"] = [udata["email"]] 465 av["name"]= udata["name"] 466 av["surName"]= udata["surName"] 467 av["organisation"] = [udata["organisation"]] 468 av["address"] = [udata["address"]] 469 av["id"] = 'LDAP:'+udata["login"] 410 470 av["status"] = "NotCreated" 411 471 return av 412 472 413 473 414 def is_empty(dict, key):415 if key not in dict:416 return False417 if dict[key]:418 return True419 else:420 return False421 422 423 474 class LDAPUser(object): 424 475 425 476 _operations = { … … 457 508 av["id"] = id 458 509 av["identity"] = LDAPIdentity 459 510 av["authenticator"] = LDAPAuthenticator() 511 Logger.get('auth.ldap').debug('LDAPUser.getById(%s) return %s '%(id,av)) 460 512 return av 461 513 462 514 463 515 def ldapFindGroups(name, exact): 516 """used in user.py""" 464 517 ldapc = LDAPConnector() 465 518 ldapc.open() 466 519 ldapc.login() … … 470 523 471 524 472 525 def ldapUserInGroup(user, name): 526 """used in user.py""" 473 527 ldapc = LDAPConnector() 474 528 ldapc.open() 475 529 ldapc.login() 476 530 ret = ldapc.userInGroup(user, name) 477 531 ldapc.close() 478 532 return ret 533 534 def ldapFindGroupMemberUids(name): 535 """used in user.py""" 536 ldapc = LDAPConnector() 537 ldapc.open() 538 ldapc.login() 539 ret = ldapc.findGroupMemberUids(name) 540 ldapc.close() 541 return ret 542