Guest User

Untitled

a guest
Jun 2nd, 2017
98
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 33.27 KB | None | 0 0
  1. import ldap
  2. import logging
  3. import random
  4. import sys
  5. import traceback
  6. import os
  7. import hashlib
  8. from base64 import urlsafe_b64encode as encode
  9. from base64 import urlsafe_b64decode as decode
  10.  
  11. LOG_FILENAME = '/tmp/ldap_backend.log'
  12. log = logging
  13. logging.basicConfig(filename=LOG_FILENAME,level=logging.DEBUG)
  14.  
  15. #log = logging.getLogger('atrium.auth.backends')
  16.  
  17. from django.conf import settings
  18. from django.contrib.auth.models import User
  19. from django.db import transaction
  20.  
  21. class LDAPBackendError(Exception):
  22. pass
  23.  
  24. #need to map all of our user attributes to a LDAP attr for commit to work
  25. #CHANGE THIS FOR INETORG, JWUSER
  26. _DEFAULTS = {
  27. 'bindname': None,
  28. 'binddn': None,
  29. 'bindpw': None,
  30. 'group': None,
  31. 'groupdn': None,
  32. 'groupsu': None,
  33. 'groupstaff': None,
  34. 'groupactive': None,
  35. 'replicas': True,
  36. 'email': 'mail',
  37. 'first_name': 'cn',
  38. 'last_name': 'sn',
  39. 'username' : 'uid',
  40. 'sqlfallback' : True,
  41. 'objectClass' : ['jwUser' ,'inetOrgPerson', 'organizationalPerson', 'person', 'top'] ,
  42. 'timeout': 3,
  43. 'disable_update': False,
  44. 'all_attr' : ('username', 'first_name', 'last_name', 'email' ),
  45. }
  46.  
  47. _REQUIRED = ('url', 'userdn')
  48. _TO_ITERABLE = ('url', 'group', 'groupsu', 'groupstaff', 'groupactive')
  49.  
  50. class LDAPBackend():
  51. def changePasswd(self, username, newpassword):
  52. log.debug('LDAPBackend.changePasswd(%s,%s)' % (username, newpassword) )
  53. self._modifyPasswd(username, None, newpassword)
  54. #we just make sure that our password is set to the unusable value
  55. #user.password = '!'
  56.  
  57. def authenticate(self, username=None, password=None):
  58. log.debug('LDAPBackend.authenticate(%s,%s)' % (username, password))
  59. if username is None or password is None:
  60. log.debug('Username or password is None, automatically returning None')
  61. return None
  62. blocks = self._buildBlocks()
  63. for block in blocks:
  64. log.debug('Processing block, settings: %s' % str(block))
  65. for opt in ('NETWORK_TIMEOUT', 'TIMELIMIT', 'TIMEOUT'):
  66. ldap.set_option(getattr(ldap, 'OPT_%s' % opt), block['timeout'])
  67.  
  68. for uri in block['url']:
  69. conn = self._buildConn(uri)
  70. if not conn:
  71. log.error('Could not initialize connection to %s' % uri)
  72. continue
  73. #we need to pull our Primary uid here
  74. uid = self._getPrimaryUid(username, block)
  75. if uid is None:
  76. log.debug('Uid %s not found in %s' % (username, uri))
  77. break
  78. try:
  79. try:
  80. conn.simple_bind_s('uid=%s,%s' % (uid, block['userdn']), password)
  81. except ldap.INVALID_CREDENTIALS:
  82. log.debug('%s returned invalid credentials for %s' % (uri,uid))
  83. if block['replicas'] is True:
  84. return None
  85. break
  86. except ldap.LDAPError, e:
  87. log.error('Got error from LDAP library: %s' % str(e))
  88. break
  89. if block['group'] is None:
  90. log.info('%s authenticated successfully against %s' % (uid, uri))
  91. return self._return_user(uid, conn, block)
  92. # If your directory is setup such that this user couldn't search (for whatever reason)
  93. # switch to an account that can so we can check the group
  94. if block['bindname'] is not None:
  95. log.debug('Rebinding to check group membership')
  96. conn.unbind()
  97. del conn
  98. conn = self._buildConn(uri)
  99. try:
  100. conn.simple_bind_s('cn=%s,%s' % (block['bindname'], block['binddn']), block['bindpw'])
  101. except ldap.LDAPError, e:
  102. log.error('Error during rebind: %s' % str(e))
  103. break
  104. for group in block['group']:
  105. log.debug('Checking if %s is a member of %s' % (uid, group))
  106. try:
  107. result = conn.search_s('cn=%s,%s' % (group, block['groupdn']), ldap.SCOPE_SUBTREE, '(objectclass=*)', ['memberUid'])
  108. except ldap.NO_SUCH_OBJECT:
  109. log.error('Could not find user object cn=%s,%s' % (group, block['groupdn']) )
  110. continue
  111. print result
  112. # If there's more than one result, it gets ignored (there shouldn't be more than one
  113. # group with the same name anyway)
  114. if not result:
  115. log.debug('No group found with name %s' % group)
  116. continue
  117. if 'memberUid' not in result[0][1]:
  118. log.debug('No memberUid in group %s' % group)
  119. continue
  120. else:
  121. log.debug('memberUid in group %s found!' % group)
  122. result = result[0][1]['memberUid']
  123. #print result[0]
  124. #print username
  125. u = 'uid=' + uid + ',' + block['userdn']
  126. if u in result:
  127. log.info('%s belongs to group %s' % (uid, group))
  128. log.info('%s authenticated successfully against %s' % (uid, uri))
  129. return self._return_user(uid, conn, block)
  130. else:
  131. log.info('%s user is not a member of %s' % (u, group))
  132. #return self._return_user(idmap, conn, block)
  133. if block['replicas'] is True:
  134. break
  135. finally:
  136. del conn
  137. if block['sqlfallback'] == True:
  138. log.debug('Going to SQL fall back for %s' % username)
  139. try:
  140. user = User.objects.get(username=username)
  141. #we set our data back to our raw password, then we import the user to LDAP, then we check the authentication
  142. if user.check_password(password):
  143. #Since our SQL user validated correctly, we now fall back to the LDAP user, and pull their details into the system.
  144. user = LDAP_User.objects.get(username=username)
  145. user.password = password
  146. #this pulls our data to LDAP ....
  147. user.save()
  148. #user = self._return_user(username, conn, block)
  149. user.password = '!'
  150. #And this pushes back to SQL some changes such as CN and SN and the password unusable hash
  151. user.save()
  152. #we want to save the SQL found user to our authentication service
  153. return user
  154. except User.DoesNotExist:
  155. #then our attempt was in vain anyway, and we just wait for the rest of our module to take it course
  156. log.debug('User %s does not exist in SQL, continuing' % username)
  157. log.info('User %s could not be authenticated against LDAP' % uid)
  158. return None
  159.  
  160. def checkPasswd(self, uid, password):
  161. log.debug('LDAPBackend.checkPassword(%s,%s)' % (uid,password) )
  162. blocks = self._buildBlocks()
  163. for block in blocks:
  164. for uri in block['url']:
  165. #we only compare the current changes, rather than changes between servers (this will put their state into sync incidentaly)
  166. conn = self._buildConn(uri)
  167. if uid == None:
  168. continue
  169. puid = self._getPrimaryUid(uid, block)
  170. try:
  171. conn.simple_bind_s('uid=%s,%s' % (puid, block['userdn']), password)
  172. conn.unbind()
  173. except ldap.INVALID_CREDENTIALS:
  174. log.info('%s user could not bind, password is incorrect' % puid)
  175. continue
  176. except ldap.LDAPError, e:
  177. log.error('Got error from LDAP library: %s' % str(e))
  178. continue
  179. return True
  180. #since one server has validated the authentication, we allow the user to proceed.
  181. return False
  182.  
  183. def commitUser(self, user):
  184. log.debug('LDAPBackend.commitUser(%s)' % user)
  185. #log.debug('Refernece from %s to modify user')
  186. blocks = self._buildBlocks()
  187. update = False
  188. for block in blocks:
  189. for uri in block['url']:
  190. #we only compare the current changes, rather than changes between servers (this will put their state into sync incidentaly)
  191. conn = self._buildConn(uri)
  192. try:
  193. conn.simple_bind_s('cn=%s,%s' % (block['bindname'], block['binddn']), block['bindpw'])
  194. except ldap.INVALID_CREDENTIALS:
  195. log.error('%s Admin user could not bind. something is wrong...' % uri)
  196. continue
  197. except ldap.LDAPError, e:
  198. log.error('Got error from LDAP library: %s' % str(e))
  199. continue
  200. #ldap_data = self._getUserByName(user.username, user, conn, block)
  201. #IDmap here to our "old" username, if not, it will find the current. if not, it will find that no user exists.
  202. uid = self._getPrimaryUid(user.username, block)
  203. #if uid is None:
  204. #if our uid fails to return data, it may be due to a username change.
  205. # oldname = User.objects.get(id=user.id).username
  206. # uid = self._getPrimaryUid(str(oldname), block)
  207. if uid == None:
  208. result = self._createUser(conn, block, user, uri)
  209. log.debug('User create returned %s' % result)
  210. else:
  211. result = self._modifyUser(conn, block, user, uid, uri)
  212. log.debug('User modify returned %s' % result)
  213. conn.unbind()
  214. log.debug('Unbound from cn=%s,%s' % (block['bindname'], block['binddn']) )
  215. del conn
  216. if result:
  217. update = True
  218. #this means our update has occured on at least one server, so we can continue
  219. if update == False:
  220. log.error('%s was unable to be (completely) saved!' % uid)
  221. #raise LDAPUserCreateError('%s was unabled to be saved/modified to the LDAP system. please view the logs' % uid)
  222.  
  223. def get_user(self, user_id):
  224. log.debug('LDAPBackend.get_user(%s)' % user_id )
  225. try:
  226. return LDAP_User.objects.get(pk=user_id)
  227. except User.DoesNotExist:
  228. return None
  229.  
  230. ################
  231. #private methods
  232. ################
  233.  
  234. def _buildBlocks(self):
  235. log.debug('LDAPBackend._buildBlocks()')
  236. if isinstance(settings.LDAP_AUTH_SETTINGS[0], dict):
  237. log.debug('Using complex settings')
  238. blocks = settings.LDAP_AUTH_SETTINGS
  239. else:
  240. log.debug('Using simple settings')
  241. blocks = (self._parse_simple_config(),)
  242. # First get our configuration into a standard format
  243. for block in blocks:
  244. for r in _REQUIRED:
  245. if r not in block:
  246. raise LDAPBackendError('Missing required configuration option: %s' % r)
  247. for d in _DEFAULTS:
  248. if d not in block:
  249. block[d] = _DEFAULTS[d]
  250. for i in _TO_ITERABLE:
  251. if isinstance(block[i], str):
  252. block[i] = (block[i],)
  253. #Dont make our access random, we want to access servers in the order in the config
  254. block['url'] = list(block['url'])
  255. return blocks
  256.  
  257. def _buildConn(self, uri):
  258. log.debug('LDAPBackend._buildConn(%s)' % uri)
  259. log.debug('Attempting to authenticate to %s' % uri)
  260. conn = ldap.initialize(uri)
  261. TLS = False
  262. for i in range(1, 3):
  263. try:
  264. conn.start_tls_s()
  265. TLS = True
  266. log.debug('TLS Established')
  267. break
  268. except ldap.LDAPError, e:
  269. log.error("Could not start TLS %s" % i)
  270. time.sleep(1)
  271. if TLS == False:
  272. log.error('TLS could not be initiated to %s!' % uri)
  273. #mail the admin
  274. #raise an error here
  275. raise LDAPBackendError('TLS Could not be initiated to the authentication server!')
  276. return None
  277. return conn
  278.  
  279. def _changeUsername(self, user, uid, conn, block):
  280. log.debug('LDAPBackend.changeUsername(%s)' % uid )
  281. #since this uid we have is the primary from our former operation
  282. uid2 = self._getUid(uid, block)
  283. if uid2 != uid:
  284. #then our name has already been changed, we want to replace the second version with the new
  285. stack = [ (ldap.MOD_DELETE, block['username'], uid2), ( ldap.MOD_ADD, block['username'], user.username ) ]
  286. else:
  287. #our name hasnt been modified, we want to add the second attribute
  288. stack = [ ( ldap.MOD_ADD, block['username'], user.username ) ]
  289. try:
  290. conn.modify_s('uid=%s,%s' % (uid, block['userdn']) , stack)
  291. except ldap.LDAPError, e:
  292. log.error('Error modifying %s username to %s - %s' % (uid, user.username, str(e)) )
  293.  
  294. def _createUser(self, conn, block, user, uri):
  295. log.debug('LDAPBackend._createUser(%s,%s,%s,%s)' % (conn,block,user,uri))
  296. log.debug('Choosing to create user %s' % user.username)
  297. exist = True
  298. #CHANGE THIS FOR INETORG
  299. try:
  300. conn.search_s('uid=%s,%s' % (user.username, block['userdn']), ldap.SCOPE_ONELEVEL, '(objectclass=*)', ['uid'])
  301. except ldap.NO_SUCH_OBJECT, e:
  302. log.debug('User %s does not exist, time to insert them' % user.username)
  303. exist = False
  304. except ldap.LDAPError, e:
  305. log.error('Got error from LDAP library: %s' % str(e))
  306. return False
  307. if exist:
  308. log.debug('User %s already exists, migrating to %s' % (user.username, uri) )
  309. #if they exist, we should be adding the group membership
  310. try:
  311. r = self._migrateUser(conn, block, user, uri)
  312. except:
  313. log.debug(sys.exc_info()[0])
  314. return r
  315. #with this, since user creates are handled by the UserManage module, we can be pretty safe we are adding a new user, not overiding one.
  316. #we need to iterate over all our user values.
  317. stack = []
  318. #for oc in block['objectClass']:
  319. stack.append( ('objectClass', block['objectClass'] ) )
  320. #Create our idmap here
  321. if user.password is not None:
  322. password = user.password
  323. else:
  324. password = LDAP_User.objects.make_random_password(20)
  325. hashpassword = self._makeSecret(password)
  326. stack.append( ('userPassword' , hashpassword) )
  327. for attr in block['all_attr']:
  328. #we need a way to skip some attributes like username and password etc.
  329. if attr != 'password' or attr != 'username':
  330. log.debug('%s attr , %s' % (attr, str(getattr(user,attr)) ) )
  331. if str(getattr(user,attr)) is None or str(getattr(user,attr)) is '' :
  332. stack.append( (block[attr] , 'Anonymous' ), )
  333. else:
  334. stack.append( (block[attr] , str(getattr(user, attr)) ), )
  335. #now we push out our user, and hope nothing bad happens .....
  336. try:
  337. log.debug(stack)
  338. conn.add_s('uid=%s,%s' % (user.username, block['userdn']) , stack)
  339. log.debug('Succesfully created user %s' % user.username)
  340. #we should also be catching when a unique constrait is violated.
  341. except ldap.LDAPError, e:
  342. log.error('Error attempting to create user %s to %s' % (user.username, uri))
  343. return False
  344. #now we want to call our actually user password change method so the new user can *actually* bind and atuhenticate
  345. #we also want to make our userpassword ! to force validation against the LDAP
  346. if password is not None:
  347. self.changePasswd(user, password)
  348. else:
  349. user.set_unusable_password()
  350. #we also need to add groups from the user_is active etc
  351. r = self._modifyUserGroup(conn, user, user.username, block)
  352. if r == False:
  353. #shit has gone down, fix it in our create
  354. conn.delete_s('uid=%s,%s' % (user.username, block['userdn']) )
  355. return True
  356.  
  357. def _getUserByUid(self, uid, user, conn, block):
  358. log.debug('LDAPBackend._getUserByName(%s)' % uid)
  359. #CHANGE HERE FOR INETORG
  360. try:
  361. r = conn.search_s('uid=%s,%s' % (uid, block['userdn']), ldap.SCOPE_SUBTREE,
  362. '(objectclass=*)', [block['email'], block['first_name'], block['last_name']])
  363. except ldap.NO_SUCH_OBJECT, e:
  364. log.warning('Could not get user information for %s, LDAP user does not exist returning possibly stale user object' % uid)
  365. return user
  366. except ldap.LDAPError, e:
  367. log.error('Got error from LDAP library, returning our user: %s' % str(e))
  368. return user
  369. #log.debug('Got back results %s' % r)
  370. results = r[0][1]
  371. ldap_data = {}
  372. if block['email'] is not None:
  373. ldap_data['email'] = results[block['email']][0] if block['email'] in results else ''
  374. if block['first_name'] is not None:
  375. ldap_data['first_name'] = results[block['first_name']][0] if block['first_name'] in results else ''
  376. if block['last_name'] is not None:
  377. ldap_data['last_name'] = results[block['last_name']][0] if block['last_name'] in results else ''
  378. if block['username'] is not None:
  379. ldap_data['username'] = self._getUid(user.username, block)
  380. #pull back our username value here. It should exist in our useralso.
  381. #If we have a secondary username, we should retrieve it here, and apply it to the user.
  382. for g, attr in (('groupsu', 'is_superuser'), ('groupstaff', 'is_staff'), ('groupactive', 'is_active')):
  383. if block[g] is not None:
  384. ldap_data[attr] = False
  385. for group in block[g]:
  386. #these groups MUST exist, but if not we want to log this error.
  387. try:
  388. result = conn.search_s('cn=%s,%s' % (group, block['groupdn']), ldap.SCOPE_SUBTREE,
  389. '(objectclass=*)', ['memberuid'])
  390. except ldap.NO_SUCH_OBJECT:
  391. log.error('Group object %s does not exist %s!' % (group, str(e)) )
  392. continue
  393. if not result or 'memberUid' not in result[0][1]:
  394. continue
  395. log.debug(result)
  396. result = result[0][1]['memberUid']
  397. if 'uid=%s,%s' % (uid, block['userdn']) in result:
  398. ldap_data[attr] = True
  399. #break
  400. log.debug(ldap_data)
  401. return ldap_data
  402.  
  403. def _getPrimaryUid(self, username, block):
  404. log.debug('LDAPBackend.getPrimaryUid(%s)' % username )
  405. for uri in block['url']:
  406. conn = self._buildConn(uri)
  407. try:
  408. conn.simple_bind_s('cn=%s,%s' % (block['bindname'], block['binddn']), block['bindpw'])
  409. except ldap.INVALID_CREDENTIALS:
  410. log.error('Admin user could not bind. something is wrong...')
  411. continue
  412. except ldap.LDAPError, e:
  413. log.error('Got error from the LDAP library: %s' % str(e))
  414. continue
  415. r = conn.search_s(block['userdn'], ldap.SCOPE_SUBTREE, 'uid=%s' % username, ['uid'])
  416. conn.unbind()
  417. del conn
  418. log.debug(r)
  419. if r != []:
  420. return r[0][1]['uid'][0]
  421. return None
  422.  
  423. def _getUid(self,username, block):
  424. log.debug('LDAPBackend.getUid(%s)' % username)
  425. for uri in block['url']:
  426. conn = self._buildConn(uri)
  427. try:
  428. conn.simple_bind_s('cn=%s,%s' % (block['bindname'], block['binddn']), block['bindpw'])
  429. except ldap.INVALID_CREDENTIALS:
  430. log.error('Admin user could not bind. something is wrong...')
  431. continue
  432. except ldap.LDAPError, e:
  433. log.error('Got error from the LDAP library: %s' % str(e))
  434. continue
  435. r = conn.search_s(block['userdn'], ldap.SCOPE_SUBTREE, 'uid=%s' % username, ['uid'])
  436. conn.unbind()
  437. del conn
  438. log.debug(r)
  439. if r != []:
  440. try:
  441. return r[0][1]['uid'][1]
  442. except:
  443. return r[0][1]['uid'][0]
  444. return None
  445.  
  446. def _makeSecret(self, password):
  447. log.debug('LDAPBackend._makeSecret(%s)' % password)
  448. salt = os.urandom(4)
  449. h = hashlib.sha1(password)
  450. h.update(salt)
  451. return "{SSHA}" + encode(h.digest() + salt)
  452.  
  453. def _migrateUser(self, conn, block, user, uri):
  454. log.debug('LDAPBackend._migrateUser(%s,%s,%s,%s)' % (conn,block,user,uri))
  455. #we know our user already exists, so we need to check they have a JW user attribute
  456. uid = self._getPrimaryUid(user.username, block)
  457. log.debug('Checking if %s has JW attrs' % uid)
  458. try:
  459. result = conn.search_s('uid=%s,%s' % (str(uid), block['userdn']), ldap.SCOPE_SUBTREE, '(objectclass=*)')
  460. except ldap.NO_SUCH_OBJECT:
  461. log.error('Could not find user object, this isnt correct, uid=%s,%s' % (uid, block['userdn']) )
  462. return False
  463. stack = []
  464. if result:
  465. if 'jwUser' not in result[0][1]['objectClass']:
  466. #we should make it so that we delete the other object classes, then add them back with the jwUser, providded that they are compatible.
  467. log.error('Cannot add %s, this would create an object class violation!' % uid)
  468. return False
  469. #technically this section should never be called, but it will be useful to 'resync' a broken user profile that lacks the attributes it needs.
  470. for oclass in block['objectClass']:
  471. if oclass not in result[0][1]['objectClass']:
  472. stack.append( (ldap.MOD_ADD, 'objectClass', str(oclass) ) )
  473. log.debug('User %s does not have %s objectClass, adding' % (str(uid) , oclass))
  474. #check that we have all of our needed attributes. if not push them to the stack to be added
  475. for attr in block['all_attr']:
  476. if block[attr] not in result[0][1]:
  477. stack.append( (ldap.MOD_ADD, block[attr] , str(getattr(user, attr)) ) )
  478. Result = self._modifyUserGroup(conn, user, uid, block)
  479. if Result:
  480. #since we don't depend on the user migrate working, we wait till we knows groups have worked, then we commit our user.
  481. try:
  482. conn.modify_s('uid=%s,%s' % (uid, block['userdn']), stack)
  483. log.debug('%s migrated succesfully' % uid)
  484. except ldap.LDAPError, e:
  485. log.error('%s at %s migrate command failed. %s %s' % (uid, uri, stack, str(e) ) )
  486. return False
  487. return Result
  488.  
  489. def _modifyPasswd(self, username, oldpassword, newpassword):
  490. log.debug('LDAPBackend._modifyPassword(%s,%s,%s)' % (username, oldpassword, newpassword))
  491. blocks = self._buildBlocks()
  492. for block in blocks:
  493. for uri in block['url']:
  494. conn = self._buildConn(uri)
  495. try:
  496. uid = self._getPrimaryUid(username, block)
  497. if uid != None:
  498. conn.simple_bind_s('cn=%s,%s' % (block['bindname'], block['binddn']), block['bindpw'])
  499. conn.passwd_s('uid=%s,%s' % (uid, block['userdn']), oldpassword, newpassword)
  500. conn.unbind()
  501. del conn
  502. else:
  503. continue
  504. except ldap.INVALID_CREDENTIALS:
  505. log.error('%s returned invalid admin credentials on attempt to change passwd by %s' % (uri, uid))
  506. break
  507. except ldap.LDAPError, e:
  508. log.error('Got error from LDAP library: %s' % str(e))
  509. break
  510. r = self.checkPasswd(uid, newpassword)
  511. if r is True:
  512. log.debug('New password for %s authenticated succesfully' % uid)
  513. else:
  514. log.error('%s error binding as changed %s password - holy shitting dick nipples something is bad!' % (uri, uid))
  515.  
  516. def _modifyUser(self, conn, block, user, uid, uri):
  517. log.debug('LDAPBackend._modifyUser(%s,%s,%s,%s,%s)' % (conn,block,user,uid,uri))
  518. log.debug('Choosing to modify user %s' % uid)
  519. user_stack = []
  520. ldap_data = self._getUserByUid(uid, user, conn, block)
  521. #if we dont find our user, we get the original object back and no update will occur.
  522. #if we do, add our uri to the stack of servers to modify / add
  523. #we need to work out if it is a group change or a attr change, and act accordingly
  524. for attr in ldap_data:
  525. if attr != 'password' or attr != 'username': #this allows us to skip values that should be immutable
  526. if getattr(user, attr) != ldap_data[attr]:
  527. #push our changed attr to a stack, ready to be modified
  528. if not (ldap.MOD_ADD, block[attr], getattr(user, attr)) in user_stack or not (ldap.MOD_REPLACE, block[attr], getattr(user, attr)) in user_stack:
  529. #work out if its a replace or add operation (we can have many emails etc but likely not)
  530. if ldap_data[attr] == None:
  531. op = ldap.MOD_ADD
  532. else:
  533. op = ldap.MOD_REPLACE
  534. if str(getattr(user,attr)) is '' or str(getattr(user,attr)) is None:
  535. user_stack.append( (op, block[attr], 'Anonymous') )
  536. else:
  537. user_stack.append( (op , block[attr], str(getattr(user, attr)) ) )
  538. #once we know the changes, we save them to the ldap in a single hit.
  539. if user_stack != []:
  540. try:
  541. conn.modify_s('uid=%s,%s' % (uid, block['userdn']), user_stack)
  542. log.debug('%s modified succesfully' % uid)
  543. except ldap.LDAPError, e:
  544. log.error('%s at %s modify command failed. %s %s' % (uid, uri, user_stack, str(e) ) )
  545. return False
  546. #does this really need to worry about the return of the group mod op?
  547. r = self._modifyUserGroup(conn, user, uid, block)
  548. if ldap_data['username'] != user.username:
  549. #likely our username has changed, and we should update this.
  550. self._changeUsername(user, uid, conn, block)
  551. return r
  552. #we should consider checking that it worked .....
  553.  
  554. def _modifyUserGroup(self, conn, user, uid, block):
  555. log.debug('LDAPBackend._modifyUserGroup(%s,%s,%s,%s)' % (conn,user,uid,block))
  556. #check that our membership exists
  557. try:
  558. member_stack = [ (ldap.MOD_ADD, 'memberUid', 'uid=%s,%s' % (str(uid), block['userdn']) ) ]
  559. conn.modify_s('cn=%s,%s' % (block['group'][0], block['groupdn']), member_stack)
  560. except ldap.TYPE_OR_VALUE_EXISTS:
  561. log.error('%s already exists in %s' % (uid, block['group']) )
  562. except ldap.LDAPError, e:
  563. #we hit an error, so we revent our change to the user, this one is the bad error to hit.
  564. log.error('Error adding %s to %s %s' % (uid, block['group'], str(e) ) )
  565. return False
  566. for g, attr in (('groupsu', 'is_superuser'), ('groupstaff', 'is_staff'), ('groupactive', 'is_active')):
  567. if block[g] is not None:
  568. for group in block[g]:
  569. #now we check that our user, and add them to the group
  570. try:
  571. if getattr(user, attr) == True:
  572. mod_attrs = [ (ldap.MOD_ADD, 'memberUid', 'uid=%s,%s' % (str(uid), block['userdn']) ) ]
  573. else:
  574. mod_attrs = [ (ldap.MOD_DELETE, 'memberUid', 'uid=%s,%s' % (str(uid), block['userdn']) ) ]
  575. conn.modify_s('cn=%s,%s' % (group, block['groupdn']), mod_attrs)
  576. log.debug('Successfully modified %s with user %s' % (group, uid) )
  577. except ldap.NO_SUCH_ATTRIBUTE:
  578. continue
  579. except ldap.TYPE_OR_VALUE_EXISTS:
  580. continue
  581. except ldap.LDAPError, e:
  582. log.error('Error adding %s to %s %s' % (uid, group, str(e) ) )
  583. #we hit an error, but, in the schema of things a group error isnt to big of a deal and is easy to correct, and also extremely unlikely to occur.
  584. #in the majority of cases, this will only result in a user having no ablitiy to login, or permissions not granted, both of which are not bad situations
  585. return True
  586.  
  587. def _parse_simple_config(self):
  588. log.debug('LDAPBackend._parse_simple_config()')
  589. if len(settings.LDAP_AUTH_SETTINGS) < 2:
  590. raise LDAPBackendError('In a minimal configuration, you must at least specify url and user DN')
  591. ret = {'url': settings.LDAP_AUTH_SETTINGS[0], 'userdn': settings.LDAP_AUTH_SETTINGS[1]}
  592.  
  593. if len(settings.LDAP_AUTH_SETTINGS) < 3:
  594. return ret
  595.  
  596. if len(settings.LDAP_AUTH_SETTINGS) < 4:
  597. raise LDAPBackendError('If you specify a required group, you must specify the group DN as well')
  598. ret['group'] = settings.LDAP_AUTH_SETTINGS[2]
  599. ret['groupdn'] = settings.LDAP_AUTH_SETTINGS[3]
  600. return ret
  601.  
  602. def _return_user(self, uid, conn, block):
  603. log.debug('LDAPBackend._return_user(%s,%s,%s)' % (uid,conn,block))
  604. try:
  605. user = LDAP_User.objects.get(username=uid)
  606. except User.DoesNotExist:
  607. log.info('User %s did not exist in Django database, creating' % uid)
  608. user = LDAP_User(username=uid, password='')
  609. user.set_unusable_password()
  610. #user.save()
  611. ldap_data = self._getUserByUid(uid, user, conn, block)
  612. log.debug(ldap_data)
  613. log.info('Updating Django database for user %s' % uid)
  614. log.debug('Setting attributes: %s' % str(ldap_data))
  615. for attr in ldap_data:
  616. setattr(user, attr, ldap_data[attr])
  617. #conviniently, this will pull our other servers into sync with the data our user has here.
  618. user.save()
  619. return user
  620.  
  621. #we define our custom error handlers.
  622. class Error(Exception):
  623. pass
  624.  
  625. class LDAPUserCreateError(Error):
  626. def __init__(self, expr, msg):
  627. self.msg = msg
  628.  
  629. class LDAP_User(User):
  630. class Meta:
  631. proxy = True
  632.  
  633. def save(self,*args,**kwargs):
  634. log.debug('LDAP_User.save()' )
  635. try:
  636. sid = transaction.savepoint()
  637. #save to ldap here
  638. ldapbackend = LDAPBackend()
  639. # we dont want to catch this raised exception, we want to use it to hook our roll back.
  640. ldapbackend.commitUser(self)
  641. del ldapbackend
  642. super(User,self).save(*args,**kwargs)
  643. transaction.savepoint_commit(sid)
  644. except:
  645. log.debug('Error saving user: %s' % sys.exc_info()[0] )
  646. transaction.savepoint_rollback(sid)
  647.  
  648. def set_password(self, rawpassword):
  649. log.debug('LDAP_User.set_password(%s)' % rawpassword)
  650. ldapbackend = LDAPBackend()
  651. ldapbackend.changePasswd(self, rawpassword)
  652. #we should be checking if our user exists before saving, and set the unusable password anyway
  653. #since the user model already exists, it must have come out of the lday system, so we dont need to check.
  654. #we dont need to do a self.save() because it is not in the original models method, its also saved to ldap internal to the methods.
  655.  
  656. def check_password(self, rawpassword):
  657. log.debug('LDAP_User.check_password(%s)' % rawpassword)
  658. #return a boolean of true or false if we can bind.
  659. ldapbackend = LDAPBackend()
  660. return ldapbackend.checkPasswd(self.username, rawpassword)
  661. #do a bind operation
Add Comment
Please, Sign In to add comment