summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCharles-Henri de Boysson <ceache@users.noreply.github.com>2018-12-17 22:44:07 +0000
committerStephen SORRIAUX <stephen.sorriaux@gmail.com>2020-02-23 11:58:28 +0100
commit5c461bdff50efd762805b40fbe283b194d175c61 (patch)
treeebfcc98b9d40154ec56b9a0be0b89dab50125224
parent7a0d54eec99f55705f5ddc7592a05a28aba47efc (diff)
downloadkazoo-5c461bdff50efd762805b40fbe283b194d175c61.tar.gz
feat(core): run SASL Kerberos tests as part of build
* Install debian packages for KDC as part of Travis init. * Setup a loopback mini KDC for running tests. * Run SASL tests as part of Travis builds. * Improve harness cluster to support: * Reconfiguration when environment changes. * Different JAAS configurations (DIGEST/GSSAPI). * Moved SASL tests into own module, with specially configured harness. * Bumped default timeout to 15 sec to mitigate false negatives on Travis.
-rw-r--r--.travis.yml2
-rwxr-xr-xensure-zookeeper-env.sh2
-rwxr-xr-xinit_krb5.sh108
-rw-r--r--kazoo/testing/common.py28
-rw-r--r--kazoo/testing/harness.py145
-rw-r--r--kazoo/tests/test_client.py108
-rw-r--r--kazoo/tests/test_sasl.py186
-rw-r--r--requirements.txt6
-rw-r--r--requirements_test.txt5
-rw-r--r--setup.py2
-rw-r--r--tox.ini14
11 files changed, 431 insertions, 175 deletions
diff --git a/.travis.yml b/.travis.yml
index aa98e5d..ef6ef66 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -4,6 +4,8 @@ addons:
apt:
packages:
- libevent-dev
+ - krb5-kdc
+ - krb5-admin-server
cache:
pip: true
directories:
diff --git a/ensure-zookeeper-env.sh b/ensure-zookeeper-env.sh
index 7d6063e..17cbc58 100755
--- a/ensure-zookeeper-env.sh
+++ b/ensure-zookeeper-env.sh
@@ -4,7 +4,7 @@ set -e
HERE=`pwd`
ZOO_BASE_DIR="$HERE/zookeeper"
-ZOOKEEPER_VERSION=${ZOOKEEPER_VERSION:-3.4.10}
+ZOOKEEPER_VERSION=${ZOOKEEPER_VERSION:-3.4.13}
ZOOKEEPER_PATH="$ZOO_BASE_DIR/$ZOOKEEPER_VERSION"
ZOOKEEPER_PREFIX=${ZOOKEEPER_PREFIX}
ZOOKEEPER_SUFFIX=${ZOOKEEPER_SUFFIX}
diff --git a/init_krb5.sh b/init_krb5.sh
new file mode 100755
index 0000000..a44bbe1
--- /dev/null
+++ b/init_krb5.sh
@@ -0,0 +1,108 @@
+#!/bin/bash
+
+set -e
+
+KRB5KDC=$(which krb5kdc || true)
+KDB5_UTIL=$(which kdb5_util || true)
+KADMIN=$(which kadmin.local || true)
+
+if [ $# -lt 2 ]; then
+ echo "Usage $0 TARGET_DIR CMD..."
+ exit 1
+fi
+WRK_DIR=$1
+shift
+
+# Check installed packages
+if [ -z ${KRB5KDC:+x} ] || [ -z ${KDB5_UTIL:+x} ] || [ -z ${KADMIN:+x} ]; then
+ echo "Missing Kerberos utilities, skipping environment setup."
+ exec $@
+fi
+
+if [ -e ${WRK_DIR} ]; then
+ echo "Working directory kdc already exists!"
+ exit 1
+fi
+
+WRK_DIR=$(readlink -f ${WRK_DIR})
+KDC_DIR="${WRK_DIR}/krb5kdc"
+
+###############################################################################
+# Cleanup handlers
+
+function kdclogs {
+ echo "Kerberos environment logs:"
+ tail -v -n50 ${KDC_DIR}/*.log
+}
+
+function killkdc {
+ if [ -e ${KDC_DIR}/kdc.pid ]; then
+ echo "Terminating KDC server listening on ${KDC_PORT}..."
+ kill -TERM $(cat ${KDC_DIR}/kdc.pid)
+ fi
+ rm -vfr ${WRK_DIR}
+}
+trap killkdc EXIT
+trap kdclogs ERR
+
+###############################################################################
+export KRB5_TEST_ENV=${WRK_DIR}
+export KRB5_CONFIG=${WRK_DIR}/krb5.conf
+
+KDC_PORT=$((${RANDOM}+1024))
+mkdir -vp ${WRK_DIR}
+mkdir -vp ${KDC_DIR}
+
+cat <<EOF >${WRK_DIR}/krb5.conf
+[logging]
+ default = FILE:${KDC_DIR}/krb5libs.log
+ kdc = FILE:${KDC_DIR}/krb5kdc.log
+ admin_server = FILE:${KDC_DIR}/kadmind.log
+
+[libdefaults]
+ dns_lookup_realm = false
+ ticket_lifetime = 24h
+ renew_lifetime = 7d
+ forwardable = true
+ rdns = false
+ default_realm = KAZOOTEST.ORG
+ #default_ccache_name = KEYRING:persistent:%{uid}
+
+[realms]
+ KAZOOTEST.ORG = {
+ database_name = ${KDC_DIR}/principal
+ admin_keytab = FILE:${KDC_DIR}/kadm5.keytab
+ key_stash_file = ${KDC_DIR}/stash
+ kdc_listen = 127.0.0.1:${KDC_PORT}
+ kdc_tcp_listen = 127.0.0.1:${KDC_PORT}
+ kdc = 127.0.0.1:${KDC_PORT}
+ default_domain = kazootest.org
+ }
+
+[domain_realm]
+ .kazootest.org = KAZOOTEST.ORG
+ kazootest.org = KAZOOTEST.ORG
+EOF
+
+cat <<EOF | ${KDB5_UTIL} create -s
+passwd123
+passwd123
+EOF
+
+cat <<EOF | ${KADMIN}
+add_principal -randkey client@KAZOOTEST.ORG
+ktadd -k ${WRK_DIR}/client.keytab -norandkey client@KAZOOTEST.ORG
+add_principal -randkey zookeeper/127.0.0.1@KAZOOTEST.ORG
+ktadd -k ${WRK_DIR}/server.keytab -norandkey zookeeper/127.0.0.1@KAZOOTEST.ORG
+quit
+EOF
+
+# Starting KDC
+echo "Starting KDC listening on ${KDC_PORT}..."
+KRB5_KDC_PROFILE=${KRB5_CONFIG} ${KRB5KDC} \
+ -P ${KDC_DIR}/kdc.pid \
+ -p ${KDC_PORT} \
+ -r KAZOOTEST.ORG
+
+# Execute the next command
+$@
diff --git a/kazoo/testing/common.py b/kazoo/testing/common.py
index 226e5f7..c8005df 100644
--- a/kazoo/testing/common.py
+++ b/kazoo/testing/common.py
@@ -75,7 +75,8 @@ class ManagedZooKeeper(object):
Hudson/Buildbot context, to ensure more test robustness."""
def __init__(self, software_path, server_info, peers=(), classpath=None,
- configuration_entries=[], java_system_properties=[]):
+ configuration_entries=[], java_system_properties=[],
+ jaas_config=None):
"""Define the ZooKeeper test instance.
@param install_path: The path to the install for ZK
@@ -90,6 +91,7 @@ class ManagedZooKeeper(object):
self._running = False
self.configuration_entries = configuration_entries
self.java_system_properties = java_system_properties
+ self.jaas_config = jaas_config
def run(self):
"""Run the ZooKeeper instance under a temporary directory.
@@ -99,7 +101,7 @@ class ManagedZooKeeper(object):
if self.running:
return
config_path = os.path.join(self.working_path, "zoo.cfg")
- jass_config_path = os.path.join(self.working_path, "jaas.conf")
+ jaas_config_path = os.path.join(self.working_path, "jaas.conf")
log_path = os.path.join(self.working_path, "log")
log4j_path = os.path.join(self.working_path, "log4j.properties")
data_path = os.path.join(self.working_path, "data")
@@ -126,7 +128,6 @@ authProvider.1=org.apache.zookeeper.server.auth.SASLAuthenticationProvider
self.server_info.admin_port,
"\n".join(self.configuration_entries))) # NOQA
-
# setup a replicated setup if peers are specified
if self.peers:
servers_cfg = []
@@ -146,14 +147,8 @@ peerType=%s
with open(os.path.join(data_path, "myid"), "w") as myid_file:
myid_file.write(str(self.server_info.server_id))
# Write JAAS configuration
- with open(jass_config_path, "w") as jaas_file:
- jaas_file.write("""
-Server {
- org.apache.zookeeper.server.auth.DigestLoginModule required
- user_super="super_secret"
- user_jaasuser="jaas_password";
-};""")
-
+ with open(jaas_config_path, "w") as jaas_file:
+ jaas_file.write(self.jaas_config or '')
with open(log4j_path, "w") as log4j:
log4j.write("""
# DEFAULT: console appender only
@@ -186,7 +181,7 @@ log4j.appender.ROLLINGFILE.File=""" + to_java_compatible_path( # NOQA
"-Djava.awt.headless=true",
# JAAS configuration for SASL authentication
- "-Djava.security.auth.login.config=%s" % jass_config_path,
+ "-Djava.security.auth.login.config=%s" % jaas_config_path,
] + self.java_system_properties + [
"org.apache.zookeeper.server.quorum.QuorumPeerMain",
config_path,
@@ -279,12 +274,12 @@ class ZookeeperCluster(object):
def __init__(self, install_path=None, classpath=None,
size=3, port_offset=20000, observer_start_id=-1,
configuration_entries=[],
- java_system_properties=[]):
+ java_system_properties=[],
+ jaas_config=None):
self._install_path = install_path
self._classpath = classpath
self._servers = []
-
# Calculate ports and peer group
port = port_offset
peers = []
@@ -309,7 +304,10 @@ class ZookeeperCluster(object):
self._install_path, server_info, server_peers,
classpath=self._classpath,
configuration_entries=configuration_entries,
- java_system_properties=java_system_properties))
+ java_system_properties=java_system_properties,
+ jaas_config=jaas_config
+ )
+ )
def __getitem__(self, k):
return self._servers[k]
diff --git a/kazoo/testing/harness.py b/kazoo/testing/harness.py
index de56795..6cb5cb1 100644
--- a/kazoo/testing/harness.py
+++ b/kazoo/testing/harness.py
@@ -16,52 +16,104 @@ from kazoo.testing.common import ZookeeperCluster
log = logging.getLogger(__name__)
CLUSTER = None
+CLUSTER_CONF = None
+CLUSTER_DEFAULTS = {
+ "ZOOKEEPER_PORT_OFFSET": 20000,
+ "ZOOKEEPER_CLUSTER_SIZE": 3,
+ "ZOOKEEPER_OBSERVER_START_ID": -1,
+}
def get_global_cluster():
- global CLUSTER
- if CLUSTER is None:
- ZK_HOME = os.environ.get("ZOOKEEPER_PATH")
- ZK_CLASSPATH = os.environ.get("ZOOKEEPER_CLASSPATH")
- ZK_PORT_OFFSET = int(os.environ.get("ZOOKEEPER_PORT_OFFSET", 20000))
- ZK_CLUSTER_SIZE = int(os.environ.get("ZOOKEEPER_CLUSTER_SIZE", 3))
- ZK_VERSION = os.environ.get("ZOOKEEPER_VERSION")
- if '-' in ZK_VERSION:
- # Ignore pre-release markers like -alpha
- ZK_VERSION = ZK_VERSION.split('-')[0]
- ZK_VERSION = tuple([int(n) for n in ZK_VERSION.split('.')])
-
- ZK_OBSERVER_START_ID = int(
- os.environ.get("ZOOKEEPER_OBSERVER_START_ID", -1))
-
- assert ZK_HOME or ZK_CLASSPATH or ZK_VERSION, (
- "Either ZOOKEEPER_PATH or ZOOKEEPER_CLASSPATH or "
- "ZOOKEEPER_VERSION environment variable must be defined.\n"
- "For deb package installations this is /usr/share/java")
-
- if ZK_VERSION >= (3, 5):
- additional_configuration_entries = [
- "4lw.commands.whitelist=*",
- "reconfigEnabled=true"
- ]
- # If defines, this sets the superuser password to "test"
- additional_java_system_properties = [
- "-Dzookeeper.DigestAuthenticationProvider.superDigest="
- "super:D/InIHSb7yEEbrWz8b9l71RjZJU="
- ]
+ global CLUSTER, CLUSTER_CONF
+ cluster_conf = {
+ k: os.environ.get(k, CLUSTER_DEFAULTS.get(k))
+ for k in ["ZOOKEEPER_PATH",
+ "ZOOKEEPER_CLASSPATH",
+ "ZOOKEEPER_PORT_OFFSET",
+ "ZOOKEEPER_CLUSTER_SIZE",
+ "ZOOKEEPER_VERSION",
+ "ZOOKEEPER_OBSERVER_START_ID",
+ "ZOOKEEPER_JAAS_AUTH"]
+ }
+ if CLUSTER is not None:
+ if CLUSTER_CONF == cluster_conf:
+ return CLUSTER
else:
- additional_configuration_entries = []
- additional_java_system_properties = []
- CLUSTER = ZookeeperCluster(
- install_path=ZK_HOME,
- classpath=ZK_CLASSPATH,
- port_offset=ZK_PORT_OFFSET,
- size=ZK_CLUSTER_SIZE,
- observer_start_id=ZK_OBSERVER_START_ID,
- configuration_entries=additional_configuration_entries,
- java_system_properties=additional_java_system_properties
- )
- atexit.register(lambda cluster: cluster.terminate(), CLUSTER)
+ log.info('Config change detected. Reconfiguring cluster...')
+ CLUSTER.terminate()
+ CLUSTER = None
+ # Create a new cluster
+ ZK_HOME = cluster_conf.get("ZOOKEEPER_PATH")
+ ZK_CLASSPATH = cluster_conf.get("ZOOKEEPER_CLASSPATH")
+ ZK_PORT_OFFSET = int(cluster_conf.get("ZOOKEEPER_PORT_OFFSET"))
+ ZK_CLUSTER_SIZE = int(cluster_conf.get("ZOOKEEPER_CLUSTER_SIZE"))
+ ZK_VERSION = cluster_conf.get("ZOOKEEPER_VERSION")
+ if '-' in ZK_VERSION:
+ # Ignore pre-release markers like -alpha
+ ZK_VERSION = ZK_VERSION.split('-')[0]
+ ZK_VERSION = tuple([int(n) for n in ZK_VERSION.split('.')])
+ ZK_OBSERVER_START_ID = int(cluster_conf.get("ZOOKEEPER_OBSERVER_START_ID"))
+
+ assert ZK_HOME or ZK_CLASSPATH or ZK_VERSION, (
+ "Either ZOOKEEPER_PATH or ZOOKEEPER_CLASSPATH or "
+ "ZOOKEEPER_VERSION environment variable must be defined.\n"
+ "For deb package installations this is /usr/share/java")
+
+ if ZK_VERSION >= (3, 5):
+ additional_configuration_entries = [
+ "4lw.commands.whitelist=*",
+ "reconfigEnabled=true"
+ ]
+ # If defined, this sets the superuser password to "test"
+ additional_java_system_properties = [
+ "-Dzookeeper.DigestAuthenticationProvider.superDigest="
+ "super:D/InIHSb7yEEbrWz8b9l71RjZJU="
+ ]
+ else:
+ additional_configuration_entries = []
+ additional_java_system_properties = []
+ ZOOKEEPER_JAAS_AUTH = cluster_conf.get("ZOOKEEPER_JAAS_AUTH")
+ if ZOOKEEPER_JAAS_AUTH == "digest":
+ jaas_config = """
+Server {
+ org.apache.zookeeper.server.auth.DigestLoginModule required
+ user_super="super_secret"
+ user_jaasuser="jaas_password";
+};"""
+ elif ZOOKEEPER_JAAS_AUTH == "gssapi":
+ # Configure Zookeeper to use our test KDC.
+ additional_java_system_properties += [
+ "-Djava.security.krb5.conf=%s" % os.path.expandvars(
+ "${KRB5_CONFIG}"
+ ),
+ "-Dsun.security.krb5.debug=true",
+ ]
+ jaas_config = """
+Server {
+ com.sun.security.auth.module.Krb5LoginModule required
+ debug=true
+ useKeyTab=true
+ keyTab="%s"
+ storeKey=true
+ useTicketCache=false
+ principal="zookeeper/127.0.0.1@KAZOOTEST.ORG";
+};""" % os.path.expandvars("${KRB5_TEST_ENV}/server.keytab")
+ else:
+ jaas_config = None
+
+ CLUSTER = ZookeeperCluster(
+ install_path=ZK_HOME,
+ classpath=ZK_CLASSPATH,
+ port_offset=ZK_PORT_OFFSET,
+ size=ZK_CLUSTER_SIZE,
+ observer_start_id=ZK_OBSERVER_START_ID,
+ configuration_entries=additional_configuration_entries,
+ java_system_properties=additional_java_system_properties,
+ jaas_config=jaas_config
+ )
+ CLUSTER_CONF = cluster_conf
+ atexit.register(lambda cluster: cluster.terminate(), CLUSTER)
return CLUSTER
@@ -90,6 +142,7 @@ class KazooTestHarness(unittest.TestCase):
something_that_needs_zk_servers(self.servers)
"""
+ DEFAULT_CLIENT_TIMEOUT = 15
def __init__(self, *args, **kw):
super(KazooTestHarness, self).__init__(*args, **kw)
@@ -109,8 +162,10 @@ class KazooTestHarness(unittest.TestCase):
self._clients.append(c)
return c
- def _get_client(self, **kwargs):
- c = KazooClient(self.hosts, **kwargs)
+ def _get_client(self, **client_options):
+ if 'timeout' not in client_options:
+ client_options['timeout'] = self.DEFAULT_CLIENT_TIMEOUT
+ c = KazooClient(self.hosts, **client_options)
self._clients.append(c)
return c
@@ -139,7 +194,7 @@ class KazooTestHarness(unittest.TestCase):
namespace = "/kazootests" + uuid.uuid4().hex
self.hosts = self.servers + namespace
if 'timeout' not in client_options:
- client_options['timeout'] = 0.8
+ client_options['timeout'] = self.DEFAULT_CLIENT_TIMEOUT
self.client = self._get_client(**client_options)
self.client.start()
self.client.ensure_path("/")
diff --git a/kazoo/tests/test_client.py b/kazoo/tests/test_client.py
index 719b24f..fb6d2d9 100644
--- a/kazoo/tests/test_client.py
+++ b/kazoo/tests/test_client.py
@@ -179,74 +179,6 @@ class TestAuthentication(KazooTestCase):
client.stop()
client.close()
- def test_connect_sasl_auth(self):
- from kazoo.security import make_acl
-
- if TRAVIS_ZK_VERSION:
- version = TRAVIS_ZK_VERSION
- else:
- version = self.client.server_version()
- if not version or version < (3, 4):
- raise SkipTest("Must use Zookeeper 3.4 or above")
- try:
- import puresasl # NOQA
- except ImportError:
- raise SkipTest('PureSASL not available.')
-
- username = "jaasuser"
- password = "jaas_password"
-
- acl = make_acl('sasl', credential=username, all=True)
-
- client = self._get_client(
- sasl_options={'mechanism': 'DIGEST-MD5',
- 'username': username,
- 'password': password}
- )
- client.start()
- try:
- client.create('/1', acl=(acl,))
- # give ZK a chance to copy data to other node
- time.sleep(0.1)
- self.assertRaises(NoAuthError, self.client.get, "/1")
- finally:
- client.delete('/1')
- client.stop()
- client.close()
-
- def test_connect_sasl_auth_leg(self):
- from kazoo.security import make_acl
-
- if TRAVIS_ZK_VERSION:
- version = TRAVIS_ZK_VERSION
- else:
- version = self.client.server_version()
- if not version or version < (3, 4):
- raise SkipTest("Must use Zookeeper 3.4 or above")
- try:
- import puresasl # NOQA
- except ImportError:
- raise SkipTest('PureSASL not available.')
-
- username = "jaasuser"
- password = "jaas_password"
-
- sasl_auth = "%s:%s" % (username, password)
-
- acl = make_acl('sasl', credential=username, all=True)
- client = self._get_client(auth_data=[('sasl', sasl_auth)])
-
- client.start()
- try:
- client.create('/1', acl=(acl,))
- # give ZK a chance to copy data to other node
- time.sleep(0.1)
- self.assertRaises(NoAuthError, self.client.get, "/1")
- finally:
- client.delete('/1')
- client.stop()
- client.close()
-
def test_unicode_auth(self):
username = u("xe4/\hm")
password = u("/\xe4hm")
@@ -285,38 +217,6 @@ class TestAuthentication(KazooTestCase):
self.assertRaises(TypeError, client.add_auth,
None, ('user', 'pass'))
- def test_invalid_sasl_auth(self):
- if TRAVIS_ZK_VERSION:
- version = TRAVIS_ZK_VERSION
- else:
- version = self.client.server_version()
- if not version or version < (3, 4):
- raise SkipTest("Must use Zookeeper 3.4 or above")
- try:
- import puresasl # NOQA
- except ImportError:
- raise SkipTest('PureSASL not available.')
- client = self._get_client(
- sasl_options={'mechanism': 'DIGEST-MD5',
- 'username': 'baduser',
- 'password': 'badpassword'}
- )
- self.assertRaises(AuthFailedError, client.start)
-
- def test_invalid_sasl_auth_leg(self):
- if TRAVIS_ZK_VERSION:
- version = TRAVIS_ZK_VERSION
- else:
- version = self.client.server_version()
- if not version or version < (3, 4):
- raise SkipTest("Must use Zookeeper 3.4 or above")
- try:
- import puresasl # NOQA
- except ImportError:
- raise SkipTest('PureSASL not available.')
- client = self._get_client(auth_data=[('sasl', 'baduser:badpassword')])
- self.assertRaises(AuthFailedError, client.start)
-
def test_async_auth(self):
client = self._get_client()
client.start()
@@ -1347,13 +1247,13 @@ class TestReconfig(KazooTestCase):
joining = 'server.100=0.0.0.0:%d:%d:observer;0.0.0.0:%d' % (
port1, port2, port3)
data, _ = client.reconfig(joining=joining,
- leaving=None,
- new_members=None)
+ leaving=None,
+ new_members=None)
self.assertIn(joining.encode('utf8'), data)
data, _ = client.reconfig(joining=None,
- leaving='100',
- new_members=None)
+ leaving='100',
+ new_members=None)
self.assertNotIn(joining.encode('utf8'), data)
# try to add it again, but a config number in the future
diff --git a/kazoo/tests/test_sasl.py b/kazoo/tests/test_sasl.py
new file mode 100644
index 0000000..08839b6
--- /dev/null
+++ b/kazoo/tests/test_sasl.py
@@ -0,0 +1,186 @@
+import os
+import subprocess
+import time
+
+from nose import SkipTest
+
+from kazoo.testing import KazooTestHarness
+from kazoo.exceptions import (
+ AuthFailedError,
+ NoAuthError,
+)
+from kazoo.tests.util import TRAVIS_ZK_VERSION
+
+
+class TestLegacySASLDigestAuthentication(KazooTestHarness):
+
+ def setUp(self):
+ try:
+ import puresasl # NOQA
+ except ImportError:
+ raise SkipTest('PureSASL not available.')
+
+ os.environ['ZOOKEEPER_JAAS_AUTH'] = 'digest'
+ self.setup_zookeeper()
+
+ if TRAVIS_ZK_VERSION:
+ version = TRAVIS_ZK_VERSION
+ else:
+ version = self.client.server_version()
+ if not version or version < (3, 4):
+ raise SkipTest("Must use Zookeeper 3.4 or above")
+
+ def tearDown(self):
+ self.teardown_zookeeper()
+
+ def test_connect_sasl_auth(self):
+ from kazoo.security import make_acl
+
+ username = "jaasuser"
+ password = "jaas_password"
+
+ acl = make_acl('sasl', credential=username, all=True)
+
+ sasl_auth = "%s:%s" % (username, password)
+ client = self._get_client(auth_data=[('sasl', sasl_auth)])
+
+ client.start()
+ try:
+ client.create('/1', acl=(acl,))
+ # give ZK a chance to copy data to other node
+ time.sleep(0.1)
+ self.assertRaises(NoAuthError, self.client.get, "/1")
+ finally:
+ client.delete('/1')
+ client.stop()
+ client.close()
+
+ def test_invalid_sasl_auth(self):
+ client = self._get_client(auth_data=[('sasl', 'baduser:badpassword')])
+ self.assertRaises(AuthFailedError, client.start)
+
+
+class TestSASLDigestAuthentication(KazooTestHarness):
+
+ def setUp(self):
+ try:
+ import puresasl # NOQA
+ except ImportError:
+ raise SkipTest('PureSASL not available.')
+
+ os.environ['ZOOKEEPER_JAAS_AUTH'] = 'digest'
+ self.setup_zookeeper()
+
+ if TRAVIS_ZK_VERSION:
+ version = TRAVIS_ZK_VERSION
+ else:
+ version = self.client.server_version()
+ if not version or version < (3, 4):
+ raise SkipTest("Must use Zookeeper 3.4 or above")
+
+ def tearDown(self):
+ self.teardown_zookeeper()
+
+ def test_connect_sasl_auth(self):
+ from kazoo.security import make_acl
+
+ username = "jaasuser"
+ password = "jaas_password"
+
+ acl = make_acl('sasl', credential=username, all=True)
+
+ client = self._get_client(
+ sasl_options={'mechanism': 'DIGEST-MD5',
+ 'username': username,
+ 'password': password}
+ )
+ client.start()
+ try:
+ client.create('/1', acl=(acl,))
+ # give ZK a chance to copy data to other node
+ time.sleep(0.1)
+ self.assertRaises(NoAuthError, self.client.get, "/1")
+ finally:
+ client.delete('/1')
+ client.stop()
+ client.close()
+
+ def test_invalid_sasl_auth(self):
+ client = self._get_client(
+ sasl_options={'mechanism': 'DIGEST-MD5',
+ 'username': 'baduser',
+ 'password': 'badpassword'}
+ )
+ self.assertRaises(AuthFailedError, client.start)
+
+
+class TestSASLGSSAPIAuthentication(KazooTestHarness):
+
+ def setUp(self):
+ try:
+ import puresasl # NOQA
+ except ImportError:
+ raise SkipTest('PureSASL not available.')
+ try:
+ import kerberos # NOQA
+ except ImportError:
+ raise SkipTest('Kerberos support not available.')
+ if not os.environ.get('KRB5_TEST_ENV'):
+ raise SkipTest('Test Kerberos environ not setup.')
+
+ os.environ['ZOOKEEPER_JAAS_AUTH'] = 'gssapi'
+ self.setup_zookeeper()
+
+ if TRAVIS_ZK_VERSION:
+ version = TRAVIS_ZK_VERSION
+ else:
+ version = self.client.server_version()
+ if not version or version < (3, 4):
+ raise SkipTest("Must use Zookeeper 3.4 or above")
+
+ def tearDown(self):
+ self.teardown_zookeeper()
+
+ def test_connect_gssapi_auth(self):
+ from kazoo.security import make_acl
+
+ # Ensure we have a client ticket
+ subprocess.check_call(
+ [
+ 'kinit',
+ '-kt', os.path.expandvars('${KRB5_TEST_ENV}/client.keytab'),
+ 'client'
+ ]
+ )
+
+ acl = make_acl('sasl', credential='client@KAZOOTEST.ORG', all=True)
+
+ client = self._get_client(
+ sasl_options={'mechanism': 'GSSAPI'}
+ )
+ client.start()
+ try:
+ client.create('/1', acl=(acl,))
+ # give ZK a chance to copy data to other node
+ time.sleep(0.1)
+ self.assertRaises(NoAuthError, self.client.get, "/1")
+ finally:
+ client.delete('/1')
+ client.stop()
+ client.close()
+
+ def test_invalid_gssapi_auth(self):
+ # Request a post-datated ticket, so that it is currently invalid.
+ subprocess.check_call(
+ [
+ 'kinit',
+ '-kt', os.path.expandvars('${KRB5_TEST_ENV}/client.keytab'),
+ '-s', '30min',
+ 'client'
+ ]
+ )
+
+ client = self._get_client(
+ sasl_options={'mechanism': 'GSSAPI'}
+ )
+ self.assertRaises(AuthFailedError, client.start)
diff --git a/requirements.txt b/requirements.txt
index f385468..ffe2fce 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,5 +1 @@
-coverage==3.7.1
-mock==1.0.1
-nose==1.3.3
-flake8==2.3.0
-objgraph==3.4.0
+six
diff --git a/requirements_test.txt b/requirements_test.txt
new file mode 100644
index 0000000..f385468
--- /dev/null
+++ b/requirements_test.txt
@@ -0,0 +1,5 @@
+coverage==3.7.1
+mock==1.0.1
+nose==1.3.3
+flake8==2.3.0
+objgraph==3.4.0
diff --git a/setup.py b/setup.py
index a619a25..e47bc31 100644
--- a/setup.py
+++ b/setup.py
@@ -78,7 +78,7 @@ setup(
tests_require=tests_require,
extras_require={
'test': tests_require,
- 'sasl': ['pure-sasl'],
+ 'sasl': ['pure-sasl==0.5.1'],
},
long_description_content_type="text/markdown",
)
diff --git a/tox.ini b/tox.ini
index 71da3f1..504c68c 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,5 +1,5 @@
[tox]
-minversion = 1.6
+minversion = 3.6
skipsdist = True
envlist =
pep8,
@@ -27,12 +27,18 @@ setenv =
ZOOKEEPER_PREFIX={env:ZOOKEEPER_PREFIX:}
ZOOKEEPER_SUFFIX={env:ZOOKEEPER_SUFFIX:}
ZOOKEEPER_LIB={env:ZOOKEEPER_LIB:}
-deps = -r{toxinidir}/requirements.txt
+deps =
+ -r{toxinidir}/requirements.txt
+ -r{toxinidir}/requirements_test.txt
docs: -r{toxinidir}/requirements_sphinx.txt
gevent: -r{toxinidir}/requirements_gevent.txt
- sasl: -r{toxinidir}/requirements_sasl.txt
eventlet: -r{toxinidir}/requirements_eventlet.txt
-commands = {toxinidir}/ensure-zookeeper-env.sh nosetests {posargs: -d -v --with-coverage kazoo.tests}
+ sasl: -r{toxinidir}/requirements_sasl.txt
+ sasl: kerberos
+commands =
+ sasl: {toxinidir}/init_krb5.sh {envtmpdir}/kerberos \
+ {toxinidir}/ensure-zookeeper-env.sh \
+ nosetests {posargs: -d -v --with-coverage kazoo.tests}
[flake8]
builtins = _