From: Frank Brehm Date: Fri, 17 Mar 2017 14:33:23 +0000 (+0100) Subject: Adding pp_lib/ldap_app.py X-Git-Tag: 0.1.2~253 X-Git-Url: https://git.uhu-banane.de/?a=commitdiff_plain;h=07983504a7a6b8b949cc28f37231532b05c46e5f;p=pixelpark%2Fadmin-tools.git Adding pp_lib/ldap_app.py --- diff --git a/mk-home b/mk-home index 4983818..12f5cca 100755 --- a/mk-home +++ b/mk-home @@ -17,7 +17,7 @@ if os.path.exists(os.path.join(cur_dir, 'pp_lib')): from pp_lib.common import pp -from pp_lib.cfg_app import PpConfigApplication +from pp_lib.ldap_app import PpLdapApplication log = logging.getLogger(__name__) @@ -28,7 +28,7 @@ appname = os.path.basename(sys.argv[0]) locale.setlocale(locale.LC_ALL, '') -app = PpConfigApplication(appname=appname, cfg_stems=['ldap', 'mk-home']) +app = PpLdapApplication(appname=appname, cfg_stems='mk-home') app.initialized = True if app.verbose > 2: diff --git a/pp_lib/cfg_app.py b/pp_lib/cfg_app.py index 23dfac1..08cfea4 100644 --- a/pp_lib/cfg_app.py +++ b/pp_lib/cfg_app.py @@ -4,7 +4,8 @@ @author: Frank Brehm @contact: frank.brehm@pixelpark.com @copyright: © 2017 by Frank Brehm, Berlin -@summary: The module for the application object. +@summary: The module for the application object with support + for configuration files. """ from __future__ import absolute_import diff --git a/pp_lib/ldap_app.py b/pp_lib/ldap_app.py new file mode 100644 index 0000000..5e828f4 --- /dev/null +++ b/pp_lib/ldap_app.py @@ -0,0 +1,213 @@ +#!/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