py.lib
33:57f63bf31fd8
Go to Latest
py.lib/ldap_utils/ldap_passwd_changer.py
* С разбегу влетел в type hinting, пришлось обкладывать костылями. И этот процесс судя по всему длительный, и место больное будет долго...
3 Смена пароля на пользователя в AD посредством Python
7 from ldap3 import Server, Connection, SIMPLE, SUBTREE
8 from ldap3.core.exceptions import LDAPException
9 from secrets import token_urlsafe
13 class LdapError(Exception):
16 _buf = ' '.join(str(e).split()) # всё в одну строку с одним пробелом
18 return "%s(%s)" % (type(e).__name__, _buf)
21 class LdapServerError(LdapError):
25 class LdapPassGenError(LdapError):
29 class LdapNotFound(LdapError):
33 class LdapPasswdChanger(object):
34 def __init__(self, ldap_server_uri, ldap_user, ldap_passwd, ldap_base_dn, ldap_timeout=120, ldap_reconnect=5,
36 self.ldap_server_uri = ldap_server_uri # URI к серверу: ldaps://server_name:port_number
37 self.ldap_user = ldap_user # Пользователь, имеющий право сброса паролей
38 self.ldap_passwd = ldap_passwd # Пароль к тому пользователю
39 self.ldap_base_dn = ldap_base_dn
40 self.ldap_timeout = ldap_timeout # Таймаут операций с сервером
41 self.passwd_len = passwd_len # Желаемая длинна пароля
42 self.ldap_reconnect = ldap_reconnect # Количество попыток переподключения к серверу
44 # Заготовка под пароли
45 self._set_punct = set(string.punctuation)
46 self._set_uper = set(string.ascii_uppercase)
47 self._set_lower = set(string.ascii_lowercase)
52 res = token_urlsafe(self.passwd_len)
55 (_res_set & self._set_punct)
56 and (_res_set & self._set_uper)
57 and (_res_set & self._set_lower)
63 raise LdapPassGenError('Не удалось сгенерировать пароль')
65 def change_passwd(self, login):
66 _reconnects = self.ldap_reconnect
67 if _reconnects is None or _reconnects < 1:
73 ldap_srv = Server(self.ldap_server_uri, connect_timeout=self.ldap_timeout)
74 ldap_conn = Connection(ldap_srv, authentication=SIMPLE,
75 user=self.ldap_user, password=self.ldap_passwd,
77 auto_referrals=False, raise_exceptions=True, auto_range=True,
80 if not ldap_conn.bind():
85 except LDAPException as e:
86 if not _reconnects > 0:
87 raise LdapServerError(f'Ошибка подключения к серверу: {LdapServerError.err_fmt(e)}')
89 filter = f'(sAMAccountName={login})'
91 if not ldap_conn.search(self.ldap_base_dn, filter):
92 raise LdapServerError('Сервер вернул статус ошибки: %(desc)s(%(code)s) - %(msg)s' % {
93 'desc': ldap_conn.result['description'],
94 'code': ldap_conn.result['result'],
95 'msg': ldap_conn.result['message'],
98 except LDAPException as e:
99 raise LdapServerError(f'Ошибка при поиска УЗ в ldap: {LdapServerError.err_fmt(e)}')
101 if not len(ldap_conn.entries) > 0:
102 raise LdapNotFound(f'Не найдена учётная запись: {login}')
104 elif len(ldap_conn.entries) != 1:
105 _found_names = '; '.join([i.entry_dn for i in ldap_conn.entries[:10]])
107 raise LdapError(f'Найдено более одной УЗ по заданному имени {login}: {len(ldap_conn.entries)} '
110 login_dn = ldap_conn.entries[0].entry_dn
111 new_passwd = self.get_passwd()
113 ldap_conn.extend.microsoft.modify_password(login_dn, new_password=new_passwd, old_password=None)
115 except LDAPException as e:
116 raise LdapServerError('Ошибка при смене пароля на УЗ: %s' % LdapServerError.err_fmt(e))