import sys
import os
import time
+import re
# 3rd party modules
from ldap3 import Server, Connection, ALL, DSA, IP_V4_PREFERRED, SAFE_SYNC
+from ldap3 import BASE, LEVEL, SUBTREE, DEREF_NEVER, DEREF_SEARCH, DEREF_BASE, DEREF_ALWAYS
+from ldap3 import ALL_ATTRIBUTES, ALL_OPERATIONAL_ATTRIBUTES
+
from ldap3.core.exceptions import LDAPException
# Own modules
from .config import LDAPMigrationConfiguration
-__version__ = '0.4.0'
+__version__ = '0.5.0'
LOG = logging.getLogger(__name__)
CFG_BASENAME = 'ldap-migration.ini'
Class for the application objects.
"""
+ re_dn_split = re.compile(r'\s*,\s*')
+ re_token_split = re.compile(r'^\s*([a-z0-9]+)\s*=\s*(\S(?:.*\S)?)\s*$', re.IGNORECASE)
+
# -------------------------------------------------------------------------
def __init__(
self, appname=None, verbose=0, version=__version__, base_dir=None):
self.source = None
self.tgt_server = None
self.target = None
+ self.tmp_dir = None
+ self.all_dns_file = None
self.object_classes = {}
self.attribute_types = {}
description=description, initialized=False,
)
+ self.tmp_dir = self.base_dir / 'tmp'
+ self.all_dns_file = self.tmp_dir / 'all-dns.txt'
self.initialized = True
# -------------------------------------------------------------------------
tmp_dict[name_lc] = self.attribute_types[name_lc]['single_name']
LOG.debug("Discovered AttributeTypes:\n" + pp(tmp_dict))
+ # -------------------------------------------------------------------------
+ def check_tmp_dir(self):
+ """Checking existence of temp-dir and creating it, if necessary."""
+
+ LOG.debug("Checking temporary directory {!r} ...".format(str(self.tmp_dir)))
+
+ if self.tmp_dir.exists():
+ if self.tmp_dir.is_dir():
+ LOG.debug("Temporary directory {!r} is already existing.".format(
+ str(self.tmp_dir)))
+ else:
+ msg = "Path {!r} is already existing, but is not a directory.".format(
+ str(self.tmp_dir))
+ raise CommonLDAPMigrationError(msg)
+ else:
+ LOG.info("Creating temporary directory {!r} ...".format(str(self.tmp_dir)))
+ self.tmp_dir.mkdir(mode=0o755, exist_ok=True)
+
+ mod2check = os.R_OK | os.W_OK | os.X_OK
+ euid = False
+ if os.access in os.supports_effective_ids:
+ euid = True
+ if not os.access(str(self.tmp_dir), mod2check, effective_ids=euid):
+ msg = "Insufficient access rights to temporary directory {!r}.".format(
+ str(self.tmp_dir))
+ raise CommonLDAPMigrationError(msg)
+
+ if self.verbose > 1:
+ LOG.debug("Access to {!r} is okay.".format(str(self.tmp_dir)))
+
+ # -------------------------------------------------------------------------
+ def lookup_for_attrtype(self, attrtype, silent=True):
+
+ at_lc = attrtype.lower()
+ if at_lc not in self.attribute_types:
+ msg = "AttributeType {!r} not found.".format(attrtype)
+ if silent:
+ if self.verbose > 2:
+ LOG.debug(msg)
+ else:
+ LOG.error(msg)
+ return None
+
+ new_at = self.attribute_types[at_lc]['single_name']
+ return new_at
+
+ # -------------------------------------------------------------------------
+ def mangle_dn_token(self, old_token):
+
+ match = self.re_token_split.search(old_token)
+ if not match:
+ return old_token
+
+ key = match.group(1)
+ new_key = self.lookup_for_attrtype(key, silent=False)
+ if not new_key:
+ msg = "F***, Whats that?"
+ raise CommonLDAPMigrationError(msg)
+ value = match.group(2)
+
+ return "{key}={val}".format(key=new_key, val=value)
+
+ # -------------------------------------------------------------------------
+ def mangle_dn(self, old_dn):
+
+ parts = self.re_dn_split.split(old_dn)
+ new_parts = []
+ for old_token in parts:
+ new_token = self.mangle_dn_token(old_token)
+ new_parts.append(new_token)
+
+ return ','.join(parts)
+
+ # -------------------------------------------------------------------------
+ def get_all_dns(self):
+
+ LOG.info("Collecting all source SNs and writing them into {!r} ...".format(
+ str(self.all_dns_file)))
+
+ open_args = {
+ 'encoding': 'utf-8',
+ 'errors': 'surrogateescape',
+ 'mode': 'w',
+ }
+ sfilter = '(objectClass=*)'
+
+ item_count = 0
+
+ with self.all_dns_file.open(**open_args) as fh:
+
+ status, result, response, _ = self.source.search(
+ search_base=self.config.suffix, search_scope=SUBTREE, search_filter=sfilter,
+ attributes=['objectClass'], time_limit=self.config.timeout)
+ for entry in response:
+ item_count += 1
+ old_dn = entry['dn']
+ new_dn = self.mangle_dn(old_dn)
+ if self.verbose > 2:
+ LOG.debug("Found DN {!r}.".format(old_dn))
+ fh.write("{old} => {new}\n".format(old=old_dn, new=new_dn))
+
+ LOG.info("Found {nr} items in subtree of {sfx!r}.".format(
+ nr=item_count, sfx=self.config.suffix))
+
# -------------------------------------------------------------------------
def _run(self):
self.connect_source()
self.connect_target()
self.discover_target_schema()
+ self.check_tmp_dir()
+ self.get_all_dns()
LOG.info("Sleeping ...")
time.sleep(2)
finally: