py.lib

Yohn Y. 2020-11-07 Parent:cab7fedf8432 Child:1668cc57225b

15:f3e62028e5d3 Go to Latest

py.lib/ldap_utils/ldap_passwd_changer.py

* Поддержка ограничений целостности по-умолчанию

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@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