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