volatildap-1.3.0/0000755000175000017500000000000013466774336014525 5ustar xelnorxelnet00000000000000volatildap-1.3.0/PKG-INFO0000644000175000017500000001606713466774336015634 0ustar xelnorxelnet00000000000000Metadata-Version: 1.1 Name: volatildap Version: 1.3.0 Summary: Temporary slapd launcher for testing purposes Home-page: https://github.com/rbarrois/volatildap/ Author: Raphaël Barrois Author-email: raphael.barrois+volatildap@polytechnique.org License: BSD Download-URL: https://pypi.python.org/pypi/volatildap/ Description: volatildap ========== .. image:: https://secure.travis-ci.org/rbarrois/volatildap.png?branch=master :target: http://travis-ci.org/rbarrois/volatildap/ .. image:: https://img.shields.io/pypi/v/volatildap.svg :target: https://pypi.python.org/pypi/volatildap/ :alt: Latest Version .. image:: https://img.shields.io/pypi/pyversions/volatildap.svg :target: https://pypi.python.org/pypi/volatildap/ :alt: Supported Python versions .. image:: https://img.shields.io/pypi/wheel/volatildap.svg :target: https://pypi.python.org/pypi/volatildap/ :alt: Wheel status .. image:: https://img.shields.io/pypi/l/volatildap.svg :target: https://pypi.python.org/pypi/volatildap/ :alt: License ``volatildap`` provides simple helpers for testing code against a LDAP database. Its main features include: * **Simple configuration:** Don't provide anything the LDAP server will start with sane defaults * **Built-in cleanup:** As soon as the test ends / the test process exits, the server is instantly removed * **Cross-distribution setup:** Automatically discover system paths for OpenLDAP binaries, schemas, etc. Usage ----- .. code-block:: python import volatildap class MyTests(unittest.TestCase): @classmethod def setUpClass(cls): super(MyTests, cls).setUpClass() cls._slapd = volatildap.LdapServer(suffix='dc=example,dc=org') def setUp(self): # Will start the server, or reset/restart it if already started from a previous test. self._slapd.start() def test_something(self): conn = ldap.connection(self._slapd.uri) # Do some tests def test_with_data(self): # Load some data self._slapd.add({'ou=people': {'cn': [b'Users']}}) # Run the tests The ``volatildap.LdapServer`` provides a few useful methods: ``start()`` Start or restart the server. This will: * Clear all data, if any * Start the server if it's not yet running * Populate the initial data ``stop()`` Stop the server. This will clean up all data and kill the proces. ``add(data)`` Add some data, see the ``initial_data`` structure below. ``get(dn)`` Retrieve an object by its distinguished name; Returns a dictionary mapping an attribute to the list of its values, as bytes. Raises ``KeyError`` if the distinguished name is unknown to the underlying database. ``reset()`` Restore the server to its pristine, initial state. This includes loading the inital_data. Configuration ------------- The ``volatildap.LdapServer`` class accepts a few parameters: ``suffix`` The suffix to use for the LDAP tree *Default:* ``dc=example,dc=org`` ``rootdn`` The administrator account for the LDAP server *Default:* ``cn=testadmin,dc=example,dc=org`` ``rootpw`` The administrator password. *Default:* A random value, available through ``LdapServer.rootpw`` ``schemas`` List of schemas to load; can be either a simple name (e.g ``cosine.schema``; looked up in openldap installation); or a path to a custom one. *Default:* ``['core.schema']`` ``initial_data`` Dict mapping a distinguished name to a dict of attribute/values: .. code-block:: python slapd(initial_data={ 'ou=people': { 'objectClass': ['organizationalUnit'], 'cn': ['People'], }, }) **Note:** When adding data, the suffix can be omitted on objects DNs. *Default:* ``{}`` ``skip_missing_schemas`` When loading schemas, this flag instructs ``volatildap`` to continue if some schemas can't be found. *Default:* ``False`` ``port`` The port to use. *Default:* An available TCP port on the system ``slapd_debug`` The debug level for slapd; see ``slapd.conf`` *Default:* ``0`` ``max_server_startup_delay`` The maximum delay allowed for server startup, in seconds. *Default:* ``30`` ``tls_config`` A set of TLS certificate files for configuring the server. A valid set for ``localhost`` is provided as ``volatildap.LOCALHOST_TLS_CONFIG``, but users may also provide their own: .. code-block:: python tls_config = volatildap.TLSConfig( root=read(ca_path), chain=[ read(intermediate_path), ], certificate=read(certificate_path), key=read(key_path), ) Per-distribution specificities ------------------------------ Ubuntu Under Ubuntu, the default AppArmor policy does not allow ``slapd`` (the LDAP daemon) to read temporary folders. Users should update the ``/etc/apparmor.d/usr.sbin.slapd`` file and add ``/tmp/** rw`` there. Keywords: ldap,test,openldap,slapd Platform: OS Independent Classifier: Development Status :: 3 - Alpha Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: BSD License Classifier: Operating System :: Unix Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: Implementation :: PyPy Classifier: Topic :: Software Development Classifier: Topic :: Software Development :: Testing Classifier: Topic :: System :: Systems Administration :: Authentication/Directory :: LDAP volatildap-1.3.0/tox.ini0000644000175000017500000000037313466774335016042 0ustar xelnorxelnet00000000000000[tox] envlist = py{27,34,35,36},pypy2,pypy3 lint [testenv] deps = -rrequirements_dev.txt whitelist_externals = make commands = make test [testenv:lint] deps = -rrequirements_dev.txt whitelist_externals = make commands = make lint volatildap-1.3.0/setup.py0000644000175000017500000000413313466774335016237 0ustar xelnorxelnet00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- # This software is distributed under the two-clause BSD license. from __future__ import unicode_literals import codecs import os import re from setuptools import find_packages, setup root_dir = os.path.abspath(os.path.dirname(__file__)) def get_version(package_name): version_re = re.compile(r"^__version__ = [\"']([\w_.-]+)[\"']$") package_components = package_name.split('.') init_path = os.path.join(root_dir, *(package_components + ['version.py'])) with codecs.open(init_path, 'r', 'utf-8') as f: for line in f: match = version_re.match(line[:-1]) if match: return match.groups()[0] return '0.1.0' PACKAGE = 'volatildap' setup( name=PACKAGE, version=get_version(PACKAGE), description="Temporary slapd launcher for testing purposes", long_description=''.join(codecs.open('README.rst', 'r', 'utf-8').readlines()), author="Raphaël Barrois", author_email="raphael.barrois+%s@polytechnique.org" % PACKAGE, license="BSD", keywords=['ldap', 'test', 'openldap', 'slapd'], url="https://github.com/rbarrois/%s/" % PACKAGE, download_url="https://pypi.python.org/pypi/%s/" % PACKAGE, packages=find_packages(exclude=['tests*']), platforms=["OS Independent"], zip_safe=False, install_requires=[ ], setup_requires=[ 'setuptools>=0.8', ], classifiers=[ "Development Status :: 3 - Alpha", "Intended Audience :: Developers", "License :: OSI Approved :: BSD License", "Operating System :: Unix", "Programming Language :: Python", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: Implementation :: PyPy", "Topic :: Software Development", "Topic :: Software Development :: Testing", "Topic :: System :: Systems Administration :: Authentication/Directory :: LDAP", ], test_suite='tests', ) volatildap-1.3.0/setup.cfg0000644000175000017500000000027213466774336016347 0ustar xelnorxelnet00000000000000[bdist_wheel] universal = 1 [zest.releaser] version-levels = 3 python-file-with-version = volatildap/version.py [distutils] index-servers = pypi [egg_info] tag_build = tag_date = 0 volatildap-1.3.0/requirements_dev.txt0000644000175000017500000000011013466774335020636 0ustar xelnorxelnet00000000000000-e . check-manifest flake8 isort psutil tox zest.releaser[recommended] volatildap-1.3.0/README.rst0000644000175000017500000001124413466774335016215 0ustar xelnorxelnet00000000000000volatildap ========== .. image:: https://secure.travis-ci.org/rbarrois/volatildap.png?branch=master :target: http://travis-ci.org/rbarrois/volatildap/ .. image:: https://img.shields.io/pypi/v/volatildap.svg :target: https://pypi.python.org/pypi/volatildap/ :alt: Latest Version .. image:: https://img.shields.io/pypi/pyversions/volatildap.svg :target: https://pypi.python.org/pypi/volatildap/ :alt: Supported Python versions .. image:: https://img.shields.io/pypi/wheel/volatildap.svg :target: https://pypi.python.org/pypi/volatildap/ :alt: Wheel status .. image:: https://img.shields.io/pypi/l/volatildap.svg :target: https://pypi.python.org/pypi/volatildap/ :alt: License ``volatildap`` provides simple helpers for testing code against a LDAP database. Its main features include: * **Simple configuration:** Don't provide anything the LDAP server will start with sane defaults * **Built-in cleanup:** As soon as the test ends / the test process exits, the server is instantly removed * **Cross-distribution setup:** Automatically discover system paths for OpenLDAP binaries, schemas, etc. Usage ----- .. code-block:: python import volatildap class MyTests(unittest.TestCase): @classmethod def setUpClass(cls): super(MyTests, cls).setUpClass() cls._slapd = volatildap.LdapServer(suffix='dc=example,dc=org') def setUp(self): # Will start the server, or reset/restart it if already started from a previous test. self._slapd.start() def test_something(self): conn = ldap.connection(self._slapd.uri) # Do some tests def test_with_data(self): # Load some data self._slapd.add({'ou=people': {'cn': [b'Users']}}) # Run the tests The ``volatildap.LdapServer`` provides a few useful methods: ``start()`` Start or restart the server. This will: * Clear all data, if any * Start the server if it's not yet running * Populate the initial data ``stop()`` Stop the server. This will clean up all data and kill the proces. ``add(data)`` Add some data, see the ``initial_data`` structure below. ``get(dn)`` Retrieve an object by its distinguished name; Returns a dictionary mapping an attribute to the list of its values, as bytes. Raises ``KeyError`` if the distinguished name is unknown to the underlying database. ``reset()`` Restore the server to its pristine, initial state. This includes loading the inital_data. Configuration ------------- The ``volatildap.LdapServer`` class accepts a few parameters: ``suffix`` The suffix to use for the LDAP tree *Default:* ``dc=example,dc=org`` ``rootdn`` The administrator account for the LDAP server *Default:* ``cn=testadmin,dc=example,dc=org`` ``rootpw`` The administrator password. *Default:* A random value, available through ``LdapServer.rootpw`` ``schemas`` List of schemas to load; can be either a simple name (e.g ``cosine.schema``; looked up in openldap installation); or a path to a custom one. *Default:* ``['core.schema']`` ``initial_data`` Dict mapping a distinguished name to a dict of attribute/values: .. code-block:: python slapd(initial_data={ 'ou=people': { 'objectClass': ['organizationalUnit'], 'cn': ['People'], }, }) **Note:** When adding data, the suffix can be omitted on objects DNs. *Default:* ``{}`` ``skip_missing_schemas`` When loading schemas, this flag instructs ``volatildap`` to continue if some schemas can't be found. *Default:* ``False`` ``port`` The port to use. *Default:* An available TCP port on the system ``slapd_debug`` The debug level for slapd; see ``slapd.conf`` *Default:* ``0`` ``max_server_startup_delay`` The maximum delay allowed for server startup, in seconds. *Default:* ``30`` ``tls_config`` A set of TLS certificate files for configuring the server. A valid set for ``localhost`` is provided as ``volatildap.LOCALHOST_TLS_CONFIG``, but users may also provide their own: .. code-block:: python tls_config = volatildap.TLSConfig( root=read(ca_path), chain=[ read(intermediate_path), ], certificate=read(certificate_path), key=read(key_path), ) Per-distribution specificities ------------------------------ Ubuntu Under Ubuntu, the default AppArmor policy does not allow ``slapd`` (the LDAP daemon) to read temporary folders. Users should update the ``/etc/apparmor.d/usr.sbin.slapd`` file and add ``/tmp/** rw`` there. volatildap-1.3.0/Makefile0000644000175000017500000000166213466774335016171 0ustar xelnorxelnet00000000000000PACKAGE=volatildap TESTS_DIR=tests FLAKE8 = flake8 default: clean: find . -type f -name '*.pyc' -delete find . -type f -path '*/__pycache__/*' -delete find . -type d -empty -delete upgrade: pip install --upgrade pip setuptools pip install --upgrade -r requirements_dev.txt pip freeze release: fullrelease .PHONY: default clean upgrade release testall: tox test: python -Wdefault -m unittest discover $(TESTS_DIR) .PHONY: test testall # Note: we run the linter in two runs, because our __init__.py files has specific warnings we want to exclude lint: flake8 isort check-manifest flake8: $(FLAKE8) --config .flake8 --exclude $(PACKAGE)/__init__.py $(PACKAGE) $(TESTS_DIR) $(FLAKE8) --config .flake8 --ignore F401 $(PACKAGE)/__init__.py isort: isort $(PACKAGE) $(TESTS_DIR) --recursive --check-only --diff --project $(PACKAGE) --project $(TESTS_DIR) check-manifest: check-manifest .PHONY: lint flake8 isort check-manifest volatildap-1.3.0/MANIFEST.in0000644000175000017500000000025413466774335016263 0ustar xelnorxelnet00000000000000include ChangeLog LICENSE README.rst include requirements*.txt graft volatildap include Makefile tox.ini .flake8 graft tests global-exclude *.py[cod] __pycache__ *.swp volatildap-1.3.0/LICENSE0000644000175000017500000000237413466774335015537 0ustar xelnorxelnet00000000000000Copyright (c) Raphaël Barrois Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. volatildap-1.3.0/ChangeLog0000644000175000017500000000243013466774335016275 0ustar xelnorxelnet00000000000000ChangeLog ========= 1.3.0 (2019-05-15) ------------------ - Add support for TLS: a provided set of certificates can be passed as ``volatildap.LdapServer(tls_config=volatildap.LOCALHOST_TLS_CONFIG)``, and will be used by the server. Test clients should retrieve the root CA from ``server.tls_config.root`` (holding certificate contents). 1.2.2 (2018-08-08) ------------------ *Improvements:* - Add support for Python 3.6, Pypy 3.5. 1.2.1 (2018-08-08) ------------------ *Packaging:* - Include test files in the sdist tarball. - Use zest.releaser for releases .. _v1.2.0: 1.2.0 (2018-07-28) ------------------ *Improvements:* - Add ``server.reset()``, which clears all entries except initial ones. .. _v1.1.0: 1.1.0 (2017-01-17) ------------------ *Improvements:* - Replace the ``ldif`` backend with the more standard ``hdb``; this should allow to send paginated requests against a volatildap instance. .. _v1.0.2: 1.0.2 (2016-06-02) ------------------ *Improvements:* - Add data starting with containers before contained entities. .. _v1.0.1: 1.0.1 (2016-06-01) ------------------ *Bugfixes:* - Fix dependencies (``pyldap`` isn't used). .. _v1.0.0: 1.0.0 (2016-06-01) ------------------ *New:* - Initial version .. vim:set ft=rst: volatildap-1.3.0/.flake80000644000175000017500000000003713466774335015677 0ustar xelnorxelnet00000000000000[flake8] max-line-length = 120 volatildap-1.3.0/volatildap.egg-info/0000755000175000017500000000000013466774336020356 5ustar xelnorxelnet00000000000000volatildap-1.3.0/volatildap.egg-info/top_level.txt0000644000175000017500000000001313466774336023102 0ustar xelnorxelnet00000000000000volatildap volatildap-1.3.0/volatildap.egg-info/not-zip-safe0000644000175000017500000000000113466774336022604 0ustar xelnorxelnet00000000000000 volatildap-1.3.0/volatildap.egg-info/dependency_links.txt0000644000175000017500000000000113466774336024424 0ustar xelnorxelnet00000000000000 volatildap-1.3.0/volatildap.egg-info/SOURCES.txt0000644000175000017500000000143613466774336022246 0ustar xelnorxelnet00000000000000.flake8 ChangeLog LICENSE MANIFEST.in Makefile README.rst requirements_dev.txt setup.cfg setup.py tox.ini tests/__init__.py tests/test_usage.py tests/minipki/Makefile tests/minipki/README.md tests/minipki/ca-volatildap.org.crt tests/minipki/interm-volatildap.org.crt tests/minipki/interm-volatildap.org.csr tests/minipki/interm-volatildap.org.key tests/minipki/localhost.volatildap.org.crt tests/minipki/localhost.volatildap.org.key tests/minipki/openssl-ca-volatildap.org.cnf tests/minipki/openssl-ca.cnf.in tests/minipki/openssl-leaf.cnf.in volatildap/__init__.py volatildap/compat.py volatildap/server.py volatildap/version.py volatildap.egg-info/PKG-INFO volatildap.egg-info/SOURCES.txt volatildap.egg-info/dependency_links.txt volatildap.egg-info/not-zip-safe volatildap.egg-info/top_level.txtvolatildap-1.3.0/volatildap.egg-info/PKG-INFO0000644000175000017500000001606713466774336021465 0ustar xelnorxelnet00000000000000Metadata-Version: 1.1 Name: volatildap Version: 1.3.0 Summary: Temporary slapd launcher for testing purposes Home-page: https://github.com/rbarrois/volatildap/ Author: Raphaël Barrois Author-email: raphael.barrois+volatildap@polytechnique.org License: BSD Download-URL: https://pypi.python.org/pypi/volatildap/ Description: volatildap ========== .. image:: https://secure.travis-ci.org/rbarrois/volatildap.png?branch=master :target: http://travis-ci.org/rbarrois/volatildap/ .. image:: https://img.shields.io/pypi/v/volatildap.svg :target: https://pypi.python.org/pypi/volatildap/ :alt: Latest Version .. image:: https://img.shields.io/pypi/pyversions/volatildap.svg :target: https://pypi.python.org/pypi/volatildap/ :alt: Supported Python versions .. image:: https://img.shields.io/pypi/wheel/volatildap.svg :target: https://pypi.python.org/pypi/volatildap/ :alt: Wheel status .. image:: https://img.shields.io/pypi/l/volatildap.svg :target: https://pypi.python.org/pypi/volatildap/ :alt: License ``volatildap`` provides simple helpers for testing code against a LDAP database. Its main features include: * **Simple configuration:** Don't provide anything the LDAP server will start with sane defaults * **Built-in cleanup:** As soon as the test ends / the test process exits, the server is instantly removed * **Cross-distribution setup:** Automatically discover system paths for OpenLDAP binaries, schemas, etc. Usage ----- .. code-block:: python import volatildap class MyTests(unittest.TestCase): @classmethod def setUpClass(cls): super(MyTests, cls).setUpClass() cls._slapd = volatildap.LdapServer(suffix='dc=example,dc=org') def setUp(self): # Will start the server, or reset/restart it if already started from a previous test. self._slapd.start() def test_something(self): conn = ldap.connection(self._slapd.uri) # Do some tests def test_with_data(self): # Load some data self._slapd.add({'ou=people': {'cn': [b'Users']}}) # Run the tests The ``volatildap.LdapServer`` provides a few useful methods: ``start()`` Start or restart the server. This will: * Clear all data, if any * Start the server if it's not yet running * Populate the initial data ``stop()`` Stop the server. This will clean up all data and kill the proces. ``add(data)`` Add some data, see the ``initial_data`` structure below. ``get(dn)`` Retrieve an object by its distinguished name; Returns a dictionary mapping an attribute to the list of its values, as bytes. Raises ``KeyError`` if the distinguished name is unknown to the underlying database. ``reset()`` Restore the server to its pristine, initial state. This includes loading the inital_data. Configuration ------------- The ``volatildap.LdapServer`` class accepts a few parameters: ``suffix`` The suffix to use for the LDAP tree *Default:* ``dc=example,dc=org`` ``rootdn`` The administrator account for the LDAP server *Default:* ``cn=testadmin,dc=example,dc=org`` ``rootpw`` The administrator password. *Default:* A random value, available through ``LdapServer.rootpw`` ``schemas`` List of schemas to load; can be either a simple name (e.g ``cosine.schema``; looked up in openldap installation); or a path to a custom one. *Default:* ``['core.schema']`` ``initial_data`` Dict mapping a distinguished name to a dict of attribute/values: .. code-block:: python slapd(initial_data={ 'ou=people': { 'objectClass': ['organizationalUnit'], 'cn': ['People'], }, }) **Note:** When adding data, the suffix can be omitted on objects DNs. *Default:* ``{}`` ``skip_missing_schemas`` When loading schemas, this flag instructs ``volatildap`` to continue if some schemas can't be found. *Default:* ``False`` ``port`` The port to use. *Default:* An available TCP port on the system ``slapd_debug`` The debug level for slapd; see ``slapd.conf`` *Default:* ``0`` ``max_server_startup_delay`` The maximum delay allowed for server startup, in seconds. *Default:* ``30`` ``tls_config`` A set of TLS certificate files for configuring the server. A valid set for ``localhost`` is provided as ``volatildap.LOCALHOST_TLS_CONFIG``, but users may also provide their own: .. code-block:: python tls_config = volatildap.TLSConfig( root=read(ca_path), chain=[ read(intermediate_path), ], certificate=read(certificate_path), key=read(key_path), ) Per-distribution specificities ------------------------------ Ubuntu Under Ubuntu, the default AppArmor policy does not allow ``slapd`` (the LDAP daemon) to read temporary folders. Users should update the ``/etc/apparmor.d/usr.sbin.slapd`` file and add ``/tmp/** rw`` there. Keywords: ldap,test,openldap,slapd Platform: OS Independent Classifier: Development Status :: 3 - Alpha Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: BSD License Classifier: Operating System :: Unix Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: Implementation :: PyPy Classifier: Topic :: Software Development Classifier: Topic :: Software Development :: Testing Classifier: Topic :: System :: Systems Administration :: Authentication/Directory :: LDAP volatildap-1.3.0/volatildap/0000755000175000017500000000000013466774336016664 5ustar xelnorxelnet00000000000000volatildap-1.3.0/volatildap/version.py0000644000175000017500000000020713466774335020721 0ustar xelnorxelnet00000000000000# -*- coding: utf-8 -*- # This software is distributed under the two-clause BSD license. __version__ = '1.3.0' VERSION = __version__ volatildap-1.3.0/volatildap/server.py0000644000175000017500000003715313466774335020554 0ustar xelnorxelnet00000000000000# -*- coding: utf-8 -*- # This software is distributed under the two-clause BSD license. """Temporary LDAP server based on OpenLdap for tests.""" from __future__ import unicode_literals import base64 import codecs import collections import logging import os import random import re import socket import subprocess import sys import time from . import compat logger = logging.getLogger(__name__.split('.')[0]) DEFAULT_SUFFIX = 'dc=example,dc=org' DEFAULT_ROOTDN = 'cn=testadmin,%s' % DEFAULT_SUFFIX DEFAULT_SCHEMAS = ( 'core.schema', ) DEFAULT_STARTUP_DELAY = 15 DEFAULT_SLAPD_DEBUG = 0 class LdapError(Exception): """Exceptions for volatildap""" class PathError(LdapError): """Exception for missing paths""" class OpenLdapPaths(object): """Collection of Openldap-related paths, distribution dependend.""" def __init__(self): self.schemas = os.path.dirname(self._find_file('core.schema', self._SCHEMA_DIRS)) self.slapd = self._find_file('slapd', self._BINARY_DIRS) self.ldapadd = self._find_file('ldapadd', self._BINARY_DIRS) self.ldapdelete = self._find_file('ldapdelete', self._BINARY_DIRS) self.ldapsearch = self._find_file('ldapsearch', self._BINARY_DIRS) self.slaptest = self._find_file('slaptest', self._BINARY_DIRS) def _find_file(self, needle, candidates): """Find the first directory containing a given candidate file.""" for candidate in candidates: fullpath = os.path.join(candidate, needle) if os.path.isfile(fullpath): return fullpath raise PathError("Unable to locate file %s; tried %s" % (needle, candidates)) _SCHEMA_DIRS = [ '/etc/ldap/schema', # Debian '/etc/openldap/schema', # Gentoo '/usr/local/openldap/schema', # Manual install ] _BINARY_DIRS = [ '/usr/sbin', '/usr/bin', '/usr/lib/openldap', '/usr/local/sbin', '/usr/local/bin', ] + os.environ.get('PATH', '').split(':') TLSConfig = collections.namedtuple('TLSConfig', ['root', 'chain', 'certificate', 'key']) class LdapServer(object): _DATASUBDIR = 'ldif-data' def __init__(self, suffix=DEFAULT_SUFFIX, rootdn=DEFAULT_ROOTDN, rootpw='', schemas=DEFAULT_SCHEMAS, initial_data=None, skip_missing_schemas=False, max_server_startup_delay=DEFAULT_STARTUP_DELAY, port=None, slapd_debug=DEFAULT_SLAPD_DEBUG, tls_config=None, ): self.paths = OpenLdapPaths() self.suffix = suffix self.rootdn = rootdn self.rootpw = rootpw or self._generate_password() self.schemas = list(self._locate_schemas(schemas, skip_missing_schemas)) self.initial_data = initial_data or {} self.max_server_startup_delay = max_server_startup_delay self.port = port or find_available_port() self.slapd_debug = slapd_debug self.tls_config = tls_config self._tempdir = None self._process = None def _generate_password(self): chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' return ''.join( random.choice(chars) for _i in range(20) ) def _locate_schemas(self, schemas, skip_missing_schemas): """Locate all schemas (look in openldap store). If skip_missing_schemas is True, ignore missing schemas; otherwise, raise. """ for schema in schemas: if schema == os.path.abspath(schema): schema_file = schema else: schema_file = os.path.join(self.paths.schemas, schema) if os.path.isfile(schema_file): # Absolute path: use it. yield schema_file elif skip_missing_schemas: logger.warning("Unable to locate schema %s at %s", schema, schema_file) else: raise PathError("Unable to locate schema %s at %s" % (schema, schema_file)) def _configuration_lines(self): def quote(base, *args): return base % tuple("%s" % arg.replace('\\', '\\\\').replace('"', '\\"') for arg in args) for schema in self.schemas: yield quote('include %s', schema) if self.tls_config: yield quote('TLSCACertificateFile %s', self._tls_chain_path) yield quote('TLSCertificateFile %s', self._tls_certificate_path) yield quote('TLSCertificateKeyFile %s', self._tls_key_path) yield quote('moduleload back_hdb') yield quote('database hdb') yield quote('directory %s', self._datadir) yield quote('suffix %s', self.suffix) yield quote('rootdn %s', self.rootdn) yield quote('rootpw %s', self.rootpw) def _normalize_dn(self, dn): if not dn.endswith(self.suffix): return '%s,%s' % (dn, self.suffix) else: return dn def start(self): try: if self._process is None: self._setup() self._start() else: self._clear() self._populate() except Exception as e: logger.exception("Error starting LDAP server: %s", e) self._shutdown() raise def stop(self): self._shutdown() def add(self, data): lines = '\n'.join(self._data_as_ldif(data)) ldif = lines.encode('utf-8') logger.info("Adding data %r", ldif) sp = subprocess.Popen( [ self.paths.ldapadd, '-x', '-D', self.rootdn, '-w', self.rootpw, '-H', self.uri, ], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=self._subprocess_env ) stdout, stderr = sp.communicate(ldif) retcode = sp.wait() if retcode != 0: raise RuntimeError("ldapadd failed with code %d: %s %s" % (retcode, stdout, stderr)) def get(self, dn): dn = self._normalize_dn(dn) logger.info("Fetching data at %s", dn) sp = subprocess.Popen( [ self.paths.ldapsearch, '-x', '-D', self.rootdn, '-w', self.rootpw, '-H', self.uri, '-LLL', # As LDIF '-b', dn, # Fetch this specific DN '-s', 'base', ], stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=self._subprocess_env ) stdout, stderr = sp.communicate() retcode = sp.wait() if retcode == 32: # Not found raise KeyError("Entry %s not found: %r" % (dn, stderr)) if retcode != 0: raise RuntimeError("ldapsearch failed with code %d: %r" % (retcode, stderr)) entries = ldif_to_entries(stdout) return entries[dn] def reset(self): """Reset all entries except inital ones.""" self._clear() self._populate() def _data_as_ldif(self, data): # Sort by dn length, thus adding parents first. for dn, attributes in sorted(data.items(), key=lambda e: (len(e[0]), e)): yield ldif_encode('dn', self._normalize_dn(dn)) for attribute, values in sorted(attributes.items()): for value in values: yield ldif_encode(attribute, value) yield '' @property def uri(self): if self.tls_config: return 'ldaps://localhost.volatildap.org:%d' % self.port else: return 'ldap://localhost:%d' % self.port @property def _datadir(self): return os.path.join(self._tempdir.name, self._DATASUBDIR) @property def _slapd_conf(self): return os.path.join(self._tempdir.name, 'slapd.conf') @property def _tls_ca_bundle_path(self): return os.path.join(self._tempdir.name, 'ca-bundle.pem') @property def _tls_chain_path(self): return os.path.join(self._tempdir.name, 'chain.pem') @property def _tls_certificate_path(self): return os.path.join(self._tempdir.name, 'server.crt') @property def _tls_key_path(self): return os.path.join(self._tempdir.name, 'server.key') @property def _core_data(self): return { self.suffix: { 'objectClass': ['dcObject', 'organization'], 'dc': [self.suffix.split(',')[0][len('dc='):]], 'o': [self.suffix], }, } def _setup(self): self._tempdir = compat.TemporaryDirectory() logger.info("Setting up openldap server in %s", self._tempdir.name) # Create datadir os.mkdir(self._datadir) # Manage TLS if self.tls_config: chain = [cert.strip() for cert in self.tls_config.chain] with codecs.open(self._tls_ca_bundle_path, 'w', encoding='utf-8') as f: f.write(self.tls_config.root) with codecs.open(self._tls_chain_path, 'w', encoding='utf-8') as f: f.write('\n'.join(chain)) with codecs.open(self._tls_certificate_path, 'w', encoding='utf-8') as f: f.write(self.tls_config.certificate) with codecs.open(self._tls_key_path, 'w', encoding='utf-8') as f: f.write(self.tls_config.key) # Write configuration with codecs.open(self._slapd_conf, 'w', encoding='utf-8') as f: f.write('\n'.join(self._configuration_lines())) slaptest = subprocess.Popen([ self.paths.slaptest, '-f', self._slapd_conf, '-u', # only test the config file ]) if slaptest.wait() != 0: raise RuntimeError("Testing configuration failed.") def _start(self): """Start the server.""" assert self._tempdir is not None assert self._process is None self._process = subprocess.Popen( [ self.paths.slapd, '-f', self._slapd_conf, '-h', self.uri, '-d', str(self.slapd_debug), ], stdout=sys.stdout, stderr=sys.stderr, ) self._poll_slapd(timeout=self.max_server_startup_delay) def _populate(self): """Populate a *running* server with initial data.""" self.add(self._core_data) if self.initial_data: self.add(self.initial_data) def _clear(self): logger.info("Preparing to clear all data") sp = subprocess.Popen( [ self.paths.ldapsearch, '-x', '-D', self.rootdn, '-w', self.rootpw, '-H', self.uri, '-LLL', # As LDIF '-b', self.suffix, # The whole tree '-s', 'sub', # All children 'dn', # Fetch only the 'dn' field ], stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=self._subprocess_env ) stdout, stderr = sp.communicate() retcode = sp.wait() if retcode != 0: raise RuntimeError("ldapsearch failed with code %d: %r" % (retcode, stderr)) data = ldif_to_entries(stdout) dns = data.keys() # Remove the furthest first dns = sorted(dns, key=lambda dn: (len(dn), dn), reverse=True) logger.info("Deleting entries %s", dns) sp = subprocess.Popen( [ self.paths.ldapdelete, '-x', '-D', self.rootdn, '-w', self.rootpw, '-H', self.uri, ] + dns, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=self._subprocess_env ) _stdout, stderr = sp.communicate() retcode = sp.wait() if retcode != 0: raise RuntimeError("ldapdelete failed with code %d: %r" % (retcode, stderr)) def _poll_slapd(self, timeout=DEFAULT_STARTUP_DELAY): """Poll slapd port until available.""" begin = time.time() time.sleep(0.5) while time.time() < begin + timeout: if self._process.poll() is not None: raise RuntimeError("LDAP server has exited before starting listen.") s = socket.socket() try: s.connect(('localhost', self.port)) except socket.error: # Not ready yet, sleep time.sleep(0.5) else: return finally: s.close() raise RuntimeError("LDAP server not responding within %s seconds." % timeout) def _shutdown(self): if self._process is not None: self._process.terminate() self._process.wait() self._process = None if self._tempdir is not None: self._tempdir.cleanup() self._tempdir = None @property def _subprocess_env(self): """Prepare the environment for a subprocess file.""" env = dict(os.environ) env.update( LDAPTLS_CACERT=self._tls_ca_bundle_path, LDAPTLS_REQCERT='hard', ) return env def __del__(self): if self._process is not None: logger.warning("Server %s removed from memory before being stopped!", self) def __repr__(self): if self._process: state = 'running:%s' % self._process.pid else: state = 'stopped' return '<%s at %s [%s]>' % (self.__class__.__name__, self.uri, state) def find_available_port(): """Find an available port. Simple trick: open a socket to localhost, see what port was allocated. Could fail in highly concurrent setups, though. """ s = socket.socket() s.bind(('localhost', 0)) _address, port = s.getsockname() s.close() return port # Valid characters for a non-base64-encoded LDIF value _BASE_LDIF = [ chr(i) for i in range(20, 128) if chr(i) not in [' ', '<', ':'] ] def ldif_encode(attr, value): """Encode a attribute: value pair for the LDIF format. See RFC2849 for details. Rules are: - Text containing only chars <= 127 except control, ' ', '<', ':' is passed as-is - Other text is encoded through UTF-8 and base64-encoded - Binary data is simply base64-encoded Returns: A 'key: value' or 'key:: b64value' text line. """ if isinstance(value, bytes): return '%s:: %s' % (attr, base64.b64encode(value).decode('ascii')) elif any(c not in _BASE_LDIF for c in value): return '%s:: %s' % (attr, base64.b64encode(value.encode('utf-8')).decode('ascii')) else: return '%s: %s' % (attr, value) def ldif_to_entries(ldif_lines): entries = {} for entry in ldif_lines.decode('ascii').split('\n\n'): if not entry.strip(): continue attributes = {} for line in entry.split('\n'): if not line.strip(): continue m = re.match(r'(\w+)(:?): (.*)', line.strip()) if m is None: raise ValueError("Invalid line in ldif output: %r" % line) field, is_extended, value = m.groups() if is_extended: value = base64.b64decode(value.encode('ascii')) else: value = value.encode('ascii') attributes.setdefault(field, []).append(value) dns = attributes.get('dn', [b'']) assert len(dns) == 1 entries[dns[0].decode('utf-8')] = attributes return entries volatildap-1.3.0/volatildap/compat.py0000644000175000017500000000074213466774335020523 0ustar xelnorxelnet00000000000000# -*- coding: utf-8 -*- # This software is distributed under the two-clause BSD license. """Compatibility layer for legacy Python (< 3.4 as of 2016)""" import shutil import sys import tempfile LEGACY_PY = sys.version_info[0] < 3 if LEGACY_PY: class TemporaryDirectory(object): def __init__(self): self.name = tempfile.mkdtemp() def cleanup(self): shutil.rmtree(self.name) else: TemporaryDirectory = tempfile.TemporaryDirectory volatildap-1.3.0/volatildap/__init__.py0000644000175000017500000001331513466774335020777 0ustar xelnorxelnet00000000000000# -*- coding: utf-8 -*- # This software is distributed under the two-clause BSD license. from .server import LdapServer, TLSConfig from .version import VERSION as __version__ LOCALHOST_TLS_CONFIG = TLSConfig( root="""-----BEGIN CERTIFICATE----- MIIDxDCCAqygAwIBAgIJAI76MjayHI8pMA0GCSqGSIb3DQEBCwUAMDIxIjAgBgNV BAMMGVNlbGYtc2lnbmVkIGxvY2FsIENBIFJvb3QxDDAKBgNVBAsMA0RldjAeFw0x OTA1MTUxMTA5MzdaFw0yOTA1MTIxMTA5MzdaMDIxIjAgBgNVBAMMGVNlbGYtc2ln bmVkIGxvY2FsIENBIFJvb3QxDDAKBgNVBAsMA0RldjCCASIwDQYJKoZIhvcNAQEB BQADggEPADCCAQoCggEBAMIpgtsshv7yxm/KXecT/5oA6fZGD9kTVMORk9M/OU9E QY8BKpfiC4Tw1ZJ6LUYS3NUfP7pa3eNXRBomBGoy64zlQUFLOUc3XWpp2hEL72kV AHJcd5L0NdlUvcIetAN2A1M8bMi/ZrYGPwLohWyFinAsQ52RDpd8mwvSJwRiykAi zWagx+pwvmW7VIV5yjc6dDuyFXFnAdLVwhN6BQQLSxQEFKfhlPNqDAf9vpuq58WS n3aOlGo8Csrj30tstuUfz4B3GkbhU7IOskWyM2I33DvMITA2Hy2M2CAueBxah8pB IWVt969DVHFoQVNwJJtjRjkNW3HJW+YSkjFPHfwIyjUCAwEAAaOB3DCB2TASBgNV HRMBAf8ECDAGAQH/AgEBMA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUwlV/AdlB ClVA/FzXApBiLeEwwkkwYgYDVR0jBFswWYAUwlV/AdlBClVA/FzXApBiLeEwwkmh NqQ0MDIxIjAgBgNVBAMMGVNlbGYtc2lnbmVkIGxvY2FsIENBIFJvb3QxDDAKBgNV BAsMA0RldoIJAI76MjayHI8pMDAGA1UdHgQpMCegJTARgg8udm9sYXRpbGRhcC5v cmcwEIIOdm9sYXRpbGRhcC5vcmcwDQYJKoZIhvcNAQELBQADggEBABUFWxgO4Lkv Md2AKbMEKnwb25ZXqEf/K6F8sfJPRV5vH3eSOCKBEOt2E3RgdleR643vHfOSnKwQ BMs7V/Vy5LYBL52M1k9UfQxajxWSmMQSxyFvK8mF9y09TahqewheqDue2LPJkMKR GnGGSYE1IB5phU80UW7cGoRNmZEWVMgo+DScfwikcDTrE2VwyCQBHxQDTX0u48SS mvuF62QnmOmoud7RqGsORIE/7eO8rcFoOkPO4jMNpCXOYEhRXWnyApSdXLQ3VeVV CjGiEP9qLV3lhhkgGcPZzimLrV7hK75hPFqNFPoKjrVLl6P3WdokKA/h0Ok1VCTG dMUH50jvlt0= -----END CERTIFICATE----- """, chain=[ """-----BEGIN CERTIFICATE----- MIIDvDCCAqSgAwIBAgIBAjANBgkqhkiG9w0BAQsFADAyMSIwIAYDVQQDDBlTZWxm LXNpZ25lZCBsb2NhbCBDQSBSb290MQwwCgYDVQQLDANEZXYwHhcNMTkwNTE1MTEw OTM3WhcNMjkwNTEyMTEwOTM3WjAyMSIwIAYDVQQDDBlTZWxmLXNpZ25lZCBsb2Nh bCBDQSBSb290MQwwCgYDVQQLDANEZXYwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw ggEKAoIBAQDLwI/12ZVK3XbWajn/p7LNQW/7Za5WKsYoAXmW/NLEj5fQvAFfuy9k ur6LW1MR7bfq6fX5tMZNvAlK2o6GYtbRhE1n7ZEEUkMjVl2kwFCjbI6NAJ9s8YAd hldekxc68mnS26VHPBOG2CwRGj8bIRZK5Hm1NGcMtLFFqmT5t+tmVaJdTBBnoxb1 4YMXU35Ltf/RXxzKG9/RQZ5jV4OzZRs22veccBcYIOuhLS0vR24Qr8mekjkpju2j XcSbIPuUBZ3FJHUGTPCXPhTbwwCCCGXE+h+Yh7x0rFTBsObjcRCcG0jTwaEpoAZ2 483daT2cM2ms1HTD2Qz5LXp/SrHpE1y9AgMBAAGjgdwwgdkwEgYDVR0TAQH/BAgw BgEB/wIBADAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFDK21gwaeINTyWPY2dQ5 pCiHYkrWMGIGA1UdIwRbMFmAFMJVfwHZQQpVQPxc1wKQYi3hMMJJoTakNDAyMSIw IAYDVQQDDBlTZWxmLXNpZ25lZCBsb2NhbCBDQSBSb290MQwwCgYDVQQLDANEZXaC CQCO+jI2shyPKTAwBgNVHR4EKTAnoCUwEYIPLnZvbGF0aWxkYXAub3JnMBCCDnZv bGF0aWxkYXAub3JnMA0GCSqGSIb3DQEBCwUAA4IBAQAac3Ep3WkrQqUCtUAOBxYa kv1v+U7RvRkJOYVOXccF93d1KKn0TxQFYgddewkEY+HVzMiJbsdSLj5qSqMV8A2m hzU9Av/K3b7r1VBRGvzwH5moa15WZEB5KfnPIbhtdPTfJ/RHEtEChkXAtOynhhNU hOwZKicbGvm7Lo1ppFGoLyxuPDxfpVslwtsxEUWYR9grkME/lro5XXjxOqv6QwVM qZuheGEe9J/gqr7ImnUkjpOfHc91J80qrfeNh01WMQD7JQft2zAVPLZCzPt8cx0b C2DEMEYWZYoFG8FBn0lb2hs4SvpvEpxpo2jKcKCvUD7OyZl9bU8Nvly3SinVBzUT -----END CERTIFICATE----- """, ], certificate="""-----BEGIN CERTIFICATE----- MIIDBTCCAe2gAwIBAgIBAjANBgkqhkiG9w0BAQsFADAyMSIwIAYDVQQDDBlTZWxm LXNpZ25lZCBsb2NhbCBDQSBSb290MQwwCgYDVQQLDANEZXYwHhcNMTkwNTE1MTEw OTM3WhcNMjkwNTEyMTEwOTM3WjAxMSEwHwYDVQQDDBhsb2NhbGhvc3Qudm9sYXRp bGRhcC5vcmcxDDAKBgNVBAsMA0RldjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC AQoCggEBANlRIzf3HibjTXowkmoVGBFiZsEMS2nYlQr/D3RCTydhzQ8k/c7fYYS/ FOJD5TVnJ9lGUv1VmD8Uad18m+nuyYimtDDhZK4ya5roVVwzywZMfF5IKI9iqbed 4IsJjkByItW1q7tz7TRoxStkWa0XG/xdRDBOAltWuPLAejxwNO9RaijcXoath7dp MmsrgE6567+gf9QZ3bNadr+C/926134ChAdxFe4IKYdCxKg2GdINQx6myS8HHUT6 sf9txaFZVYkqOFC5T8MpZ11fDRl0P6blkBQdQCdooPFi7zCEtwUefxjuTxDWTkoM CUl5eOxbYxQwbUuJyKjOMqRnyM4paSsCAwEAAaMnMCUwIwYDVR0RBBwwGoIYbG9j YWxob3N0LnZvbGF0aWxkYXAub3JnMA0GCSqGSIb3DQEBCwUAA4IBAQBXVkLr6krL psQjZq7x6ODZFK0Hvs5Xbt1nA2Ib09j+5h3t39Eu4k+p+XUYVniuAhnijNQ4pl7I xeEl6s6hXiYrMD6y7+F3Hhqn7uW2We/Ptu6r+iKN4aVs19Od6HftmOxFtM6idhd5 ExY29ID1kZpJ9GygcOWh7EiY9BgVMMj/Oy/wmIFP9zaPATbms17zgXCrnhmeyqCM F+01SLza+Xw+K5Q327qVd1xkePVXC0iUwQLY/N+0j24QcfCeNeE7fCT19UruYQ+g Vv8JBsuMSOyXPDdfWG6csFoP8gkU44n+KkRT9lZF27jzcwbS85FjaHwQOqiEuUnN jflWAqV1w0bP -----END CERTIFICATE----- """, key="""-----BEGIN PRIVATE KEY----- MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDZUSM39x4m4016 MJJqFRgRYmbBDEtp2JUK/w90Qk8nYc0PJP3O32GEvxTiQ+U1ZyfZRlL9VZg/FGnd fJvp7smIprQw4WSuMmua6FVcM8sGTHxeSCiPYqm3neCLCY5AciLVtau7c+00aMUr ZFmtFxv8XUQwTgJbVrjywHo8cDTvUWoo3F6GrYe3aTJrK4BOueu/oH/UGd2zWna/ gv/dutd+AoQHcRXuCCmHQsSoNhnSDUMepskvBx1E+rH/bcWhWVWJKjhQuU/DKWdd Xw0ZdD+m5ZAUHUAnaKDxYu8whLcFHn8Y7k8Q1k5KDAlJeXjsW2MUMG1LiciozjKk Z8jOKWkrAgMBAAECggEAUiz6Ullhz3a4f3IKi466cShyjqFlivrZMTVN1LqWT+AN uKJWT3ns2FOhRAZEkDMXwHN1QlKxpXjEp3pNQuKG1uOi07EHb0+lnQgp7xmutg0K ImTK4Z1/a+2JUUolvnzqa0GtVM77bYwIsuXZezh33rAt7s4w2fUrLrBWCLCtc6j1 ItWAHXBrdD9eEDJ4l97Zm6Ne8WMA1sIv4zOOCgbtYd9RcB0S7fjKemlRX/lMkjcN QDDoAwePanqQkceGy0h9AgJ/n4d1bKl5tE4waqqTm6XYkkEd7A5ZA78izoEZhzci H1IRUT085LU4L1I1Hk8NGFdIQ8giKtG+3zgnsYeekQKBgQD1i9I7Jn2G7lvJ0oJu GuR/dS2M1F5oXOp4SxFITqfw7rcLpEd4wjeF2wjHZFQyiwMzakbtvBoj4rlYv+KK 3F7CqH3zTkbBvoxYEBOLF/D2WkDZosgYSsB7psMrJ+7jqCg7tn8hDTub4Ar8cEzd 6e1JfefVw92R+TEhihcF8gf55QKBgQDikaY3QwKFVYMlyS+E7L9yLIXjebCRcBuC Bmn6MTRq4TnK08aZhi92/jqR7Bxy3M8FJ7Kj741atqGtFWVqYfuVROE5sACVXy2o C3O/EYjseJKSHwFQogqJ96wIPIaekrvH+glcInxIp8GSreMc37ghppnnbrYMYoK/ u65sxbNlzwKBgQDzZjprL+hWjIioMuffxQCj57/TTeCXiyNRQvhKuM144ujHvXyr k7n+EfVN0YI059+wY7UTi9ZNpD0eYHdsNq4oP2U6fWlePEhaMvVsd7sAOM737Yhz rAXJjLECYn9HZDUdm/XBCaEBDsFVIuzPPUdeBlR9bb6BKIcvO/c+iYCmkQKBgADw uc/g51HGWRT/EMNseB7xMF0RVDpGTNbtT+NJ8P3AkYzV9C0O8YN86qFQaY3QfMt4 rrUfeWa2+1TMke8vGOw1rPmgUWBNVNMtuP1CBV914KXamxbKlWCdNomSNrzRvxXF Y+SOABYNHDCPqDGmfA0ns9vSCc9jWUeSb9bjuyrhAoGBAME77wEd/3dhgk8iX6zz b0+wWOBz+CqBBLD8hs/N2/Sj8Cwactebi0sRZqgc5HAVUsUC2OV+mUovUzQpcMou qVFgjUBT72YMkEqZsKVdiI2+A9YjWYwKka1dllIFaZG7CbiyCI5We2Qv4P7N0GY8 +9LOsFxClEOIHKHjIFTMNnfL -----END PRIVATE KEY----- """, ) volatildap-1.3.0/tests/0000755000175000017500000000000013466774336015667 5ustar xelnorxelnet00000000000000volatildap-1.3.0/tests/test_usage.py0000644000175000017500000001116613466774335020410 0ustar xelnorxelnet00000000000000# -*- coding: utf-8 -*- # This software is distributed under the two-clause BSD license. import os import time import unittest import psutil import volatildap class LdapServerTestCase(unittest.TestCase): def _launch_server(self, **kwargs): server = volatildap.LdapServer(**kwargs) server.start() context = { 'dirname': server._tempdir.name, 'pid': server._process.pid, } return server, context def assertServerStopped(self, context, max_delay=5): now = time.time() # Allow some time for proper shutdown while time.time() < now + max_delay and os.path.exists(context['dirname']): time.sleep(0.2) self.assertFalse(os.path.exists(context['dirname'])) # Check that the process is no longer running. # We cannot rely solely on "the pid is no longer running", as it may # have been reused by the operating system. # If a process by that pid still exists, we'll check that we aren't its parent. try: stats = psutil.Process(context['pid']) ppid = stats.ppid() except (psutil.NoSuchProcess, psutil.AccessDenied): # Process has died / is not in our context: all is fine. return self.assertNotEqual(ppid, os.getpid()) class ReadWriteTests(LdapServerTestCase): data = { 'ou=test': { 'objectClass': ['organizationalUnit'], 'ou': ['test'], }, } def test_initial_data(self): server, context = self._launch_server(initial_data=self.data) entry = server.get('ou=test,dc=example,dc=org') self.assertEqual({ 'dn': [b'ou=test,dc=example,dc=org'], 'objectClass': [b'organizationalUnit'], 'ou': [b'test'], }, entry) server.stop() self.assertServerStopped(context) def test_post_start_add(self): server, context = self._launch_server() server.add(self.data) entry = server.get('ou=test,dc=example,dc=org') self.assertEqual({ 'dn': [b'ou=test,dc=example,dc=org'], 'objectClass': [b'organizationalUnit'], 'ou': [b'test'], }, entry) server.stop() self.assertServerStopped(context) def test_get_missing_entry(self): server, context = self._launch_server() with self.assertRaises(KeyError): server.get('ou=test,dc=example,dc=org') server.stop() self.assertServerStopped(context) def test_clear_data(self): server, context = self._launch_server() server.add(self.data) self.assertIsNotNone(server.get('ou=test,dc=example,dc=org')) server.start() # Actually a restart # Custom data has been removed with self.assertRaises(KeyError): server.get('ou=test,dc=example,dc=org') # Core data is still there self.assertIsNotNone(server.get('dc=example,dc=org')) server.stop() self.assertServerStopped(context) class ResetTests(LdapServerTestCase): data = { 'ou=test': { 'objectClass': ['organizationalUnit'], 'ou': ['test'], }, } def test_cleanup(self): server, context = self._launch_server(initial_data=self.data) extra = { 'ou=subarea,ou=test': { 'objectClass': ['organizationalUnit'], }, } server.add(extra) entry = server.get('ou=subarea,ou=test,dc=example,dc=org') server.reset() # Extra data should have been removed self.assertRaises(KeyError, server.get, 'ou=subarea,ou=test,dc=example,dc=org') # Initial data should still be here entry = server.get('ou=test,dc=example,dc=org') self.assertEqual({ 'dn': [b'ou=test,dc=example,dc=org'], 'objectClass': [b'organizationalUnit'], 'ou': [b'test'], }, entry) server.stop() self.assertServerStopped(context) class TLSTests(LdapServerTestCase): def test_connection(self): server, context = self._launch_server(tls_config=volatildap.LOCALHOST_TLS_CONFIG) self.assertEqual(server.uri[:8], 'ldaps://') entry = server.get('dc=example,dc=org') self.assertEqual([b'dc=example,dc=org'], entry['dn']) server.stop() self.assertServerStopped(context) class AutoCleanupTests(LdapServerTestCase): def test_stop(self): """Deleting the LdapServer object causes its cleanup.""" server, context = self._launch_server() server.stop() self.assertServerStopped(context) volatildap-1.3.0/tests/__init__.py0000644000175000017500000000000013466774335017765 0ustar xelnorxelnet00000000000000volatildap-1.3.0/tests/minipki/0000755000175000017500000000000013466774336017327 5ustar xelnorxelnet00000000000000volatildap-1.3.0/tests/minipki/openssl-leaf.cnf.in0000644000175000017500000000075213466774335023017 0ustar xelnorxelnet00000000000000# This definition stops the following lines choking if HOME isn't # defined. HOME = . RANDFILE = $ENV::HOME/.rnd # Basics for a default request # ============================ [ req ] default_bits = 2048 basicConstraints = CA:FALSE keyUsage = nonRepudiation, digitalSignature, keyEncipherment distinguished_name = req_distinguished_name prompt = no [ req_distinguished_name ] CN = ${FQDN} OU = Dev # Use with openssl req -reqexts leaf_cert [ leaf_cert ] subjectAltName=DNS:${FQDN} volatildap-1.3.0/tests/minipki/openssl-ca.cnf.in0000644000175000017500000000262413466774335022473 0ustar xelnorxelnet00000000000000# This definition stops the following lines choking if HOME isn't # defined. HOME = . RANDFILE = $ENV::HOME/.rnd [ req ] default_bits = 2048 prompt = no distinguished_name = ca_distinguished_name # Root certificate authority # ========================== # Use with openssl req -x509 -extensions root_certificate [ root_certificate ] # CA:true => this is a CA # pathlen:0 => It can only sign intermediate/leaf certificates basicConstraints = critical, CA:TRUE, pathlen:1 # A CA can sign CRLs and leaf certificates keyUsage = critical, cRLSign, keyCertSign # Best practices. See https://www.openssl.org/docs/man1.0.2/man5/x509v3_config.html. subjectKeyIdentifier = hash authorityKeyIdentifier=keyid:always,issuer:always # Can only sign certificates ending with .${DOMAIN} nameConstraints = permitted;DNS:.${DOMAIN},permitted;DNS:${DOMAIN} # Intermediate CA # =============== # Use with openssl req -reqexts interm_ca [ interm_ca ] basicConstraints = critical, CA:TRUE, pathlen:0 # A CA can sign CRLs and leaf certificates keyUsage = critical, cRLSign, keyCertSign # Best practices. See https://www.openssl.org/docs/man1.0.2/man5/x509v3_config.html. subjectKeyIdentifier = hash authorityKeyIdentifier=keyid:always,issuer:always # Can only sign certificates ending with .${DOMAIN} nameConstraints = permitted;DNS:.${DOMAIN},permitted;DNS:${DOMAIN} [ ca_distinguished_name ] CN = Self-signed local CA Root OU = Dev volatildap-1.3.0/tests/minipki/openssl-ca-volatildap.org.cnf0000644000175000017500000000265613466774335025016 0ustar xelnorxelnet00000000000000# This definition stops the following lines choking if HOME isn't # defined. HOME = . RANDFILE = ::HOME/.rnd [ req ] default_bits = 2048 prompt = no distinguished_name = ca_distinguished_name # Root certificate authority # ========================== # Use with openssl req -x509 -extensions root_certificate [ root_certificate ] # CA:true => this is a CA # pathlen:0 => It can only sign intermediate/leaf certificates basicConstraints = critical, CA:TRUE, pathlen:1 # A CA can sign CRLs and leaf certificates keyUsage = critical, cRLSign, keyCertSign # Best practices. See https://www.openssl.org/docs/man1.0.2/man5/x509v3_config.html. subjectKeyIdentifier = hash authorityKeyIdentifier=keyid:always,issuer:always # Can only sign certificates ending with .volatildap.org nameConstraints = permitted;DNS:.volatildap.org,permitted;DNS:volatildap.org # Intermediate CA # =============== # Use with openssl req -reqexts interm_ca [ interm_ca ] basicConstraints = critical, CA:TRUE, pathlen:0 # A CA can sign CRLs and leaf certificates keyUsage = critical, cRLSign, keyCertSign # Best practices. See https://www.openssl.org/docs/man1.0.2/man5/x509v3_config.html. subjectKeyIdentifier = hash authorityKeyIdentifier=keyid:always,issuer:always # Can only sign certificates ending with .volatildap.org nameConstraints = permitted;DNS:.volatildap.org,permitted;DNS:volatildap.org [ ca_distinguished_name ] CN = Self-signed local CA Root OU = Dev volatildap-1.3.0/tests/minipki/localhost.volatildap.org.key0000644000175000017500000000325013466774335024754 0ustar xelnorxelnet00000000000000-----BEGIN PRIVATE KEY----- MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDZUSM39x4m4016 MJJqFRgRYmbBDEtp2JUK/w90Qk8nYc0PJP3O32GEvxTiQ+U1ZyfZRlL9VZg/FGnd fJvp7smIprQw4WSuMmua6FVcM8sGTHxeSCiPYqm3neCLCY5AciLVtau7c+00aMUr ZFmtFxv8XUQwTgJbVrjywHo8cDTvUWoo3F6GrYe3aTJrK4BOueu/oH/UGd2zWna/ gv/dutd+AoQHcRXuCCmHQsSoNhnSDUMepskvBx1E+rH/bcWhWVWJKjhQuU/DKWdd Xw0ZdD+m5ZAUHUAnaKDxYu8whLcFHn8Y7k8Q1k5KDAlJeXjsW2MUMG1LiciozjKk Z8jOKWkrAgMBAAECggEAUiz6Ullhz3a4f3IKi466cShyjqFlivrZMTVN1LqWT+AN uKJWT3ns2FOhRAZEkDMXwHN1QlKxpXjEp3pNQuKG1uOi07EHb0+lnQgp7xmutg0K ImTK4Z1/a+2JUUolvnzqa0GtVM77bYwIsuXZezh33rAt7s4w2fUrLrBWCLCtc6j1 ItWAHXBrdD9eEDJ4l97Zm6Ne8WMA1sIv4zOOCgbtYd9RcB0S7fjKemlRX/lMkjcN QDDoAwePanqQkceGy0h9AgJ/n4d1bKl5tE4waqqTm6XYkkEd7A5ZA78izoEZhzci H1IRUT085LU4L1I1Hk8NGFdIQ8giKtG+3zgnsYeekQKBgQD1i9I7Jn2G7lvJ0oJu GuR/dS2M1F5oXOp4SxFITqfw7rcLpEd4wjeF2wjHZFQyiwMzakbtvBoj4rlYv+KK 3F7CqH3zTkbBvoxYEBOLF/D2WkDZosgYSsB7psMrJ+7jqCg7tn8hDTub4Ar8cEzd 6e1JfefVw92R+TEhihcF8gf55QKBgQDikaY3QwKFVYMlyS+E7L9yLIXjebCRcBuC Bmn6MTRq4TnK08aZhi92/jqR7Bxy3M8FJ7Kj741atqGtFWVqYfuVROE5sACVXy2o C3O/EYjseJKSHwFQogqJ96wIPIaekrvH+glcInxIp8GSreMc37ghppnnbrYMYoK/ u65sxbNlzwKBgQDzZjprL+hWjIioMuffxQCj57/TTeCXiyNRQvhKuM144ujHvXyr k7n+EfVN0YI059+wY7UTi9ZNpD0eYHdsNq4oP2U6fWlePEhaMvVsd7sAOM737Yhz rAXJjLECYn9HZDUdm/XBCaEBDsFVIuzPPUdeBlR9bb6BKIcvO/c+iYCmkQKBgADw uc/g51HGWRT/EMNseB7xMF0RVDpGTNbtT+NJ8P3AkYzV9C0O8YN86qFQaY3QfMt4 rrUfeWa2+1TMke8vGOw1rPmgUWBNVNMtuP1CBV914KXamxbKlWCdNomSNrzRvxXF Y+SOABYNHDCPqDGmfA0ns9vSCc9jWUeSb9bjuyrhAoGBAME77wEd/3dhgk8iX6zz b0+wWOBz+CqBBLD8hs/N2/Sj8Cwactebi0sRZqgc5HAVUsUC2OV+mUovUzQpcMou qVFgjUBT72YMkEqZsKVdiI2+A9YjWYwKka1dllIFaZG7CbiyCI5We2Qv4P7N0GY8 +9LOsFxClEOIHKHjIFTMNnfL -----END PRIVATE KEY----- volatildap-1.3.0/tests/minipki/localhost.volatildap.org.crt0000644000175000017500000000212313466774335024752 0ustar xelnorxelnet00000000000000-----BEGIN CERTIFICATE----- MIIDBTCCAe2gAwIBAgIBAjANBgkqhkiG9w0BAQsFADAyMSIwIAYDVQQDDBlTZWxm LXNpZ25lZCBsb2NhbCBDQSBSb290MQwwCgYDVQQLDANEZXYwHhcNMTkwNTE1MTEw OTM3WhcNMjkwNTEyMTEwOTM3WjAxMSEwHwYDVQQDDBhsb2NhbGhvc3Qudm9sYXRp bGRhcC5vcmcxDDAKBgNVBAsMA0RldjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC AQoCggEBANlRIzf3HibjTXowkmoVGBFiZsEMS2nYlQr/D3RCTydhzQ8k/c7fYYS/ FOJD5TVnJ9lGUv1VmD8Uad18m+nuyYimtDDhZK4ya5roVVwzywZMfF5IKI9iqbed 4IsJjkByItW1q7tz7TRoxStkWa0XG/xdRDBOAltWuPLAejxwNO9RaijcXoath7dp MmsrgE6567+gf9QZ3bNadr+C/926134ChAdxFe4IKYdCxKg2GdINQx6myS8HHUT6 sf9txaFZVYkqOFC5T8MpZ11fDRl0P6blkBQdQCdooPFi7zCEtwUefxjuTxDWTkoM CUl5eOxbYxQwbUuJyKjOMqRnyM4paSsCAwEAAaMnMCUwIwYDVR0RBBwwGoIYbG9j YWxob3N0LnZvbGF0aWxkYXAub3JnMA0GCSqGSIb3DQEBCwUAA4IBAQBXVkLr6krL psQjZq7x6ODZFK0Hvs5Xbt1nA2Ib09j+5h3t39Eu4k+p+XUYVniuAhnijNQ4pl7I xeEl6s6hXiYrMD6y7+F3Hhqn7uW2We/Ptu6r+iKN4aVs19Od6HftmOxFtM6idhd5 ExY29ID1kZpJ9GygcOWh7EiY9BgVMMj/Oy/wmIFP9zaPATbms17zgXCrnhmeyqCM F+01SLza+Xw+K5Q327qVd1xkePVXC0iUwQLY/N+0j24QcfCeNeE7fCT19UruYQ+g Vv8JBsuMSOyXPDdfWG6csFoP8gkU44n+KkRT9lZF27jzcwbS85FjaHwQOqiEuUnN jflWAqV1w0bP -----END CERTIFICATE----- volatildap-1.3.0/tests/minipki/interm-volatildap.org.key0000644000175000017500000000325013466774335024261 0ustar xelnorxelnet00000000000000-----BEGIN PRIVATE KEY----- MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDLwI/12ZVK3XbW ajn/p7LNQW/7Za5WKsYoAXmW/NLEj5fQvAFfuy9kur6LW1MR7bfq6fX5tMZNvAlK 2o6GYtbRhE1n7ZEEUkMjVl2kwFCjbI6NAJ9s8YAdhldekxc68mnS26VHPBOG2CwR Gj8bIRZK5Hm1NGcMtLFFqmT5t+tmVaJdTBBnoxb14YMXU35Ltf/RXxzKG9/RQZ5j V4OzZRs22veccBcYIOuhLS0vR24Qr8mekjkpju2jXcSbIPuUBZ3FJHUGTPCXPhTb wwCCCGXE+h+Yh7x0rFTBsObjcRCcG0jTwaEpoAZ2483daT2cM2ms1HTD2Qz5LXp/ SrHpE1y9AgMBAAECggEBAMOsCBTYglFFYg0EMvLcQBByWXKa8dP6fBaLZBOmdVsj c26YPtGK2DbqSHdYmuxK2SeWzAykmRSqElp6jgls/lRztEqskWoO/j7/gCrqLgbk B/pNsM1VF+5Cx8EhjVuk1hATtCQ3A6GqFJfQFu6c1eXSiTeGPbisBtqaiyJuiHja 6V2y4IkzUOMRZovK3B6gaYVfweVY4umUFReTyMe+ZX/4+lq90PItOAlCUQMrP6JU pZqwKZe2LeZwakQ0aI/A2Iypj32S+eK/GYeI51RowkuoIU+Ije/2kr/2N8DXGWUW qWGMLI7pLTTnJmsNpvCa55iZCGLMT21AMsoFeLxSalkCgYEA7qUUtSlaWmUrRH5M Eoqr6ZTY151Cc4JhLTpubT4wk9XqkgZXE8tVmfrf6V8ff5WmgXbv3SCXkgrH9J1a CttuTOK7VaCIUNMahK+1Rp7jRg+f9DW33ZF7arfJ5T3PZtzM4HbU8ZdTZEWVKbo8 NC3lBAXHxLpl5rflcOy5nk0KD8sCgYEA2pHgApRo7SdtfJsfQb0qPFmHVsAbEDGY SC1XK0C2pJcEvbjRMLnNarpUJ3Tlh8+EWSbKBDXOpuICpcroAYTnXO8wtLPYhkOc NktMYNgv057Cdtj3E0hnj+rGIlQ+3uzJ7W0ZRfVotPnZecUBR1yMVgkOr1JcYwZx Si4CUAVSpJcCgYBtfhZrLEcFVIeujFfw5ekMabu36bJUdwyJCNcjjjYRjkNqrjgD n/ZtiJp1Y6x7CDl2CxDwnwSMACBQDmYCtzz7T8ft+JKyqrjvCgMdHMdgHMkh7gen RBmhFCQw5tQi+MmTb1IyamDbj8VpeMYolqoN120ZyP/6BIPj9OkRRX2JUwKBgHpB sl1/ChntPe6/DgLFfsR3B57cocazFwz4X5PQBNmXp40/vbNVmrMiOkQ/vMv0pYEA k2s7dbYfIX//OTrwTkHLgSDyoCCp3Mz53WUmR8pFOV0lCz779wGnPuCg4vHq3SAz MDnThZgFUUItkgOKnuzY8B/dH9Q8HE5z4dNExu9NAoGBAMZYOihz2Gez9Bz7eyDd MD0i5LJuSvn/3SPizq6bEeXieZyXnSRXCHjgkHw+7znU7c+0IcxGQWRElG+LOogP HhdD+63vbVapGS8K1HcRW1iDIqyrH7pLORU6vKDRRWLrZjzfU9dSsqOCWBiO9GXZ /Y3tOPNalbpABGhXkEzy1akr -----END PRIVATE KEY----- volatildap-1.3.0/tests/minipki/interm-volatildap.org.csr0000644000175000017500000000164413466774335024265 0ustar xelnorxelnet00000000000000-----BEGIN CERTIFICATE REQUEST----- MIICdzCCAV8CAQAwMjEiMCAGA1UEAwwZU2VsZi1zaWduZWQgbG9jYWwgQ0EgUm9v dDEMMAoGA1UECwwDRGV2MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA y8CP9dmVSt121mo5/6eyzUFv+2WuVirGKAF5lvzSxI+X0LwBX7svZLq+i1tTEe23 6un1+bTGTbwJStqOhmLW0YRNZ+2RBFJDI1ZdpMBQo2yOjQCfbPGAHYZXXpMXOvJp 0tulRzwThtgsERo/GyEWSuR5tTRnDLSxRapk+bfrZlWiXUwQZ6MW9eGDF1N+S7X/ 0V8cyhvf0UGeY1eDs2UbNtr3nHAXGCDroS0tL0duEK/JnpI5KY7to13EmyD7lAWd xSR1Bkzwlz4U28MAgghlxPofmIe8dKxUwbDm43EQnBtI08GhKaAGduPN3Wk9nDNp rNR0w9kM+S16f0qx6RNcvQIDAQABoAAwDQYJKoZIhvcNAQELBQADggEBAHQBW7BQ 0y3/nc+LCAJAo36uFzzupFYmeXuPJJnE5cPnhlTsx3MxTDeGk2mnXuW4qGO/7mds 1OYDIk/RjKVKy2tKiqW36ctJDkMy8YP1gdY3s5R2lVS82ajxV8lKOt9W8qGYxLZq qFiusIrKEF0YPMbbUmUXI8tS6W9jc9uE1MiBLZKzwHwKf60Q0HxlqhTlRiYK56S/ Bzlalv9zSJIdPXE+ktjKhg3nnn6b1jhOxU6Wq+hOTEWwvBBhP3KkeMsh7pb1jkBX SGYQXyBT+9uerL9p2QFCK5vfuz5lX39BtZlVZcOdQycQFltwmN9MeOMNZL7HyiGt +6c7TA5qJ3awEd8= -----END CERTIFICATE REQUEST----- volatildap-1.3.0/tests/minipki/interm-volatildap.org.crt0000644000175000017500000000251213466774335024261 0ustar xelnorxelnet00000000000000-----BEGIN CERTIFICATE----- MIIDvDCCAqSgAwIBAgIBAjANBgkqhkiG9w0BAQsFADAyMSIwIAYDVQQDDBlTZWxm LXNpZ25lZCBsb2NhbCBDQSBSb290MQwwCgYDVQQLDANEZXYwHhcNMTkwNTE1MTEw OTM3WhcNMjkwNTEyMTEwOTM3WjAyMSIwIAYDVQQDDBlTZWxmLXNpZ25lZCBsb2Nh bCBDQSBSb290MQwwCgYDVQQLDANEZXYwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw ggEKAoIBAQDLwI/12ZVK3XbWajn/p7LNQW/7Za5WKsYoAXmW/NLEj5fQvAFfuy9k ur6LW1MR7bfq6fX5tMZNvAlK2o6GYtbRhE1n7ZEEUkMjVl2kwFCjbI6NAJ9s8YAd hldekxc68mnS26VHPBOG2CwRGj8bIRZK5Hm1NGcMtLFFqmT5t+tmVaJdTBBnoxb1 4YMXU35Ltf/RXxzKG9/RQZ5jV4OzZRs22veccBcYIOuhLS0vR24Qr8mekjkpju2j XcSbIPuUBZ3FJHUGTPCXPhTbwwCCCGXE+h+Yh7x0rFTBsObjcRCcG0jTwaEpoAZ2 483daT2cM2ms1HTD2Qz5LXp/SrHpE1y9AgMBAAGjgdwwgdkwEgYDVR0TAQH/BAgw BgEB/wIBADAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFDK21gwaeINTyWPY2dQ5 pCiHYkrWMGIGA1UdIwRbMFmAFMJVfwHZQQpVQPxc1wKQYi3hMMJJoTakNDAyMSIw IAYDVQQDDBlTZWxmLXNpZ25lZCBsb2NhbCBDQSBSb290MQwwCgYDVQQLDANEZXaC CQCO+jI2shyPKTAwBgNVHR4EKTAnoCUwEYIPLnZvbGF0aWxkYXAub3JnMBCCDnZv bGF0aWxkYXAub3JnMA0GCSqGSIb3DQEBCwUAA4IBAQAac3Ep3WkrQqUCtUAOBxYa kv1v+U7RvRkJOYVOXccF93d1KKn0TxQFYgddewkEY+HVzMiJbsdSLj5qSqMV8A2m hzU9Av/K3b7r1VBRGvzwH5moa15WZEB5KfnPIbhtdPTfJ/RHEtEChkXAtOynhhNU hOwZKicbGvm7Lo1ppFGoLyxuPDxfpVslwtsxEUWYR9grkME/lro5XXjxOqv6QwVM qZuheGEe9J/gqr7ImnUkjpOfHc91J80qrfeNh01WMQD7JQft2zAVPLZCzPt8cx0b C2DEMEYWZYoFG8FBn0lb2hs4SvpvEpxpo2jKcKCvUD7OyZl9bU8Nvly3SinVBzUT -----END CERTIFICATE----- volatildap-1.3.0/tests/minipki/ca-volatildap.org.crt0000644000175000017500000000252713466774335023354 0ustar xelnorxelnet00000000000000-----BEGIN CERTIFICATE----- MIIDxDCCAqygAwIBAgIJAI76MjayHI8pMA0GCSqGSIb3DQEBCwUAMDIxIjAgBgNV BAMMGVNlbGYtc2lnbmVkIGxvY2FsIENBIFJvb3QxDDAKBgNVBAsMA0RldjAeFw0x OTA1MTUxMTA5MzdaFw0yOTA1MTIxMTA5MzdaMDIxIjAgBgNVBAMMGVNlbGYtc2ln bmVkIGxvY2FsIENBIFJvb3QxDDAKBgNVBAsMA0RldjCCASIwDQYJKoZIhvcNAQEB BQADggEPADCCAQoCggEBAMIpgtsshv7yxm/KXecT/5oA6fZGD9kTVMORk9M/OU9E QY8BKpfiC4Tw1ZJ6LUYS3NUfP7pa3eNXRBomBGoy64zlQUFLOUc3XWpp2hEL72kV AHJcd5L0NdlUvcIetAN2A1M8bMi/ZrYGPwLohWyFinAsQ52RDpd8mwvSJwRiykAi zWagx+pwvmW7VIV5yjc6dDuyFXFnAdLVwhN6BQQLSxQEFKfhlPNqDAf9vpuq58WS n3aOlGo8Csrj30tstuUfz4B3GkbhU7IOskWyM2I33DvMITA2Hy2M2CAueBxah8pB IWVt969DVHFoQVNwJJtjRjkNW3HJW+YSkjFPHfwIyjUCAwEAAaOB3DCB2TASBgNV HRMBAf8ECDAGAQH/AgEBMA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUwlV/AdlB ClVA/FzXApBiLeEwwkkwYgYDVR0jBFswWYAUwlV/AdlBClVA/FzXApBiLeEwwkmh NqQ0MDIxIjAgBgNVBAMMGVNlbGYtc2lnbmVkIGxvY2FsIENBIFJvb3QxDDAKBgNV BAsMA0RldoIJAI76MjayHI8pMDAGA1UdHgQpMCegJTARgg8udm9sYXRpbGRhcC5v cmcwEIIOdm9sYXRpbGRhcC5vcmcwDQYJKoZIhvcNAQELBQADggEBABUFWxgO4Lkv Md2AKbMEKnwb25ZXqEf/K6F8sfJPRV5vH3eSOCKBEOt2E3RgdleR643vHfOSnKwQ BMs7V/Vy5LYBL52M1k9UfQxajxWSmMQSxyFvK8mF9y09TahqewheqDue2LPJkMKR GnGGSYE1IB5phU80UW7cGoRNmZEWVMgo+DScfwikcDTrE2VwyCQBHxQDTX0u48SS mvuF62QnmOmoud7RqGsORIE/7eO8rcFoOkPO4jMNpCXOYEhRXWnyApSdXLQ3VeVV CjGiEP9qLV3lhhkgGcPZzimLrV7hK75hPFqNFPoKjrVLl6P3WdokKA/h0Ok1VCTG dMUH50jvlt0= -----END CERTIFICATE----- volatildap-1.3.0/tests/minipki/README.md0000644000175000017500000000202513466774335020604 0ustar xelnorxelnet00000000000000# mini-pki This mini PKI is intended for local development. It is able to generate valid certificates, with proper X509 extensions, using a root CA that can **safely** be added to the operating system trust store: * The root CA's signing key has been removed (it can't be used to sign new intermediate certificates) * It is restricted only to the local development domain ## Usage First, run `make bootstrap`. This will generate a root CA and the associated intermediate. Afterwards, simply run `make leaf-foo.example.org.crt` to generate a certificate for that domain. ## Bootstrap workflow On first run (or when changing the development domain name), the stack will: * Generate a self-signed root CA, restricted to the development domain * Generate an intermediary CA, restricted to the development domain * Signed the intermediary CA with the self-signed CA * **Throw away** the key for the root CA The intermediary CA can be used to sign any local certificate; it will refuse to sign any certificate outside the development domain. volatildap-1.3.0/tests/minipki/Makefile0000644000175000017500000000333513466774335020772 0ustar xelnorxelnet00000000000000 DOMAIN = volatildap.org export DOMAIN # Main paths ROOT_CAFILE = ca-$(DOMAIN).crt ROOT_TEMPKEY = ca-$(DOMAIN).key INTERM_CRT = interm-$(DOMAIN).crt INTERM_CSR = interm-$(DOMAIN).csr INTERM_KEY = interm-$(DOMAIN).key OPENSSL_CA = openssl-ca-$(DOMAIN).cnf # Certificate validity (same for every file) CERT_DAYS = 3650 default: bootstrap bootstrap: $(INTERM_CRT) test-cert: localhost.$(DOMAIN).crt .PHONY: default test-cert # Generate root CA, intermediate csr, sign intermediate. $(OPENSSL_CA): openssl-ca.cnf.in cat $< | envsubst > $@ $(ROOT_CAFILE) $(ROOT_TEMPKEY): $(OPENSSL_CA) openssl req -config $< -x509 -extensions root_certificate -out $(ROOT_CAFILE) -keyout $(ROOT_TEMPKEY) \ -days $(CERT_DAYS) -nodes $(INTERM_CSR) $(INTERM_KEY): $(OPENSSL_CA) openssl req -config $< -newkey rsa -nodes -out $(INTERM_CSR) -keyout $(INTERM_KEY) $(INTERM_CRT): $(ROOT_CAFILE) $(INTERM_CSR) $(OPENSSL_CA) echo '01' > $<.srl openssl x509 -extfile $(OPENSSL_CA) -extensions interm_ca -req -days $(CERT_DAYS) \ -in $(INTERM_CSR) -out $@ \ -CA $(ROOT_CAFILE) -CAkey $(ROOT_TEMPKEY) -CAserial $<.srl rm $(ROOT_TEMPKEY) rm $<.srl # Generate all forms of leaf certificates %.csr %.key: openssl-%.cnf openssl req -config $< -newkey rsa -nodes -out $*.csr -keyout $*.key %.crt: %.csr $(INTERM_CRT) $(INTERM_KEY) openssl-%.cnf echo '01' > $*.srl openssl x509 -extfile openssl-$*.cnf -extensions leaf_cert -req -days $(CERT_DAYS) \ -in $< -out $@ \ -CA $(INTERM_CRT) -CAkey $(INTERM_KEY) -CAserial $*.srl rm $*.srl openssl-%.cnf: openssl-leaf.cnf.in cat $< | FQDN=$* envsubst > $@ clean: -rm leaf-* .PHONY: clean hard-reset: clean -rm $(ROOT_CAFILE) $(INTERM_KEY) $(INTERM_CRT) *.cnf *.csr *.srl .PHONY: hard-reset