Files
sd_dispatcher/ad/test_ad_lookup.py
T
2026-07-04 09:42:03 +07:00

251 lines
9.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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()