diff --git a/.gitignore b/.gitignore index 798a078..176e6d5 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,7 @@ build dist *egg-info __pycache__ +venv +.venv +.coverage +htmlcov diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..9648112 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,21 @@ +FROM ubuntu:18.04 + +RUN apt-get update && \ + apt-get install -y --no-install-recommends \ + python3-pip \ + python3-setuptools \ + gettext \ + krb5-user && \ + rm -rf /var/lib/apt/lists/* + +WORKDIR /app +COPY README.md setup.py entrypoint.sh pytest.ini /app/ +COPY krbticket /app/krbticket +COPY tests /app/tests +COPY tests/conf/krb5.conf.tmpl tests/conf/krb5.keytab /etc/ +RUN chmod 755 /app/entrypoint.sh + +ENV KRB5_HOST localhost + +ENTRYPOINT ["/app/entrypoint.sh"] +CMD ["pytest"] diff --git a/README.md b/README.md index e745f89..a091202 100644 --- a/README.md +++ b/README.md @@ -12,3 +12,10 @@ from krbticket import KrbTicket ticket = KrbTicket.init("", "") ticket.updater_start() ``` + +## Test + +``` +docker run --rm -p 88:88 ksauzz/docker-krb5:0.0.1 +KRB5_CONFIG=tests/conf/krb5.conf.local pytest +``` diff --git a/entrypoint.sh b/entrypoint.sh new file mode 100644 index 0000000..d2f641e --- /dev/null +++ b/entrypoint.sh @@ -0,0 +1,7 @@ +#!/bin/bash +set -eu + +envsubst < /etc/krb5.conf.tmpl > /etc/krb5.conf +pip3 install -e '.[test]' + +exec "$@" diff --git a/krbticket/ticket.py b/krbticket/ticket.py index 80dfbb7..bae7660 100644 --- a/krbticket/ticket.py +++ b/krbticket/ticket.py @@ -158,12 +158,15 @@ def parseDatetime(str): class KrbConfig(): def __init__(self, principal=None, keytab=None, kinit_bin="kinit", - klist_bin="klist", renewal_threshold=timedelta(minutes=30)): + klist_bin="klist", kdestroy_bin="kdestroy", + renewal_threshold=timedelta(minutes=30), ticket_lifetime=None): self.principal = principal self.keytab = keytab self.kinit_bin = kinit_bin self.klist_bin = klist_bin + self.kdestroy_bin = kdestroy_bin self.renewal_threshold = renewal_threshold + self.ticket_lifetime = ticket_lifetime class KrbCommand(): @@ -171,6 +174,9 @@ class KrbCommand(): def kinit(config): commands = [] commands.append(config.kinit_bin) + if config.ticket_lifetime: + commands.append("-l") + commands.append(config.ticket_lifetime) commands.append("-k") commands.append("-t") commands.append(config.keytab) @@ -196,6 +202,12 @@ def klist(config): commands.append(config.klist_bin) return KrbCommand._call(config, commands) + @staticmethod + def kdestroy(config): + commands = [] + commands.append(config.kdestroy_bin) + return KrbCommand._call(config, commands) + @staticmethod def _call(config, commands): logging.debug("Executing {}".format(" ".join(commands))) diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 0000000..56df72c --- /dev/null +++ b/pytest.ini @@ -0,0 +1,2 @@ +[pytest] +addopts = -v -s --cov=krbticket --cov-report=html diff --git a/setup.py b/setup.py index 1f16fa3..28d88d6 100644 --- a/setup.py +++ b/setup.py @@ -3,6 +3,11 @@ with open("README.md", "r") as fh: long_description = fh.read() +test_require = ["pytest", "pytest-cov"] +extras = { + 'test': test_require +} + setuptools.setup( name="krbticket", version="0.0.1.3", @@ -13,6 +18,8 @@ long_description_content_type="text/markdown", url="https://github.com/ksauzz/krbticket", packages=setuptools.find_packages(), + tests_require=test_require, + extras_require=extras, classifiers=[ "Programming Language :: Python :: 3", "License :: OSI Approved :: MIT License", diff --git a/tests/conf/krb5.conf.local b/tests/conf/krb5.conf.local new file mode 100644 index 0000000..81f0707 --- /dev/null +++ b/tests/conf/krb5.conf.local @@ -0,0 +1,26 @@ +[libdefaults] +default_realm = EXAMPLE.COM +dns_lookup_kdc = false +dns_lookup_realm = false +ticket_lifetime = 7d +renew_lifetime = 28d +forwardable = true +default_tgs_enctypes = rc4-hmac +default_tkt_enctypes = rc4-hmac +permitted_enctypes = rc4-hmac +udp_preference_limit = 1 +kdc_timeout = 3000 + +[realms] +EXAMPLE.COM = { + kdc = localhost + admin_server = localhost + default_domain = EXAMPLE.COM +} + +[domain_realm] +example.com = EXAMPLE.COM +.example.com = EXAMPLE.COM + +[logging] +default = CONSOLE diff --git a/tests/conf/krb5.conf.tmpl b/tests/conf/krb5.conf.tmpl new file mode 100644 index 0000000..09c7278 --- /dev/null +++ b/tests/conf/krb5.conf.tmpl @@ -0,0 +1,26 @@ +[libdefaults] +default_realm = EXAMPLE.COM +dns_lookup_kdc = false +dns_lookup_realm = false +ticket_lifetime = 7d +renew_lifetime = 28d +forwardable = true +default_tgs_enctypes = rc4-hmac +default_tkt_enctypes = rc4-hmac +permitted_enctypes = rc4-hmac +udp_preference_limit = 1 +kdc_timeout = 3000 + +[realms] +EXAMPLE.COM = { + kdc = ${KRB5_HOST} + admin_server = ${KRB5_HOST} + default_domain = EXAMPLE.COM +} + +[domain_realm] +example.com = EXAMPLE.COM +.example.com = EXAMPLE.COM + +[logging] +default = CONSOLE diff --git a/tests/conf/krb5.keytab b/tests/conf/krb5.keytab new file mode 100644 index 0000000..975237b Binary files /dev/null and b/tests/conf/krb5.keytab differ diff --git a/tests/helper.py b/tests/helper.py new file mode 100644 index 0000000..0036890 --- /dev/null +++ b/tests/helper.py @@ -0,0 +1,19 @@ +import pytest +from krbticket import KrbConfig + +DEFAULT_PRINCIPAL = 'user@EXAMPLE.COM' +DEFAULT_KEYTAB = './tests/conf/krb5.keytab' +DEFAULT_TICKET_LIFETIME = '2s' + +def assert_ticket(t1, t2): + assert t1.principal == t2.principal + assert t1.file == t2.file + assert t1.starting == t2.starting + assert t1.expires == t2.expires + assert t1.service_principal == t2.service_principal + + +@pytest.fixture +def config(): + return KrbConfig(DEFAULT_PRINCIPAL, DEFAULT_KEYTAB, ticket_lifetime=DEFAULT_TICKET_LIFETIME) + diff --git a/tests/test_command.py b/tests/test_command.py new file mode 100644 index 0000000..1a0ba9e --- /dev/null +++ b/tests/test_command.py @@ -0,0 +1,10 @@ +from krbticket import KrbConfig, KrbCommand +from helper import config + + +def test_commands(config): + KrbCommand.kdestroy(config) + KrbCommand.kinit(config) + KrbCommand.renewal(config) + KrbCommand.klist(config) + diff --git a/tests/test_ticket.py b/tests/test_ticket.py new file mode 100644 index 0000000..edfc0f0 --- /dev/null +++ b/tests/test_ticket.py @@ -0,0 +1,56 @@ +from krbticket import KrbTicket, KrbCommand +from krbticket.ticket import NoCredentialFound +from helper import * +import time + + +def test_init(config): + KrbCommand.kdestroy(config) + ticket1 = KrbTicket.init_by_config(config) + ticket2 = KrbTicket.init(DEFAULT_PRINCIPAL, DEFAULT_KEYTAB) + assert ticket1.principal == ticket2.principal + assert ticket1.file == ticket2.file + + +def test_get(config): + KrbCommand.kdestroy(config) + with pytest.raises(NoCredentialFound): + KrbTicket.get(DEFAULT_PRINCIPAL, DEFAULT_KEYTAB) + + assert_ticket( + KrbTicket.init_by_config(config), + KrbTicket.get(DEFAULT_KEYTAB, DEFAULT_PRINCIPAL)) + + +def test_ticket(config): + KrbCommand.kdestroy(config) + ticket = KrbTicket.init_by_config(config) + assert ticket.config == config + assert ticket.file + assert ticket.principal == 'user@EXAMPLE.COM' + assert ticket.starting + assert ticket.expires + assert ticket.service_principal + + +def test_updater(config): + KrbCommand.kdestroy(config) + ticket = KrbTicket.init_by_config(config) + updater = ticket.updater(interval=1) + updater.start() + updater.stop() + time.sleep(2) + assert not updater.is_alive() + + +def test_renewal(config): + KrbCommand.kdestroy(config) + ticket = KrbTicket.init_by_config(config) + starting = ticket.starting + expires = ticket.expires + updater = ticket.updater(interval=1) + updater.start() + time.sleep(2) + updater.stop() + assert ticket.starting > starting + assert ticket.expires > expires