Synchronize Dolibarr extra fields to LDAP

This commit is contained in:
Emmy D'Anello 2025-02-16 20:32:42 +01:00
commit 123c7f1209
Signed by: ynerant
GPG Key ID: 3A75C55819C8CF85
5 changed files with 141 additions and 0 deletions

17
.env.example Normal file
View File

@ -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=

5
.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
__pycache__
.env
venv

25
config.py Normal file
View File

@ -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 []

90
main.py Normal file
View File

@ -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()

4
requirements.txt Normal file
View File

@ -0,0 +1,4 @@
ldap3
dolibarrpy
icecream
requests