diff --git a/pym/cl_client.py b/pym/cl_client.py index a1951d5..7b2f608 100644 --- a/pym/cl_client.py +++ b/pym/cl_client.py @@ -38,17 +38,18 @@ from client.progressbar import ProgressBar from cl_utils import runOsCommand, getpathenv, getModeFile, removeDir, isMount from _cl_keys import getKey, clearKey from convertenv import convertEnv +from encrypt import encrypt lang().setLanguage(sys.modules[__name__]) class DataVarsClient(DataVars): """Хранение переменных""" - def flClient(self, **args): - '''Заполнить конфигурацию переменных, для десктопа''' - # Имя секции в calculate.env + def importClient(self, **args): + '''Импорт переменных для клиента''' + # Имя секции в calculate2.env envSection = "client" - # заполнить переменные окружения алгоритмом по умолнанию + # импорт переменных self.importData(envSection, ('cl_vars_client','cl_fill_client')) @@ -70,9 +71,11 @@ class share(color_print): """Создает объект Vars""" if not clVars: clVars = DataVarsClient() - clVars.flClient() + # Импортируем переменные + clVars.importClient() + # Заменяем значения переменных переменными из env файлов clVars.flIniFile() - # Устанавливаем у объекта объект Vars + # Устанавливаем у объекта атрибут объект переменных self.clVars = clVars return True @@ -243,7 +246,73 @@ class ldapData(ldapUser): return True return False -class client(share): +class commandServer(color_print): + """Отправка команд на сервер (изменение пароля пользователя на сервере)""" + + def setServerCommand(self, command, varsCommand, fileConfig, + uid=None, gid=None): + """Установить команду для сервера""" + pathConfig = os.path.split(fileConfig)[0] + # Создаем директорию если ее нет + if not os.path.exists(pathConfig): + os.makedirs(pathConfig) + if not uid is None and not gid is None: + os.chown(pathConfig, uid, gid) + objConfig = iniParser(fileConfig) + varsRun = {"run":"on"} + varsRun.update(varsCommand) + if not objConfig.setVar(["command"]+command, varsRun): + self.printERROR(_("Can not write variables in file %s")\ + %fileConfig) + return False + if not uid is None and not gid is None: + os.chown(fileConfig, uid, gid) + return True + + def checkUserPwdLDAP(self, server, userDN, password): + """Проверка пароля Unix пользователя на сервере""" + ldapInit = ldap.initialize("ldap://%s"%server) + errMessage = "" + try: + ldapInit.bind_s(userDN, password) + except ldap.INVALID_CREDENTIALS: + errMessage = _("Password incorrect") + return False, errMessage + except ldap.LDAPError, e: + errMessage = e[0]['desc'] + return False, errMessage + return True, errMessage + + def getUserPwd(self, options, optDialog, optStdIn, pwDialog=False): + """Получить пароль у пользователя + + options - полученные опции командной строки + optDialog - опция командной строки для вывода диалога для получения + пароля + optStdIn - опция командной строки для получения пароля из + стандартного ввода (stdin) + pwDialog - структура для вывода приглашения в режиме диалога + """ + userPwd = "" + if optStdIn and options.has_key(optStdIn): + pwdA = sys.stdin.readline().rstrip() + pwdB = sys.stdin.readline().rstrip() + elif optDialog and options.has_key(optDialog): + if not pwDialog: + pwDialog = [_("New password"), + _("Retype new password")] + pwdA = getpass.getpass(pwDialog[0]+":") + pwdB = getpass.getpass(pwDialog[1]+":") + if (optStdIn and options.has_key(optStdIn)) or\ + (optDialog and options.has_key(optDialog)): + if not pwdA or not (pwdA == pwdB): + self.printERROR (_("ERROR") + ": " +\ + _("passwords do not match")) + return False + userPwd = pwdA + return userPwd + +class client(share, commandServer, encrypt): """Методы работы для подключения пользователя к серверу и синхронизации""" # Объект для поиска пользовательских данных в LDAP ldapDataObj = ldapData() @@ -627,26 +696,6 @@ class client(share): return False return varsConfig - def setServerCommand(self, command, varsCommand, fileConfig, - uid=None, gid=None): - """Установить команду для сервера""" - pathConfig = os.path.split(fileConfig)[0] - # Создаем директорию если ее нет - if not os.path.exists(pathConfig): - os.makedirs(pathConfig) - if not uid is None and not gid is None: - os.chown(pathConfig, uid, gid) - objConfig = iniParser(fileConfig) - varsRun = {"run":"on"} - varsRun.update(varsCommand) - if not objConfig.setVar(["command"]+command, varsRun): - self.printERROR(_("Can not write variables in file %s")\ - %fileConfig) - return False - if not uid is None and not gid is None: - os.chown(fileConfig, uid, gid) - return True - def foundArchFile(self,strCurrentTime,archPathProcess, archPathSuccess, progress=False, remoteServer=False): @@ -1810,6 +1859,7 @@ class client(share): if not (textLine is None): self.printERROR(_("Can not unmount") + " /home") return False + self.restartDBus() return True self.printSUCCESS(_("Mount Samba resource [%s]") % "remote" +\ " ...") @@ -1829,7 +1879,6 @@ class client(share): self.clVars.flIniFile() if not self.applyRelevanceTemplates(domain): return False - self.restartDBus() return True def updateEnvFiles(self): @@ -1859,3 +1908,50 @@ class client(share): flagUpdate = True return flagUpdate + def setUserPasswordToServer(self): + """Установка пароля пользователя на сервере""" + # Проверяем на root + if self.isRoot(False): + self.printERROR(_("The user is root")) + self.printWARNING(\ + _("The program can be executed from a non-root user only")) + return False + # DNS имя хоста + server = self.ldapDataObj.getHost() + # DN пользователей + usersDN = self.ldapDataObj.getUsersDN() + if not (server and usersDN): + self.printERROR(_("The computer is not in domain")) + self.printWARNING(_("Use passwd")) + return False + # Получаем старый пароль пользователя + curPassword = self.getUserPassword(_("Enter current password")) + if not curPassword: + self.printERROR(_("Current password is empty")) + return False + userDN = self.ldapDataObj.addDN("uid=%s"%os.environ["USER"], usersDN) + # Проверяем в LDAP сервере текущий пароль пользователя + ret, err = self.checkUserPwdLDAP(server, userDN, curPassword) + if not ret: + self.printERROR(err) + return False + password = self.getUserPwd({"p":""}, "p", False) + if password is False: + return False + # Переменные для записи в env файл + varsConfig = {"unix_hash":self.getHashPasswd(password,"ssha"), + "samba_lm_hash":self.getHashPasswd(password,"lm"), + "samba_nt_hash":self.getHashPasswd(password,"nt"), + "samba_nt_hash_old":self.getHashPasswd(curPassword,"nt")} + if filter(lambda x: not x, varsConfig.values()): + return False + # ~/.calculate/server.env + fileConfig = os.path.join(os.environ["HOME"], self.configFileServer) + if not self.setServerCommand(["passwd_samba"], varsConfig, fileConfig): + return False + self.printOK(_("Changed password of user %s")%os.environ["USER"] + \ + " ...") + self.printWARNING(_("Password will be changed when you logout from the \ +X session")) + return True + diff --git a/pym/cl_passwd_cmd.py b/pym/cl_passwd_cmd.py new file mode 100644 index 0000000..b10f140 --- /dev/null +++ b/pym/cl_passwd_cmd.py @@ -0,0 +1,65 @@ +#-*- coding: utf-8 -*- + +# Copyright 2010 Mir 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. + +from cl_client import client, __app__, __version__ +from cl_opt import opt +import sys +from cl_share_cmd import share_cmd + +# Перевод сообщений для программы +from cl_lang import lang +lang().setLanguage(sys.modules[__name__]) + +# Использование программы +USAGE = _("%prog [options]") + +# Описание программы (что делает программа) +DESCRIPTION = _("Change user password") + +# Коментарии к использованию программы +COMMENT_EXAMPLES = _("change password of user for Samba and Unix services") + +# Пример использования программы +EXAMPLES = _("%prog") + +class passwd_cmd(share_cmd): + def __init__(self): + # Объект опций командной строки + self.optobj = opt(\ + package=__app__, + version=__version__, + usage=USAGE, + examples=EXAMPLES, + comment_examples=COMMENT_EXAMPLES, + description=DESCRIPTION, + option_list=opt.variable_control+opt.color_control, + check_values=self.checkOpts) + # Создаем объект логики + self.logicObj = client() + # Создаем переменные + self.logicObj.createClVars() + + def checkOpts(self, optObj, args): + """Проверка опций командной строки""" + if args: + errMsg = _("invalid argument") + ":" + " %s" %" ".join(args) + self.optobj.error(errMsg) + return False + return optObj, args + + def setUserPasswordToServer(self): + """Изменение пароля пользователя на сервере""" + return self.logicObj.setUserPasswordToServer() diff --git a/scripts/cl-passwd b/scripts/cl-passwd new file mode 100644 index 0000000..ed96414 --- /dev/null +++ b/scripts/cl-passwd @@ -0,0 +1,52 @@ +#!/usr/bin/python +#-*- coding: utf-8 -*- + +# Copyright 2010 Mir 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 sys +import os +sys.path.insert(0,os.path.abspath('/usr/lib/calculate-2.2/calculate-lib/pym')) +sys.path.insert(0,\ + os.path.abspath('/usr/lib/calculate-2.2/calculate-client/pym')) + +from cl_passwd_cmd import passwd_cmd + +from cl_lang import lang +tr = lang() +tr.setGlobalDomain('cl_client') +tr.setLanguage(sys.modules[__name__]) + +if __name__ == "__main__": + obj = passwd_cmd() + ret = obj.optobj.parse_args() + if ret is False: + sys.exit(1) + opts, args = ret + # Установка цвета при печати сообщений + obj.setPrintNoColor(opts) + # Установка переменных + if not obj.setVars(opts): + sys.exit(1) + # Печать переменных + obj.printVars(opts) + # Если нет печати переменных выполняем логику программы + if not opts.vars: + # Изменение пароля пользователя + if not obj.setUserPasswordToServer(): + sys.exit(1) + # Запись переменных + if not obj.writeVars(opts): + sys.exit(1) + sys.exit(0) diff --git a/setup.py b/setup.py index ee3a574..cefcec0 100755 --- a/setup.py +++ b/setup.py @@ -25,7 +25,7 @@ from distutils.command.build_scripts import build_scripts from distutils.command.install_scripts import install_scripts -__version__ = "2.2.0" +__version__ = "2.2.0.0" __app__ = "calculate-client" @@ -148,6 +148,7 @@ setup( data_files = data_files, scripts=["./scripts/cl-sync", "./scripts/cl-client", + "./scripts/cl-passwd", "./scripts/install", "./scripts/uninstall"], ext_modules = [Extension('calculate-client.pym._cl_keys',