Skip to content

Commit aa0a07b

Browse files
committed
Make LDAPI optional
LDAPI is LDAP over Unix Domain Socket, also referred as LDAP IPC. AF_UNIX is only supported on POSIX compatible operating systems. Don't bind to LDAPI socket and skip LDAPI specific tests, e.g. SASL EXTERNAL with SO_PEERCRED. Signed-off-by: Christian Heimes <cheimes@redhat.com>
1 parent ab93063 commit aa0a07b

File tree

6 files changed

+55
-21
lines changed

6 files changed

+55
-21
lines changed

Lib/slapdtest/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,5 @@
88
__version__ = '3.0.0b2'
99

1010
from slapdtest._slapdtest import SlapdObject, SlapdTestCase, SysLogHandler
11-
from slapdtest._slapdtest import skip_unless_ci, requires_sasl, requires_tls
11+
from slapdtest._slapdtest import requires_ldapi, requires_sasl, requires_tls
12+
from slapdtest._slapdtest import skip_unless_ci

Lib/slapdtest/_slapdtest.py

Lines changed: 38 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,12 @@
5656

5757
LOCALHOST = '127.0.0.1'
5858

59+
CI_DISABLED = set(os.environ.get('CI_DISABLED', '').split(':'))
60+
if 'LDAPI' in CI_DISABLED:
61+
HAVE_LDAPI = False
62+
else:
63+
HAVE_LDAPI = hasattr(socket, 'AF_UNIX')
64+
5965

6066
def identity(test_item):
6167
"""Identity decorator
@@ -69,7 +75,7 @@ def skip_unless_ci(reason, feature=None):
6975
"""
7076
if not os.environ.get('CI', False):
7177
return unittest.skip(reason)
72-
elif feature in os.environ.get('CI_DISABLED', '').split(':'):
78+
elif feature in CI_DISABLED:
7379
return unittest.skip(reason)
7480
else:
7581
# Don't skip on Travis
@@ -95,6 +101,14 @@ def requires_sasl():
95101
return identity
96102

97103

104+
def requires_ldapi():
105+
if not HAVE_LDAPI:
106+
return skip_unless_ci(
107+
"test needs ldapi support (AF_UNIX)", feature='LDAPI')
108+
else:
109+
return identity
110+
111+
98112
def combined_logger(
99113
log_name,
100114
log_level=logging.WARN,
@@ -149,8 +163,6 @@ class SlapdObject(object):
149163
root_dn = 'cn=%s,%s' % (root_cn, suffix)
150164
root_pw = 'password'
151165
slapd_loglevel = 'stats stats2'
152-
# use SASL/EXTERNAL via LDAPI when invoking OpenLDAP CLI tools
153-
cli_sasl_external = True
154166
local_host = '127.0.0.1'
155167
testrunsubdirs = (
156168
'schema',
@@ -192,8 +204,17 @@ def __init__(self):
192204
self._slapd_conf = os.path.join(self.testrundir, 'slapd.conf')
193205
self._db_directory = os.path.join(self.testrundir, "openldap-data")
194206
self.ldap_uri = "ldap://%s:%d/" % (LOCALHOST, self._port)
195-
ldapi_path = os.path.join(self.testrundir, 'ldapi')
196-
self.ldapi_uri = "ldapi://%s" % quote_plus(ldapi_path)
207+
if HAVE_LDAPI:
208+
ldapi_path = os.path.join(self.testrundir, 'ldapi')
209+
self.ldapi_uri = "ldapi://%s" % quote_plus(ldapi_path)
210+
self.default_ldap_uri = self.ldapi_uri
211+
# use SASL/EXTERNAL via LDAPI when invoking OpenLDAP CLI tools
212+
self.cli_sasl_external = True
213+
else:
214+
self.ldapi_uri = None
215+
self.default_ldap_uri = self.ldap_uri
216+
# Use simple bind via LDAP uri
217+
self.cli_sasl_external = False
197218
# TLS certs
198219
self.cafile = os.path.join(HERE, 'certs/ca.pem')
199220
self.servercert = os.path.join(HERE, 'certs/server.pem')
@@ -331,11 +352,14 @@ def _start_slapd(self):
331352
"""
332353
Spawns/forks the slapd process
333354
"""
355+
urls = [self.ldap_uri]
356+
if self.ldapi_uri:
357+
urls.append(self.ldapi_uri)
334358
slapd_args = [
335359
self.PATH_SLAPD,
336360
'-f', self._slapd_conf,
337361
'-F', self.testrundir,
338-
'-h', '%s' % ' '.join((self.ldap_uri, self.ldapi_uri)),
362+
'-h', '%s' % ' '.join(urls),
339363
]
340364
if self._log.isEnabledFor(logging.DEBUG):
341365
slapd_args.extend(['-d', '-1'])
@@ -346,18 +370,21 @@ def _start_slapd(self):
346370
# Waits until the LDAP server socket is open, or slapd crashed
347371
# no cover to avoid spurious coverage changes, see
348372
# https://github.com/python-ldap/python-ldap/issues/127
349-
while 1: # pragma: no cover
373+
for _ in range(10): # pragma: no cover
350374
if self._proc.poll() is not None:
351375
self._stopped()
352376
raise RuntimeError("slapd exited before opening port")
353377
time.sleep(self._start_sleep)
354378
try:
355-
self._log.debug("slapd connection check to %s", self.ldapi_uri)
379+
self._log.debug(
380+
"slapd connection check to %s", self.default_ldap_uri
381+
)
356382
self.ldapwhoami()
357383
except RuntimeError:
358384
pass
359385
else:
360386
return
387+
raise RuntimeError("slapd did not start properly")
361388

362389
def start(self):
363390
"""
@@ -435,9 +462,11 @@ def _cli_auth_args(self):
435462
# no cover to avoid spurious coverage changes
436463
def _cli_popen(self, ldapcommand, extra_args=None, ldap_uri=None,
437464
stdin_data=None): # pragma: no cover
465+
if ldap_uri is None:
466+
ldap_uri = self.default_ldap_uri
438467
args = [
439468
ldapcommand,
440-
'-H', ldap_uri or self.ldapi_uri,
469+
'-H', ldap_uri,
441470
] + self._cli_auth_args() + (extra_args or [])
442471
self._log.debug('Run command: %r', ' '.join(args))
443472
proc = subprocess.Popen(

Tests/t_ldap_sasl.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@
1414

1515
from ldap.ldapobject import SimpleLDAPObject
1616
import ldap.sasl
17-
from slapdtest import SlapdTestCase, requires_sasl, requires_tls
17+
from slapdtest import SlapdTestCase
18+
from slapdtest import requires_ldapi, requires_sasl, requires_tls
1819

1920

2021
LDIF = """
@@ -60,7 +61,7 @@ def setUpClass(cls):
6061
)
6162
cls.server.ldapadd(ldif)
6263

63-
@unittest.skipUnless(hasattr(socket, 'AF_UNIX'), "needs Unix socket")
64+
@requires_ldapi()
6465
def test_external_ldapi(self):
6566
# EXTERNAL authentication with LDAPI (AF_UNIX)
6667
ldap_conn = self.ldap_object_class(self.server.ldapi_uri)

Tests/t_ldap_schema_subentry.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
from ldap.ldapobject import SimpleLDAPObject
1717
import ldap.schema
1818
from ldap.schema.models import ObjectClass
19-
from slapdtest import SlapdTestCase
19+
from slapdtest import SlapdTestCase, requires_ldapi
2020

2121
HERE = os.path.abspath(os.path.dirname(__file__))
2222

@@ -88,6 +88,7 @@ def test_urlfetch_ldap(self):
8888
dn, schema = ldap.schema.urlfetch(self.server.ldap_uri)
8989
self.assertSlapdSchema(dn, schema)
9090

91+
@requires_ldapi()
9192
def test_urlfetch_ldapi(self):
9293
dn, schema = ldap.schema.urlfetch(self.server.ldapi_uri)
9394
self.assertSlapdSchema(dn, schema)

Tests/t_ldapobject.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@
2222
import unittest
2323
import warnings
2424
import pickle
25-
import warnings
26-
from slapdtest import SlapdTestCase, requires_sasl, requires_tls
25+
from slapdtest import SlapdTestCase
26+
from slapdtest import requires_ldapi, requires_sasl, requires_tls
2727

2828
# Switch off processing .ldaprc or ldap.conf before importing _ldap
2929
os.environ['LDAPNOINIT'] = '1'
@@ -303,6 +303,7 @@ def test005_invalid_credentials(self):
303303
self.fail("expected INVALID_CREDENTIALS, got %r" % r)
304304

305305
@requires_sasl()
306+
@requires_ldapi()
306307
def test006_sasl_extenal_bind_s(self):
307308
l = self.ldap_object_class(self.server.ldapi_uri)
308309
l.sasl_external_bind_s()
@@ -441,6 +442,7 @@ class Test01_ReconnectLDAPObject(Test00_SimpleLDAPObject):
441442
ldap_object_class = ReconnectLDAPObject
442443

443444
@requires_sasl()
445+
@requires_ldapi()
444446
def test101_reconnect_sasl_external(self):
445447
l = self.ldap_object_class(self.server.ldapi_uri)
446448
l.sasl_external_bind_s()
@@ -450,15 +452,15 @@ def test101_reconnect_sasl_external(self):
450452
self.assertEqual(l.whoami_s(), authz_id)
451453

452454
def test102_reconnect_simple_bind(self):
453-
l = self.ldap_object_class(self.server.ldapi_uri)
455+
l = self.ldap_object_class(self.server.ldap_uri)
454456
bind_dn = 'cn=user1,'+self.server.suffix
455457
l.simple_bind_s(bind_dn, 'user1_pw')
456458
self.assertEqual(l.whoami_s(), 'dn:'+bind_dn)
457459
self.server.restart()
458460
self.assertEqual(l.whoami_s(), 'dn:'+bind_dn)
459461

460462
def test103_reconnect_get_state(self):
461-
l1 = self.ldap_object_class(self.server.ldapi_uri)
463+
l1 = self.ldap_object_class(self.server.ldap_uri)
462464
bind_dn = 'cn=user1,'+self.server.suffix
463465
l1.simple_bind_s(bind_dn, 'user1_pw')
464466
self.assertEqual(l1.whoami_s(), 'dn:'+bind_dn)
@@ -477,15 +479,15 @@ def test103_reconnect_get_state(self):
477479
str('_start_tls'): 0,
478480
str('_trace_level'): 0,
479481
str('_trace_stack_limit'): 5,
480-
str('_uri'): self.server.ldapi_uri,
482+
str('_uri'): self.server.ldap_uri,
481483
str('bytes_mode'): l1.bytes_mode,
482484
str('bytes_mode_hardfail'): l1.bytes_mode_hardfail,
483485
str('timeout'): -1,
484486
},
485487
)
486488

487489
def test104_reconnect_restore(self):
488-
l1 = self.ldap_object_class(self.server.ldapi_uri)
490+
l1 = self.ldap_object_class(self.server.ldap_uri)
489491
bind_dn = 'cn=user1,'+self.server.suffix
490492
l1.simple_bind_s(bind_dn, 'user1_pw')
491493
self.assertEqual(l1.whoami_s(), 'dn:'+bind_dn)

tox.ini

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@ basepython = python2
3333
deps = {[testenv]deps}
3434
passenv = {[testenv]passenv}
3535
setenv =
36-
CI_DISABLED=TLS:SASL
37-
# rebuild without SASL and TLS
36+
CI_DISABLED=LDAPI:SASL:TLS
37+
# rebuild without SASL and TLS, run without LDAPI
3838
commands = {envpython} \
3939
-m coverage run --parallel setup.py \
4040
clean --all \

0 commit comments

Comments
 (0)