|
|
|
|
#-*- coding: utf-8 -*-
|
|
|
|
|
|
|
|
|
|
# Copyright 2008-2010 Calculate Ltd. http://www.calculate-linux.org
|
|
|
|
|
#
|
|
|
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
|
# you may not use this file except in compliance with the License.
|
|
|
|
|
# You may obtain a copy of the License at
|
|
|
|
|
#
|
|
|
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
#
|
|
|
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
|
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
|
# See the License for the specific language governing permissions and
|
|
|
|
|
# limitations under the License.
|
|
|
|
|
|
|
|
|
|
import os
|
|
|
|
|
from ldap import LDAPError, MOD_REPLACE, MOD_ADD, SCOPE_SUBTREE, SCOPE_ONELEVEL
|
|
|
|
|
from ldif import LDIFParser, LDIFWriter
|
|
|
|
|
import cStringIO, StringIO
|
|
|
|
|
from cl_utils import _error
|
|
|
|
|
from cl_print import color_print
|
|
|
|
|
from cl_template import template, iniParser
|
|
|
|
|
from cl_ldap import ldapFun
|
|
|
|
|
from server.share import shareVars
|
|
|
|
|
from server.utils import genSleep, stringIsJpeg
|
|
|
|
|
# Перевод модуля
|
|
|
|
|
import cl_lang
|
|
|
|
|
tr = cl_lang.lang()
|
|
|
|
|
tr.setLocalDomain('cl_lib')
|
|
|
|
|
tr.setLanguage(sys.modules[__name__])
|
|
|
|
|
|
|
|
|
|
def adminConnectLdap(fun):
|
|
|
|
|
"""Cоединение с LDAP администратором сервиса (декоратор)
|
|
|
|
|
|
|
|
|
|
соединение с LDAP и проверка установки необходимых переменных
|
|
|
|
|
"""
|
|
|
|
|
def ret (self, *arg, **argv):
|
|
|
|
|
flagError = False
|
|
|
|
|
if not self.clVars:
|
|
|
|
|
self.createClVars()
|
|
|
|
|
if not self.ldapObj:
|
|
|
|
|
if not self.getLdapObjInFile():
|
|
|
|
|
flagError = True
|
|
|
|
|
if not self.baseDN:
|
|
|
|
|
if self.clVars.defined("ld_base_dn"):
|
|
|
|
|
self.baseDN = self.clVars.Get("ld_base_dn")
|
|
|
|
|
if not self.baseDN:
|
|
|
|
|
self.printERROR (_('Not found LDAP base DN'))
|
|
|
|
|
if flagError:
|
|
|
|
|
return False
|
|
|
|
|
else:
|
|
|
|
|
return fun(self, *arg , **argv)
|
|
|
|
|
return ret
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class addLdif(LDIFParser):
|
|
|
|
|
"""Класс необходимый для добавления записей в LDAP"""
|
|
|
|
|
def __init__(self, strInput,ldapCon):
|
|
|
|
|
FD = cStringIO.StringIO(strInput)
|
|
|
|
|
LDIFParser.__init__(self, FD)
|
|
|
|
|
self.ldapCon = ldapCon
|
|
|
|
|
|
|
|
|
|
def handle(self, dn, entry):
|
|
|
|
|
self.ldapCon.add_s(dn, entry.items())
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ldapFunction(ldapFun):
|
|
|
|
|
'''Объект для работы с LDAP сервером'''
|
|
|
|
|
def __init__(self, dnUser, password):
|
|
|
|
|
ldapFun.__init__(self, dnUser, password)
|
|
|
|
|
|
|
|
|
|
def ldapAdd(self, strLdif):
|
|
|
|
|
"""Добавляем строку содержащую ldif в LDAP
|
|
|
|
|
|
|
|
|
|
Если данные существуют - ошибка
|
|
|
|
|
"""
|
|
|
|
|
if self.conLdap:
|
|
|
|
|
try:
|
|
|
|
|
# Записываем параметры из ldif файла в LDAP сервер
|
|
|
|
|
parser = addLdif(strLdif,self.conLdap)
|
|
|
|
|
parser.parse()
|
|
|
|
|
except LDAPError, e:
|
|
|
|
|
self.setError(e[0]['desc'])
|
|
|
|
|
return False
|
|
|
|
|
except:
|
|
|
|
|
self.setError("Error in ldif file")
|
|
|
|
|
return False
|
|
|
|
|
return True
|
|
|
|
|
else:
|
|
|
|
|
self.setError(_("No connect to LDAP server"))
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class iniLdapParser(iniParser):
|
|
|
|
|
"""Класс для работы c ini-файлом ldap"""
|
|
|
|
|
def __init__(self):
|
|
|
|
|
# название ini файла
|
|
|
|
|
self.nameIniFile = "/etc/calculate/calculate.ldap"
|
|
|
|
|
iniParser.__init__(self, self.nameIniFile)
|
|
|
|
|
# права создаваемого ini-файла
|
|
|
|
|
self.setMode(0600)
|
|
|
|
|
pathIniFile = os.path.split(self.nameIniFile)[0]
|
|
|
|
|
if not os.path.exists(pathIniFile):
|
|
|
|
|
os.makedirs(pathIniFile)
|
|
|
|
|
|
|
|
|
|
class shareldap(_error, color_print, shareVars):
|
|
|
|
|
"""Общие методы для работы с LDAP для серверных программ"""
|
|
|
|
|
|
|
|
|
|
# DN сервисов относительно базового
|
|
|
|
|
ServicesDN = "ou=Services"
|
|
|
|
|
# Переменная объект Vars
|
|
|
|
|
clVars = False
|
|
|
|
|
# Переменная объект ldapFunction
|
|
|
|
|
ldapObj = False
|
|
|
|
|
# Переменная соединение с LDAP сервером
|
|
|
|
|
conLdap = False
|
|
|
|
|
# Базовый DN LDAP сервера
|
|
|
|
|
baseDN = False
|
|
|
|
|
|
|
|
|
|
def addDN(self, *arg):
|
|
|
|
|
"""Складывает текстовые элементы DN"""
|
|
|
|
|
DNs = []
|
|
|
|
|
for dn in arg:
|
|
|
|
|
if dn:
|
|
|
|
|
DNs.append(dn)
|
|
|
|
|
return ','.join(DNs)
|
|
|
|
|
|
|
|
|
|
@adminConnectLdap
|
|
|
|
|
def searchLdapDN(self, name, relDN, attr, retAttr=None):
|
|
|
|
|
"""Находит DN в LDAP"""
|
|
|
|
|
DN = self.addDN(relDN,self.baseDN)
|
|
|
|
|
#searchScope = SCOPE_SUBTREE
|
|
|
|
|
searchScope = SCOPE_ONELEVEL
|
|
|
|
|
searchFilter = "%s=%s" %(attr,name)
|
|
|
|
|
retrieveAttributes = retAttr
|
|
|
|
|
resSearch = self.ldapObj.ldapSearch(DN, searchScope,
|
|
|
|
|
searchFilter, retrieveAttributes)
|
|
|
|
|
return resSearch
|
|
|
|
|
|
|
|
|
|
@adminConnectLdap
|
|
|
|
|
def addEntry(self, DN, entry, errorMessage):
|
|
|
|
|
"""Добавление узла в LDAP"""
|
|
|
|
|
try:
|
|
|
|
|
self.conLdap.add_s(DN, entry)
|
|
|
|
|
except LDAPError, e:
|
|
|
|
|
self.printERROR(_("LDAP Error") + ": " + e[0]['desc'].strip())
|
|
|
|
|
self.printERROR(errorMessage)
|
|
|
|
|
return False
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
def getLdapObjInFile(self, part="admin"):
|
|
|
|
|
"""Получаем объект ldapFunction из ini файла
|
|
|
|
|
|
|
|
|
|
В выходном объекте есть соединение с LDAP сервером: self.conLdap
|
|
|
|
|
"""
|
|
|
|
|
# Если раннее была ошибка то выходим
|
|
|
|
|
if self.getError():
|
|
|
|
|
self.printERROR (_("ERROR") + ": " +\
|
|
|
|
|
self.getError().strip())
|
|
|
|
|
return False
|
|
|
|
|
ldapParser = iniLdapParser()
|
|
|
|
|
adminDn = ldapParser.getVar(part,"DN")
|
|
|
|
|
adminPw = ldapParser.getVar(part,"PASS")
|
|
|
|
|
if not (adminDn or adminPw):
|
|
|
|
|
if part == "admin":
|
|
|
|
|
service = "LDAP"
|
|
|
|
|
else:
|
|
|
|
|
service = part
|
|
|
|
|
self.printERROR(\
|
|
|
|
|
_("Admin password for the service %s could not be found")%service)
|
|
|
|
|
return False
|
|
|
|
|
ldapObj = ldapFunction(adminDn, adminPw)
|
|
|
|
|
# Генератор задержек
|
|
|
|
|
wait = genSleep()
|
|
|
|
|
while ldapObj.getError():
|
|
|
|
|
try:
|
|
|
|
|
# Задержка
|
|
|
|
|
wait.next()
|
|
|
|
|
except StopIteration:
|
|
|
|
|
break
|
|
|
|
|
# Очистка ошибки
|
|
|
|
|
_error.error = []
|
|
|
|
|
ldapObj = ldapFunction(adminDn, adminPw)
|
|
|
|
|
if ldapObj.getError():
|
|
|
|
|
# Удаляем одинаковые ошибки
|
|
|
|
|
listError = []
|
|
|
|
|
for e in ldapObj.error:
|
|
|
|
|
if not e in listError:
|
|
|
|
|
listError.append(e)
|
|
|
|
|
_error.error = listError
|
|
|
|
|
self.printERROR (_("LDAP connect error") + ": " +\
|
|
|
|
|
ldapObj.getError().strip())
|
|
|
|
|
return False
|
|
|
|
|
# Устанавливаем у объекта соединение и объект LDAP функций
|
|
|
|
|
self.ldapObj = ldapObj
|
|
|
|
|
self.conLdap = ldapObj.conLdap
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
def connectToLDAP(self, adminDn, adminPw):
|
|
|
|
|
"""Подключаемся к LDAP - для внешних программ запускающихся не от root
|
|
|
|
|
"""
|
|
|
|
|
ldapObj = ldapFunction(adminDn, adminPw)
|
|
|
|
|
# Генератор задержек
|
|
|
|
|
wait = genSleep()
|
|
|
|
|
while ldapObj.getError():
|
|
|
|
|
try:
|
|
|
|
|
# Задержка
|
|
|
|
|
wait.next()
|
|
|
|
|
except StopIteration:
|
|
|
|
|
break
|
|
|
|
|
# Очистка ошибки
|
|
|
|
|
_error.error = []
|
|
|
|
|
ldapObj = ldapFunction(adminDn, adminPw)
|
|
|
|
|
if ldapObj.getError():
|
|
|
|
|
# Удаляем одинаковые ошибки
|
|
|
|
|
listError = []
|
|
|
|
|
for e in ldapObj.error:
|
|
|
|
|
if not e in listError:
|
|
|
|
|
listError.append(e)
|
|
|
|
|
_error.error = listError
|
|
|
|
|
self.printERROR (_("LDAP connect error") + ": " +\
|
|
|
|
|
ldapObj.getError().strip())
|
|
|
|
|
return False
|
|
|
|
|
# Устанавливаем у объекта соединение и объект LDAP функций
|
|
|
|
|
self.ldapObj = ldapObj
|
|
|
|
|
self.conLdap = ldapObj.conLdap
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
@adminConnectLdap
|
|
|
|
|
def modAttrsDN(self, relDN, modAttrs):
|
|
|
|
|
"""Модифицирует аттрибуты DN"""
|
|
|
|
|
DN = self.addDN(relDN,self.baseDN)
|
|
|
|
|
if modAttrs:
|
|
|
|
|
try:
|
|
|
|
|
self.conLdap.modify_s(DN, modAttrs)
|
|
|
|
|
except LDAPError, e:
|
|
|
|
|
self.printERROR(e[0]['desc'])
|
|
|
|
|
return False
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
@adminConnectLdap
|
|
|
|
|
def modifyElemDN(self, relDN, newFirstDn):
|
|
|
|
|
"""Изменяет основной элемент DN (uid, cn и др.)"""
|
|
|
|
|
DN = self.addDN(relDN,self.baseDN)
|
|
|
|
|
try:
|
|
|
|
|
self.conLdap.modrdn_s(DN, newFirstDn)
|
|
|
|
|
except LDAPError, e:
|
|
|
|
|
self.printERROR(e[0]['desc'])
|
|
|
|
|
return False
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
def getMaxAttrDN(self, relDN, name, attr, numMin, numMax, attrSearch):
|
|
|
|
|
"""Находит максимальный добавленный аттрибут в LDAP DN"""
|
|
|
|
|
resSearch = self.searchLdapDN(name, relDN, attr, [attrSearch])
|
|
|
|
|
lst = []
|
|
|
|
|
lst.append(0)
|
|
|
|
|
if resSearch:
|
|
|
|
|
for scope in resSearch:
|
|
|
|
|
if scope[0][1].has_key(attrSearch):
|
|
|
|
|
uid = int(scope[0][1][attrSearch][0])
|
|
|
|
|
if uid<=numMax and uid>=numMin:
|
|
|
|
|
lst.append(uid)
|
|
|
|
|
return max(lst)
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
def createLdif(self, ldifFile):
|
|
|
|
|
"""Cоздает ldif из ldif - шаблона"""
|
|
|
|
|
if not os.access(ldifFile, os.F_OK):
|
|
|
|
|
self.setError(_("File not found") + ":\n " + ldifFile)
|
|
|
|
|
return False
|
|
|
|
|
FD = open (ldifFile)
|
|
|
|
|
ldifTemplate = FD.read()
|
|
|
|
|
FD.close()
|
|
|
|
|
clTempl = template(self.clVars)
|
|
|
|
|
# Применяем условия к шаблону
|
|
|
|
|
ldifTemplate = clTempl.applyTermsTemplate(ldifTemplate, ldifFile)
|
|
|
|
|
# Заменяем переменные в шаблоне
|
|
|
|
|
ldifTemplate = clTempl.applyVarsTemplate(ldifTemplate, ldifFile)
|
|
|
|
|
return ldifTemplate
|
|
|
|
|
|
|
|
|
|
@adminConnectLdap
|
|
|
|
|
def delDN(self, relDN):
|
|
|
|
|
"""Удаляет одиночный DN"""
|
|
|
|
|
DN = self.addDN(relDN,self.baseDN)
|
|
|
|
|
try:
|
|
|
|
|
self.conLdap.delete_s(DN)
|
|
|
|
|
except LDAPError, e:
|
|
|
|
|
self.printERROR(e[0]['desc'])
|
|
|
|
|
return False
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
@adminConnectLdap
|
|
|
|
|
def deleteDN(self, relDelDN):
|
|
|
|
|
"""Удаляет DN и все внутренние элементы"""
|
|
|
|
|
delDN = self.addDN(relDelDN, self.baseDN)
|
|
|
|
|
delListDN=[]
|
|
|
|
|
try:
|
|
|
|
|
dnList = self.conLdap.search_s(delDN,
|
|
|
|
|
SCOPE_SUBTREE,
|
|
|
|
|
'(objectclass=*)',
|
|
|
|
|
[''])
|
|
|
|
|
except LDAPError, e:
|
|
|
|
|
self.printERROR("deleteDN: "+e[0]['desc'])
|
|
|
|
|
return False
|
|
|
|
|
for dn, f in dnList:
|
|
|
|
|
delListDN.append(dn)
|
|
|
|
|
delListDN.sort(lambda x, y: cmp(len(y), len(x)))
|
|
|
|
|
for dn in delListDN:
|
|
|
|
|
try:
|
|
|
|
|
self.conLdap.delete_s(dn)
|
|
|
|
|
except LDAPError, e:
|
|
|
|
|
self.printERROR("deleteDN: "+e[0]['desc'])
|
|
|
|
|
return False
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
@adminConnectLdap
|
|
|
|
|
def fullElementDNtoText(self, relDN="", ldapFilter='(objectclass=*)'):
|
|
|
|
|
"""Выводит все внутренние элементы DN виде текста"""
|
|
|
|
|
DN = self.addDN(relDN, self.baseDN)
|
|
|
|
|
listDN=[]
|
|
|
|
|
try:
|
|
|
|
|
dnList = self.conLdap.search_s(DN,
|
|
|
|
|
SCOPE_SUBTREE,
|
|
|
|
|
ldapFilter,None)
|
|
|
|
|
except LDAPError, e:
|
|
|
|
|
self.printERROR("fullElementDN: "+e[0]['desc'])
|
|
|
|
|
return False
|
|
|
|
|
FDOUT = StringIO.StringIO("")
|
|
|
|
|
writer = LDIFWriter(FDOUT)
|
|
|
|
|
for dn, f in dnList:
|
|
|
|
|
writer.unparse(dn, f)
|
|
|
|
|
FDOUT.seek(0)
|
|
|
|
|
return FDOUT.read()
|
|
|
|
|
|
|
|
|
|
@adminConnectLdap
|
|
|
|
|
def fullElementSambaDNtoText(self, relDN=""):
|
|
|
|
|
"""Выводит все внутренние элементы ветки Samba в виде текста"""
|
|
|
|
|
return self.fullElementDNtoText(relDN,'(|(|(|(|(ou:dn:=Samba)\
|
|
|
|
|
(ou:dn:=Unix))(ou:dn:=LDAP))(!(ou:dn:=Services)))(ou=Services))')
|
|
|
|
|
|
|
|
|
|
@adminConnectLdap
|
|
|
|
|
def fullElementUnixDNtoText(self, relDN=""):
|
|
|
|
|
"""Выводит все внутренние элементы ветки Unix в виде текста"""
|
|
|
|
|
return self.fullElementDNtoText(relDN,'(|(|(|(ou:dn:=Unix)\
|
|
|
|
|
(ou:dn:=LDAP))(!(ou:dn:=Services)))(ou=Services))')
|
|
|
|
|
|
|
|
|
|
@adminConnectLdap
|
|
|
|
|
def fullElementMailDNtoText(self, relDN=""):
|
|
|
|
|
"""Выводит все внутренние элементы ветки Mail в виде текста"""
|
|
|
|
|
baseDN = self.clVars.Get("ld_base_dn")
|
|
|
|
|
baseDNName, baseLogin = baseDN.split(",")[0].split("=")
|
|
|
|
|
proxyDN = self.clVars.Get("ld_bind_dn")
|
|
|
|
|
proxyDNName, proxyLogin = proxyDN.split(",")[0].split("=")
|
|
|
|
|
#return self.fullElementDNtoText(relDN,'(&(|(|(&(ou:dn:=Replication)\
|
|
|
|
|
#(ou:dn:=Mail))(!(ou:dn:=Services)))(ou=Services))(!(&(%s:dn:=%s)\
|
|
|
|
|
#(%s:dn:=%s))))'%(proxyDNName, proxyLogin, baseDNName, baseLogin))
|
|
|
|
|
return self.fullElementDNtoText(relDN,'(&(&(|(|(|(ou:dn:=LDAP)\
|
|
|
|
|
(ou=Mail))(!(ou:dn:=Services)))(ou=Services))(!(&(%s:dn:=%s)(%s:dn:=%s))))\
|
|
|
|
|
(!(ou:dn:=Worked)))'%(proxyDNName, proxyLogin, baseDNName, baseLogin))
|
|
|
|
|
|
|
|
|
|
@adminConnectLdap
|
|
|
|
|
def fullElementMailSambaDNtoText(self, relDN=""):
|
|
|
|
|
"""Выводит все внутренние элементы ветки Samba и Mail в виде текста"""
|
|
|
|
|
return self.fullElementDNtoText(relDN,'(&(|(|(|(|(|(ou:dn:=Samba)\
|
|
|
|
|
(ou:dn:=Unix))(ou:dn:=LDAP))(ou:dn:=Mail))(!(ou:dn:=Services)))(ou=Services))\
|
|
|
|
|
(!(|(&(&(ou:dn:=Users)(ou:dn:=Mail))(uid=*))(&(&(ou:dn:=Groups)(ou:dn:=Mail))\
|
|
|
|
|
(cn=*)))))')
|
|
|
|
|
|
|
|
|
|
@adminConnectLdap
|
|
|
|
|
def fullElementMailUnixDNtoText(self, relDN=""):
|
|
|
|
|
"""Выводит все внутренние элементы ветки Unix и Mail в виде текста"""
|
|
|
|
|
return self.fullElementDNtoText(relDN,'(&(|(|(|(|(ou:dn:=Unix)\
|
|
|
|
|
(ou:dn:=LDAP))(ou:dn:=Mail))(!(ou:dn:=Services)))(ou=Services))\
|
|
|
|
|
(!(|(&(&(ou:dn:=Users)(ou:dn:=Mail))(uid=*))(&(&(ou:dn:=Groups)(ou:dn:=Mail))\
|
|
|
|
|
(cn=*)))))')
|
|
|
|
|
|
|
|
|
|
def setJpegPhotoUser(self, userName, photoPath, attr="uid"):
|
|
|
|
|
"""Добавляем jpeg фотографию пользователя в LDAP"""
|
|
|
|
|
import subprocess
|
|
|
|
|
try:
|
|
|
|
|
FD = open(photoPath)
|
|
|
|
|
photoData = FD.read()
|
|
|
|
|
FD.close()
|
|
|
|
|
except:
|
|
|
|
|
self.printERROR(_("Not open file") + ": " + str(photoPath))
|
|
|
|
|
return False
|
|
|
|
|
searchUser = self.searchLdapDN(userName, self.relUsersDN, attr)
|
|
|
|
|
if not searchUser:
|
|
|
|
|
self.printERROR(_("User") + " " + str(userName) + " "+\
|
|
|
|
|
_("not found"))
|
|
|
|
|
return False
|
|
|
|
|
modAttrs = []
|
|
|
|
|
if not stringIsJpeg(photoData):
|
|
|
|
|
flagError = False
|
|
|
|
|
pipe = subprocess.Popen("convert '%s' jpg:-" %photoPath,
|
|
|
|
|
stdin=subprocess.PIPE,
|
|
|
|
|
stdout=subprocess.PIPE,
|
|
|
|
|
stderr=subprocess.PIPE, close_fds=True,
|
|
|
|
|
shell=True)
|
|
|
|
|
fOut, fIn, fErr = (pipe.stdout, pipe.stdin, pipe.stderr)
|
|
|
|
|
fIn.close()
|
|
|
|
|
# Код возврата
|
|
|
|
|
retcode = pipe.wait()
|
|
|
|
|
if retcode != 0:
|
|
|
|
|
self.printERROR(_("Can not convert file '%s' in jpeg format")\
|
|
|
|
|
%photoPath)
|
|
|
|
|
flagError = True
|
|
|
|
|
fErr.close()
|
|
|
|
|
if not flagError:
|
|
|
|
|
photoData = fOut.read()
|
|
|
|
|
if not stringIsJpeg(photoData):
|
|
|
|
|
self.printERROR(\
|
|
|
|
|
_("Can not convert file '%s' in jpeg format") %photoPath)
|
|
|
|
|
flagError = True
|
|
|
|
|
fOut.close()
|
|
|
|
|
if flagError:
|
|
|
|
|
return False
|
|
|
|
|
if searchUser[0][0][1].has_key('jpegPhoto'):
|
|
|
|
|
modAttrs.append((MOD_REPLACE, 'jpegPhoto', photoData))
|
|
|
|
|
else:
|
|
|
|
|
modAttrs.append((MOD_ADD, 'jpegPhoto', photoData))
|
|
|
|
|
userDN = self.addDN("%s=%s"%(attr,userName),self.relUsersDN)
|
|
|
|
|
if not self.modAttrsDN(userDN, modAttrs):
|
|
|
|
|
return False
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
def searchService(self):
|
|
|
|
|
"""Поиск DN сервиса"""
|
|
|
|
|
name, value = self.relServDN.split('=')
|
|
|
|
|
resSearch = self.searchLdapDN(value, self.ServicesDN, name)
|
|
|
|
|
return resSearch
|