|
|
#-*- coding: utf-8 -*-
|
|
|
|
|
|
#Copyright 2008 Calculate Pack, http://www.calculate-linux.ru
|
|
|
#
|
|
|
# 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
|
|
|
import re
|
|
|
import sys
|
|
|
|
|
|
import cl_base
|
|
|
import cl_profile
|
|
|
import cl_utils2
|
|
|
import cl_utils
|
|
|
|
|
|
import ldap
|
|
|
|
|
|
Version = "calculate-client 0.0.1"
|
|
|
|
|
|
tr = cl_base.lang()
|
|
|
tr.setLanguage(sys.modules[__name__])
|
|
|
|
|
|
pcs = cl_utils.prettyColumnStr
|
|
|
|
|
|
# Импортированные классы в cl_ldap
|
|
|
# Запись ошибок
|
|
|
imp_cl_err = cl_profile._error
|
|
|
# Работа с XML
|
|
|
imp_cl_xml = cl_profile.xmlShare
|
|
|
# Обработка параметров командной строки
|
|
|
imp_cl_help = cl_utils2.cl_help
|
|
|
# Форматированный вывод
|
|
|
imp_cl_smcon = cl_utils2.cl_smartcon
|
|
|
|
|
|
class cl_client(imp_cl_err, imp_cl_xml, imp_cl_help, imp_cl_smcon):
|
|
|
"""Основной класс для работы с LDAP"""
|
|
|
def __init__(self, cmdName):
|
|
|
# объект для форматированного вывода
|
|
|
imp_cl_help.__init__(self, cmdName)
|
|
|
|
|
|
servName = ""
|
|
|
if "user" in cmdName:
|
|
|
servName = _("user")
|
|
|
|
|
|
self.chapter = [\
|
|
|
# расположение разделов на странице
|
|
|
# имя раздела, видимый или невидимый, кол. "\n" после
|
|
|
# названия раздела, кол. "\n" после раздела
|
|
|
# тип раздела
|
|
|
("Copyright",False,0,2,""),
|
|
|
(_("Usage"),True,0,1,""),
|
|
|
("Function",False,0,2,""),
|
|
|
(_("Examples"),True,1,1,""),
|
|
|
(_("Common options"),True,1,0,"options"),
|
|
|
]
|
|
|
|
|
|
# имена используемых программ и их номера для доступа к переменным
|
|
|
# self.data
|
|
|
self.progName = { 'cl-client':0,
|
|
|
'cl-createhome':1
|
|
|
}
|
|
|
|
|
|
# Cвязь длинных опций помощи и выводимых разделов помощи с опциями
|
|
|
self.relOptions = {"h":[_("Common options")],}
|
|
|
|
|
|
# список разделов, которые на наличие в ней информации
|
|
|
# используется для автоматического отображения/скрытия
|
|
|
# опций help-имя
|
|
|
# Пример: self.relOption =
|
|
|
# { "help-all":[_("Common options"], _("Unix service options"),
|
|
|
# _("Samba service options"), _("LDAP service options")}]
|
|
|
# self.relChapterPass = (_("Common options"),)
|
|
|
# это означается что опция будет активна, если только в разделах
|
|
|
# кроме Common options есть хоть одна доступная опция.
|
|
|
self.relChapterPass = (_("Common options"),)
|
|
|
|
|
|
self.data = [\
|
|
|
{
|
|
|
#Copyright
|
|
|
#'progAccess':(3,),
|
|
|
'helpChapter':"Copyright",
|
|
|
'help':Version
|
|
|
},
|
|
|
#Usage
|
|
|
{
|
|
|
'progAccess':(0,),
|
|
|
'helpChapter':_("Usage"),
|
|
|
'help': cmdName + " [" + _("options") + "] " + _("user")
|
|
|
},
|
|
|
{
|
|
|
'progAccess':(1,),
|
|
|
'helpChapter':_("Usage"),
|
|
|
'help': cmdName + " " + _("user")
|
|
|
},
|
|
|
# Function
|
|
|
{
|
|
|
'progAccess':(0,),
|
|
|
'helpChapter':"Function",
|
|
|
'help':_("Client for the calculate-server")
|
|
|
},
|
|
|
# Examples
|
|
|
{
|
|
|
'progAccess':(0,),
|
|
|
'helpChapter':_("Examples"),
|
|
|
'help':pcs( " cl-client -m test", self.column_width,
|
|
|
"# " + _("mounts a test user directory on the server \
|
|
|
calculate-server"),
|
|
|
self.consolewidth-self.column_width )
|
|
|
},
|
|
|
# Options
|
|
|
{'shortOption':"h",
|
|
|
'longOption':"help",
|
|
|
'helpChapter':_("Common options"),
|
|
|
'help':_("display this help and exit")
|
|
|
},
|
|
|
{'progAccess':(0,),
|
|
|
'shortOption':"m",
|
|
|
'longOption':"create-home",
|
|
|
'helpChapter':_("Common options"),
|
|
|
'help':_("create home directory for the new user account")
|
|
|
},
|
|
|
{'progAccess':(1,),
|
|
|
'longOption':"vars",
|
|
|
'optVal':_("TYPE_VAR"),
|
|
|
'helpChapter':_("Common options"),
|
|
|
'help':_("print variables (TYPE_VAR - all:full var)")
|
|
|
},
|
|
|
{'progAccess':(0,),
|
|
|
'shortOption':"s",
|
|
|
'longOption':"cl-server",
|
|
|
'optVal':_("CL_SERVER"),
|
|
|
'helpChapter':_("Common options"),
|
|
|
'help':_("name or ip calculate server")
|
|
|
},
|
|
|
{'progAccess':(0,),
|
|
|
'shortOption':"p",
|
|
|
'longOption':"prvar",
|
|
|
'optVal':_("TYPES_VAR"),
|
|
|
'helpChapter':_("Common options"),
|
|
|
'help':_("print variable (filter type - comma delimited)")
|
|
|
},
|
|
|
|
|
|
]
|
|
|
|
|
|
self._cl_help__setParamHelp()
|
|
|
|
|
|
# Удаляем ненужный аттрибут класса cl_profile.xmlShare
|
|
|
self._createElement = False
|
|
|
delattr(self, "_createElement")
|
|
|
|
|
|
# Переменная объект Vars
|
|
|
self.clVars = False
|
|
|
# Переменная объект ldapFunction
|
|
|
self.ldapObj = False
|
|
|
# Переменная соединение с LDAP сервером
|
|
|
self.conLdap = False
|
|
|
# Базовый DN LDAP сервера
|
|
|
self.baseDN = False
|
|
|
# DN сервисов относительно базового
|
|
|
self.ServicesDN = "ou=Services"
|
|
|
|
|
|
self.relUsDN = 'ou=Users'
|
|
|
self.relServDN = 'ou=Unix'
|
|
|
self.relDN = self.addDN(self.relServDN,self.ServicesDN)
|
|
|
# DN пользователей, относительно базового DN
|
|
|
self.relUsersDN = self.addDN(self.relUsDN, self.relDN)
|
|
|
# Имя пользователя
|
|
|
userName = os.environ['USER']
|
|
|
self.userName = userName.lower()
|
|
|
# Объект хранения переменных
|
|
|
self.clVars = False
|
|
|
|
|
|
|
|
|
def createClVars(self, clVars=False):
|
|
|
"""Создает объект Vars"""
|
|
|
if not clVars:
|
|
|
clVars = cl_base.DataVars()
|
|
|
clVars.flClient()
|
|
|
clVars.flIniFile()
|
|
|
# Устанавливаем у объекта объект Vars
|
|
|
self.clVars = clVars
|
|
|
return True
|
|
|
|
|
|
def addDN(self, *arg):
|
|
|
"""Складывает текстовые элементы DN"""
|
|
|
DNs = []
|
|
|
for dn in arg:
|
|
|
if dn:
|
|
|
DNs.append(dn)
|
|
|
return ','.join(DNs)
|
|
|
|
|
|
def searchLdapDN(self, name, relDN, attr, retAttr=None):
|
|
|
"""Находит DN в LDAP"""
|
|
|
baseDN = self.clVars.Get("ld_base_dn")
|
|
|
DN = self.addDN(relDN,baseDN)
|
|
|
#searchScope = ldap.SCOPE_SUBTREE
|
|
|
searchScope = ldap.SCOPE_ONELEVEL
|
|
|
searchFilter = "%s=%s" %(attr,name)
|
|
|
retrieveAttributes = retAttr
|
|
|
resSearch = self.ldapObj.ldapSearch(DN, searchScope,
|
|
|
searchFilter, retrieveAttributes)
|
|
|
return resSearch
|
|
|
|
|
|
|
|
|
def searchUnixUser(self, userName):
|
|
|
"""Находит пользователя сервиса Unix"""
|
|
|
resSearch = self.searchLdapDN(userName, self.relUsersDN, "uid")
|
|
|
return resSearch
|
|
|
|
|
|
def getLdapObjBind(self, host):
|
|
|
"""Получаем объект ldapFunction
|
|
|
|
|
|
Соединяемся пользователем bind
|
|
|
В выходном объекте есть соединение с LDAP сервером: self.conLdap
|
|
|
"""
|
|
|
self.createClVars(self.clVars)
|
|
|
bindDn = self.clVars.Get("ld_bind_dn")
|
|
|
bindPw = self.clVars.Get("ld_bind_pw")
|
|
|
if not (bindDn or bindPw):
|
|
|
self.printERROR(_("not found LDAP bind DN or password") + " ...")
|
|
|
return False
|
|
|
ldapObj = cl_utils2.ldapFun(bindDn, bindPw, host)
|
|
|
if ldapObj.getError():
|
|
|
self.printERROR (_("LDAP connect error") + ": " +\
|
|
|
ldapObj.getError().strip())
|
|
|
return False
|
|
|
# Устанавливаем у объекта соединение и объект LDAP функций
|
|
|
self.ldapObj = ldapObj
|
|
|
self.conLdap = ldapObj.conLdap
|
|
|
return True
|
|
|
|
|
|
def getUserMail(self, userName):
|
|
|
"""Выдаем основной почтовый адрес"""
|
|
|
searchUser = self.searchUnixUser(userName)
|
|
|
if not searchUser:
|
|
|
self.printERROR(_("User %s not found in Unix service")\
|
|
|
%str(userName))
|
|
|
return False
|
|
|
if searchUser[0][0][1].has_key('mail'):
|
|
|
return searchUser[0][0][1]['mail'][0]
|
|
|
else:
|
|
|
return ""
|
|
|
|
|
|
def searchLineInFile(self, name, fileName, numEl=0):
|
|
|
"""Ищет строку в которой есть название разделенное ':'
|
|
|
|
|
|
в файле похожем на /etc/passwd"""
|
|
|
if os.path.exists(fileName):
|
|
|
FD = open(fileName)
|
|
|
lines = FD.readlines()
|
|
|
FD.close()
|
|
|
lineFound = ""
|
|
|
for line in lines:
|
|
|
if name == line.split(":")[numEl]:
|
|
|
lineFound = line
|
|
|
break
|
|
|
if lineFound:
|
|
|
return lineFound
|
|
|
else:
|
|
|
return False
|
|
|
|
|
|
def searchPasswdUser(self, userName):
|
|
|
"""Ищет пользователей в /etc/passwd"""
|
|
|
filePasswd = "/etc/passwd"
|
|
|
return self.searchLineInFile(userName, filePasswd)
|
|
|
|
|
|
def getUserUidGid(self, userName):
|
|
|
"""Выдаем uid и gid пользователя"""
|
|
|
searchUser = self.searchUnixUser(userName)
|
|
|
if not searchUser:
|
|
|
self.printERROR(_("User %s not found in Unix service")\
|
|
|
%str(userName))
|
|
|
return False
|
|
|
if searchUser[0][0][1].has_key('uidNumber') and\
|
|
|
searchUser[0][0][1].has_key('gidNumber'):
|
|
|
return (searchUser[0][0][1]['uidNumber'][0],
|
|
|
searchUser[0][0][1]['gidNumber'][0])
|
|
|
else:
|
|
|
return ()
|
|
|
|
|
|
def createUserDir(self, uid, gid, userDir, mode=0700):
|
|
|
"""Создание пользовательской директории"""
|
|
|
if not os.path.exists(userDir):
|
|
|
os.makedirs(userDir)
|
|
|
if mode:
|
|
|
os.chmod(userDir,mode)
|
|
|
os.chown(userDir,uid,gid)
|
|
|
return True
|
|
|
else:
|
|
|
self.printERROR(_("Path %s exists") %userDir)
|
|
|
return False
|
|
|
|
|
|
def applyProfilesFromUser(self):
|
|
|
"""Применяем профили для пользователя"""
|
|
|
# Cоздаем объект профиль
|
|
|
clProf = cl_profile.profile(self.clVars)
|
|
|
# Объединяем профили
|
|
|
clProf.applyProfiles()
|
|
|
if clProf.getError():
|
|
|
self.printERROR(clProf.getError())
|
|
|
return False
|
|
|
else:
|
|
|
return True
|
|
|
|
|
|
def chownR(self, directory, uid, gid):
|
|
|
"""изменяет владельца и группу
|
|
|
|
|
|
для всех файлов и директорий внутри directory
|
|
|
"""
|
|
|
fileObj = cl_profile._file()
|
|
|
scanObjs = fileObj.scanDirs([directory])
|
|
|
# меняем владельца домашней директории
|
|
|
os.chown(directory, uid,gid)
|
|
|
# Меняем владельца директорий
|
|
|
for dirCh in scanObjs[0].dirs:
|
|
|
os.chown(dirCh, uid,gid)
|
|
|
# Меняем владельца файлов
|
|
|
for fileCh in scanObjs[0].files:
|
|
|
os.chown(fileCh, uid,gid)
|
|
|
# Меняем владельца ссылок
|
|
|
for linkCh in scanObjs[0].links:
|
|
|
os.lchown(linkCh[1], uid, gid)
|
|
|
return True
|
|
|
|
|
|
def execProg(self, cmdStrProg, inStr=False, retFull=True):
|
|
|
"""Выполняет внешнюю программу
|
|
|
|
|
|
Параметры:
|
|
|
cmdStrProg внешняя программа
|
|
|
inStr данные передаваемые программе на страндартный вход.
|
|
|
Возвращаемые параметры:
|
|
|
строка которую выведет внешняя программа
|
|
|
"""
|
|
|
return cl_utils.runOsCommand(cmdStrProg, inStr, retFull)
|
|
|
|
|
|
#def getUidAndGidUser(self, userName):
|
|
|
#strRes = self.execProg("id %s" %userName)
|
|
|
#reFind = re.compile("uid=(\d+)\(.+gid=(\d+)\(")
|
|
|
#res = reFind.search(strRes)
|
|
|
#if res:
|
|
|
#return res.group(1), res.group(2)
|
|
|
#else:
|
|
|
#self.printERROR(_("User %s not found")\
|
|
|
#%str(userName))
|
|
|
#return False
|
|
|
|
|
|
def getUidAndGidUser(self, userName):
|
|
|
"""получаем uid и gid пользователя из /etc/passwd"""
|
|
|
resPasswd = self.searchPasswdUser(userName)
|
|
|
if resPasswd:
|
|
|
uid = resPasswd.split(":")[2]
|
|
|
gid = resPasswd.split(":")[3]
|
|
|
full_name = resPasswd.split(":")[4]
|
|
|
return (uid, gid, full_name)
|
|
|
else:
|
|
|
return False
|
|
|
|
|
|
|
|
|
def createHome(self, userName=False):
|
|
|
# Подсоединяемся к movie
|
|
|
#if not self.getLdapObjBind(server):
|
|
|
#return False
|
|
|
# Создаем объект переменных
|
|
|
self.createClVars()
|
|
|
if userName:
|
|
|
self.userName = userName
|
|
|
#uidGid = self.getUserUidGid(self.userName)
|
|
|
uidGid = self.getUidAndGidUser(self.userName)
|
|
|
if not uidGid:
|
|
|
self.printERROR(_(" Not found user uid and gid"))
|
|
|
return False
|
|
|
uid = int(uidGid[0])
|
|
|
gid = int(uidGid[1])
|
|
|
fullName = uidGid[2]
|
|
|
# Создаем пользовательскую директорию
|
|
|
homeDir = os.path.join("/home",self.userName)
|
|
|
self.clVars.Set('cl_root_path',homeDir,True)
|
|
|
if os.path.exists(homeDir) and os.listdir(homeDir):
|
|
|
self.printWARNING(_("home dir %s exists")%homeDir)
|
|
|
return True
|
|
|
if not os.path.exists(homeDir):
|
|
|
self.createUserDir(uid, gid, homeDir)
|
|
|
# Записываем переменную логин
|
|
|
self.clVars.Set('ur_login',self.userName)
|
|
|
self.clVars.Set('ur_fullname',fullName)
|
|
|
# Применяем профили для пользователя
|
|
|
if not self.applyProfilesFromUser():
|
|
|
self.printERROR(_(" Not apply user profile"))
|
|
|
return False
|
|
|
self.chownR(homeDir, uid, gid)
|
|
|
self.printSUCCESS(_("created home dir %s")%homeDir)
|
|
|
return True
|
|
|
|
|
|
class tsOpt(cl_base.opt):
|
|
|
"""Класс для обработки параметров и вывода help
|
|
|
|
|
|
Параметры:
|
|
|
helpObj объект-справка содержащий необходимые опции
|
|
|
notOptError выдавать ошибку при отсутствии опций командной строки
|
|
|
"""
|
|
|
def __init__(self, helpObj, notOptError=False):
|
|
|
# от cl_help получаем короткие и длинные опции
|
|
|
shortOpt,longOpt = helpObj.getAllOpt('all', helpObj.relOptions['h'])
|
|
|
# вызвать конструктор объекта, распознающего опции
|
|
|
cl_base.opt.__init__(self,shortOpt,longOpt)
|
|
|
self.nameParams = ['user']
|
|
|
self.sysArgv = sys.argv[1:]
|
|
|
self.helpObj = helpObj
|
|
|
self.__iter = 0
|
|
|
self.opt = {}
|
|
|
self.params = {}
|
|
|
self.getopt()
|
|
|
# Обработка help
|
|
|
self.flagHelp = False
|
|
|
# определяем есть ли среди опций опции, которые влияют на показ
|
|
|
# опциональных разделов (метод пересечения множеств)
|
|
|
helpopt = \
|
|
|
tuple(set(self.opt.keys()).intersection(helpObj.relOptions.keys()))
|
|
|
#Если есть опции help
|
|
|
if len(helpopt) > 0:
|
|
|
print helpObj.getHelp(helpObj.relOptions[helpopt[0]])
|
|
|
self.flagHelp = True
|
|
|
#Если нет хвостов
|
|
|
#elif not self.params:
|
|
|
#print helpObj.getHelp(helpObj.relOptions['h'])
|
|
|
#self.flagHelp = True
|
|
|
else:
|
|
|
if self.params.has_key('user'):
|
|
|
if len(self.nameParams) != self.__iter:
|
|
|
self.handlerErrOpt()
|
|
|
# В случае остсутствия опций командной строки и имени пользователя
|
|
|
if notOptError and not self.opt and not self.params.has_key('user'):
|
|
|
self.printErrorNotOpt()
|
|
|
self.flagHelp = True
|
|
|
|
|
|
def printErrorNotOpt(self):
|
|
|
"""Сообщение в случае отсутствия опций"""
|
|
|
print _("Options are absent.")
|
|
|
|
|
|
def handlerOpt(self,option,value):
|
|
|
# Обработчик (опция значение)
|
|
|
#print option, value
|
|
|
shortOpt = self.helpObj.getShortOpt(option)
|
|
|
if not shortOpt:
|
|
|
shortOpt = option
|
|
|
if not shortOpt in self.opt:
|
|
|
self.opt[shortOpt] = value
|
|
|
|
|
|
def handlerErrOpt(self):
|
|
|
# Обработчик ошибок
|
|
|
argv = " ".join(sys.argv[1:])
|
|
|
print _("Unrecognized option") + ' "' + argv + '"\n' + \
|
|
|
_("Try") + ' "' + sys.argv[0].split("/")[-1] + ' --help" ' +\
|
|
|
_("for more information.")
|
|
|
|
|
|
|
|
|
def handlerParam(self,param):
|
|
|
# Обработчик хвостов (значение)
|
|
|
self.__iter += 1
|
|
|
if self.__iter<=len(self.nameParams):
|
|
|
self.params[self.nameParams[self.__iter-1]] = param
|
|
|
|