py.lib
2020-07-24
Child:1668cc57225b
py.lib/ldap_utils/ldap_passwd_changer.py
.. Реструктуризация
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/ldap_utils/ldap_passwd_changer.py Fri Jul 24 14:40:37 2020 +0300 1.3 @@ -0,0 +1,111 @@ 1.4 +# coding: utf-8 1.5 +""" 1.6 +Смена пароля на пользователя в AD посредством Python 1.7 + 1.8 +""" 1.9 + 1.10 +from ldap3 import Server, Connection, SIMPLE, SUBTREE 1.11 +from ldap3.core.exceptions import LDAPException 1.12 +from secrets import token_urlsafe 1.13 +import string 1.14 + 1.15 +class LdapError(Exception): 1.16 + @staticmethod 1.17 + def errFmt(e): 1.18 + _buf = ' '.join(str(e).split()) # всё в одну строку с одним пробелом 1.19 + # между словами 1.20 + return "%s(%s)" % (type(e).__name__, _buf) 1.21 + 1.22 +class LdapServerError(LdapError): pass 1.23 +class LdapPassGenError(LdapError): pass 1.24 +class LdapNotFound(LdapError): pass 1.25 + 1.26 + 1.27 +class LdapPasswdChanger(object): 1.28 + def __init__(self, ldap_server_uri, ldap_user, ldap_passwd, ldap_baseDN, ldap_timeout=120, ldap_reconnect=5, passwd_len=25): 1.29 + self.ldap_server_uri = ldap_server_uri # URI к серверу: ldaps://server_name:port_number 1.30 + self.ldap_user = ldap_user # Пользователь, имеющий право сброса паролей 1.31 + self.ldap_passwd = ldap_passwd # Пароль к тому пользователю 1.32 + self.ldap_baseDN = ldap_baseDN 1.33 + self.ldap_timeout = ldap_timeout # Таймаут операций с сервером 1.34 + self.passwd_len = passwd_len # Желаемая длинна пароля 1.35 + self.ldap_reconnect = ldap_reconnect # Количество попыток переподключения к серверу 1.36 + 1.37 + # Заготовка под пароли 1.38 + self._set_punct = set(string.punctuation) 1.39 + self._set_uper = set(string.ascii_uppercase) 1.40 + self._set_lower = set(string.ascii_lowercase) 1.41 + 1.42 + def getPasswd(self): 1.43 + _cnt = 0 1.44 + while True: 1.45 + res = token_urlsafe(self.passwd_len) 1.46 + _res_set = set(res) 1.47 + if ((_res_set & self._set_punct) 1.48 + and (_res_set & self._set_uper) 1.49 + and (_res_set & self._set_lower) 1.50 + ): 1.51 + return res 1.52 + 1.53 + _cnt += 1 1.54 + if _cnt > 60: 1.55 + raise LdapPassGenError('Не удалось сгенерировать пароль') 1.56 + 1.57 + 1.58 + def changePass(self, login): 1.59 + _reconnects = self.ldap_reconnect 1.60 + if _reconnects is None or _reconnects < 1: 1.61 + _reconnects = 1 1.62 + 1.63 + ldapConn = None 1.64 + while True: 1.65 + _reconnects -= 1 1.66 + try: 1.67 + ldapSrv = Server(self.ldap_server_uri, connect_timeout=self.ldap_timeout) 1.68 + ldapConn = Connection(ldapSrv, authentication=SIMPLE, 1.69 + user=self.ldap_user, password=self.ldap_passwd, 1.70 + check_names=True, 1.71 + auto_referrals=False, raise_exceptions=True, auto_range=True, 1.72 + ) 1.73 + ldapConn.open() 1.74 + if not ldapConn.bind(): 1.75 + continue 1.76 + 1.77 + break 1.78 + except LDAPException as e: 1.79 + if not _reconnects > 0: 1.80 + raise LdapServerError('Ошибка подключения к серверу: %s' % LdapServerError.errFmt(e)) 1.81 + 1.82 + filter = '(sAMAccountName=%s)' % login 1.83 + try: 1.84 + if not ldapConn.search(self.ldap_baseDN, filter): 1.85 + raise LdapServerError('Сервер вернул статус ощибки: %(desc)s(%(code)s) - %(msg)s' % { 1.86 + 'desc': ldapConn.result['description'], 1.87 + 'code': ldapConn.result['result'], 1.88 + 'msg': ldapConn.result['message'], 1.89 + }) 1.90 + except LDAPException as e: 1.91 + raise LdapServerError('Ошибка при поиска УЗ в ldap: %s' % LdapServerError.errFmt(e)) 1.92 + 1.93 + if not len(ldapConn.entries) > 0: 1.94 + raise LdapNotFound('Не найдена учётная запись: %s' % login) 1.95 + elif len(ldapConn.entries) != 1: 1.96 + raise LdapError('Наёдено более одной УЗ по заданному имени %s: %s - %s' %( 1.97 + login, 1.98 + len(ldapConn.entries), 1.99 + '; '.join([i.entry_dn for i in ldapConn.entries[:10]]) 1.100 + )) 1.101 + 1.102 + login_dn = ldapConn.entries[0].entry_dn 1.103 + new_passwd = self.getPasswd() 1.104 + try: 1.105 + ldapConn.extend.microsoft.modify_password(login_dn, new_password=new_passwd, old_password=None) 1.106 + except LDAPException as e: 1.107 + raise LdapServerError('Ошибка при смене пароля на УЗ: %s' % LdapServerError.errFmt(e)) 1.108 + 1.109 + return new_passwd 1.110 + 1.111 + 1.112 + 1.113 + 1.114 +