From 2fc9e19ecd07dea5fe3b750ba69656470c9bef86 Mon Sep 17 00:00:00 2001 From: Frank Brehm Date: Tue, 18 Oct 2022 14:37:10 +0200 Subject: [PATCH] Adding config parameter 'sync-source' for a LDAP server instance. --- etc/ldap.yaml.default | 4 + lib/pp_admintools/config/ldap.py | 204 ++++++++++++++++++------------- 2 files changed, 121 insertions(+), 87 deletions(-) diff --git a/etc/ldap.yaml.default b/etc/ldap.yaml.default index 47c885a..f7f55a6 100644 --- a/etc/ldap.yaml.default +++ b/etc/ldap.yaml.default @@ -20,6 +20,7 @@ ldap: # bind_pw: ****** is_admin: true readonly: false + sync-source: 'dpx-prod' tier: 'dev' dpx-dev-ro: host: 'dev-ldap2.pixelpark.com' @@ -80,4 +81,7 @@ ldap: # bind_pw: ****** is_admin: true readonly: false + sync-source: 'spk-live' tier: 'test' + +# vim: filetype=yaml diff --git a/lib/pp_admintools/config/ldap.py b/lib/pp_admintools/config/ldap.py index 50bd968..3e09977 100644 --- a/lib/pp_admintools/config/ldap.py +++ b/lib/pp_admintools/config/ldap.py @@ -32,7 +32,7 @@ from . import VALID_TIERS, DEFAULT_TIER from ..xlate import XLATOR -__version__ = '0.4.2' +__version__ = '0.6.0' LOG = logging.getLogger(__name__) _ = XLATOR.gettext @@ -53,12 +53,22 @@ class LdapConfigError(PpConfigurationError): class LdapConnectionInfo(FbBaseObject): """Encapsulating all necessary data to connect to a LDAP server.""" + re_ldap_host_key = re.compile(r'^\s*(?:host|server)\s*$', re.IGNORECASE) + re_ldap_ldaps_key = re.compile(r'^\s*(?:use[_-]?)?(?:ldaps|ssl)\s*$', re.IGNORECASE) + re_ldap_port_key = re.compile(r'^\s*port\s*$', re.IGNORECASE) + re_ldap_base_dn_key = re.compile(r'^\s*base[_-]*dn\s*$', re.IGNORECASE) + re_ldap_bind_dn_key = re.compile(r'^\s*bind[_-]*dn\s*$', re.IGNORECASE) + re_ldap_bind_pw_key = re.compile(r'^\s*bind[_-]*pw\s*$', re.IGNORECASE) + re_ldap_is_admin_key = re.compile(r'^\s*(?:is[_-]*)?admin\s*$', re.IGNORECASE) + re_ldap_readonly_key = re.compile(r'^\s*read[_-]*only\s*$', re.IGNORECASE) + re_ldap_sync_source_key = re.compile(r'^\s*sync[_-]*source\s*$', re.IGNORECASE) + # ------------------------------------------------------------------------- def __init__( self, appname=None, verbose=0, version=__version__, base_dir=None, host=None, use_ldaps=False, port=DEFAULT_PORT_LDAP, base_dn=None, bind_dn=None, bind_pw=None, is_admin=None, readonly=None, tier=None, - initialized=False): + sync_source=None, initialized=False): self._host = None self._use_ldaps = False @@ -69,6 +79,7 @@ class LdapConnectionInfo(FbBaseObject): self._is_admin = False self._readonly = True self._tier = DEFAULT_TIER + self._sync_source = None super(LdapConnectionInfo, self).__init__( appname=appname, verbose=verbose, version=version, base_dir=base_dir, @@ -87,6 +98,7 @@ class LdapConnectionInfo(FbBaseObject): self.is_admin = is_admin self.readonly = readonly self.tier = tier + self.sync_source = sync_source if initialized: self.initialized = True @@ -105,17 +117,18 @@ class LdapConnectionInfo(FbBaseObject): res = super(LdapConnectionInfo, self).as_dict(short=short) - res['host'] = self.host - res['use_ldaps'] = self.use_ldaps - res['port'] = self.port res['base_dn'] = self.base_dn res['bind_dn'] = self.bind_dn res['bind_pw'] = None - res['schema'] = self.schema - res['url'] = self.url res['is_admin'] = self.is_admin + res['host'] = self.host + res['port'] = self.port res['readonly'] = self.readonly + res['schema'] = self.schema + res['sync_source'] = self.sync_source res['tier'] = self.tier + res['url'] = self.url + res['use_ldaps'] = self.use_ldaps if self.bind_pw: if self.verbose > 4: @@ -265,6 +278,19 @@ class LdapConnectionInfo(FbBaseObject): raise LdapConfigError(msg) self._tier = val_lc + # ----------------------------------------------------------- + @property + def sync_source(self): + """The host name (or IP address) of the LDAP server.""" + return self._sync_source + + @sync_source.setter + def sync_source(self, value): + if value is None or str(value).strip() == '': + self._sync_source = None + return + self._sync_source = str(value).strip() + # ------------------------------------------------------------------------- def __repr__(self): """Typecasting into a string for reproduction.""" @@ -282,6 +308,7 @@ class LdapConnectionInfo(FbBaseObject): fields.append("is_admin={!r}".format(self.is_admin)) fields.append("readonly={!r}".format(self.readonly)) fields.append("tier={!r}".format(self.tier)) + fields.append("sync_source={!r}".format(self.sync_source)) fields.append("initialized={!r}".format(self.initialized)) out += ", ".join(fields) + ")>" @@ -294,10 +321,89 @@ class LdapConnectionInfo(FbBaseObject): appname=self.appname, verbose=self.verbose, base_dir=self.base_dir, host=self.host, use_ldaps=self.use_ldaps, port=self.port, base_dn=self.base_dn, bind_dn=self.bind_dn, bind_pw=self.bind_pw, is_admin=self.is_admin, readonly=self.readonly, tier=self.tier, - initialized=self.initialized) + sync_source=self.sync_source, initialized=self.initialized) return new + # ------------------------------------------------------------------------- + @classmethod + def from_config(cls, section, connection_name, appname=None, verbose=0, base_dir=None): + + msg_invalid = _("Invalid value {val!r} in section {sec!r} for a LDAP {what}.") + section_name = "ldap:" + connection_name + + new = cls(appname=appname, verbose=verbose, base_dir=base_dir, initialized=False) + + for key in section.keys(): + + value = section[key] + + if cls.re_ldap_host_key.match(key): + if value.strip(): + new.host = value + else: + msg = msg_invalid.format(val=value, sec=section_name, what='host') + LOG.error(msg) + continue + + if cls.re_ldap_ldaps_key.match(key): + new.use_ldaps = value + continue + + if cls.re_ldap_port_key.match(key): + port = DEFAULT_PORT_LDAP + try: + port = int(value) + except (ValueError, TypeError) as e: + msg = msg_invalid.format(val=value, sec=section_name, what='port') + msg += ' ' + str(e) + LOG.error(msg) + continue + if port <= 0 or port > MAX_PORT_NUMBER: + msg = msg_invalid.format(val=value, sec=section_name, what='port') + LOG.error(msg) + continue + new.port = port + continue + + if cls.re_ldap_base_dn_key.match(key): + if value.strip(): + new.base_dn = value + else: + msg = msg_invalid.format(val=value, sec=section_name, what='base_dn') + LOG.error(msg) + continue + + if cls.re_ldap_bind_dn_key.match(key): + new.bind_dn = value + continue + + if cls.re_ldap_bind_pw_key.match(key): + new.bind_pw = value + continue + + if cls.re_ldap_is_admin_key.match(key): + new.is_admin = value + continue + + if cls.re_ldap_readonly_key.match(key): + new.readonly = value + continue + + if key.strip().lower() == 'tier': + new.tier = value + continue + + if cls.re_ldap_sync_source_key.match(key): + new.sync_source = value + + msg = _("Unknown LDAP configuration key {key} found in section {sec!r}.").format( + key=key, sec=section_name) + LOG.error(msg) + + new.initialized = True + return new + # ============================================================================= class LdapConnectionDict(dict, FbGenericBaseObject): @@ -348,15 +454,6 @@ class LdapConfiguration(PpBaseConfiguration): re_ldap_section_w_name = re.compile(r'^\s*ldap\s*:\s*(\S+)') - re_ldap_host_key = re.compile(r'^\s*(?:host|server)\s*$', re.IGNORECASE) - re_ldap_ldaps_key = re.compile(r'^\s*(?:use[_-]?)?(?:ldaps|ssl)\s*$', re.IGNORECASE) - re_ldap_port_key = re.compile(r'^\s*port\s*$', re.IGNORECASE) - re_ldap_base_dn_key = re.compile(r'^\s*base[_-]*dn\s*$', re.IGNORECASE) - re_ldap_bind_dn_key = re.compile(r'^\s*bind[_-]*dn\s*$', re.IGNORECASE) - re_ldap_bind_pw_key = re.compile(r'^\s*bind[_-]*pw\s*$', re.IGNORECASE) - re_ldap_is_admin_key = re.compile(r'^\s*(?:is[_-]*)?admin\s*$', re.IGNORECASE) - re_ldap_readonly_key = re.compile(r'^\s*read[_-]*only\s*$', re.IGNORECASE) - # ------------------------------------------------------------------------- def __init__( self, appname=None, verbose=0, version=__version__, base_dir=None, @@ -448,76 +545,9 @@ class LdapConfiguration(PpBaseConfiguration): msg = _("Reading configuration of LDAP instance {!r} ...").format(connection_name) LOG.debug(msg) - connection = LdapConnectionInfo( - appname=self.appname, verbose=self.verbose, base_dir=self.base_dir, - initialized=False) - - section_name = "ldap:" + connection_name - msg_invalid = _("Invalid value {val!r} in section {sec!r} for a LDAP {what}.") - - for key in section.keys(): - - value = section[key] - - if self.re_ldap_host_key.match(key): - if value.strip(): - connection.host = value - else: - msg = msg_invalid.format(val=value, sec=section_name, what='host') - LOG.error(msg) - continue - - if self.re_ldap_ldaps_key.match(key): - connection.use_ldaps = value - continue - - if self.re_ldap_port_key.match(key): - port = DEFAULT_PORT_LDAP - try: - port = int(value) - except (ValueError, TypeError) as e: - msg = msg_invalid.format(val=value, sec=section_name, what='port') - msg += ' ' + str(e) - LOG.error(msg) - continue - if port <= 0 or port > MAX_PORT_NUMBER: - msg = msg_invalid.format(val=value, sec=section_name, what='port') - LOG.error(msg) - continue - connection.port = port - continue - - if self.re_ldap_base_dn_key.match(key): - if value.strip(): - connection.base_dn = value - else: - msg = msg_invalid.format(val=value, sec=section_name, what='base_dn') - LOG.error(msg) - continue - - if self.re_ldap_bind_dn_key.match(key): - connection.bind_dn = value - continue - - if self.re_ldap_bind_pw_key.match(key): - connection.bind_pw = value - continue - - if self.re_ldap_is_admin_key.match(key): - connection.is_admin = value - continue - - if self.re_ldap_readonly_key.match(key): - connection.readonly = value - continue - - if key.strip().lower() == 'tier': - connection.tier = value - continue - - msg = _("Unknown LDAP configuration key {key} found in section {sec!r}.").format( - key=key, sec=section_name) - LOG.error(msg) + connection = LdapConnectionInfo.from_config( + section, connection_name, appname=self.appname, + verbose=self.verbose, base_dir=self.base_dir) self.ldap_connection[connection_name] = connection -- 2.39.5