fixed: name of volume is persistent after format
fixed: use 'cryptsetup luksUUID' to retrieve UUIDs of luks devices added: analyse request URL andd add information to hdf dataset added: attribute 'device' is persistent (like 'weblang') to allow redirects between volume plugins changed: moved URLs of plugins from 'plugins/???' to '???' (flat hierarchie -> simple links) added: new action for all plugins: "redirect" - call specific plugin after completing the current one fixed: permission fix for vfat filesystems during mount / for ext2/3: after mount regular checks for config partition (mount, if it becomes available)
This commit is contained in:
parent
4df227a99d
commit
0c23c5e2c3
8 changed files with 225 additions and 94 deletions
|
@ -122,6 +122,7 @@ class CryptoBoxProps(CryptoBox):
|
||||||
|
|
||||||
|
|
||||||
def reReadContainerList(self):
|
def reReadContainerList(self):
|
||||||
|
self.log.debug("rereading container list")
|
||||||
self.containers = []
|
self.containers = []
|
||||||
for device in CryptoBoxTools.getAvailablePartitions():
|
for device in CryptoBoxTools.getAvailablePartitions():
|
||||||
if self.isDeviceAllowed(device) and not self.isConfigPartition(device):
|
if self.isDeviceAllowed(device) and not self.isConfigPartition(device):
|
||||||
|
@ -248,6 +249,14 @@ class CryptoBoxProps(CryptoBox):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def removeUUID(self, uuid):
|
||||||
|
if uuid in self.prefs.nameDB.keys():
|
||||||
|
del self.prefs.nameDB[uuid]
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
def getAvailableLanguages(self):
|
def getAvailableLanguages(self):
|
||||||
'''reads all files in path LangDir and returns a list of
|
'''reads all files in path LangDir and returns a list of
|
||||||
basenames from existing hdf files, that should are all available
|
basenames from existing hdf files, that should are all available
|
||||||
|
@ -261,6 +270,7 @@ class CryptoBoxProps(CryptoBox):
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
cb = CryptoBoxProps()
|
cb = CryptoBoxProps()
|
||||||
|
|
||||||
|
|
|
@ -120,15 +120,18 @@ class CryptoBoxContainer:
|
||||||
|
|
||||||
|
|
||||||
def create(self, type, password=None):
|
def create(self, type, password=None):
|
||||||
|
old_name = self.getName()
|
||||||
if type == self.Types["luks"]:
|
if type == self.Types["luks"]:
|
||||||
self.__createLuks(password)
|
self.__createLuks(password)
|
||||||
self.resetObject()
|
elif type == self.Types["plain"]:
|
||||||
return
|
|
||||||
if type == self.Types["plain"]:
|
|
||||||
self.__createPlain()
|
self.__createPlain()
|
||||||
self.resetObject()
|
else:
|
||||||
return
|
|
||||||
raise CBInvalidType("invalid container type (%d) supplied" % (type, ))
|
raise CBInvalidType("invalid container type (%d) supplied" % (type, ))
|
||||||
|
## no exception was raised during creation -> we can continue
|
||||||
|
## reset the properties (encryption state, ...) of the device
|
||||||
|
self.resetObject()
|
||||||
|
## restore the old name (must be after resetObject)
|
||||||
|
self.setName(old_name)
|
||||||
|
|
||||||
|
|
||||||
def changePassword(self, oldpw, newpw):
|
def changePassword(self, oldpw, newpw):
|
||||||
|
@ -215,16 +218,42 @@ class CryptoBoxContainer:
|
||||||
|
|
||||||
|
|
||||||
def __getUUID(self):
|
def __getUUID(self):
|
||||||
"""return UUID for luks partitions, ext2/3 and vfat filesystems"""
|
if self.__getTypeOfPartition() == self.Types["luks"]:
|
||||||
emergency_default = self.device.replace(os.path.sep, "_")
|
guess = self.__getLuksUUID()
|
||||||
devnull = None
|
else:
|
||||||
|
guess = self.__getNonLuksUUID()
|
||||||
|
## did we get a valid value?
|
||||||
|
if guess:
|
||||||
|
return guess
|
||||||
|
else:
|
||||||
|
## emergency default value
|
||||||
|
return self.device.replace(os.path.sep, "_")
|
||||||
|
|
||||||
|
|
||||||
|
def __getLuksUUID(self):
|
||||||
|
"""get uuid for luks devices"""
|
||||||
|
proc = subprocess.Popen(
|
||||||
|
shell = False,
|
||||||
|
stdout = subprocess.PIPE,
|
||||||
|
stderr = subprocess.PIPE,
|
||||||
|
args = [self.cbox.prefs["Programs"]["cryptsetup"],
|
||||||
|
"luksUUID",
|
||||||
|
self.device])
|
||||||
|
(stdout, stderr) = proc.communicate()
|
||||||
|
if proc.returncode != 0:
|
||||||
|
self.cbox.log.info("could not retrieve luks uuid (%s): %s", (self.device, stderr.strip()))
|
||||||
|
return None
|
||||||
|
return stdout.strip()
|
||||||
|
|
||||||
|
|
||||||
|
def __getNonLuksUUID(self):
|
||||||
|
"""return UUID for ext2/3 and vfat filesystems"""
|
||||||
try:
|
try:
|
||||||
devnull = open(os.devnull, "w")
|
devnull = open(os.devnull, "w")
|
||||||
except IOError:
|
except IOError:
|
||||||
self.warn("Could not open %s" % (os.devnull, ))
|
self.warn("Could not open %s" % (os.devnull, ))
|
||||||
proc = subprocess.Popen(
|
proc = subprocess.Popen(
|
||||||
shell=False,
|
shell=False,
|
||||||
stdin=None,
|
|
||||||
stdout=subprocess.PIPE,
|
stdout=subprocess.PIPE,
|
||||||
stderr=subprocess.PIPE,
|
stderr=subprocess.PIPE,
|
||||||
args=[self.cbox.prefs["Programs"]["blkid"],
|
args=[self.cbox.prefs["Programs"]["blkid"],
|
||||||
|
@ -233,14 +262,14 @@ class CryptoBoxContainer:
|
||||||
"-c", os.devnull,
|
"-c", os.devnull,
|
||||||
"-w", os.devnull,
|
"-w", os.devnull,
|
||||||
self.device])
|
self.device])
|
||||||
proc.wait()
|
(stdout, stderr) = proc.communicate()
|
||||||
result = proc.stdout.read().strip()
|
|
||||||
if proc.returncode != 0:
|
|
||||||
self.log.warn("retrieving of partition type via 'blkid' failed: %s" % (proc.stderr.read().strip(), ))
|
|
||||||
return emergency_default
|
|
||||||
devnull.close()
|
devnull.close()
|
||||||
if result: return result
|
## execution failed?
|
||||||
return emergency_default
|
if proc.returncode != 0:
|
||||||
|
self.log.info("retrieving of partition type (%s) via 'blkid' failed: %s - maybe it is encrypted?" % (self.device, stderr.strip()))
|
||||||
|
return None
|
||||||
|
## return output of blkid
|
||||||
|
return stdout.strip()
|
||||||
|
|
||||||
|
|
||||||
def __getTypeOfPartition(self):
|
def __getTypeOfPartition(self):
|
||||||
|
|
|
@ -23,6 +23,7 @@ allowedProgs = {
|
||||||
"cryptsetup": "/sbin/cryptsetup",
|
"cryptsetup": "/sbin/cryptsetup",
|
||||||
"mount": "/bin/mount",
|
"mount": "/bin/mount",
|
||||||
"umount": "/bin/umount",
|
"umount": "/bin/umount",
|
||||||
|
"blkid": "/sbin/blkid",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -185,6 +186,23 @@ def run_sfdisk(args):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def getFSType(device):
|
||||||
|
"""get the filesystem type of a device"""
|
||||||
|
proc = subprocess.Popen(
|
||||||
|
shell = False,
|
||||||
|
stdout = subprocess.PIPE,
|
||||||
|
args = [ allowedProgs["blkid"],
|
||||||
|
"-s", "TYPE",
|
||||||
|
"-o", "value",
|
||||||
|
"-c", os.devnull,
|
||||||
|
"-w", os.devnull,
|
||||||
|
device])
|
||||||
|
(stdout, stderr) = proc.communicate()
|
||||||
|
if proc.returncode != 0:
|
||||||
|
return None
|
||||||
|
return stdout.strip()
|
||||||
|
|
||||||
|
|
||||||
def run_mount(args):
|
def run_mount(args):
|
||||||
"""execute mount
|
"""execute mount
|
||||||
"""
|
"""
|
||||||
|
@ -198,7 +216,7 @@ def run_mount(args):
|
||||||
# check permissions for the device
|
# check permissions for the device
|
||||||
if not isWriteable(device, DEV_TYPES["block"]):
|
if not isWriteable(device, DEV_TYPES["block"]):
|
||||||
raise "WrongArguments", "%s is not a writeable block device" % (device, )
|
raise "WrongArguments", "%s is not a writeable block device" % (device, )
|
||||||
# check permissions for the mountpoint
|
## check permissions for the mountpoint
|
||||||
if not isWriteable(destination, DEV_TYPES["dir"]):
|
if not isWriteable(destination, DEV_TYPES["dir"]):
|
||||||
raise "WrongArguments", "the mountpoint (%s) is not writeable" % (destination, )
|
raise "WrongArguments", "the mountpoint (%s) is not writeable" % (destination, )
|
||||||
# check for additional (not allowed) arguments
|
# check for additional (not allowed) arguments
|
||||||
|
@ -210,18 +228,35 @@ def run_mount(args):
|
||||||
# first overwrite the real uid, as 'mount' wants this to be zero (root)
|
# first overwrite the real uid, as 'mount' wants this to be zero (root)
|
||||||
savedUID = os.getuid()
|
savedUID = os.getuid()
|
||||||
os.setuid(os.geteuid())
|
os.setuid(os.geteuid())
|
||||||
|
## we have to change the permissions of the mounted directory - otherwise it will
|
||||||
|
## not be writeable for the cryptobox user
|
||||||
|
## for 'vfat' we have to do this during mount
|
||||||
|
## for ext2/3 we have to do it afterward
|
||||||
|
## first: get the user/group of the target
|
||||||
|
(trustUserName, trustUID, groupsOfTrustUser) = getUserInfo(savedUID)
|
||||||
|
trustGID = groupsOfTrustUser[0]
|
||||||
|
fsType = getFSType(device)
|
||||||
|
## define arguments
|
||||||
|
if fsType == "vfat":
|
||||||
|
## add the "uid/gid" arguments to the mount call
|
||||||
|
mount_args = [allowedProgs["mount"],
|
||||||
|
"-o", "uid=%d,gid=%d" % (trustUID, trustGID),
|
||||||
|
device,
|
||||||
|
destination]
|
||||||
|
else:
|
||||||
|
## all other filesystem types will be handled after mount
|
||||||
|
mount_args = [allowedProgs["mount"], device, destination]
|
||||||
# execute mount
|
# execute mount
|
||||||
proc = subprocess.Popen(
|
proc = subprocess.Popen(
|
||||||
shell = False,
|
shell = False,
|
||||||
args = [allowedProgs["mount"], device, destination])
|
args = mount_args)
|
||||||
proc.wait()
|
proc.wait()
|
||||||
## return in case of an error
|
## return in case of an error
|
||||||
if proc.returncode != 0:
|
if proc.returncode != 0:
|
||||||
return False
|
return False
|
||||||
## chown the mounted directory - otherwise it will not be writeable for
|
## for vfat: we are done
|
||||||
## the cryptobox user (at least for the configuration partition this is
|
if fsType == "vfat": return True
|
||||||
## absolutely necessary) TODO: check if this is valid for data, too
|
## for all other filesystem types: chown the mount directory
|
||||||
(trustUserName, trustUID, groupsOfTrustUser) = getUserInfo(savedUID)
|
|
||||||
try:
|
try:
|
||||||
os.chown(destination, trustUID, groupsOfTrustUser[0])
|
os.chown(destination, trustUID, groupsOfTrustUser[0])
|
||||||
except OSError, errMsg:
|
except OSError, errMsg:
|
||||||
|
|
|
@ -33,6 +33,7 @@ class CryptoBoxSettings:
|
||||||
self.__validateConfig()
|
self.__validateConfig()
|
||||||
self.__configureLogHandler()
|
self.__configureLogHandler()
|
||||||
self.__checkUnknownPreferences()
|
self.__checkUnknownPreferences()
|
||||||
|
self.preparePartition()
|
||||||
self.nameDB = self.__getNameDatabase()
|
self.nameDB = self.__getNameDatabase()
|
||||||
self.pluginConf = self.__getPluginConfig()
|
self.pluginConf = self.__getPluginConfig()
|
||||||
self.userDB = self.__getUserDB()
|
self.userDB = self.__getUserDB()
|
||||||
|
@ -66,8 +67,8 @@ class CryptoBoxSettings:
|
||||||
return ok
|
return ok
|
||||||
|
|
||||||
|
|
||||||
def isWriteable(self):
|
def requiresPartition(self):
|
||||||
return os.access(self.prefs["Locations"]["SettingsDir"], os.W_OK)
|
return bool(self.prefs["Main"]["UseConfigPartition"])
|
||||||
|
|
||||||
|
|
||||||
def getActivePartition(self):
|
def getActivePartition(self):
|
||||||
|
@ -85,8 +86,9 @@ class CryptoBoxSettings:
|
||||||
|
|
||||||
|
|
||||||
def mountPartition(self):
|
def mountPartition(self):
|
||||||
if self.isWriteable():
|
self.log.debug("trying to mount configuration partition")
|
||||||
self.log.warn("mountConfigPartition: configuration is already writeable - mounting anyway")
|
if not self.requiresPartition():
|
||||||
|
self.log.warn("mountConfigPartition: configuration partition is not required - mounting anyway")
|
||||||
if self.getActivePartition():
|
if self.getActivePartition():
|
||||||
self.log.warn("mountConfigPartition: configuration partition already mounted - not mounting again")
|
self.log.warn("mountConfigPartition: configuration partition already mounted - not mounting again")
|
||||||
return False
|
return False
|
||||||
|
@ -152,6 +154,11 @@ class CryptoBoxSettings:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
def preparePartition(self):
|
||||||
|
if self.requiresPartition() and not self.getActivePartition():
|
||||||
|
self.mountPartition()
|
||||||
|
|
||||||
|
|
||||||
def __getitem__(self, key):
|
def __getitem__(self, key):
|
||||||
"""redirect all requests to the 'prefs' attribute"""
|
"""redirect all requests to the 'prefs' attribute"""
|
||||||
return self.prefs[key]
|
return self.prefs[key]
|
||||||
|
@ -347,6 +354,7 @@ AllowedDevices = list(min=1)
|
||||||
DefaultVolumePrefix = string(min=1)
|
DefaultVolumePrefix = string(min=1)
|
||||||
DefaultCipher = string(default="aes-cbc-essiv:sha256")
|
DefaultCipher = string(default="aes-cbc-essiv:sha256")
|
||||||
ConfigVolumeLabel = string(min=1, default="cbox_config")
|
ConfigVolumeLabel = string(min=1, default="cbox_config")
|
||||||
|
UseConfigPartition = integer(min=0, max=1, default=0)
|
||||||
|
|
||||||
[Locations]
|
[Locations]
|
||||||
MountParentDir = directoryExists(default="/var/cache/cryptobox/mnt")
|
MountParentDir = directoryExists(default="/var/cache/cryptobox/mnt")
|
||||||
|
|
|
@ -14,13 +14,47 @@ class WebInterfaceDataset(dict):
|
||||||
self.prefs = prefs
|
self.prefs = prefs
|
||||||
self.cbox = cbox
|
self.cbox = cbox
|
||||||
self.__setConfigValues()
|
self.__setConfigValues()
|
||||||
self.__setCryptoBoxState()
|
|
||||||
self.plugins = plugins
|
self.plugins = plugins
|
||||||
|
self.setCryptoBoxState()
|
||||||
self.setPluginData()
|
self.setPluginData()
|
||||||
|
self.setContainersState()
|
||||||
|
|
||||||
|
|
||||||
def setPluginData(self):
|
def setCryptoBoxState(self):
|
||||||
self.__setPluginList(self.plugins)
|
import cherrypy
|
||||||
|
self["Data.Version"] = self.cbox.VERSION
|
||||||
|
langs = self.cbox.getAvailableLanguages()
|
||||||
|
langs.sort()
|
||||||
|
for (index, lang) in enumerate(langs):
|
||||||
|
self.cbox.log.info("language loaded: %s" % lang)
|
||||||
|
self["Data.Languages.%d.name" % index] = lang
|
||||||
|
self["Data.Languages.%d.link" % index] = self.__getLanguageName(lang)
|
||||||
|
try:
|
||||||
|
self["Data.ScriptURL.Prot"] = cherrypy.request.scheme
|
||||||
|
host = cherrypy.request.headers["Host"]
|
||||||
|
self["Data.ScriptURL.Host"] = host.split(":",1)[0]
|
||||||
|
complete_url = "%s://%s" % (self["Data.ScriptURL.Prot"], self["Data.ScriptURL.Host"])
|
||||||
|
try:
|
||||||
|
port = int(host.split(":",1)[1])
|
||||||
|
complete_url += ":%s" % port
|
||||||
|
except (IndexError, ValueError):
|
||||||
|
if cherrypy.request.scheme == "http":
|
||||||
|
port = 80
|
||||||
|
elif cherrypy.request.scheme == "https":
|
||||||
|
port = 443
|
||||||
|
else:
|
||||||
|
## unknown scheme -> port 0
|
||||||
|
self.cbox.log.info("unknown protocol scheme used: %s" % (cherrypy.request.scheme,))
|
||||||
|
port = 0
|
||||||
|
self["Data.ScriptURL.Port"] = port
|
||||||
|
## retrieve the relative address of the CGI (or the cherrypy base address)
|
||||||
|
## remove the last part of the url and add a slash
|
||||||
|
path = "/".join(cherrypy.request.path.split("/")[:-1]) + "/"
|
||||||
|
self["Data.ScriptURL.Path"] = path
|
||||||
|
complete_url += path
|
||||||
|
self["Data.ScriptURL"] = complete_url
|
||||||
|
except AttributeError:
|
||||||
|
self["Data.ScriptURL"] = ""
|
||||||
|
|
||||||
|
|
||||||
def setCurrentDiskState(self, device):
|
def setCurrentDiskState(self, device):
|
||||||
|
@ -42,6 +76,7 @@ class WebInterfaceDataset(dict):
|
||||||
self["Data.CurrentDisk.capacity.free"] = avail
|
self["Data.CurrentDisk.capacity.free"] = avail
|
||||||
self["Data.CurrentDisk.capacity.size"] = size
|
self["Data.CurrentDisk.capacity.size"] = size
|
||||||
self["Data.CurrentDisk.capacity.percent"] = percent
|
self["Data.CurrentDisk.capacity.percent"] = percent
|
||||||
|
self["Settings.LinkAttrs.device"] = device
|
||||||
|
|
||||||
|
|
||||||
def setContainersState(self):
|
def setContainersState(self):
|
||||||
|
@ -64,6 +99,19 @@ class WebInterfaceDataset(dict):
|
||||||
self["Data.activeDisksCount"] = active_counter
|
self["Data.activeDisksCount"] = active_counter
|
||||||
|
|
||||||
|
|
||||||
|
def setPluginData(self):
|
||||||
|
for p in self.plugins:
|
||||||
|
lang_data = p.getLanguageData()
|
||||||
|
entryName = "Settings.PluginList." + p.getName()
|
||||||
|
self[entryName] = p.getName()
|
||||||
|
self[entryName + ".Link"] = lang_data.getValue("Link", p.getName())
|
||||||
|
self[entryName + ".Rank"] = p.getRank()
|
||||||
|
self[entryName + ".RequestAuth"] = p.isAuthRequired() and "1" or "0"
|
||||||
|
self[entryName + ".Enabled"] = p.isEnabled() and "1" or "0"
|
||||||
|
for a in p.pluginCapabilities:
|
||||||
|
self[entryName + ".Types." + a] = "1"
|
||||||
|
|
||||||
|
|
||||||
def __setConfigValues(self):
|
def __setConfigValues(self):
|
||||||
self["Settings.TemplateDir"] = os.path.abspath(self.prefs["Locations"]["TemplateDir"])
|
self["Settings.TemplateDir"] = os.path.abspath(self.prefs["Locations"]["TemplateDir"])
|
||||||
self["Settings.LanguageDir"] = os.path.abspath(self.prefs["Locations"]["LangDir"])
|
self["Settings.LanguageDir"] = os.path.abspath(self.prefs["Locations"]["LangDir"])
|
||||||
|
@ -74,12 +122,6 @@ class WebInterfaceDataset(dict):
|
||||||
self["Settings.SettingsDir"] = self.prefs["Locations"]["SettingsDir"]
|
self["Settings.SettingsDir"] = self.prefs["Locations"]["SettingsDir"]
|
||||||
|
|
||||||
|
|
||||||
def __setCryptoBoxState(self):
|
|
||||||
self["Data.Version"] = self.cbox.VERSION
|
|
||||||
for lang in self.cbox.getAvailableLanguages():
|
|
||||||
self["Data.Languages." + lang] = self.__getLanguageName(lang)
|
|
||||||
|
|
||||||
|
|
||||||
def __getLanguageName(self, lang):
|
def __getLanguageName(self, lang):
|
||||||
try:
|
try:
|
||||||
import neo_cgi, neo_util, neo_cs
|
import neo_cgi, neo_util, neo_cs
|
||||||
|
@ -91,16 +133,4 @@ class WebInterfaceDataset(dict):
|
||||||
return hdf.getValue("Name",lang)
|
return hdf.getValue("Name",lang)
|
||||||
|
|
||||||
|
|
||||||
def __setPluginList(self, plugins):
|
|
||||||
for p in plugins:
|
|
||||||
lang_data = p.getLanguageData()
|
|
||||||
entryName = "Settings.PluginList." + p.getName()
|
|
||||||
self[entryName] = p.getName()
|
|
||||||
self[entryName + ".Link"] = lang_data.getValue("Link", p.getName())
|
|
||||||
self[entryName + ".Rank"] = p.getRank()
|
|
||||||
self[entryName + ".RequestAuth"] = p.isAuthRequired() and "1" or "0"
|
|
||||||
self[entryName + ".Enabled"] = p.isEnabled() and "1" or "0"
|
|
||||||
for a in p.pluginCapabilities:
|
|
||||||
self[entryName + ".Types." + a] = "1"
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -17,28 +17,6 @@ except ImportError:
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class WebInterfacePlugins:
|
|
||||||
|
|
||||||
def __init__(self, log, plugins, handler_func):
|
|
||||||
for plugin in plugins.getPlugins():
|
|
||||||
if not plugin: continue
|
|
||||||
if not plugin.isEnabled(): continue
|
|
||||||
plname = plugin.getName()
|
|
||||||
log.info("Plugin '%s' loaded" % plname)
|
|
||||||
## this should be the "easiest" way to expose all plugins as URLs
|
|
||||||
setattr(self, plname, handler_func(plugin))
|
|
||||||
setattr(getattr(self, plname), "exposed", True)
|
|
||||||
# TODO: check, if this really works - for now the "stream_response" feature seems to be broken
|
|
||||||
#setattr(getattr(self, plname), "stream_respones", True)
|
|
||||||
|
|
||||||
|
|
||||||
class IconHandler:
|
|
||||||
|
|
||||||
def __init__(self, plugins):
|
|
||||||
self.plugins = PluginIconHandler(plugins)
|
|
||||||
self.plugins.exposed = True
|
|
||||||
|
|
||||||
|
|
||||||
class PluginIconHandler:
|
class PluginIconHandler:
|
||||||
|
|
||||||
def __init__(self, plugins):
|
def __init__(self, plugins):
|
||||||
|
@ -72,11 +50,31 @@ class WebInterfaceSites:
|
||||||
important: for _every_ "site" action (cherrypy is stateful)
|
important: for _every_ "site" action (cherrypy is stateful)
|
||||||
also take care for the plugins, as they also contain datasets
|
also take care for the plugins, as they also contain datasets
|
||||||
"""
|
"""
|
||||||
self.pluginList = Plugins.PluginManager(self.cbox, self.prefs["Locations"]["PluginDir"])
|
self.__loadPlugins()
|
||||||
self.plugins = WebInterfacePlugins(self.log, self.pluginList, self.return_plugin_action)
|
|
||||||
self.dataset = WebInterfaceDataset.WebInterfaceDataset(self.cbox, self.prefs, self.pluginList.getPlugins())
|
self.dataset = WebInterfaceDataset.WebInterfaceDataset(self.cbox, self.prefs, self.pluginList.getPlugins())
|
||||||
## publish plugin icons
|
## publish plugin icons
|
||||||
self.icons = IconHandler(self.pluginList)
|
self.icons = PluginIconHandler(self.pluginList)
|
||||||
|
self.icons.exposed = True
|
||||||
|
## check, if a configuration partition has become available
|
||||||
|
self.cbox.prefs.preparePartition()
|
||||||
|
|
||||||
|
|
||||||
|
def __loadPlugins(self):
|
||||||
|
self.pluginList = Plugins.PluginManager(self.cbox, self.prefs["Locations"]["PluginDir"])
|
||||||
|
for plugin in self.pluginList.getPlugins():
|
||||||
|
if not plugin: continue
|
||||||
|
plname = plugin.getName()
|
||||||
|
if plugin.isEnabled():
|
||||||
|
self.cbox.log.info("Plugin '%s' loaded" % plname)
|
||||||
|
## this should be the "easiest" way to expose all plugins as URLs
|
||||||
|
setattr(self, plname, self.return_plugin_action(plugin))
|
||||||
|
setattr(getattr(self, plname), "exposed", True)
|
||||||
|
# TODO: check, if this really works - for now the "stream_response" feature seems to be broken
|
||||||
|
#setattr(getattr(self, plname), "stream_respones", True)
|
||||||
|
else:
|
||||||
|
self.cbox.log.info("Plugin '%s' is disabled" % plname)
|
||||||
|
## remove the plugin, if it was active before
|
||||||
|
setattr(self, plname, None)
|
||||||
|
|
||||||
|
|
||||||
## this is a function decorator to check authentication
|
## this is a function decorator to check authentication
|
||||||
|
@ -137,35 +135,58 @@ class WebInterfaceSites:
|
||||||
self.__resetDataset()
|
self.__resetDataset()
|
||||||
self.__checkEnvironment()
|
self.__checkEnvironment()
|
||||||
args_orig = dict(args)
|
args_orig = dict(args)
|
||||||
|
## set web interface language
|
||||||
try:
|
try:
|
||||||
self.__setWebLang(args["weblang"])
|
self.__setWebLang(args["weblang"])
|
||||||
del args["weblang"]
|
del args["weblang"]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
self.__setWebLang("")
|
self.__setWebLang("")
|
||||||
## check the device argument of volume plugins
|
## we always read the "device" setting - otherwise volume-plugin links
|
||||||
if "volume" in plugin.pluginCapabilities:
|
## would not work easily (see "volume_props" linking to "format_fs")
|
||||||
|
## it will get ignored for non-volume plugins
|
||||||
try:
|
try:
|
||||||
## initialize the dataset of the selected device if necessary
|
plugin.device = None
|
||||||
if self.__setDevice(args["device"]):
|
if self.__setDevice(args["device"]):
|
||||||
plugin.device = args["device"]
|
plugin.device = args["device"]
|
||||||
|
del args["device"]
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
## check the device argument of volume plugins
|
||||||
|
if "volume" in plugin.pluginCapabilities:
|
||||||
|
## initialize the dataset of the selected device if necessary
|
||||||
|
if plugin.device:
|
||||||
self.dataset.setCurrentDiskState(plugin.device)
|
self.dataset.setCurrentDiskState(plugin.device)
|
||||||
else:
|
else:
|
||||||
|
## invalid (or missing) device setting
|
||||||
return self.__render(self.defaultTemplate)
|
return self.__render(self.defaultTemplate)
|
||||||
|
## check if there is a "redirect" setting - this will override the return
|
||||||
|
## value of the doAction function (e.g. useful for umount-before-format)
|
||||||
|
try:
|
||||||
|
if args["redirect"]:
|
||||||
|
override_nextTemplate = { "plugin":args["redirect"] }
|
||||||
|
if "volume" in plugin.pluginCapabilities:
|
||||||
|
override_nextTemplate["values"] = {"device":plugin.device}
|
||||||
|
del args["redirect"]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
return self.__render(self.defaultTemplate)
|
override_nextTemplate = None
|
||||||
else:
|
|
||||||
## the parameter 'device' exists - we have to remove it
|
|
||||||
del args["device"]
|
|
||||||
## call the plugin handler
|
## call the plugin handler
|
||||||
nextTemplate = plugin.doAction(**args)
|
nextTemplate = plugin.doAction(**args)
|
||||||
## for 'volume' plugins: reread the dataset of the current disk
|
## for 'volume' plugins: reread the dataset of the current disk
|
||||||
## additionally: set the default template for plugins
|
## additionally: set the default template for plugins
|
||||||
if "volume" in plugin.pluginCapabilities:
|
if "volume" in plugin.pluginCapabilities:
|
||||||
|
## maybe the state of the current volume was changed?
|
||||||
self.dataset.setCurrentDiskState(plugin.device)
|
self.dataset.setCurrentDiskState(plugin.device)
|
||||||
if not nextTemplate: nextTemplate = { "plugin":"volume_mount", "values":{"device":plugin.device}}
|
if not nextTemplate: nextTemplate = { "plugin":"volume_mount", "values":{"device":plugin.device}}
|
||||||
else:
|
else:
|
||||||
|
## maybe a non-volume plugin changed some plugin settings (e.g. plugin_manager)
|
||||||
self.dataset.setPluginData()
|
self.dataset.setPluginData()
|
||||||
|
## update the container hdf-dataset (maybe a plugin changed the state of a container)
|
||||||
|
self.dataset.setContainersState()
|
||||||
|
## default page for non-volume plugins is the disk selection
|
||||||
if not nextTemplate: nextTemplate = { "plugin":"disks", "values":{} }
|
if not nextTemplate: nextTemplate = { "plugin":"disks", "values":{} }
|
||||||
|
## was a redirect requested?
|
||||||
|
if override_nextTemplate:
|
||||||
|
nextTemplate = override_nextTemplate
|
||||||
## if another plugins was choosen for 'nextTemplate', then do it!
|
## if another plugins was choosen for 'nextTemplate', then do it!
|
||||||
if isinstance(nextTemplate, types.DictType) \
|
if isinstance(nextTemplate, types.DictType) \
|
||||||
and "plugin" in nextTemplate.keys() \
|
and "plugin" in nextTemplate.keys() \
|
||||||
|
@ -216,7 +237,8 @@ class WebInterfaceSites:
|
||||||
|
|
||||||
examples are: non-https, readonly-config, ...
|
examples are: non-https, readonly-config, ...
|
||||||
"""
|
"""
|
||||||
if not self.cbox.prefs.isWriteable():
|
## TODO: maybe add an option "mount"?
|
||||||
|
if self.cbox.prefs.requiresPartition() and not self.cbox.prefs.getActivePartition():
|
||||||
self.dataset["Data.EnvironmentWarning"] = "ReadOnlyConfig"
|
self.dataset["Data.EnvironmentWarning"] = "ReadOnlyConfig"
|
||||||
# TODO: turn this on soon (add "not") - for now it is annoying
|
# TODO: turn this on soon (add "not") - for now it is annoying
|
||||||
if self.__checkHTTPS():
|
if self.__checkHTTPS():
|
||||||
|
@ -298,7 +320,6 @@ class WebInterfaceSites:
|
||||||
def __setDevice(self, device):
|
def __setDevice(self, device):
|
||||||
if device and re.match(u'[\w /\-]+$', device) and self.cbox.getContainer(device):
|
if device and re.match(u'[\w /\-]+$', device) and self.cbox.getContainer(device):
|
||||||
self.log.debug("select device: %s" % device)
|
self.log.debug("select device: %s" % device)
|
||||||
self.dataset.setCurrentDiskState(device)
|
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
self.log.warn("invalid device: %s" % device)
|
self.log.warn("invalid device: %s" % device)
|
||||||
|
@ -383,9 +404,6 @@ class WebInterfaceSites:
|
||||||
yield "Couldn't read clearsilver file: %s" % cs_path
|
yield "Couldn't read clearsilver file: %s" % cs_path
|
||||||
return
|
return
|
||||||
|
|
||||||
## update the container hdf-dataset (necessary if a plugin changed the state of a container)
|
|
||||||
self.dataset.setContainersState()
|
|
||||||
|
|
||||||
self.log.debug(self.dataset)
|
self.log.debug(self.dataset)
|
||||||
for key in self.dataset.keys():
|
for key in self.dataset.keys():
|
||||||
hdf.setValue(key,str(self.dataset[key]))
|
hdf.setValue(key,str(self.dataset[key]))
|
||||||
|
|
|
@ -4,9 +4,11 @@
|
||||||
# beware: .e.g "/dev/hd" grants access to _all_ harddisks
|
# beware: .e.g "/dev/hd" grants access to _all_ harddisks
|
||||||
AllowedDevices = /dev/loop, /dev/ubdb
|
AllowedDevices = /dev/loop, /dev/ubdb
|
||||||
|
|
||||||
|
# use sepepate config partition? (1=yes / 0=no)
|
||||||
|
UseConfigPartition = 1
|
||||||
|
|
||||||
# the default name prefix of not unnamed containers
|
# the default name prefix of not unnamed containers
|
||||||
DefaultVolumePrefix = "Data "
|
DefaultVolumePrefix = "Disk "
|
||||||
|
|
||||||
# which cipher should cryptsetup-luks use?
|
# which cipher should cryptsetup-luks use?
|
||||||
#TODO: uml does not support this module - DefaultCipher = aes-cbc-essiv:sha256
|
#TODO: uml does not support this module - DefaultCipher = aes-cbc-essiv:sha256
|
||||||
|
@ -22,8 +24,7 @@ ConfigVolumeLabel = cbox_config
|
||||||
MountParentDir = /var/cache/cryptobox/mnt
|
MountParentDir = /var/cache/cryptobox/mnt
|
||||||
|
|
||||||
# settings directory: contains name database and plugin configuration
|
# settings directory: contains name database and plugin configuration
|
||||||
#SettingsDir = /var/cache/cryptobox/settings
|
SettingsDir = /var/cache/cryptobox/settings
|
||||||
SettingsDir = .
|
|
||||||
|
|
||||||
# where are the clearsilver templates?
|
# where are the clearsilver templates?
|
||||||
#TemplateDir = /usr/share/cryptobox/templates
|
#TemplateDir = /usr/share/cryptobox/templates
|
||||||
|
|
|
@ -107,7 +107,7 @@ CryptoBoxRootActions = CryptoBoxRootActions
|
||||||
|
|
||||||
def testBrokenConfigs(self):
|
def testBrokenConfigs(self):
|
||||||
"""Check various broken configurations"""
|
"""Check various broken configurations"""
|
||||||
self.writeConfig("SettingsDir", "#out", filename=self.filenames["configFileBroken"])
|
self.writeConfig("SettingsDir", "SettingsDir=/foo/bar", filename=self.filenames["configFileBroken"])
|
||||||
self.assertRaises(CBConfigError, self.CryptoBox.CryptoBoxProps,self.filenames["configFileBroken"])
|
self.assertRaises(CBConfigError, self.CryptoBox.CryptoBoxProps,self.filenames["configFileBroken"])
|
||||||
self.writeConfig("Level", "Level = ho", filename=self.filenames["configFileBroken"])
|
self.writeConfig("Level", "Level = ho", filename=self.filenames["configFileBroken"])
|
||||||
self.assertRaises(CBConfigError, self.CryptoBox.CryptoBoxProps,self.filenames["configFileBroken"])
|
self.assertRaises(CBConfigError, self.CryptoBox.CryptoBoxProps,self.filenames["configFileBroken"])
|
||||||
|
|
Loading…
Reference in a new issue