Files
ArchiveCostWebapp/logic/scripts/ad_lookup.py
T

157 lines
6.8 KiB
Python

"""
Dieses Skript liest eine JSON-Datei mit Gruppeninformationen ein und fragt für jede Gruppe
das zugehörige Department im Active Directory (AD) ab. Department ist in Form der entsprechenden
Zahl (z.B. 6000 fuer CPS) angegeben.
Es unterscheidet dabei zwischen zwei Hauptfällen:
1. a-Gruppen (z.B. a-groupname): Diese Gruppen enthalten direkt das Attribut 'department'.
2. p-Gruppen (z.B. p12345): Diese Gruppen sind jeweils einer Beamline zugeordnet. Jede
Beamline besitzt ein 'department' Attribut. Das Skripte ermittelt zuerst die Beamline
und danach das Department. Die Zuordnung Beamline -> Department wird gecached, was
AD Anfragen spart.
Die Ergebnisse (oder Fehlermeldungen für nicht zuordenbare Gruppen) werden ausgegeben.
"""
import json
import os
import sys
from ldap3 import ALL, Connection, Server
# --- Konfiguration ---
AD_SERVER = 'd.psi.ch'
AD_USER = os.environ.get('AD_USER')
AD_PASSWORD = os.environ.get('AD_PASSWORD')
AD_SEARCH_BASE = 'DC=d,DC=psi,DC=ch'
FILE_PATH = os.environ.get('GROUPINFO_JSON_LNK_NAME_WITH_PATH')
# FILE_PATH = "/data/" + os.environ.get('GROUPINFO_JSON_LNK_NAME')
def lookup_ad():
try:
# 1. Datenbasis aus lokaler JSON-Datei laden
if FILE_PATH is None:
print("ERROR: Environment variable GROUPINFO_JSON_LNK_NAME_WITH_PATH is not set.", file=sys.stderr, flush=True)
return
if not os.path.exists(FILE_PATH):
print(f"ERROR: Datei {FILE_PATH} nicht gefunden!", file=sys.stderr, flush=True)
return
with open(FILE_PATH, 'r') as f:
items = json.load(f)
print(f"INFO: {len(items)} Einträge geladen.", file=sys.stderr, flush=True)
# 2. AD-Verbindung initialisieren (SSL, Port 636)
server = Server(AD_SERVER, port=636, use_ssl=True, get_info=ALL, connect_timeout=10)
with Connection(server, user=AD_USER, password=AD_PASSWORD, auto_bind=True) as conn:
# Interner Cache zur Speicherung der Zuordnung von p-Gruppe zu Beamline
beamline2department_cache = {}
count = 0
for item in items:
count += 1
group_name = item.get('ownerGroup', '')
# --- Fall A: Direkte a-Gruppen ---
if group_name.startswith('a-'):
search_filter = f'(&(objectClass=group)(cn={group_name}))'
conn.search(
search_base=AD_SEARCH_BASE,
search_filter=search_filter,
attributes=['department']
)
if conn.entries:
res = str(conn.entries[0].department) if 'department' in conn.entries[0] else "no-dept"
item['department'] = res
else:
item['department'] = "not_found"
# --- Fall B: Zweistufige p-Gruppen ---
elif group_name.startswith('p') and group_name[1:2].isdigit():
if group_name in beamline2department_cache:
beamline = beamline2department_cache[group_name]
else:
# Stufe 1: memberOf-Attribut des p-Objekts abfragen, um übergeordnete Gruppe zu finden
p_filter = f'(cn={group_name})'
conn.search(
search_base=AD_SEARCH_BASE,
search_filter=p_filter,
attributes=['memberOf']
)
beamline = "not_found"
if conn.entries and 'memberOf' in conn.entries[0] and conn.entries[0].memberOf:
# Suche spezifisch nach dem Beamline-Serviceordner
selected_dn = None
for dn in conn.entries[0].memberOf:
dn_str = str(dn)
if 'OU=Beamlines,OU=Experiment,OU=IT' in dn_str:
selected_dn = dn_str
break
# Fallback, falls der spezifische Ordner nicht in memberOf gefunden wurde
if selected_dn is None:
selected_dn = str(conn.entries[0].memberOf[0])
# Zielgruppe (CN) aus dem Distinguished Name extrahieren
try:
beamline = selected_dn.split(',')[0].split('=')[1]
beamline2department_cache[group_name] = beamline
except IndexError:
print(f"WARN: Format von DN '{selected_dn}' unerwartet.", file=sys.stderr, flush=True)
beamline = "format_error"
item['beamline'] = beamline
# Stufe 2: Department anhand der ermittelten beamline (aus memberOf) bestimmen
search_filter = f'(&(objectClass=group)(cn={beamline}))'
conn.search(
search_base=AD_SEARCH_BASE,
search_filter=search_filter,
attributes=['department']
)
if conn.entries:
res = str(conn.entries[0].department) if 'department' in conn.entries[0] else "no-dept"
item['department'] = res
else:
item['department'] = "not_found"
# --- Fall C: Abweichende Schemata ---
else:
print(f"WARN: Unbekanntes Gruppen-Schema: {group_name}", file=sys.stderr)
item['department'] = "unknown_schema"
continue
# Fortschritt alle 500 Einträge im stderr protokollieren
if count % 500 == 0:
print(f"Fortschritt: {count}/{len(items)}...", file=sys.stderr, flush=True)
# 3. Finales Ergebnis auswerten
# Kompletten JSON-Output drucken (momentan auskommentiert):
# print(json.dumps(items))
# Test-Output: Alle nicht gefundenen bzw. leeren Zuweisungen drucken
for item in items:
# Debug (auskommentiert):
# print(f"group: {item['ownerGroup']} -> {item['department']} -> type: {type(item['department'])}")
if item['department'] == "[]":
if 'beamline' in item:
print(f"LEER -> {item['ownerGroup']} -> {item['beamline']}")
else:
print(f"LEER -> {item['ownerGroup']} has no beamline")
except Exception as e:
print(f"ERROR: {str(e)}", file=sys.stderr, flush=True)
# Bei Fehler optional Items als JSON ausgeben (auskommentiert)
# if 'items' in locals():
# print(json.dumps(items))
if __name__ == "__main__":
lookup_ad()