# HG changeset patch # User Aurelien Campeas # Date 1338971454 -7200 # Node ID 39c5bb4dcc59e44a5b818481f6223debe3aada02 # Parent ff9d6d26987744c250daaabf4f92452f72d448c7 [ldapfeed] do not crash on ldap user deletion + pull + already deactivated users, cleanups (closes #2392933) diff -r ff9d6d269877 -r 39c5bb4dcc59 server/serverctl.py --- a/server/serverctl.py Wed Jun 06 10:26:34 2012 +0200 +++ b/server/serverctl.py Wed Jun 06 10:30:54 2012 +0200 @@ -447,7 +447,7 @@ get_connection( system['db-driver'], database=system['db-name'], host=system.get('db-host'), port=system.get('db-port'), - user=system.get('db-user'), password=system.get('db-password'), + user=system.get('db-user') or '', password=system.get('db-password') or '', **extra) except Exception, ex: raise ConfigurationError( diff -r ff9d6d269877 -r 39c5bb4dcc59 server/sources/datafeed.py --- a/server/sources/datafeed.py Wed Jun 06 10:26:34 2012 +0200 +++ b/server/sources/datafeed.py Wed Jun 06 10:30:54 2012 +0200 @@ -378,7 +378,6 @@ % (etype, ','.join(eids))) def update_if_necessary(self, entity, attrs): - self.notify_updated(entity) entity.complete(tuple(attrs)) # check modification date and compare attribute values to only update # what's actually needed @@ -388,7 +387,7 @@ if v != getattr(entity, k)) if attrs: entity.set_attributes(**attrs) - + self.notify_updated(entity) class DataFeedXMLParser(DataFeedParser): diff -r ff9d6d269877 -r 39c5bb4dcc59 server/test/unittest_ldapuser.py --- a/server/test/unittest_ldapuser.py Wed Jun 06 10:26:34 2012 +0200 +++ b/server/test/unittest_ldapuser.py Wed Jun 06 10:30:54 2012 +0200 @@ -20,9 +20,8 @@ import os import shutil import time -from os.path import abspath, join, exists +from os.path import join, exists import subprocess -from socket import socket, error as socketerror from logilab.common.testlib import TestCase, unittest_main, mock_object, Tags @@ -32,13 +31,15 @@ from cubicweb.devtools.httptest import get_available_port from cubicweb.devtools import get_test_db_handler -from cubicweb.server.sources.ldapuser import * +from cubicweb.server.session import security_enabled +from cubicweb.server.sources.ldapuser import GlobTrFunc, UnknownEid, RQL2LDAPFilter CONFIG = u'user-base-dn=ou=People,dc=cubicweb,dc=test' URL = None -def create_slapd_configuration(config): - global slapd_process, URL +def create_slapd_configuration(cls): + global URL + config = cls.config basedir = join(config.apphome, "ldapdb") slapdconf = join(config.apphome, "slapd.conf") confin = file(join(config.apphome, "slapd.conf.in")).read() @@ -60,28 +61,26 @@ ldapuri = 'ldap://%s' % host cmdline = ["/usr/sbin/slapd", "-f", slapdconf, "-h", ldapuri, "-d", "0"] config.info('Starting slapd:', ' '.join(cmdline)) - slapd_process = subprocess.Popen(cmdline) + cls.slapd_process = subprocess.Popen(cmdline) time.sleep(0.2) - if slapd_process.poll() is None: - config.info('slapd started with pid %s' % slapd_process.pid) + if cls.slapd_process.poll() is None: + config.info('slapd started with pid %s' % cls.slapd_process.pid) else: raise EnvironmentError('Cannot start slapd with cmdline="%s" (from directory "%s")' % (" ".join(cmdline), os.getcwd())) URL = u'ldap://%s' % host -def terminate_slapd(config): - global slapd_process - if slapd_process.returncode is None: +def terminate_slapd(cls): + config = cls.config + if cls.slapd_process and cls.slapd_process.returncode is None: config.info('terminating slapd') - if hasattr(slapd_process, 'terminate'): - slapd_process.terminate() + if hasattr(cls.slapd_process, 'terminate'): + cls.slapd_process.terminate() else: import os, signal - os.kill(slapd_process.pid, signal.SIGTERM) - slapd_process.wait() + os.kill(cls.slapd_process.pid, signal.SIGTERM) + cls.slapd_process.wait() config.info('DONE') - del slapd_process - class LDAPTestBase(CubicWebTC): loglevel = 'ERROR' @@ -90,11 +89,11 @@ def setUpClass(cls): from cubicweb.cwctl import init_cmdline_log_threshold init_cmdline_log_threshold(cls.config, cls.loglevel) - create_slapd_configuration(cls.config) + create_slapd_configuration(cls) @classmethod def tearDownClass(cls): - terminate_slapd(cls.config) + terminate_slapd(cls) class DeleteStuffFromLDAPFeedSourceTC(LDAPTestBase): test_db_id = 'ldap-feed' @@ -108,24 +107,36 @@ lfsource = isession.repo.sources_by_uri['ldapuser'] stats = lfsource.pull_data(isession, force=True, raise_on_error=True) + def _pull(self): + with self.session.repo.internal_session() as isession: + with security_enabled(isession, read=False, write=False): + lfsource = isession.repo.sources_by_uri['ldapuser'] + stats = lfsource.pull_data(isession, force=True, raise_on_error=True) + isession.commit() + def test_delete(self): + """ delete syt, pull, check deactivation, repull, + readd syt, pull, check activation + """ uri = self.repo.sources_by_uri['ldapuser'].urls[0] - from subprocess import call deletecmd = ("ldapdelete -H %s 'uid=syt,ou=People,dc=cubicweb,dc=test' " "-v -x -D cn=admin,dc=cubicweb,dc=test -w'cw'" % uri) os.system(deletecmd) - isession = self.session.repo.internal_session(safe=False) - from cubicweb.server.session import security_enabled - with security_enabled(isession, read=False, write=False): - lfsource = isession.repo.sources_by_uri['ldapuser'] - stats = lfsource.pull_data(isession, force=True, raise_on_error=True) - isession.commit() + self._pull() self.assertRaises(AuthenticationError, self.repo.connect, 'syt', password='syt') self.assertEqual(self.execute('Any N WHERE U login "syt", ' 'U in_state S, S name N').rows[0][0], 'deactivated') - - + # check that it doesn't choke + self._pull() + # reset the fscking ldap thing + self.tearDownClass() + self.setUpClass() + self._pull() + # still deactivated, but a warning has been emitted ... + self.assertEqual(self.execute('Any N WHERE U login "syt", ' + 'U in_state S, S name N').rows[0][0], + 'deactivated') class LDAPFeedSourceTC(LDAPTestBase): test_db_id = 'ldap-feed' diff -r ff9d6d269877 -r 39c5bb4dcc59 sobjects/ldapparser.py --- a/sobjects/ldapparser.py Wed Jun 06 10:26:34 2012 +0200 +++ b/sobjects/ldapparser.py Wed Jun 06 10:30:54 2012 +0200 @@ -67,9 +67,23 @@ self.warning('deactivate %s %s entities', len(eids), etype) for eid in eids: wf = session.entity_from_eid(eid).cw_adapt_to('IWorkflowable') - wf.fire_transition('deactivate') + wf.fire_transition_if_possible('deactivate') session.commit(free_cnxset=False) + def update_if_necessary(self, entity, attrs): + entity.complete(tuple(attrs)) + if entity.__regid__ == 'CWUser': + wf = entity.cw_adapt_to('IWorkflowable') + if wf.state == 'deactivated': + self.warning('update on deactivated user %s', entity.login) + mdate = attrs.get('modification_date') + if not mdate or mdate > entity.modification_date: + attrs = dict( (k, v) for k, v in attrs.iteritems() + if v != getattr(entity, k)) + if attrs: + entity.set_attributes(**attrs) + self.notify_updated(entity) + def ldap2cwattrs(self, sdict, tdict=None): if tdict is None: tdict = {}