py.lib

Yohn Y. 2022-08-14 Parent:1668cc57225b

34:84b54a8a6d4c Go to Latest

py.lib/ldap_utils/ldap_passwd_changer.py

+ Возможность обработки параметров конфигурации перед добавлением в класс конфигурации . Переформатирование части кода по PEP

History
awgur@13 1 # coding: utf-8
awgur@13 2 """
awgur@13 3 Смена пароля на пользователя в AD посредством Python
awgur@13 4
awgur@13 5 """
awgur@13 6
awgur@13 7 from ldap3 import Server, Connection, SIMPLE, SUBTREE
awgur@13 8 from ldap3.core.exceptions import LDAPException
awgur@13 9 from secrets import token_urlsafe
awgur@13 10 import string
awgur@13 11
awgur@23 12
awgur@13 13 class LdapError(Exception):
awgur@13 14 @staticmethod
awgur@23 15 def err_fmt(e):
awgur@23 16 _buf = ' '.join(str(e).split()) # всё в одну строку с одним пробелом
awgur@23 17 # между словами
awgur@13 18 return "%s(%s)" % (type(e).__name__, _buf)
awgur@23 19
awgur@23 20
awgur@23 21 class LdapServerError(LdapError):
awgur@23 22 pass
awgur@23 23
awgur@23 24
awgur@23 25 class LdapPassGenError(LdapError):
awgur@23 26 pass
awgur@23 27
awgur@23 28
awgur@23 29 class LdapNotFound(LdapError):
awgur@23 30 pass
awgur@13 31
awgur@13 32
awgur@13 33 class LdapPasswdChanger(object):
awgur@23 34 def __init__(self, ldap_server_uri, ldap_user, ldap_passwd, ldap_base_dn, ldap_timeout=120, ldap_reconnect=5,
awgur@23 35 passwd_len=25):
awgur@13 36 self.ldap_server_uri = ldap_server_uri # URI к серверу: ldaps://server_name:port_number
awgur@23 37 self.ldap_user = ldap_user # Пользователь, имеющий право сброса паролей
awgur@23 38 self.ldap_passwd = ldap_passwd # Пароль к тому пользователю
awgur@23 39 self.ldap_base_dn = ldap_base_dn
awgur@23 40 self.ldap_timeout = ldap_timeout # Таймаут операций с сервером
awgur@23 41 self.passwd_len = passwd_len # Желаемая длинна пароля
awgur@23 42 self.ldap_reconnect = ldap_reconnect # Количество попыток переподключения к серверу
awgur@13 43
awgur@13 44 # Заготовка под пароли
awgur@13 45 self._set_punct = set(string.punctuation)
awgur@13 46 self._set_uper = set(string.ascii_uppercase)
awgur@13 47 self._set_lower = set(string.ascii_lowercase)
awgur@13 48
awgur@23 49 def get_passwd(self):
awgur@13 50 _cnt = 0
awgur@13 51 while True:
awgur@13 52 res = token_urlsafe(self.passwd_len)
awgur@13 53 _res_set = set(res)
awgur@23 54 if (
awgur@23 55 (_res_set & self._set_punct)
awgur@23 56 and (_res_set & self._set_uper)
awgur@23 57 and (_res_set & self._set_lower)
awgur@13 58 ):
awgur@13 59 return res
awgur@13 60
awgur@13 61 _cnt += 1
awgur@13 62 if _cnt > 60:
awgur@13 63 raise LdapPassGenError('Не удалось сгенерировать пароль')
awgur@13 64
awgur@23 65 def change_passwd(self, login):
awgur@13 66 _reconnects = self.ldap_reconnect
awgur@13 67 if _reconnects is None or _reconnects < 1:
awgur@13 68 _reconnects = 1
awgur@13 69
awgur@13 70 while True:
awgur@13 71 _reconnects -= 1
awgur@13 72 try:
awgur@23 73 ldap_srv = Server(self.ldap_server_uri, connect_timeout=self.ldap_timeout)
awgur@23 74 ldap_conn = Connection(ldap_srv, authentication=SIMPLE,
awgur@23 75 user=self.ldap_user, password=self.ldap_passwd,
awgur@23 76 check_names=True,
awgur@23 77 auto_referrals=False, raise_exceptions=True, auto_range=True,
awgur@23 78 )
awgur@23 79 ldap_conn.open()
awgur@23 80 if not ldap_conn.bind():
awgur@13 81 continue
awgur@13 82
awgur@13 83 break
awgur@23 84
awgur@13 85 except LDAPException as e:
awgur@13 86 if not _reconnects > 0:
awgur@23 87 raise LdapServerError(f'Ошибка подключения к серверу: {LdapServerError.err_fmt(e)}')
awgur@13 88
awgur@23 89 filter = f'(sAMAccountName={login})'
awgur@13 90 try:
awgur@23 91 if not ldap_conn.search(self.ldap_base_dn, filter):
awgur@23 92 raise LdapServerError('Сервер вернул статус ошибки: %(desc)s(%(code)s) - %(msg)s' % {
awgur@23 93 'desc': ldap_conn.result['description'],
awgur@23 94 'code': ldap_conn.result['result'],
awgur@23 95 'msg': ldap_conn.result['message'],
awgur@23 96 })
awgur@23 97
awgur@13 98 except LDAPException as e:
awgur@23 99 raise LdapServerError(f'Ошибка при поиска УЗ в ldap: {LdapServerError.err_fmt(e)}')
awgur@13 100
awgur@23 101 if not len(ldap_conn.entries) > 0:
awgur@23 102 raise LdapNotFound(f'Не найдена учётная запись: {login}')
awgur@23 103
awgur@23 104 elif len(ldap_conn.entries) != 1:
awgur@23 105 _found_names = '; '.join([i.entry_dn for i in ldap_conn.entries[:10]])
awgur@13 106
awgur@23 107 raise LdapError(f'Найдено более одной УЗ по заданному имени {login}: {len(ldap_conn.entries)} '
awgur@23 108 f'- {_found_names}')
awgur@23 109
awgur@23 110 login_dn = ldap_conn.entries[0].entry_dn
awgur@23 111 new_passwd = self.get_passwd()
awgur@13 112 try:
awgur@23 113 ldap_conn.extend.microsoft.modify_password(login_dn, new_password=new_passwd, old_password=None)
awgur@23 114
awgur@13 115 except LDAPException as e:
awgur@23 116 raise LdapServerError('Ошибка при смене пароля на УЗ: %s' % LdapServerError.err_fmt(e))
awgur@13 117
awgur@13 118 return new_passwd