cryptonas-branches/pythonrewrite/bin/CryptoBox.py

285 lines
8.9 KiB
Python
Raw Normal View History

#!/usr/bin/env python2.4
2006-08-16 13:07:57 +02:00
'''
This is the web interface for a fileserver managing encrypted filesystems.
2006-08-16 20:17:16 +02:00
2006-08-16 13:07:57 +02:00
It was originally written in bash/perl. Now a complete rewrite is in
progress. So things might be confusing here. Hopefully not for long.
:)
'''
2006-08-16 20:17:16 +02:00
# check python version
import sys
(ver_major, ver_minor, ver_sub, ver_desc, ver_subsub) = sys.version_info
if (ver_major < 2) or ((ver_major == 2) and (ver_minor < 4)):
sys.stderr.write("You need a python version >= 2.4\nCurrent version is:\n %s\n" % sys.version)
sys.exit(1)
2006-08-16 13:07:57 +02:00
import CryptoBoxContainer
from CryptoBoxExceptions import *
2006-08-16 20:17:16 +02:00
import re
import os
import CryptoBoxTools
2006-08-16 09:17:44 +02:00
2006-08-20 18:33:52 +02:00
class CryptoBox:
'''this class rules them all!
put things like logging, conf and oter stuff in here,
that might be used by more classes, it will be passed on to them'''
VERSION = "0.3~1"
def __init__(self, config_file=None):
import CryptoBoxSettings
self.log = self.__getStartupLogger()
self.prefs = CryptoBoxSettings.CryptoBoxSettings(config_file)
self.__runTests()
2006-08-20 18:33:52 +02:00
def __getStartupLogger(self):
2006-08-20 20:23:35 +02:00
import logging
2006-08-20 18:33:52 +02:00
'''initialises the logging system
use it with: 'self.log.[debug|info|warning|error|critical](logmessage)'
all classes should get the logging instance during __init__:
self.log = logging.getLogger("CryptoBox")
2006-08-20 18:33:52 +02:00
first we output all warnings/errors to stderr
as soon as we opened the config file successfully, we redirect debug output
to the configured destination'''
2006-08-20 18:33:52 +02:00
## basicConfig(...) needs python >= 2.4
2006-08-20 20:23:35 +02:00
try:
log_handler = logging.getLogger("CryptoBox")
logging.basicConfig(
format='%(asctime)s %(module)s %(levelname)s %(message)s',
stderr=sys.stderr)
log_handler.setLevel(logging.ERROR)
log_handler.info("loggingsystem is up'n running")
2006-08-20 20:23:35 +02:00
## from now on everything can be logged via self.log...
except:
2006-08-27 10:38:48 +02:00
raise CBEnvironmentError("couldn't initialise the loggingsystem. I give up.")
return log_handler
2006-08-20 18:33:52 +02:00
# do some initial checks
def __runTests(self):
2006-08-27 10:38:48 +02:00
self.__runTestRootPriv()
2006-08-27 10:38:48 +02:00
def __runTestRootPriv(self):
"""try to run 'super' with 'CryptoBoxRootActions'"""
import subprocess
try:
devnull = open(os.devnull, "w")
except IOError:
2006-08-27 10:38:48 +02:00
raise CBEnvironmentError("could not open %s for writing!" % os.devnull)
try:
prog_super = self.prefs["Programs"]["super"]
2006-08-27 10:38:48 +02:00
except KeyError:
raise CBConfigUndefinedError("Programs", "super")
try:
prog_rootactions = self.prefs["Programs"]["CryptoBoxRootActions"]
2006-08-27 10:38:48 +02:00
except KeyError:
raise CBConfigUndefinedError("Programs", "CryptoBoxRootActions")
2006-08-23 18:44:14 +02:00
try:
proc = subprocess.Popen(
shell = False,
stdout = devnull,
stderr = devnull,
2006-08-27 10:38:48 +02:00
args = [prog_super, prog_rootactions, "check"])
2006-08-23 18:44:14 +02:00
except OSError:
raise CBEnvironmentError("failed to execute 'super' (%s)" % self.prefs["Programs"]["super"])
proc.wait()
if proc.returncode != 0:
2006-08-27 10:38:48 +02:00
raise CBEnvironmentError("failed to call CryptoBoxRootActions (%s) via 'super' - maybe you did not add the appropriate line to /etc/super.tab?" % prog_rootactions)
# this method just demonstrates inheritance effects - may be removed
2006-08-23 20:19:37 +02:00
def cbx_inheritance_test(self, string="you lucky widow"):
self.log.info(string)
# RFC: why should CryptoBoxProps inherit CryptoBox? [l]
# RFC: shouldn't we move all useful functions of CryptoBoxProps to CryptoBox? [l]
2006-08-20 18:33:52 +02:00
class CryptoBoxProps(CryptoBox):
2006-08-16 13:07:57 +02:00
'''Get and set the properties of a CryptoBox
This class contains all available devices that may be accessed.
All properties of the cryptobox can be accessed by this class.
2006-08-16 13:07:57 +02:00
'''
2006-08-16 09:17:44 +02:00
def __init__(self, config_file=None):
2006-08-16 13:07:57 +02:00
'''read config and fill class variables'''
CryptoBox.__init__(self, config_file)
self.reReadContainerList()
def reReadContainerList(self):
2006-08-16 20:17:16 +02:00
self.containers = []
for device in CryptoBoxTools.getAvailablePartitions():
if self.isDeviceAllowed(device) and not self.isConfigPartition(device):
self.containers.append(CryptoBoxContainer.CryptoBoxContainer(device, self))
## sort by container name
self.containers.sort(cmp = lambda x,y: x.getName() < y.getName() and -1 or 1)
2006-08-16 20:17:16 +02:00
def isConfigPartition(self, device):
import subprocess
proc = subprocess.Popen(
shell = False,
stdout = subprocess.PIPE,
args = [
self.prefs["Programs"]["blkid"],
"-c", os.path.devnull,
"-o", "value",
"-s", "LABEL",
device])
(output, error) = proc.communicate()
return output.strip() == self.prefs["Main"]["ConfigVolumeLabel"]
2006-08-16 20:17:16 +02:00
def isDeviceAllowed(self, devicename):
"check if a device is white-listed for being used as cryptobox containers"
import types
allowed = self.prefs["Main"]["AllowedDevices"]
if type(allowed) == types.StringType: allowed = [allowed]
for a_dev in allowed:
"remove double dots and so on ..."
real_device = os.path.realpath(devicename)
if a_dev and re.search('^' + a_dev, real_device): return True
2006-08-16 20:17:16 +02:00
return False
def getLogData(self, lines=None, maxSize=None):
"""get the most recent log entries of the cryptobox
the maximum number and size of these entries can be limited by 'lines' and 'maxSize'
"""
# return nothing if the currently selected log output is not a file
try:
if self.prefs["Log"]["Destination"].upper() != "FILE": return []
log_file = self.prefs["Log"]["Details"]
except KeyError:
self.log.error("could not evaluate one of the following config settings: [Log]->Destination or [Log]->Details")
2006-08-27 10:38:48 +02:00
return []
try:
fd = open(log_file, "r")
if maxSize: fd.seek(-maxSize, 2) # seek relative to the end of the file
content = fd.readlines()
fd.close()
except IOError:
self.log.warn("failed to read the log file (%s)" % log_file)
2006-08-27 10:38:48 +02:00
return []
if lines: content = content[-lines:]
content.reverse()
return content
2006-08-16 20:17:16 +02:00
def getContainerList(self, filterType=None, filterName=None):
"retrieve the list of all containers of this cryptobox"
try:
result = self.containers[:]
if filterType != None:
if filterType in range(len(CryptoBoxContainer.Types)):
return [e for e in self.containers if e.getType() == filterType]
else:
self.log.info("invalid filterType (%d)" % filterType)
2006-08-16 20:17:16 +02:00
result.clear()
if filterName != None:
result = [e for e in self.containers if e.getName() == filterName]
return result
except AttributeError:
return []
def getContainer(self, device):
"retrieve the container element for this device"
all = [e for e in self.getContainerList() if e.device == device]
if all:
return all[0]
else:
return None
2006-08-16 20:17:16 +02:00
def setNameForUUID(self, uuid, name):
"assign a name to a uuid in the ContainerNameDatabase"
used_uuid = self.getUUIDForName(name)
"first remove potential conflicting uuid/name combination"
if used_uuid:
## remember the container which name was overriden
for e in self.containers:
if e.getName() == name:
forcedRename = e
break
del self.prefs.nameDB[used_uuid]
self.prefs.nameDB[uuid] = name
self.prefs.nameDB.write()
## rename the container that lost its name (necessary while we use cherrypy)
if used_uuid:
## this is surely not the best way to regenerate the name
dev = e.getDevice()
old_index = self.containers.index(e)
self.containers.remove(e)
self.containers.insert(old_index, CryptoBoxContainer.CryptoBoxContainer(dev,self))
2006-08-16 20:17:16 +02:00
def getNameForUUID(self, uuid):
"get the name belonging to a specified key (usually the UUID of a fs)"
try:
return self.prefs.nameDB[uuid]
2006-08-16 20:17:16 +02:00
except KeyError:
return None
def getUUIDForName(self, name):
""" get the key belonging to a value in the ContainerNameDatabase
this is the reverse action of 'getNameForUUID' """
for key in self.prefs.nameDB.keys():
if self.prefs.nameDB[key] == name: return key
2006-08-16 20:17:16 +02:00
"the uuid was not found"
return None
def getAvailableLanguages(self):
'''reads all files in path LangDir and returns a list of
basenames from existing hdf files, that should are all available
languages'''
languages = []
for file in os.listdir(self.prefs["Locations"]["LangDir"]):
if file.endswith(".hdf"): languages.append(file.rstrip(".hdf"))
if len(languages) < 1:
self.log.warn("No .hdf files found! The website won't render properly.")
return languages
def getAvailableDocLanguages(self):
'''reads all dirs in path DocDir and returns a list of
available documentation languages, this list might be empty.'''
doclangs = []
regpat = re.compile(r"^\w+$")
try:
doc_dir = self.prefs["Locations"]["DocDir"]
except KeyError:
self.log.error("Could not find a configuration setting: [Locations]->DocDir - please check the config file")
return []
if not os.path.exists(doc_dir):
self.log.error("The configured documentation directory (%s) does not exist" % (doc_dir, ))
return []
try:
for e in os.listdir(doc_dir):
if regpat.search(e) and os.path.isdir(os.path.join(doc_dir, e)):
doclangs.append(e)
if len(doclangs) < 1:
self.log.warn("Didn't find any documentation files.")
return doclangs
except OSError:
self.log.error("Could not access the documentations directory (%s)" % (doc_dir,))
return []
2006-08-16 20:17:16 +02:00
if __name__ == "__main__":
2006-08-22 06:29:53 +02:00
cb = CryptoBox()