diff --git a/.gitignore b/.gitignore index 0878e81dbeb8ad9bf814b59bdcddedb14735e13a..ca763b8fb5da6d6da6ccd165c9af30999d515801 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ .idea/ *.pyc __pycache__/ +etc/ diff --git a/oxldapsync/__main__.py b/oxldapsync/__main__.py index 5d5770e307a604ea6fc38617bec5299f2c68c668..7179e2fb138820d1892c743cad3aa33ea5666092 100644 --- a/oxldapsync/__main__.py +++ b/oxldapsync/__main__.py @@ -3,7 +3,7 @@ import logging from . import config from . import sync - +logging.basicConfig(level=logging.INFO) logger = logging.getLogger() @@ -12,8 +12,7 @@ logger = logging.getLogger() @click.option('-c', '--config-file', default='/opt/oxldapsync/etc/ldapsync.conf', show_default=True, help='configuration file name') def main(verbose, config_file): - if verbose: - logger.setLevel(logging.INFO) + logger.setLevel(logging.INFO if verbose else logging.WARNING) logger.info(f'reading config file {config_file}') config.read_config(config_file) logger.info('calling do_sync()') diff --git a/oxldapsync/ldap.py b/oxldapsync/ldap.py index 1c3e5e4360d731ce963678c461757c4ef8808170..454de2f417ea409eb181e0b73ce63e467f608799 100644 --- a/oxldapsync/ldap.py +++ b/oxldapsync/ldap.py @@ -18,11 +18,11 @@ class LDAPConnection: host = config.get('ldap_host').split(',') if len(host) == 1: host = host[0] - self.server = ldap3.Server(host, config.get('ldap_port'), + self.server = ldap3.Server(host, int(config.get('ldap_port')), use_ssl=True, tls=tls_config) else: self.server = ldap3.ServerPool([ - ldap3.Server(entry, config.get('ldap_port'), + ldap3.Server(entry, int(config.get('ldap_port')), use_ssl=True, tls=tls_config) for entry in host], ldap3.FIRST) self.domain = config.get('ldap_domain') diff --git a/oxldapsync/ox.py b/oxldapsync/ox.py index 955994744c02e0dcdc056eb0acb7b107bb6c07df..04c11a3aaa426ee04e622ab4bedf50ddb65021ce 100644 --- a/oxldapsync/ox.py +++ b/oxldapsync/ox.py @@ -1,6 +1,7 @@ import subprocess import csv import logging +import io from . import config @@ -11,11 +12,17 @@ logger = logging.getLogger() def run_cmd(cmd): cmd[0] = config.get('ox_path') + '/' + cmd[0] if config.get('ox_admin_username', None): - cmd.insert(1, '-A ' + config.get('ox_admin_username')) - cmd.insert(2, '-P ' + config.get('ox_admin_password')) - cmd.insert(1, '-c ' + config.get('ox_context_id')) + cmd.extend(['-A', config.get('ox_admin_username')]) + cmd.extend(['-P', config.get('ox_admin_password')]) + cmd.extend(['-c', config.get('ox_context_id')]) logger.info(str(cmd)) - return subprocess.run(cmd, check=True, capture_output=True, text=True) + try: + return subprocess.run(cmd, check=True, capture_output=True, text=True) + except subprocess.CalledProcessError as error: + logger.error(error.stdout) + logger.error(error.stderr) + logger.exception(error) + raise def add_user(entry): @@ -43,19 +50,33 @@ entry_to_cmdline = { 'mail': '--email', } +entry_to_oxuser = { + 'cn': 'Name', + 'displayName': 'Display_name', + 'givenName': 'Given_name', + 'sn': 'Sur_name', + 'mail': 'PrimaryEmail', +} + def get_user_props(entry): ret = [] - for k, v in entry_to_cmdline: + for k, v in entry_to_cmdline.items(): mapped = entry[k].value if mapped: ret.extend([v, mapped]) return ret +def compare_user(entry, ox_user): + for ldap_name, ox_name in entry_to_oxuser.items(): + if entry[ldap_name] != ox_user[ox_name]: + return False + return True + def get_users(): proc = run_cmd(['listuser', '--csv']) - reader = csv.DictReader(proc.stdout) + reader = csv.DictReader(io.StringIO(proc.stdout)) return list(reader) @@ -98,5 +119,9 @@ def get_group_props(group, display_name, members, remove_members=False): def get_groups(): proc = run_cmd(['listgroup', '--csv']) - reader = csv.DictReader(proc.stdout) - return list(reader) + reader = csv.DictReader(io.StringIO(proc.stdout)) + result = [] + for entry in reader: + entry["Name"] = entry["name"] + result.append(entry) + return result diff --git a/oxldapsync/sync.py b/oxldapsync/sync.py index 9316414f92f84daf4e4a2aef28f8c31e64686af3..23a39c91e044c5eac28bc49bea6680486f1d7797 100644 --- a/oxldapsync/sync.py +++ b/oxldapsync/sync.py @@ -15,7 +15,7 @@ def do_sync(): def _find(haystack, needle): for cnt, entry in enumerate(haystack): - if entry['name'] == needle: + if entry['Name'] == needle: return cnt return None @@ -29,12 +29,13 @@ def sync_users(ldap_conn): if element is None: ox.add_user(entry) continue - ox.modify_user(entry) + if not ox.compare_user(entry, ox_users[element]): + ox.modify_user(entry) ox_users[element]['touched'] = True dont_modify = config.get('oxdontmodify', '').split(',') for user in ox_users: - if not user.get('touched', False) and user['name'] not in dont_modify: - ox.delete_user(user['name']) + if not user.get('touched', False) and user['Name'] not in dont_modify: + ox.delete_user(user['Name']) def sync_groups(ldap_conn): @@ -42,31 +43,42 @@ def sync_groups(ldap_conn): ox_groups = ox.get_groups() ox_users = ox.get_users() for entry in ldap_conn.search_groups(): - group = entry['name'].value + group = entry['Name'].value display_name = entry['displayName'].value if display_name is None: display_name = group element = _find(ox_groups, group) if element is None: members = [] - for member in entry['member'].value: - member = member.split(',')[0].split('"')[1] + member_value = entry['member'].value + if member_value is None: + continue + if isinstance(member_value, str): + member_value = [member_value] + for member in member_value: + member = member.split(',')[0].split('=')[1] cnt = _find(ox_users, member) if cnt is not None: - members.append(ox_users[cnt]['id']) - ox.add_group(group, display_name, members) + members.append(ox_users[cnt]['Id']) + if members: + ox.add_group(group, display_name, members) continue - current_members = ox_groups[element]['members'].split(',') + current_members = [name for name in ox_groups[element]['members'].split(',') if name] active_members = len(current_members) new_members = [] - for cnt, member in enumerate(entry['member'].value): - member = member.split(',')[0].split('"')[1] + member_value = entry['member'].value + if member_value is None: + member_value = [] + if isinstance(member_value, str): + member_value = [member_value] + for cnt, member in enumerate(member_value): + member = member.split(',')[0].split('=')[1] cnt = _find(ox_users, member) if cnt is not None: - if ox_users[cnt]['id'] in current_members: - current_members.remove(ox_users[cnt]['id']) + if ox_users[cnt]['Id'] in current_members: + current_members.remove(ox_users[cnt]['Id']) else: - new_members.append(ox_users[cnt]['id']) + new_members.append(ox_users[cnt]['Id']) if current_members: ox.delete_group_members(group, display_name, current_members) if new_members: @@ -74,4 +86,4 @@ def sync_groups(ldap_conn): ox_groups[element]['touched'] = (len(current_members) < active_members or new_members) for group in ox_groups: if not group.get('touched', False): - ox.delete_group(group['name'].value) + ox.delete_group(group['Name'])