diff --git a/files/gitlab-ldap-sync.py b/files/gitlab-ldap-sync.py index 1e3539121feb69242450c4eb9f42f6e0b941b65c..30f09b8001ee43af6222900aa9c3d770db69f025 100644 --- a/files/gitlab-ldap-sync.py +++ b/files/gitlab-ldap-sync.py @@ -37,6 +37,7 @@ import ssl import secrets import json import logging +import re import ldap3 import gitlab @@ -62,14 +63,41 @@ def connect_ldap(host, port, encryption, ca_file, bind_dn, bind_passwd): conn.search = search return conn +def sanitize_username(username): + # Gitlab's algorithm as of Gitlab v17.5 + # Gitlab::Slug::Path.generate + # EXTRACT_LOCAL_EMAIL_PART + username = re.sub(r'@.*$', '', username) + # FORBIDDEN_CHARACTERS + username = re.sub(r'[^a-zA-Z0-9_.-]', '', username) + # PATH_TRAILING_VIOLATIONS + for word in ('.atom', '.git', '.'): + if username.endswith(word): + username = username[:-len(word)] + # LEADING_DASHES + username = username.lstrip('-') + # DEFAULT_SLUG + username = username or 'blank' + + # Gitlab::Auth::ExternalUsernameSanitizer.sanitize + # remove leading - , _ , or . characters + # remove trailing - , _ or . characters + username = username.strip('.-_') + # remove consecutive - , _ , or . characters + username = re.sub(r'([_.-])[_.-]+', r'\1', username) + return username + def sync_user(gl, dn, username, name, email, is_admin=False, ldap_provider='ldapmain', dry_run=True): logging.debug('Synchronizing user %s', dn) user = (gl.users.list(provider=ldap_provider, extern_uid=dn) or [None])[0] if user is None: logging.debug('No corresponding Gitlab user found') logging.info('Creating user %s', dn) + sanitized_username = sanitize_username(username) + if sanitized_username != username: + logging.debug('Changing username "%s" to "%s" to meet Gitlab\'s requirements'%(username, sanitized_username)) attrs = {'provider': ldap_provider, 'extern_uid': dn} - attrs['username'] = username + attrs['username'] = sanitized_username attrs['name'] = name attrs['email'] = email attrs['admin'] = is_admin @@ -89,7 +117,7 @@ def sync_user(gl, dn, username, name, email, is_admin=False, ldap_provider='ldap logging.debug('User creation with username "%s" failed: %s', attrs['username'], e.error_message) else: logging.exception('User creation with username "%s" failed', attrs['username']) - attrs['username'] = username + '%d'%(i + 1) + attrs['username'] = sanitized_username + '%d'%(i + 1) logging.debug('Giving up user creation') return logging.debug('Found corresponding Gitlab user "%s", synchronizing attributes', user.username)