|
|
|
@ -28,7 +28,8 @@ from datavars import DataVarsDesktop, DataVars, __version__,__app__
|
|
|
|
|
from calculate.lib.cl_template import (Template, ProgressTemplate,
|
|
|
|
|
TemplatesError,templateFunction,iniParser)
|
|
|
|
|
from calculate.lib.utils.files import (runOsCommand, isMount,process,
|
|
|
|
|
getRunCommands,STDOUT,childMounts)
|
|
|
|
|
getRunCommands,STDOUT,childMounts,getLoopFromPath,
|
|
|
|
|
getMdRaidDevices,listDirectory,removeDir)
|
|
|
|
|
from calculate.lib.utils.common import (getpathenv,appendProgramToEnvFile,
|
|
|
|
|
removeProgramToEnvFile,mountEcryptfs,
|
|
|
|
|
CommonError, isBootstrapDataOnly)
|
|
|
|
@ -41,6 +42,7 @@ from itertools import ifilter
|
|
|
|
|
import tarfile
|
|
|
|
|
import tempfile
|
|
|
|
|
import shutil
|
|
|
|
|
from itertools import count
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class DesktopError(Exception):
|
|
|
|
@ -58,25 +60,55 @@ class Desktop:
|
|
|
|
|
self.clTempl = None
|
|
|
|
|
self.clVars = None
|
|
|
|
|
|
|
|
|
|
def createCryptDir(self,userName,uid,gud,userDir):
|
|
|
|
|
def createCryptDir(self,userName,uid,gid,userDir,recreateOnError=False):
|
|
|
|
|
"""
|
|
|
|
|
Создать шифрование домашней директории, или подключить существующую
|
|
|
|
|
|
|
|
|
|
userName,uid,gid,userDir: параметры пользовательской учётной записи
|
|
|
|
|
recreateOnError: пересоздать профиль при ошбиках (используется при доменной
|
|
|
|
|
ученой записи, так пользователь при этом ничего не теряет - профиль на сервере)
|
|
|
|
|
"""
|
|
|
|
|
userPwd = getKey(userName)
|
|
|
|
|
error = ""
|
|
|
|
|
# проверить наличие пароля в ключах ядра
|
|
|
|
|
if not userPwd or userPwd == "XXXXXXXX":
|
|
|
|
|
raise DesktopError(_("User password not found"))
|
|
|
|
|
ecryptfsPath = path.join('/home/.ecryptfs',userName)
|
|
|
|
|
# если шифрование уже используется
|
|
|
|
|
if path.exists(ecryptfsPath):
|
|
|
|
|
for d in (".ecryptfs",".Private"):
|
|
|
|
|
source,target = path.join(ecryptfsPath,d),path.join(userDir,d)
|
|
|
|
|
if not path.lexists(target):
|
|
|
|
|
os.symlink(source,target)
|
|
|
|
|
# попытаться подключить шифрованные данные
|
|
|
|
|
try:
|
|
|
|
|
if not mountEcryptfs(userName,userPwd,userDir):
|
|
|
|
|
raise DesktopError(_("Failed to mount ecrypted data"))
|
|
|
|
|
error = _("Failed to mount ecrypted data")
|
|
|
|
|
except CommonError as e:
|
|
|
|
|
raise DesktopError(_("Failed to mount ecrypted data")+": \"%s\""%str(e))
|
|
|
|
|
else:
|
|
|
|
|
error = _("Failed to mount ecrypted data")+": \"%s\""%str(e)
|
|
|
|
|
# если при подключении произошли ошибки
|
|
|
|
|
if error:
|
|
|
|
|
# заархивировать текущий профиль и удалить его
|
|
|
|
|
if recreateOnError:
|
|
|
|
|
self.printWARNING(_("Recreating the encryption data"))
|
|
|
|
|
if self.getMountUserPaths(userDir):
|
|
|
|
|
raise DesktopError(_("Failed to use directory encryption"))
|
|
|
|
|
for source in (userDir,ecryptfsPath):
|
|
|
|
|
if path.exists(source):
|
|
|
|
|
if listDirectory(source):
|
|
|
|
|
target = source+".bak"
|
|
|
|
|
newtarget = target
|
|
|
|
|
if path.exists(target):
|
|
|
|
|
removeDir(target)
|
|
|
|
|
os.rename(source,newtarget)
|
|
|
|
|
else:
|
|
|
|
|
os.rmdir(source)
|
|
|
|
|
self.createUserDir(userName,uid,gid,userDir)
|
|
|
|
|
# ошибка создания шифрования
|
|
|
|
|
else:
|
|
|
|
|
raise DesktopError(error)
|
|
|
|
|
# если нет шифрованных данных
|
|
|
|
|
if not path.exists(ecryptfsPath):
|
|
|
|
|
tf = None
|
|
|
|
|
try:
|
|
|
|
|
# если профиль содержит только данные от бутстрапа core
|
|
|
|
@ -106,6 +138,8 @@ class Desktop:
|
|
|
|
|
with tarfile.open(fileobj=tf,mode='r:') as tarf:
|
|
|
|
|
tarf.extractall(userDir)
|
|
|
|
|
except Exception as e:
|
|
|
|
|
# в случае ошибки сохраняем архив (с данными bootstrap)
|
|
|
|
|
# из памяти в файловую систему
|
|
|
|
|
if tf:
|
|
|
|
|
tf.seek(0)
|
|
|
|
|
bakArchName = path.join(userDir,".calculate.tar.bz2")
|
|
|
|
@ -116,6 +150,7 @@ class Desktop:
|
|
|
|
|
finally:
|
|
|
|
|
if tf:
|
|
|
|
|
tf.close()
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
def createUserDir(self, userName, uid, gid, userDir, mode=0700):
|
|
|
|
|
"""
|
|
|
|
@ -130,89 +165,15 @@ class Desktop:
|
|
|
|
|
else:
|
|
|
|
|
raise DesktopError(_("Path %s exists") %userDir)
|
|
|
|
|
|
|
|
|
|
def applyTemplatesFromUser(self):
|
|
|
|
|
"""Apply templates for user"""
|
|
|
|
|
if self.clTempl:
|
|
|
|
|
self.closeClTemplate()
|
|
|
|
|
|
|
|
|
|
templates_locate = self.clVars.Get('cl_templates_locate')
|
|
|
|
|
self.clVars.Set("cl_template_path",
|
|
|
|
|
map(lambda x:x[1],
|
|
|
|
|
filter(lambda x:x[0] in templates_locate,
|
|
|
|
|
zip(self.clVars.Get('cl_template_location'),
|
|
|
|
|
self.clVars.Get('cl_template_path')))),
|
|
|
|
|
True)
|
|
|
|
|
|
|
|
|
|
self.clTempl = ProgressTemplate(self.setProgress,self.clVars,
|
|
|
|
|
cltObj=False,
|
|
|
|
|
printERROR=self.printERROR,
|
|
|
|
|
printWARNING=self.printWARNING,
|
|
|
|
|
askConfirm=self.askConfirm,
|
|
|
|
|
printSUCCESS=self.printSUCCESS,
|
|
|
|
|
userProfile=True)
|
|
|
|
|
dirsFiles = self.clTempl.applyTemplates()
|
|
|
|
|
if self.clTempl.getError():
|
|
|
|
|
self.printERROR(self.clTempl.getError().strip())
|
|
|
|
|
return False
|
|
|
|
|
else:
|
|
|
|
|
return dirsFiles
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def closeClTemplate(self):
|
|
|
|
|
if self.clTempl:
|
|
|
|
|
if self.clTempl.cltObj:
|
|
|
|
|
self.clTempl.cltObj.closeFiles()
|
|
|
|
|
self.clTempl.closeFiles()
|
|
|
|
|
self.clTempl = None
|
|
|
|
|
|
|
|
|
|
def umountUserRes(self, error):
|
|
|
|
|
"""
|
|
|
|
|
Umount user directory
|
|
|
|
|
"""
|
|
|
|
|
self.closeClTemplate()
|
|
|
|
|
if error and self.homeDir:
|
|
|
|
|
umountPaths = self.getMountUserPaths(self.homeDir)
|
|
|
|
|
ret = True
|
|
|
|
|
for umountPath in umountPaths:
|
|
|
|
|
if not self.umountSleepPath(umountPath):
|
|
|
|
|
ret = False
|
|
|
|
|
break
|
|
|
|
|
return ret
|
|
|
|
|
|
|
|
|
|
#!!!!post_action=umountUserRes)
|
|
|
|
|
def createHome(self, userName,):
|
|
|
|
|
def umountUserRes(self, *umountPaths):
|
|
|
|
|
"""
|
|
|
|
|
Creating user profile and userdir
|
|
|
|
|
Отключить пользовательские ресурсы
|
|
|
|
|
"""
|
|
|
|
|
self.homeDir = self.clVars.Get('ur_home_path')
|
|
|
|
|
rootPath = self.clVars.Get('cl_root_path')
|
|
|
|
|
# real path to home dir
|
|
|
|
|
self.homeDir = path.join(rootPath, self.homeDir[1:])
|
|
|
|
|
if not path.exists(self.homeDir):
|
|
|
|
|
self.startTask(_("Creating the home directory for %s")%self.homeDir)
|
|
|
|
|
self.createUserDir(userName,uid,gid,self.homeDir)
|
|
|
|
|
self.endTask()
|
|
|
|
|
if (self.clVars.Get('ur_home_crypt_set') == 'on' and
|
|
|
|
|
self.clVars.Get('install.cl_autologin') != userName):
|
|
|
|
|
self.createCryptDir(userName,uid,gid,self.homeDir)
|
|
|
|
|
|
|
|
|
|
domainUser = self.clVars.Get('ur_domain_set') == 'on'
|
|
|
|
|
lastTimestamp = templateFunction.getLastElog()
|
|
|
|
|
iniEnv = path.join(self.homeDir,'.calculate/ini.env')
|
|
|
|
|
userIni = iniParser(iniEnv)
|
|
|
|
|
userTimestamp = userIni.getVar('main','elog').encode('utf-8')
|
|
|
|
|
|
|
|
|
|
if (domainUser or not path.exists(iniEnv) or
|
|
|
|
|
userTimestamp != lastTimestamp):
|
|
|
|
|
# action - "user profile configuration"
|
|
|
|
|
self.clVars.Set("cl_action", "desktop", True)
|
|
|
|
|
# apply user profiles
|
|
|
|
|
self.startTask(_("Setting up the user profile"),progress=True)
|
|
|
|
|
dirsAndFiles = self.applyTemplatesFromUser()
|
|
|
|
|
self.endTask()
|
|
|
|
|
if not dirsAndFiles:
|
|
|
|
|
raise DesktopError(_("Failed to apply user profile templates"))
|
|
|
|
|
self.printSUCCESS(_("User account %s is configured")%userName + " ...")
|
|
|
|
|
if umountPaths and type(umountPaths[0]) == list:
|
|
|
|
|
umountPaths = umountPaths[0]
|
|
|
|
|
for umountPath in umountPaths:
|
|
|
|
|
if not self.umountSleepPath(umountPath):
|
|
|
|
|
return False
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
def getMountUserPaths(self, homeDir=False):
|
|
|
|
@ -247,9 +208,21 @@ class Desktop:
|
|
|
|
|
|
|
|
|
|
def umountSleepPath(self, rpath):
|
|
|
|
|
"""
|
|
|
|
|
Unmount path, sleep by failed and repeat
|
|
|
|
|
Отмонтировать указанный путь, а также отключить используемые
|
|
|
|
|
в этом пути loop устройства и raid
|
|
|
|
|
"""
|
|
|
|
|
# check for mount
|
|
|
|
|
loops = getLoopFromPath(rpath)
|
|
|
|
|
if loops:
|
|
|
|
|
setLoops = set(map(lambda x:x.partition('/dev/')[2],loops))
|
|
|
|
|
mdInfo = getMdRaidDevices()
|
|
|
|
|
for k,v in mdInfo.items():
|
|
|
|
|
if setLoops & set(v):
|
|
|
|
|
self.umountSleepPath('/dev/%s'%k)
|
|
|
|
|
process('/sbin/mdadm','--stop','/dev/%s'%k).success()
|
|
|
|
|
for loop in loops:
|
|
|
|
|
self.umountSleepPath(loop)
|
|
|
|
|
process('/sbin/losetup','-d',loop).success()
|
|
|
|
|
if isMount(rpath):
|
|
|
|
|
for waittime in [0,0.5,1,2]:
|
|
|
|
|
time.sleep(waittime)
|
|
|
|
@ -264,6 +237,7 @@ class Desktop:
|
|
|
|
|
or not isMount(rpath):
|
|
|
|
|
if not isMount(rpath):
|
|
|
|
|
return True
|
|
|
|
|
self.execProg("umount -l %s"%rpath)
|
|
|
|
|
else:
|
|
|
|
|
if isMount(rpath):
|
|
|
|
|
self.printERROR(_("Failed to unmount directory %s")%rpath)
|
|
|
|
|