--- /dev/null
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+"""
+@author: Frank Brehm
+@contact: frank.brehm@pixelpark.com
+@copyright: © 2017 by Frank Brehm, Berlin
+@summary: The module for a LDAP based application object.
+"""
+from __future__ import absolute_import
+
+# Standard modules
+import sys
+import os
+import logging
+import logging.config
+import re
+import traceback
+import textwrap
+import copy
+
+# Third party modules
+import six
+
+import ldap3
+
+# Own modules
+from .global_version import __version__ as __global_version__
+
+from .errors import FunctionNotImplementedError, PpAppError
+
+from .common import pp, terminal_can_colors, to_bytes, to_bool
+
+from .merge import merge_structure
+
+from .cfg_app import PpCfgAppError, PpConfigApplication
+
+__version__ = '0.2.1'
+LOG = logging.getLogger(__name__)
+
+
+# =============================================================================
+class PpLdapAppError(PpCfgAppError):
+ """Base error class for all exceptions happened during
+ execution this configured application"""
+
+ pass
+
+
+# =============================================================================
+class PpLdapApplication(PpConfigApplication):
+ """
+ Class for configured application objects.
+ """
+
+ default_ldap_hosts = [
+ 'ldap.pixelpark.com'
+ ]
+
+ default_ldap_port = 389
+ default_ldap_port_ssl = 636
+ default_ldap_use_ssl = False
+
+ default_ldap_base_dn = 'o=isp'
+ default_ldap_bind_dn = 'uid=Solaris_NSS,ou=Unix NSS,ou=Applications,o=pixelpark,o=isp'
+ default_ldap_timeout = 10
+
+ fs_re = re.compile(r'(?:\s+|\s*[,;]\s*)')
+
+ # -------------------------------------------------------------------------
+ def __init__(
+ self, appname=None, verbose=0, version=__version__, base_dir=None,
+ initialized=None, usage=None, description=None,
+ argparse_epilog=None, argparse_prefix_chars='-', env_prefix=None,
+ cfg_dir=None, cfg_stems=None, cfg_encoding='utf-8', need_config_file=False):
+
+ self.ldap_hosts = copy.copy(self.default_ldap_hosts)
+ self.ldap_use_ssl = self.default_ldap_use_ssl
+ self.ldap_port = self.default_ldap_port
+ if self.ldap_use_ssl:
+ self.ldap_port = self.default_ldap_port_ssl
+
+ self.ldap_base_dn = self.default_ldap_base_dn
+ self.ldap_bind_dn = self.default_ldap_bind_dn
+ self.ldap_bind_pw = None
+ self.ldap_timeout = self.default_ldap_timeout
+
+ # Either a single Server object or a ServerPool object
+ self.ldap_server = None
+ self.ldap_connection = None
+
+ stems = []
+ if cfg_stems:
+ if isinstance(cfg_stems, list):
+ for stem in cfg_stems:
+ s = str(stem).strip()
+ if not s:
+ msg = "Invalid configuration stem {!r} given.".format(stem)
+ raise PpLdapAppError(msg)
+ stems.append(s)
+ else:
+ s = str(cfg_stems).strip()
+ if not s:
+ msg = "Invalid configuration stem {!r} given.".format(cfg_stems)
+ raise PpLdapAppError(msg)
+ stems.append(s)
+ else:
+ stems = self.appname
+ if not 'ldap' in stems:
+ stems.insert(0, 'ldap')
+
+ super(PpLdapApplication, self).__init__(
+ appname=appname, verbose=verbose, version=version, base_dir=base_dir,
+ initialized=False, usage=usage, description=description,
+ argparse_epilog=argparse_epilog, argparse_prefix_chars=argparse_prefix_chars,
+ env_prefix=env_prefix, cfg_dir=cfg_dir, cfg_stems=stems,
+ cfg_encoding=cfg_encoding, need_config_file=need_config_file,
+ )
+
+ pass
+
+ # -------------------------------------------------------------------------
+ def perform_config(self):
+
+ """
+ Execute some actions after reading the configuration.
+
+ This method should be explicitely called by all perform_config()
+ methods in descendant classes.
+ """
+
+ got_host = False
+ for section in self.cfg.keys():
+
+ if not section.lower() == 'ldap':
+ continue
+ ldap_section = self.cfg[section]
+
+ if 'host' in ldap_section:
+ hosts = self.fs_re.split(ldap_section['host'])
+ for host in hosts:
+ if not host:
+ continue
+ if not got_host:
+ self.ldap_hosts = []
+ got_host = True
+ host = host.lower()
+ if host in self.ldap_hosts:
+ continue
+ self.ldap_hosts.append(host)
+
+ if 'port' in ldap_section:
+ try:
+ port = int(ldap_section['port'])
+ except (ValueError, TypeError) as e:
+ msg = "Invalid LDAP port ({s}/port => {v!r}) found in configuration.".format(
+ s=section, v=ldap_section['port'])
+ raise PpLdapAppError(msg)
+ if port <= 0 or port >= 2 ** 16:
+ msg = "Invalid LDAP port ({s}/port => {v!r}) found in configuration.".format(
+ s=section, v=port)
+ raise PpLdapAppError(msg)
+ self.ldap_port = port
+
+ if 'ssl' in ldap_section:
+ self.ldap_use_ssl = to_bool(ldap_section['ssl'])
+
+ if 'tls' in ldap_section:
+ self.ldap_use_ssl = to_bool(ldap_section['tls'])
+
+ if 'base_dn' in ldap_section:
+ self.ldap_base_dn = ldap_section['base_dn'].strip()
+ if 'bind_dn' in ldap_section:
+ self.ldap_bind_dn = ldap_section['bind_dn'].strip()
+ if 'bind_pw' in ldap_section:
+ self.ldap_bind_pw = ldap_section['bind_pw']
+ if 'timeout' in ldap_section:
+ try:
+ timeout = int(ldap_section['timeout'])
+ except (ValueError, TypeError) as e:
+ msg = "Invalid LDAP timeout ({s}/port => {v!r}) found in configuration.".format(
+ s=section, v=ldap_section['timeout'])
+ LOG.error(msg)
+ if timeout > 0:
+ self.ldap_timeout = timeout
+
+ # ----------------------
+ def _get_ldap_server(host):
+ return ldap3.Server(
+ host, port=self.ldap_port, use_ssl=self.ldap_use_ssl,
+ mode=ldap3.IP_V4_PREFERRED, connect_timeout=self.ldap_timeout)
+
+ # Init LDAP Server objects
+ if len(self.ldap_hosts) > 1:
+ self.ldap_server = ldap3.ServerPool(None, ldap3.ROUND_ROBIN)
+ for h in self.ldap_hosts:
+ server = _get_ldap_server(h)
+ self.ldap_server.add(server)
+ elif len(self.ldap_hosts) == 1:
+ self.ldap_server = _get_ldap_server(self.ldap_hosts[0])
+ else:
+ msg = "No LDAP servers found in configuration."
+ raise PpLdapAppError(msg)
+
+
+# =============================================================================
+
+if __name__ == "__main__":
+
+ pass
+
+# =============================================================================
+
+# vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 list