diff --git a/ad/test_ad_lookup.py b/ad/test_ad_lookup.py new file mode 100644 index 0000000..84e4cc3 --- /dev/null +++ b/ad/test_ad_lookup.py @@ -0,0 +1,250 @@ +#!/usr/bin/env python3 +""" +Тестовый скрипт — поиск пользователя в Active Directory. +Аналог find_user_in_ad() из sd_dispatcher.py. + +Единственная зависимость: ldap3 + pip install ldap3 + +Запуск (перенесите на любой сервер с доступом к AD): + python3 test_ad_lookup.py --user "DOMAIN\\service_account" --pass "password" --login ivanov + python3 test_ad_lookup.py --user "DOMAIN\\service_account" --pass "password" --email ivanov@sibcem.ru + python3 test_ad_lookup.py --user "DOMAIN\\service_account" --pass "password" --phone 79131234567 + python3 test_ad_lookup.py --user "DOMAIN\\service_account" --pass "password" --test-all +""" + +import argparse +import os +import re +import sys + + +# ========================================================================= +# 1. Подключение к LDAP +# ========================================================================= + +def connect_to_ad(ad_user, ad_pass): + from ldap3 import Server, Connection + + server = Server( + "ldap://172.16.20.20", + get_info=None, + connect_timeout=5, + ) + conn = Connection( + server, + user=ad_user, + password=ad_pass, + auto_bind=True, + receive_timeout=5, + ) + return conn + + +# ========================================================================= +# 2. Поиск пользователя +# ========================================================================= + +def find_user(conn, login=None, email=None, phone=None): + """ + Приоритет: телефон > логин > email. + Возвращает dict атрибутов или None. + """ + if phone: + filt = ( + f"(|(telephoneNumber=*{phone}*)(mobile=*{phone}*) " + f"(homePhone=*{phone}*)(pager=*{phone}*) " + f"(facsimileTelephoneNumber=*{phone}*)(ipPhone=*{phone}*))" + ) + by = f"телефону {phone}" + elif login: + clean = login.split("\\")[-1].split("/")[-1] + filt = f"(sAMAccountName={clean})" + by = f"логину {clean}" + elif email: + filt = f"(mail={email})" + by = f"почте {email}" + else: + print("❌ Укажите --login, --email или --phone") + sys.exit(1) + + print(f"\n[LDAP] Фильтр: {filt}") + print(f"[LDAP] Base: OU=-Пользователи,DC=sibcem,DC=ru") + + conn.search( + search_base="OU=-Пользователи,DC=sibcem,DC=ru", + search_filter=filt, + attributes=[ + "sAMAccountName", "displayName", "givenName", "sn", + "mail", "title", "department", "l", "company", + "manager", "telephoneNumber", "mobile", + ], + ) + + if not conn.entries: + print(f"\n❌ Не найден по {by}") + return None + + user = conn.entries[0] + print(f"\n✅ Найдено: {user.entry_dn}") + + result = { + "sam": user.sAMAccountName.value if "sAMAccountName" in user else None, + "given_name": user.givenName.value if "givenName" in user else None, + "surname": user.sn.value if "sn" in user else None, + "display_name": user.displayName.value if "displayName" in user else None, + "mail": user.mail.value if "mail" in user else None, + "title": user.title.value if "title" in user else None, + "department": user.department.value if "department" in user else None, + "city": user.l.value if "l" in user and user.l.value else None, + "company": user.company.value if "company" in user and user.company.value else None, + "dn": user.entry_dn, + "phone": user.telephoneNumber.value if "telephoneNumber" in user else None, + "mobile": user.mobile.value if "mobile" in user else None, + } + + # Город из OU если атрибут l пустой + if not result["city"]: + match = re.search(r"OU=([^,]+),OU=-Пользователи", result["dn"], re.IGNORECASE) + if match: + result["city"] = match.group(1).strip() + if result["city"].lower() == "красноярск" and "OU=ООО Комбинат Волна" in result["dn"]: + result["company"] = "ООО Комбинат Волна" + print(f" Город из OU: {result['city']}") + + # Руководитель — второй запрос + manager_dn = user.manager.value if "manager" in user else None + if manager_dn: + conn.search( + search_base=manager_dn, + search_filter="(objectClass=user)", + attributes=["mail", "displayName"], + search_scope="BASE", + ) + if conn.entries and "mail" in conn.entries[0]: + result["manager_email"] = conn.entries[0].mail.value + result["manager_name"] = ( + conn.entries[0].displayName.value if "displayName" in conn.entries[0] else None + ) + else: + result["manager_email"] = None + else: + result["manager_email"] = None + + return result + + +# ========================================================================= +# 3. Вывод +# ========================================================================= + +def print_report(data): + print("\n" + "=" * 60) + print(" РЕЗУЛЬТАТ ПОИСКА В ACTIVE DIRECTORY") + print("=" * 60) + print(f" sAMAccountName: {data['sam']}") + print(f" Имя: {data['given_name']}") + print(f" Фамилия: {data['surname']}") + print(f" ФИО: {data['display_name']}") + print(f" Email: {data['mail']}") + print(f" Должность: {data['title']}") + print(f" Отдел: {data['department']}") + print(f" Город: {data['city'] or '—'}") + print(f" Компания: {data['company'] or '—'}") + print(f" Телефон: {data['phone'] or '—'}") + print(f" Мобильный: {data['mobile'] or '—'}") + print(f" Руководитель: {data['manager_name'] or '—'}") + if data.get("manager_email"): + print(f" Email руково.: {data['manager_email']}") + print("-" * 60) + print(f" DN: {data['dn']}") + + # OU-дерево + ou_parts = [p.strip() for p in data["dn"].split(",") if p.strip().startswith("OU=")] + if ou_parts: + print(f"\n OU-структура:") + for i, ou in enumerate(reversed(ou_parts)): + print(f" {' ' * i}└── {ou}") + + print("=" * 60) + + +# ========================================================================= +# 4. Тест всех методов +# ========================================================================= + +def test_all_methods(conn): + print("\n" + "─" * 60) + print(" ТЕСТИРОВАНИЕ: все методы поиска") + print("─" * 60) + + conn.search( + search_base="OU=-Пользователи,DC=sibcem,DC=ru", + search_filter="(objectClass=user)", + attributes=["sAMAccountName", "mail", "telephoneNumber"], + size_limit=1, + ) + + if not conn.entries: + print("❌ Нет пользователей в AD.") + return + + sample = conn.entries[0] + sam = sample.sAMAccountName.value + mail = sample.mail.value if "mail" in sample else None + phone = sample.telephoneNumber.value if "telephoneNumber" in sample else None + + print(f"\n📌 Тестовый: {sam} ({mail})") + + for label, kwargs in [("логину", {"login": sam}), + ("email", {"email": mail}) if mail else None, + ("телефону", {"phone": phone}) if phone else None]: + if kwargs is None: + continue + print(f"\n[Тест] По {label}...") + r = find_user(conn, **kwargs) + if r: + print_report(r) + + print("\n" + "─" * 60) + print(" ✅ Готово") + print("─" * 60) + + +# ========================================================================= +# MAIN +# ========================================================================= + +if __name__ == "__main__": + parser = argparse.ArgumentParser( + description="Поиск пользователя в Active Directory", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=""" +Примеры: + python3 test_ad_lookup.py --user "sibcem\\\\svc_ad" --pass "pass" --login ivanov + python3 test_ad_lookup.py --user "sibcem\\\\svc_ad" --pass "pass" --email ivanov@sibcem.ru + python3 test_ad_lookup.py --user "sibcem\\\\svc_ad" --pass "pass" --phone 79131234567 + python3 test_ad_lookup.py --user "sibcem\\\\svc_ad" --pass "pass" --test-all + """, + ) + parser.add_argument("--user", required=True, help="LDAP user (DOMAIN\\\\account)") + parser.add_argument("--pass", dest="password", required=True, help="LDAP password") + group = parser.add_mutually_exclusive_group(required=True) + group.add_argument("--login", help="sAMAccountName пользователя") + group.add_argument("--email", help="Email пользователя") + group.add_argument("--phone", help="Номер телефона") + parser.add_argument("--test-all", action="store_true", + help="Найти первого пользователя и проверить все методы") + args = parser.parse_args() + + conn = connect_to_ad(args.user, args.password) + print(f"[LDAP] Подключено к ldap://172.16.20.20 как {args.user}") + + if args.test_all: + test_all_methods(conn) + else: + data = find_user(conn, login=args.login, email=args.email, phone=args.phone) + if data: + print_report(data) + + conn.unbind()