From 123c7f12090c268bb7c2f406d93caafce197bfc6 Mon Sep 17 00:00:00 2001 From: Emmy D'Anello Date: Sun, 16 Feb 2025 20:32:42 +0100 Subject: [PATCH] Synchronize Dolibarr extra fields to LDAP --- .env.example | 17 +++++++++ .gitignore | 5 +++ config.py | 25 ++++++++++++++ main.py | 90 ++++++++++++++++++++++++++++++++++++++++++++++++ requirements.txt | 4 +++ 5 files changed, 141 insertions(+) create mode 100644 .env.example create mode 100644 .gitignore create mode 100644 config.py create mode 100644 main.py create mode 100644 requirements.txt diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..1740f5e --- /dev/null +++ b/.env.example @@ -0,0 +1,17 @@ +DOLIBARR_URL=https://dolibarr.example.com +DOLIBARR_API_TOKEN=DOLAPIKEY +DOLIBARR_API_DEBUG=false + +LDAP_HOST=127.0.0.1 +LDAP_PORT=389 +LDAP_BIND_USER= +LDAP_BIND_PASSWORD= +LDAP_BASE="dc=example,dc=com" + +# LDAP_USERS_OU="ou=users,dc=example,dc=com" +LDAP_USERS_EXTRA_FIELDS= +LDAP_USERS_EXTRA_OBJECT_CLASSES= + +# LDAP_GROUPS_OU="ou=groups,dc=example,dc=com" +LDAP_GROUPS_EXTRA_FIELDS= +LDAP_GROUPS_EXTRA_OBJECT_CLASSES= diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c4bbc0c --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +__pycache__ + +.env + +venv diff --git a/config.py b/config.py new file mode 100644 index 0000000..8aa8f61 --- /dev/null +++ b/config.py @@ -0,0 +1,25 @@ +import os + + +DOLIBARR_URL = os.getenv("DOLIBARR_URL", "https://dolibarr.example.com") +DOLIBARR_API_BASE = DOLIBARR_URL + "/api/index.php/" +DOLIBARR_API_TOKEN = os.getenv("DOLIBARR_API_TOKEN", "DOLAPIKEY") +DOLIBARR_API_DEBUG = os.getenv("DOLIBARR_API_DEBUG", "False").lower() == "true" + +LDAP_HOST = os.getenv("LDAP_HOST", "locahost") +LDAP_PORT = int(os.getenv("LDAP_PORT", 389)) +LDAP_BIND_USER = os.getenv("LDAP_BIND_USER", None) or None +LDAP_BIND_PASSWORD = os.getenv("LDAP_BIND_PASSWORD", None) or None +LDAP_BASE = os.getenv("LDAP_BASE", "dc=example,dc=com") + +LDAP_USERS_OU = os.getenv("LDAP_USERS_OU", f"ou=users,{LDAP_BASE}") +LDAP_USERS_EXTRA_FIELDS = os.getenv("LDAP_USERS_EXTRA_FIELDS", "") +LDAP_USERS_EXTRA_FIELDS = LDAP_USERS_EXTRA_FIELDS.split(';') if LDAP_USERS_EXTRA_FIELDS else [] +LDAP_USERS_EXTRA_OBJECT_CLASSES = os.getenv("LDAP_USERS_EXTRA_OBJECT_CLASSES", "") +LDAP_USERS_EXTRA_OBJECT_CLASSES = LDAP_USERS_EXTRA_OBJECT_CLASSES.split(';') if LDAP_USERS_EXTRA_OBJECT_CLASSES else [] + +LDAP_GROUPS_OU = os.getenv("LDAP_GROUPS_OU", f"ou=groups,{LDAP_BASE}") +LDAP_GROUPS_EXTRA_FIELDS = os.getenv("LDAP_GROUPS_EXTRA_FIELDS", "") +LDAP_GROUPS_EXTRA_FIELDS = LDAP_GROUPS_EXTRA_FIELDS.split(';') if LDAP_GROUPS_EXTRA_FIELDS else [] +LDAP_GROUPS_EXTRA_OBJECT_CLASSES = os.getenv("LDAP_GROUPS_EXTRA_OBJECT_CLASSES", "") +LDAP_GROUPS_EXTRA_OBJECT_CLASSES = LDAP_GROUPS_EXTRA_OBJECT_CLASSES.split(';') if LDAP_GROUPS_EXTRA_OBJECT_CLASSES else [] diff --git a/main.py b/main.py new file mode 100644 index 0000000..782bfd2 --- /dev/null +++ b/main.py @@ -0,0 +1,90 @@ +#!/usr/bin/env python3 + +from dolibarrpy import Dolibarrpy +from ldap3 import ALL, Connection, ObjectDef, Reader, Server, Writer + +import config + + +def main(): + dolibarr_client = Dolibarrpy(url=config.DOLIBARR_API_BASE, token=config.DOLIBARR_API_TOKEN, timeout=16, debug=config.DOLIBARR_API_DEBUG) + + ldap_server = Server(config.LDAP_HOST, config.LDAP_PORT, get_info=ALL) + with Connection(ldap_server, config.LDAP_BIND_USER, config.LDAP_BIND_PASSWORD) as ldap_conn: + if config.LDAP_USERS_EXTRA_FIELDS or config.LDAP_USERS_EXTRA_OBJECT_CLASSES: + manage_users_extra_fields(ldap_conn, dolibarr_client) + + if config.LDAP_GROUPS_EXTRA_FIELDS or config.LDAP_GROUPS_EXTRA_OBJECT_CLASSES: + manage_groups_extra_fields(ldap_conn, dolibarr_client) + + +def manage_users_extra_fields(ldap_conn: Connection, dolibarr_client: Dolibarrpy): + dolibarr_users = dolibarr_client.find_all_users() + + obj_inetorgperson = ObjectDef(['inetOrgPerson'] + config.LDAP_USERS_EXTRA_OBJECT_CLASSES, ldap_conn) + users_reader = Reader(ldap_conn, obj_inetorgperson, config.LDAP_USERS_OU) + users_reader.search() + users_writer = Writer.from_cursor(users_reader) + for ldap_user in users_writer: + uid = ldap_user.uid + for dolibarr_user in dolibarr_users: + if dolibarr_user['login'] == uid: + break + else: + continue + + for extra_object_class in config.LDAP_USERS_EXTRA_OBJECT_CLASSES: + if extra_object_class not in ldap_user.objectClass: + ldap_user.objectClass.append(extra_object_class) + + for extra_field in config.LDAP_USERS_EXTRA_FIELDS: + dolibarr_attr, ldap_attr = extra_field.split(':') + if dolibarr_attr.endswith('[]'): + dolibarr_attr = dolibarr_attr[:-2] + value = dolibarr_user['array_options'][f'options_{dolibarr_attr}'] + value = value.split() if value else [] + setattr(ldap_user, ldap_attr, value) + else: + value = dolibarr_user['array_options'][f'options_{dolibarr_attr}'] or "" + setattr(ldap_user, ldap_attr, value) + users_writer.commit() + + +def manage_groups_extra_fields(ldap_conn: Connection, dolibarr_client: Dolibarrpy): + dolibarr_groups = dolibarr_client.call_list_api('users/groups') + + obj_posixgroup = ObjectDef(['posixGroup'] + config.LDAP_GROUPS_EXTRA_OBJECT_CLASSES, ldap_conn) + groups_reader = Reader(ldap_conn, obj_posixgroup, config.LDAP_GROUPS_OU) + groups_reader.search() + groups_writer = Writer.from_cursor(groups_reader) + for ldap_group in groups_writer: + print(ldap_group) + name = ldap_group.cn + for dolibarr_group in dolibarr_groups: + if dolibarr_group['nom'] == name: + break + else: + continue + + for extra_object_class in config.LDAP_GROUPS_EXTRA_OBJECT_CLASSES: + if extra_object_class not in ldap_group.objectClass: + ldap_group.objectClass.append(extra_object_class) + + for extra_field in config.LDAP_GROUPS_EXTRA_FIELDS: + dolibarr_attr, ldap_attr = extra_field.split(':') + if dolibarr_attr.endswith('[]'): + dolibarr_attr = dolibarr_attr[:-2] + print(dolibarr_group) + value = dolibarr_group['array_options'][f'options_{dolibarr_attr}'] + value = value.split() if value else [] + print(ldap_attr, value) + setattr(ldap_group, ldap_attr, value) + else: + value = dolibarr_group['array_options'][f'options_{dolibarr_attr}'] or "" + print(ldap_attr, value) + setattr(ldap_group, ldap_attr, value) + groups_writer.commit() + + +if __name__ == '__main__': + main() diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..4337eff --- /dev/null +++ b/requirements.txt @@ -0,0 +1,4 @@ +ldap3 +dolibarrpy +icecream +requests