changed coding style according to pylint

This commit is contained in:
lars 2006-12-05 12:24:47 +00:00
parent f6295a4b2d
commit fe69eb38ae
66 changed files with 2355 additions and 1515 deletions

View File

@ -31,6 +31,8 @@ Syntax:
this script will always return with an exitcode 0 (true), if "check" is the only argument
"""
__revision__ = "$Id"
import os
import sys
import subprocess

View File

@ -24,16 +24,42 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
__revision__ = "$Id"
import os, sys
import cryptobox.web.sites
from cryptobox.core.exceptions import *
from optparse import OptionParser
## check python version
(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\n")
sys.stderr.write("Current version is: %s\n" % sys.version)
sys.exit(1)
## check cherrypy dependency
try:
import cherrypy
except:
print "Could not import the cherrypy module! Try 'apt-get install python-cherrypy'."
sys.stderr.write("Could not import the cherrypy module!\n")
sys.stderr.write("Try 'apt-get install python-cherrypy'.\n")
sys.exit(1)
## check clearsilver dependency
try:
import neo_cgi, neo_util
except:
sys.stderr.write("Could not import the clearsilver module!\n")
sys.stderr.write("Try 'apt-get install python-clearsilver'.\n")
sys.exit(1)
## check configobj dependency
try:
import configobj, validate
except:
sys.stderr.write("Could not import the configobj or validate module!\n")
sys.stderr.write("Try 'apt-get install python-configobj'.\n")
sys.exit(1)
@ -129,17 +155,22 @@ def close_open_files():
def write_pid_file(pid_file):
if os.path.exists(pid_file):
sys.stderr.write(
"Warning: pid file (%s) already exists - overwriting ...\n" % pid_file)
try:
pidf = open(pid_file,"w")
pidf.write(str(os.getpid()))
pidf.close()
except (IOError, OSError), errMsg:
sys.stderr.write("Warning: failed to write pid file (%s): %s\n" % (pid_file, errMsg))
sys.stderr.write(
"Warning: failed to write pid file (%s): %s\n" % (pid_file, errMsg))
## it is just a warning - no need to break
def parseOptions():
version = "%prog" + cryptobox.core.main.VERSION
import cryptobox
version = "%prog" + cryptobox.__version__
parser = OptionParser(version=version)
parser.set_defaults(conffile="/etc/cryptobox-server/cryptobox.conf",
pidfile="/var/run/cryptobox-server/webserver.pid",

33
bin/do_pylint.sh Executable file
View File

@ -0,0 +1,33 @@
#!/bin/sh
#
# set some environmental variables for pylint
#
PROJ_DIR=$(dirname "$0")/..
PROJ_DIR=$(cd "$PROJ_DIR"; pwd)
PYLINTRC=$PROJ_DIR/src/pylintrc
PYTHONPATH=$PROJ_DIR/src
function check_for_filename()
{
# maybe the argument is a file instead of a module name
if echo "$1" | grep -q "\.py$" && test -e "$1"
then local FILE_DIR=$(dirname "$1")
local MODULE=$(basename "${1%.py}")
ARGS="${ARGS} ${MODULE}"
PYTHONPATH="${PYTHONPATH}:${FILE_DIR}"
else ARGS="${ARGS} ${1}"
fi
}
while test $# -gt 0
do check_for_filename "$1"
shift
done
export PYTHONPATH
export PYLINTRC
pylint $ARGS

View File

@ -16,7 +16,6 @@
# Short-Description: start CryptoBox webserver
### END INIT INFO
# read the default setting file, if it exists
[ -r /etc/default/cryptobox-server ] && source /etc/default/cryptobox-server
@ -33,6 +32,7 @@ CONF_FILE=/etc/cryptobox-server/cryptobox.conf
[ "$NO_START" = "1" ] && exit 0
DAEMON=/usr/sbin/CryptoBoxWebserver
PYTHON_EXEC=/usr/bin/python
PIDFILE=/var/run/cryptobox-server/webserver.pid
DESC="CryptoBox Daemon (webinterface)"
OPTIONS="-B --pidfile=$PIDFILE --config=$CONF_FILE --logfile=$LOGFILE --host=$HOST --port=$PORT $SERVER_OPTS"
@ -45,11 +45,7 @@ test -e "$DAEMON" || exit 0
case "$1" in
start )
# TODO: mount config dir
# TODO: create certificate
# TODO: run stunnel
# the lines above should go into the live-cd scripts
## create the directory of the pid file if necessary
# create the directory of the pid file if necessary
PIDDIR=$(dirname "$PIDFILE")
if [ -d "$PIDDIR" ]
then mkdir -p "$PIDDIR"
@ -60,14 +56,21 @@ case "$1" in
if start-stop-daemon \
--chuid $RUNAS: --quiet --start \
--user $RUNAS --pidfile "$PIDFILE" \
--exec /usr/bin/python --startas "$DAEMON" -- $OPTIONS
--startas "$PYTHON_EXEC" -- "$DAEMON" $OPTIONS
then log_end_msg 0
else log_end_msg 1
fi
;;
stop )
log_daemon_msg "Stopping cryptobox webserver" "$DESC"
if start-stop-daemon --quiet --stop \
# if there is no pid file for some reason, then we try to find the process
if test ! -e "$PIDFILE"
then if start-stop-daemon --quiet --stop --user "$RUNAS" --exec "$PYTHON_EXEC"
then log_end_msg 0
else log_end_msg 1
fi
# there is a pid file - great!
elif start-stop-daemon --quiet --stop \
--pidfile "$PIDFILE" \
--user "$RUNAS"
then test -e "$PIDFILE" && rm "$PIDFILE"

View File

@ -18,29 +18,39 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
"""Change date and time.
"""
__revision__ = "$Id"
import cryptobox.plugins.base
class date(cryptobox.plugins.base.CryptoBoxPlugin):
"""The date feature of the CryptoBox.
"""
pluginCapabilities = [ "system" ]
pluginVisibility = [ "preferences" ]
requestAuth = False
plugin_capabilities = [ "system" ]
plugin_visibility = [ "preferences" ]
request_auth = False
rank = 10
def doAction(self, store=None, year=0, month=0, day=0, hour=0, minute=0):
def do_action(self, store=None, year=0, month=0, day=0, hour=0, minute=0):
"""The action handler.
"""
import datetime
if store:
try:
year, month, day = int(year), int(month), int(day)
hour, minute = int(hour), int(minute)
new_date = datetime.datetime(year, month, day, hour, minute)
## check if the values are valid
datetime.datetime(year, month, day, hour, minute)
except ValueError:
self.hdf["Data.Warning"] = "Plugins.date.InvalidDate"
self.__prepareFormData()
self.__prepare_form_data()
return "form_date"
date = "%02d%02d%02d%02d%d" % (month, day, hour, minute, year)
if self.__setDate(date):
if self.__set_date(date):
self.cbox.log.info("changed date to: %s" % date)
self.hdf["Data.Success"] = "Plugins.date.DateChanged"
return None
@ -48,33 +58,42 @@ class date(cryptobox.plugins.base.CryptoBoxPlugin):
## a failure should usually be an invalid date (we do not check it really)
self.cbox.log.info("failed to set date: %s" % date)
self.hdf["Data.Warning"] = "Plugins.date.InvalidDate"
self.__prepareFormData()
self.__prepare_form_data()
return "form_date"
else:
self.__prepareFormData()
self.__prepare_form_data()
return "form_date"
def getStatus(self):
now = self.__getCurrentDate()
return "%d/%d/%d/%d/%d/%d" % (now.year, now.month, now.day, now.hour, now.minute, now.second)
def get_status(self):
"""Retrieve the status of the feature.
"""
now = self.__get_current_date()
return "%d/%d/%d/%d/%d/%d" % \
(now.year, now.month, now.day, now.hour, now.minute, now.second)
def __prepareFormData(self):
date = self.__getCurrentDate()
self.hdf[self.hdf_prefix + "year"] = date.year
self.hdf[self.hdf_prefix + "month"] = date.month
self.hdf[self.hdf_prefix + "day"] = date.day
self.hdf[self.hdf_prefix + "hour"] = date.hour
self.hdf[self.hdf_prefix + "minute"] = date.minute
def __prepare_form_data(self):
"""Set some hdf values.
"""
cur_date = self.__get_current_date()
self.hdf[self.hdf_prefix + "year"] = cur_date.year
self.hdf[self.hdf_prefix + "month"] = cur_date.month
self.hdf[self.hdf_prefix + "day"] = cur_date.day
self.hdf[self.hdf_prefix + "hour"] = cur_date.hour
self.hdf[self.hdf_prefix + "minute"] = cur_date.minute
def __getCurrentDate(self):
def __get_current_date(self):
"""Retrieve the current date and time.
"""
import datetime
return datetime.datetime(2000,1,1).now()
return datetime.datetime(2000, 1, 1).now()
def __setDate(self, date):
def __set_date(self, date):
"""Set a new date and time.
"""
import subprocess
import os
proc = subprocess.Popen(
@ -83,7 +102,7 @@ class date(cryptobox.plugins.base.CryptoBoxPlugin):
self.cbox.prefs["Programs"]["super"],
self.cbox.prefs["Programs"]["CryptoBoxRootActions"],
"plugin",
os.path.join(self.pluginDir, "root_action.py"),
os.path.join(self.plugin_dir, "root_action.py"),
date])
proc.wait()
return proc.returncode == 0

View File

@ -19,6 +19,8 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
__revision__ = "$Id"
## necessary: otherwise CryptoBoxRootActions.py will refuse to execute this script
PLUGIN_TYPE = "cryptobox"

View File

@ -18,18 +18,20 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
__revision__ = "$Id"
import cryptobox.web.testclass
class unittests(cryptobox.web.testclass.WebInterfaceTestClass):
def test_get_date(self):
"""retrieve the current date"""
date = self._getCurrentDate()
date = self._get_current_date()
def test_change_date(self):
"""set the date back and forth"""
now = self._getCurrentDate()
now = self._get_current_date()
## copy current time
new_date = dict(now)
## move three minutes forward (more is not nice because of screensavers)
@ -38,10 +40,10 @@ class unittests(cryptobox.web.testclass.WebInterfaceTestClass):
new_date["hour"] = now["hour"] + ((now["minute"] + 3) / 60)
## move forward ...
self._setDate(new_date)
self.assertEquals(new_date, self._getCurrentDate())
self.assertEquals(new_date, self._get_current_date())
## ... and backward
self._setDate(now)
self.assertEquals(now, self._getCurrentDate())
self.assertEquals(now, self._get_current_date())
def test_try_broken_date(self):
@ -54,8 +56,8 @@ class unittests(cryptobox.web.testclass.WebInterfaceTestClass):
self.cmd.find("invalid value for date")
def _getCurrentDate(self):
date_url = self.URL + "date"
def _get_current_date(self):
date_url = self.url + "date"
self.register_auth(date_url)
self.cmd.go(date_url)
self.cmd.find("Data.Status.Plugins.date=([0-9]+/[0-9]+/[0-9]+/[0-9]+/[0-9]+/[0-9]+)$", "m")
@ -73,7 +75,7 @@ class unittests(cryptobox.web.testclass.WebInterfaceTestClass):
def _setDate(self, date):
"""for now we have to use this function instead of the one below"""
date_url = self.URL + "date?weblang=en&store=1&year=%s&month=%s&day=%s&hour=%s&minute=%s"\
date_url = self.url + "date?weblang=en&store=1&year=%s&month=%s&day=%s&hour=%s&minute=%s"\
% (str(date["year"]), str(date["month"]), str(date["day"]),
str(date["hour"]), str(date["minute"]))
self.register_auth(date_url)
@ -83,7 +85,7 @@ class unittests(cryptobox.web.testclass.WebInterfaceTestClass):
def _setDateBroken(self, date):
"""this should work, but the parsing of twill seems to be broken
as soon as the twill bug is fixed, we should use this function"""
date_url = self.URL + "date"
date_url = self.url + "date"
self.register_auth(date_url)
self.cmd.go(date_url)
self.cmd.formvalue("set_date", "year", str(date["year"]))

View File

@ -18,21 +18,31 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
"""The disks feature of the CryptoBox.
"""
__revision__ = "$Id"
import cryptobox.plugins.base
class disks(cryptobox.plugins.base.CryptoBoxPlugin):
"""The disk feature of the CryptoBox.
"""
pluginCapabilities = [ "system" ]
pluginVisibility = [ "menu" ]
requestAuth = False
plugin_capabilities = [ "system" ]
plugin_visibility = [ "menu" ]
request_auth = False
rank = 10
def doAction(self):
self.cbox.reReadContainerList()
def do_action(self):
"""The action handler.
"""
self.cbox.reread_container_list()
return "disks"
def getStatus(self):
return ":".join([e.getDevice() for e in self.cbox.getContainerList()])
def get_status(self):
"""Retrieve the current status of the feature.
"""
return ":".join([e.get_device() for e in self.cbox.get_container_list()])

View File

@ -18,24 +18,26 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
__revision__ = "$Id"
import cryptobox.web.testclass
class unittests(cryptobox.web.testclass.WebInterfaceTestClass):
def test_read_form(self):
'''display all devices'''
self.register_auth(self.URL)
self.cmd.go(self.URL + "disks?weblang=en")
self.register_auth(self.url)
self.cmd.go(self.url + "disks?weblang=en")
self.cmd.find("Available disks")
def test_is_device_in_list(self):
"""check if the device-under-test is in the device list"""
self.register_auth(self.URL)
self.cmd.go(self.URL + "disks?weblang=en")
self.register_auth(self.url)
self.cmd.go(self.url + "disks?weblang=en")
self.cmd.find("Available disks")
self.cmd.find(u'Data.Status.Plugins.disks=(.*)$', "m")
devices = self.locals["__match__"].split(":")
self.assertTrue(len(devices)>0)
self.assertTrue(("/dev/%s1" % self.device in devices) or ("/dev/%s2" % self.device in devices))
self.assertTrue("/dev/%s" % self.device in devices)

View File

@ -18,26 +18,34 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
"""The help feature of the CryptoBox.
"""
__revision__ = "$Id"
import cryptobox.plugins.base
class help(cryptobox.plugins.base.CryptoBoxPlugin):
"""The help feature of the CryptoBox.
"""
pluginCapabilities = [ "system" ]
pluginVisibility = [ "menu" ]
requestAuth = False
plugin_capabilities = [ "system" ]
plugin_visibility = [ "menu" ]
request_auth = False
rank = 80
default_lang = 'en'
default_page = "CryptoBoxUser"
def doAction(self, page=""):
def do_action(self, page=""):
'''prints the offline wikipage
'''
import re,os
import re, os
## check for invalid characters and if the page exists in the default language
if page and \
not re.search(u'\W', page) and \
os.path.isfile(os.path.join(self.cbox.prefs["Locations"]["DocDir"], self.default_lang, page + '.html')):
os.path.isfile(os.path.join(self.cbox.prefs["Locations"]["DocDir"],
self.default_lang, page + '.html')):
## everything is ok
pass
else:
@ -49,20 +57,23 @@ class help(cryptobox.plugins.base.CryptoBoxPlugin):
## store the name of the page
self.hdf[self.hdf_prefix + "Page"] = page
## choose the right language
for l in self.site.langOrder:
if os.path.isfile(os.path.join(self.cbox.prefs["Locations"]["DocDir"], l, page + '.html')):
lang = l
for lang in self.site.lang_order:
if os.path.isfile(os.path.join(self.cbox.prefs["Locations"]["DocDir"],
lang, page + '.html')):
doc_lang = lang
break
else:
lang = self.default_lang
self.hdf[self.hdf_prefix + "Language"] = lang
doc_lang = self.default_lang
self.hdf[self.hdf_prefix + "Language"] = doc_lang
## store the current setting for a later "getStatus" call
self.current_lang = lang
self.current_lang = doc_lang
self.current_page = page
return "doc"
def getStatus(self):
def get_status(self):
"""Retrieve the current status of the feature.
"""
return "%s:%s" % (self.current_lang, self.current_page)

View File

@ -18,6 +18,8 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
__revision__ = "$Id"
import cryptobox.web.testclass
from twill.errors import *
@ -27,7 +29,7 @@ class unittests(cryptobox.web.testclass.WebInterfaceTestClass):
'''help pages should be available in different languages'''
## check english help pages
self.cmd.go(self.URL + "help?weblang=en")
self.cmd.go(self.url + "help?weblang=en")
self.cmd.find("Table of Contents")
self.cmd.find("Getting started")
(lang,page) = self._getHelpStatus()
@ -35,7 +37,7 @@ class unittests(cryptobox.web.testclass.WebInterfaceTestClass):
self.assertTrue(page == "CryptoBoxUser")
## check german help pages
self.cmd.go(self.URL + "help?weblang=de")
self.cmd.go(self.url + "help?weblang=de")
self.cmd.find("Table of Contents")
self.cmd.find("Wie geht es los")
(lang,page) = self._getHelpStatus()
@ -43,7 +45,7 @@ class unittests(cryptobox.web.testclass.WebInterfaceTestClass):
self.assertTrue(page == "CryptoBoxUser")
## check slovene help pages
self.cmd.go(self.URL + "help?weblang=sl")
self.cmd.go(self.url + "help?weblang=sl")
self.assertRaises(TwillAssertionError, self.cmd.notfind, "Table of Contents")
## add a slovene text here, as soon as the help is translated
(lang,page) = self._getHelpStatus()
@ -52,7 +54,7 @@ class unittests(cryptobox.web.testclass.WebInterfaceTestClass):
self.assertTrue(page == "CryptoBoxUser")
## check french help pages
self.cmd.go(self.URL + "help?weblang=fr")
self.cmd.go(self.url + "help?weblang=fr")
self.assertRaises(TwillAssertionError, self.cmd.notfind, "Table of Contents")
## add a french text here, as soon as the help is translated
(lang,page) = self._getHelpStatus()
@ -61,7 +63,7 @@ class unittests(cryptobox.web.testclass.WebInterfaceTestClass):
self.assertTrue(page == "CryptoBoxUser")
## test a random language - it should fall back to english
self.cmd.go(self.URL + "help?weblang=foobar")
self.cmd.go(self.url + "help?weblang=foobar")
self.assertRaises(TwillAssertionError, self.cmd.notfind, "Table of Contents")
(lang,page) = self._getHelpStatus()
self.assertTrue(lang == "en")
@ -70,18 +72,18 @@ class unittests(cryptobox.web.testclass.WebInterfaceTestClass):
def test_help_pages(self):
"""check invalid page requests"""
self.cmd.go(self.URL + "help?page=foobar")
self.cmd.go(self.url + "help?page=foobar")
(lang,page) = self._getHelpStatus()
self.assertTrue(page == "CryptoBoxUser")
self.cmd.go(self.URL + "help?page=CryptoBoxUser")
self.cmd.go(self.url + "help?page=CryptoBoxUser")
(lang,page) = self._getHelpStatus()
self.assertTrue(page == "CryptoBoxUser")
def test_help_default_languages(self):
"""check invalid page requests"""
self.cmd.go(self.URL + "help?weblang=foobar")
self.cmd.go(self.url + "help?weblang=foobar")
(lang,page) = self._getHelpStatus()
self.assertTrue(lang == "en")

View File

@ -18,20 +18,31 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
"""The language_selection feature of the CryptoBox.
"""
__revision__ = "$Id"
import cryptobox.plugins.base
class language_selection(cryptobox.plugins.base.CryptoBoxPlugin):
"""The language_selection feature of the CryptoBox.
"""
pluginCapabilities = [ "system" ]
pluginVisibility = [ "menu", "preferences" ]
requestAuth = False
plugin_capabilities = [ "system" ]
plugin_visibility = [ "menu", "preferences" ]
request_auth = False
rank = 60
def doAction(self):
def do_action(self):
"""Show all available languages.
"""
return "language_selection"
def getStatus(self):
return ":".join(self.site.langOrder)
def get_status(self):
"""The current status of the feature is defined as the current language.
"""
return ":".join(self.site.lang_order)

View File

@ -18,19 +18,21 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
__revision__ = "$Id"
import cryptobox.web.testclass
class unittests(cryptobox.web.testclass.WebInterfaceTestClass):
def test_read_form(self):
url = self.URL + "language_selection?weblang=en"
url = self.url + "language_selection?weblang=en"
self.register_auth(url)
self.cmd.go(url)
self.cmd.find('hoose an interface language')
def test_check_language_list(self):
url = self.URL + "language_selection"
url = self.url + "language_selection"
self.register_auth(url)
self.cmd.go(url)
self.cmd.find(u'Data.Status.Plugins.language_selection=(.*)$', "m")

View File

@ -18,56 +18,76 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
"""The logs feature of the CryptoBox.
"""
__revision__ = "$Id"
import cryptobox.plugins.base
import os
class logs(cryptobox.plugins.base.CryptoBoxPlugin):
"""The logs feature of the CryptoBox.
"""
pluginCapabilities = [ "system" ]
pluginVisibility = [ "preferences" ]
requestAuth = False
plugin_capabilities = [ "system" ]
plugin_visibility = [ "preferences" ]
request_auth = False
rank = 90
def doAction(self, lines=50, size=3000, pattern=None):
def do_action(self, lines=50, size=3000, pattern=None):
"""Show the latest part of the log file.
"""
import re
## filter input
try:
lines = int(lines)
if lines <= 0: raise(ValueError)
if lines <= 0:
raise(ValueError)
except ValueError:
lines = 50
try:
size = int(size)
if size <= 0: raise(ValueError)
if size <= 0:
raise(ValueError)
except ValueError:
size = 3000
if not pattern is None:
pattern = str(pattern)
if re.search(u'\W', pattern): pattern = None
self.hdf[self.hdf_prefix + "Content"] = self.__getLogContent(lines, size, pattern)
self.hdf[self.hdf_prefix + "StyleSheetFile"] = os.path.abspath(os.path.join(self.pluginDir, "logs.css"))
if re.search(u'\W', pattern):
pattern = None
self.hdf[self.hdf_prefix + "Content"] = self.__get_log_content(
lines, size, pattern)
self.hdf[self.hdf_prefix + "StyleSheetFile"] = os.path.abspath(os.path.join(
self.plugin_dir, "logs.css"))
return "show_log"
def getStatus(self):
def get_status(self):
"""The current status includes the log configuration details.
"""
return "%s:%s:%s" % (
self.cbox.prefs["Log"]["Level"],
self.cbox.prefs["Log"]["Destination"],
self.cbox.prefs["Log"]["Details"])
def __getLogContent(self, lines, maxSize, pattern):
import re
def __get_log_content(self, lines, max_size, pattern):
"""Filter, sort and shorten the log content.
"""
if pattern:
content = []
current_length = 0
for line in self.cbox.getLogData():
for line in self.cbox.get_log_data():
if line.find(pattern) != -1:
content.append(line)
current_length += len(line)
if lines and len(content) >=lines: break
if maxSize and current_length >=maxSize: break
if lines and len(content) >= lines:
break
if max_size and current_length >= max_size:
break
else:
content = self.cbox.getLogData(lines, maxSize)
content = self.cbox.get_log_data(lines, max_size)
return "<br/>".join(content)

View File

@ -18,12 +18,14 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
__revision__ = "$Id"
import cryptobox.web.testclass
class unittests(cryptobox.web.testclass.WebInterfaceTestClass):
def test_read_logs(self):
log_url = self.URL + "logs"
log_url = self.url + "logs"
self.register_auth(log_url)
self.cmd.go(log_url)
self.cmd.find('class="console"')
@ -31,13 +33,13 @@ class unittests(cryptobox.web.testclass.WebInterfaceTestClass):
def test_write_logs(self):
log_text = "unittest - just a marker - please ignore"
self.cbox.log.error(log_text)
log_url = self.URL + "logs"
log_url = self.url + "logs"
self.register_auth(log_url)
self.cmd.go(log_url + "?pattern=ERROR")
self.cmd.find(log_text)
def test_invalid_args(self):
log_url = self.URL + "logs"
log_url = self.url + "logs"
self.cmd.go(log_url + "?lines=10")
self.cmd.find('class="console"')
self.cmd.go(log_url + "?lines=0")

View File

@ -18,23 +18,32 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
"""The network feature of the CryptoBox.
"""
__revision__ = "$Id"
import subprocess
import os
import cryptobox.plugins.base
## specify (in seconds), how long we should wait before redirecting and ip change
REDIRECT_DELAY=20
CHANGE_IP_DELAY=1
REDIRECT_DELAY = 10
CHANGE_IP_DELAY = 2
class network(cryptobox.plugins.base.CryptoBoxPlugin):
"""The network feature of the CryptoBox.
"""
pluginCapabilities = [ "system" ]
pluginVisibility = [ "preferences" ]
requestAuth = True
plugin_capabilities = [ "system" ]
plugin_visibility = [ "preferences" ]
request_auth = True
rank = 30
def doAction(self, store=None, redirected="", ip1="", ip2="", ip3="", ip4=""):
def do_action(self, store=None, redirected="", ip1="", ip2="", ip3="", ip4=""):
"""Show a form containing the current IP - change it if requested.
"""
## if we were redirected, then we should display the default page
self.cbox.log.debug("executing network plugin")
if redirected == "1":
@ -46,58 +55,68 @@ class network(cryptobox.plugins.base.CryptoBoxPlugin):
try:
for ip_in in (ip1, ip2, ip3, ip4):
if (int(ip_in) < 0) or (int(ip_in) > 255):
self.cbox.log.info("invalid IP supplied: %s" % str((ip1,ip2,ip3,ip4)))
self.cbox.log.info("invalid IP supplied: %s" % \
str((ip1, ip2, ip3, ip4)))
raise ValueError
ip = "%d.%d.%d.%d" % (int(ip1), int(ip2), int(ip3), int(ip4))
new_ip = "%d.%d.%d.%d" % (int(ip1), int(ip2), int(ip3), int(ip4))
except ValueError:
self.hdf["Data.Warning"] = "Plugins.network.InvalidIP"
self.__prepareFormData()
self.__prepare_form_data()
return "form_network"
if self.__setIP(ip):
self.cbox.log.info("the IP was successfully changed: %s" % ip)
if self.__set_ip(new_ip):
self.cbox.log.info("the IP was successfully changed: %s" % new_ip)
self.hdf["Data.Success"] = "Plugins.network.IPChanged"
self.hdf["Data.Redirect.URL"] = self.__getRedirectDestination(ip)
self.hdf["Data.Redirect.URL"] = self.__get_redirect_destination(new_ip)
self.hdf["Data.Redirect.Delay"] = REDIRECT_DELAY
return None
else:
self.cbox.log.warn("failed to change IP address to: %s" % ip)
self.cbox.log.warn("failed to change IP address to: %s" % new_ip)
self.hdf["Data.Warning"] = "Plugins.network.InvalidIP"
self.__prepareFormData()
self.__prepare_form_data()
return "form_network"
else:
self.cbox.log.debug("network plugin: show form")
## just show the form
self.__prepareFormData()
self.__prepare_form_data()
return "form_network"
def getStatus(self):
return "%d.%d.%d.%d" % self.__getCurrentIP()
def get_status(self):
"""The current IP is the status of this feature.
"""
return "%d.%d.%d.%d" % self.__get_current_ip()
def __getRedirectDestination(self, ip):
def __get_redirect_destination(self, ip):
"""Put the new URL together.
"""
import cherrypy
req = cherrypy.request
base_parts = req.base.split(":")
dest = "%s:%s" % (base_parts[0], ip)
dest = "%s://%s" % (base_parts[0], ip)
if len(base_parts) == 3:
dest += ":%s" % base_parts[2]
return dest
def __prepareFormData(self):
(oc1, oc2, oc3, oc4) = self.__getCurrentIP()
def __prepare_form_data(self):
"""Set some hdf values.
"""
(oc1, oc2, oc3, oc4) = self.__get_current_ip()
self.hdf[self.hdf_prefix + "ip.oc1"] = oc1
self.hdf[self.hdf_prefix + "ip.oc2"] = oc2
self.hdf[self.hdf_prefix + "ip.oc3"] = oc3
self.hdf[self.hdf_prefix + "ip.oc4"] = oc4
def __getCurrentIP(self):
def __get_current_ip(self):
"""Retrieve the current IP.
"""
import re
import imp
## load some values from the root_action.py script
root_action_plug = imp.load_source("root_action", os.path.join(self.pluginDir, "root_action.py"))
root_action_plug = imp.load_source("root_action",
os.path.join(self.plugin_dir, "root_action.py"))
## get the current IP of the network interface
proc = subprocess.Popen(
shell = False,
@ -106,20 +125,25 @@ class network(cryptobox.plugins.base.CryptoBoxPlugin):
root_action_plug.IFCONFIG_BIN,
root_action_plug.IFACE])
(stdout, stderr) = proc.communicate()
if proc.returncode != 0: return (0,0,0,0)
if proc.returncode != 0:
return (0,0,0,0)
## this regex matches the four numbers of the IP
match = re.search(u'inet [\w]+:(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\s', stdout)
if match:
## use the previously matched numbers
return tuple([int(e) for e in match.groups()])
else:
return (0,0,0,0)
return (0, 0, 0, 0)
def __setIP(self, ip):
def __set_ip(self, new_ip):
"""Change the IP.
"""
import threading
## call the root_action script after some seconds - so we can deliver the page before
def delayedIPchange():
def delayed_ip_change():
"""A threaded function to change the IP.
"""
import time
time.sleep(CHANGE_IP_DELAY)
proc = subprocess.Popen(
@ -129,19 +153,17 @@ class network(cryptobox.plugins.base.CryptoBoxPlugin):
self.cbox.prefs["Programs"]["super"],
self.cbox.prefs["Programs"]["CryptoBoxRootActions"],
"plugin",
os.path.join(self.pluginDir, "root_action.py"),
ip])
os.path.join(self.plugin_dir, "root_action.py"),
new_ip])
proc.wait()
if proc.returncode != 0:
self.cbox.log.warn("failed to change IP address: %s" % ip)
self.cbox.log.warn("failed to change IP address: %s" % new_ip)
self.cbox.log.warn("error output: %s" % str(proc.stderr.read()))
return
thread = threading.Thread()
thread.run = delayedIPchange
thread.run = delayed_ip_change
thread.setDaemon(True)
thread.start()
# TODO: how could we guess, if it failed?
return True

View File

@ -19,6 +19,8 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
__revision__ = "$Id"
#TODO: add netmask and gateway

View File

@ -18,6 +18,8 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
__revision__ = "$Id"
import cryptobox.web.testclass
from network import CHANGE_IP_DELAY
@ -28,43 +30,47 @@ class unittests(cryptobox.web.testclass.WebInterfaceTestClass):
'''change of network address'''
## the time module is necessary for the CHANGE_IP_DELAY
import time
self.register_auth(self.URL + "network")
self.cmd.go(self.URL + "network")
self.register_auth(self.url + "network")
## do not follow redirects - they would break the test otherwise
self.cmd.config("acknowledge_equiv_refresh", 0)
self.cmd.go(self.url + "network")
## extract the current IP from the network plugin output
def getCurrentIP():
self.cmd.go(self.URL + "network")
def get_current_ip():
self.register_auth(self.url + "network")
self.cmd.go(self.url + "network")
self.cmd.find(u'Data.Status.Plugins.network=([0-9\.]*)$', "m")
return self.locals["__match__"]
origIPtext = getCurrentIP()
origIPocts = origIPtext.split(".")
orig_ip_text = get_current_ip()
orig_ip_octs = orig_ip_text.split(".")
## check, if the original IP is valid (contains four octets)
self.assertEquals(4, len(origIPocts))
wrongIP = "192.168.123.321"
def setIP((ip1, ip2, ip3, ip4)):
self.cmd.go(self.URL + "network")
self.assertEquals(4, len(orig_ip_octs))
def set_ip((ip1, ip2, ip3, ip4)):
self.cmd.go(self.url + "network")
self.cmd.formvalue("set_ip", "ip1", str(ip1))
self.cmd.formvalue("set_ip", "ip2", str(ip2))
self.cmd.formvalue("set_ip", "ip3", str(ip3))
self.cmd.formvalue("set_ip", "ip4", str(ip4))
self.cmd.submit()
## sleep a little bit longer than the delay necessary for ip-change
time.sleep(CHANGE_IP_DELAY + 0.2)
setIP([1,-2,0,1])
self.assertEquals(origIPtext, getCurrentIP())
setIP([1,0,0,256])
self.assertEquals(origIPtext, getCurrentIP())
setIP([1,"foo",0,1])
self.assertEquals(origIPtext, getCurrentIP())
setIP([10,12,0,2])
self.assertEquals("10.12.0.2", getCurrentIP())
setIP(origIPocts)
self.assertEquals(origIPtext, getCurrentIP())
time.sleep(CHANGE_IP_DELAY + 3)
set_ip([1,-2,0,1])
self.assertEquals(orig_ip_text, get_current_ip())
set_ip([1,0,0,256])
self.assertEquals(orig_ip_text, get_current_ip())
set_ip([1,"foo",0,1])
self.assertEquals(orig_ip_text, get_current_ip())
new_ip = orig_ip_octs[:]
new_ip[3] = str((int(orig_ip_octs[3]) + 128) % 256)
set_ip(new_ip)
self.assertEquals(".".join(new_ip), get_current_ip())
set_ip(orig_ip_octs)
self.assertEquals(orig_ip_text, get_current_ip())
def test_inputs(self):
self.register_auth(self.URL + "network")
self.cmd.go(self.URL + "network" + "?redirected=1")
self.register_auth(self.url + "network")
self.cmd.go(self.url + "network" + "?redirected=1")
self.cmd.notfind("problem")
self.cmd.go(self.URL + "network" + "?store=1")
self.cmd.go(self.url + "network" + "?store=1")
self.cmd.find("invalid network address")

View File

@ -18,114 +18,141 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
"""The partition feature of the CryptoBox.
"""
__revision__ = "$Id"
import subprocess
import os
import logging
import cryptobox.core.tools as cbxTools
import cryptobox.plugins.base
class partition(cryptobox.plugins.base.CryptoBoxPlugin):
pluginCapabilities = [ "system" ]
pluginVisibility = [ "preferences" ]
requestAuth = True
PARTTYPES = {
"windows" : ["0xC", "vfat"],
"linux" : ["L", "ext3"]}
CONFIGPARTITION = {
"size" : 5, # size of configuration partition (if necessary) in MB
"type" : "L",
"fs" : "ext2"}
class partition(cryptobox.plugins.base.CryptoBoxPlugin):
"""The partition feature of the CryptoBox.
"""
plugin_capabilities = [ "system" ]
plugin_visibility = [ "preferences" ]
request_auth = True
rank = 80
PartTypes = {
"windows" : ["0xC", "vfat"],
"linux" : ["L", "ext3"]}
ConfigPartition = {
"size" : 5, # size of configuration partition (if necessary) in MB
"type" : "L",
"fs" : "ext2"}
def doAction(self, **args):
def do_action(self, **args):
"""Show the partitioning form and execute the requested action.
"""
## load default hdf values
self.__prepareDataset()
self.__prepare_dataset()
## retrieve some values from 'args' - defaults are empty
self.device = self.__getSelectedDevice(args)
self.withConfigPartition = self.__isWithConfigPartition()
self.device = self.__get_selected_device(args)
self.with_config_partition = self.__is_with_config_partition()
self.cbox.log.debug("partition plugin: selected device=%s" % str(self.device))
self.deviceSize = self.__getAvailableDeviceSize(self.device)
self.device_size = self.__get_available_device_size(self.device)
try:
step = args["step"]
del args["step"]
except KeyError:
step = "select_device"
try:
## this way of selecting the easy setup is necessary: see select_device.py for details
if args["easy"]: step = "easy"
## this way of selecting the easy setup is necessary:
## see select_device.cs for details (button values for ie)
if args["easy"]:
step = "easy"
except KeyError:
pass
## no (or invalid) device was supplied
if not self.device:
step = "select_device"
if step == "add_partition":
return self.__actionAddPartition(args)
return self.__action_add_partition(args)
elif step == "del_partition":
return self.__actionDelPartition(args)
return self.__action_del_partition(args)
elif step == "finish":
return self.__actionFinish(args)
return self.__action_finish(args)
elif step == "easy":
return self.__actionEasySetup(args)
return self.__action_easy_setup()
else: # for "select_device" and for invalid targets
return self.__actionSelectDevice(args)
return self.__action_select_device()
def getStatus(self):
return "%s / %s / %s" % (self.device, self.deviceSize, self.withConfigPartition)
def get_status(self):
"""The status of this plugin is the selected device and some information.
"""
return "%s / %s / %s" % (self.device, self.device_size,
self.with_config_partition)
def __prepareDataset(self):
self.hdf[self.hdf_prefix + "StyleSheetFile"] = os.path.join(self.pluginDir, "partition.css")
def __prepare_dataset(self):
"""Set some hdf values.
"""
self.hdf[self.hdf_prefix + "StyleSheetFile"] = \
os.path.join(self.plugin_dir, "partition.css")
def __getSelectedDevice(self, args):
def __get_selected_device(self, args):
"""Check the selected device (valid, not busy, ...).
"""
try:
device = args["block_device"]
except KeyError:
return None
if not self.__isDeviceValid(device):
if not self.__is_device_valid(device):
return None
if self.__isDeviceBusy(device):
if self.__is_device_busy(device):
self.hdf["Data.Warning"] = "Plugins.partition.DiskIsBusy"
return None
return device
def __isDeviceValid(self, device):
def __is_device_valid(self, device):
"""Check if the device is valid and allowed.
"""
if not device:
return False
if not self.cbox.isDeviceAllowed(device):
if not self.cbox.is_device_allowed(device):
return False
if not device in cbxTools.getParentBlockDevices():
if not device in cbxTools.get_parent_blockdevices():
return False
return True
def __isDeviceBusy(self, device):
"""check if the device (or one of its partitions) is mounted"""
def __is_device_busy(self, device):
"""check if the device (or one of its partitions) is mounted
"""
# the config partition is ignored, as it will get unmounted if necessary
import re
for c in self.cbox.getContainerList():
if re.match(device + "\d*$", c.getDevice()):
if c.isMounted(): return True
for cont in self.cbox.get_container_list():
if re.match(device + "\d*$", cont.get_device()):
if cont.is_mounted():
return True
return False
def __actionSelectDevice(self, args):
def __action_select_device(self):
"""Show a form to select the device for partitioning.
"""
block_devices = [e
for e in cbxTools.getParentBlockDevices()
if self.cbox.isDeviceAllowed(e)]
for e in cbxTools.get_parent_blockdevices()
if self.cbox.is_device_allowed(e)]
counter = 0
for a in block_devices:
self.hdf[self.hdf_prefix + "BlockDevices.%d.name" % counter] = a
self.hdf[self.hdf_prefix + "BlockDevices.%d.size" % counter] = cbxTools.getBlockDeviceSizeHumanly(a)
self.cbox.log.debug("found a suitable block device: %s" % a)
for dev in block_devices:
self.hdf[self.hdf_prefix + "BlockDevices.%d.name" % counter] = dev
self.hdf[self.hdf_prefix + "BlockDevices.%d.size" % counter] = \
cbxTools.get_blockdevice_size_humanly(dev)
self.cbox.log.debug("found a suitable block device: %s" % dev)
counter += 1
if self.withConfigPartition:
if self.with_config_partition:
self.hdf[self.hdf_prefix + "CreateConfigPartition"] = "1"
## there is no disk available
if not block_devices:
@ -133,59 +160,72 @@ class partition(cryptobox.plugins.base.CryptoBoxPlugin):
return "select_device"
def __actionAddPartition(self, args):
def __action_add_partition(self, args):
"""Add a selected partition to the currently proposed partition table.
"""
self.hdf[self.hdf_prefix + "Device"] = self.device
self.hdf[self.hdf_prefix + "Device.Size"] = self.deviceSize
parts = self.__getPartitionsFromArgs(args)
self.__setPartitionData(parts)
self.hdf[self.hdf_prefix + "Device.Size"] = self.device_size
parts = self.__get_partitions_from_args(args)
self.__set_partition_data(parts)
return "set_partitions"
def __actionDelPartition(self, args):
def __action_del_partition(self, args):
"""Remove a partition from the proposed partition table.
"""
try:
part_num = int(args["del_num"])
except (TypeError,KeyError):
return self.__actionAddPartition(args)
return self.__action_add_partition(args)
self.hdf[self.hdf_prefix + "Device"] = self.device
self.hdf[self.hdf_prefix + "Device.Size"] = self.deviceSize
parts = self.__getPartitionsFromArgs(args)
self.hdf[self.hdf_prefix + "Device.Size"] = self.device_size
parts = self.__get_partitions_from_args(args)
## valid partition number to be deleted?
if part_num < len(parts):
del parts[part_num]
else:
return self.__actionAddPartition(args)
self.__setPartitionData(parts)
return self.__action_add_partition(args)
self.__set_partition_data(parts)
return "set_partitions"
def __actionFinish(self, args):
parts = self.__getPartitionsFromArgs(args)
def __action_finish(self, args):
"""Write the partition table.
"""
parts = self.__get_partitions_from_args(args)
if parts:
self.__setPartitionData(parts)
if cbxTools.isPartOfBlockDevice(self.device, self.cbox.prefs.getActivePartition()):
self.cbox.prefs.umountPartition()
if not self.__runFDisk(parts):
self.__set_partition_data(parts)
if cbxTools.is_part_of_blockdevice(self.device,
self.cbox.prefs.get_active_partition()):
self.cbox.prefs.umount_partition()
if not self.__run_fdisk(parts):
self.hdf["Data.Warning"] = "Plugins.partition.PartitioningFailed"
self.cbox.log.warn("partition: failed to partition device: %s" % self.device)
return self.__actionAddPartition(args)
self.cbox.log.warn(
"partition: failed to partition device: %s" % self.device)
return self.__action_add_partition(args)
else:
"""
tricky problem: if the device was partitioned, then a created config partition is still part of the containerlist, as the label is not checked again - very ugly!!! So we will call reReadContainerList after formatting the last partition - see below
"""
self.cbox.reReadContainerList()
## tricky problem: if the device was partitioned, then a created config
## partition is still part of the containerlist, as the label is not
## checked again - very ugly!!! So we will call reReadContainerList
## after formatting the last partition - see below
self.cbox.reread_container_list()
def result_generator():
"""Generate the results of formatting - may be threaded.
"""
counter = 0
## initialize the generator
formatPart_gen = self.__formatPartitions(parts)
format_part_gen = self.__format_partitions(parts)
while counter < len(parts):
## first part: get the device name
yield formatPart_gen.next()
yield format_part_gen.next()
counter += 1
## second part: do the real formatting of a partition
result = formatPart_gen.next()
## after the first partiton, we can reRead the containerList (as the possible config partition was already created)
if self.withConfigPartition and (counter == 1):
## important: reRead the containerList - but somehow it breaks the flow (hanging process)
result = format_part_gen.next()
## after the first partiton, we can reRead the containerList
## (as the possible config partition was already created)
if self.with_config_partition and (counter == 1):
## important: reRead the containerList - but somehow it
## breaks the flow (hanging process)
#self.cbox.reReadContainerList()
## write config data
self.cbox.prefs.mountPartition()
@ -200,24 +240,28 @@ class partition(cryptobox.plugins.base.CryptoBoxPlugin):
"template": "show_format_progress",
"generator": result_generator}
else:
return self.__actionAddPartition(args)
return self.__action_add_partition(args)
def __actionEasySetup(self, args):
def __action_easy_setup(self):
"""Do automatic partitioning (create only one big partition).
"""
import types
## we do not have to take special care for a possible config partition
parts = [ { "size": self.deviceSize, "type": "windows" } ]
parts = [ { "size": self.device_size, "type": "windows" } ]
## umount partition if necessary
if cbxTools.isPartOfBlockDevice(self.device, self.cbox.prefs.getActivePartition()):
self.cbox.prefs.umountPartition()
if cbxTools.is_part_of_blockdevice(self.device,
self.cbox.prefs.get_active_partition()):
self.cbox.prefs.umount_partition()
## partition it
if not self.__runFDisk(parts):
if not self.__run_fdisk(parts):
self.hdf["Data.Warning"] = "Plugins.partition.PartitioningFailed"
return None
## "formatPartitions" is a generator, returning device names and bolean values
result = [e for e in self.__formatPartitions(parts) if type(e) == types.BooleanType]
if self.withConfigPartition:
self.cbox.prefs.mountPartition()
result = [e for e in self.__format_partitions(parts)
if type(e) == types.BooleanType]
if self.with_config_partition:
self.cbox.prefs.mount_partition()
self.cbox.prefs.write()
## check if there is a "False" return value
if False in result:
@ -229,50 +273,59 @@ class partition(cryptobox.plugins.base.CryptoBoxPlugin):
## operation was successful
self.hdf["Data.Success"] = "Plugins.partition.EasySetup"
self.cbox.log.info("easy partitioning succeeded")
## do not show the disk overview immediately - it does not get updated that fast
## do not show the disk overview immediately
## it does not get updated that fast
return { "plugin":"system_preferences", "values":[] }
def __setPartitionData(self, parts):
availSize = self.deviceSize
def __set_partition_data(self, parts):
"""Set some hdf values for the currently proposed partition table.
"""
avail_size = self.device_size
i = 0
for part in parts:
self.cbox.log.debug(part)
self.hdf[self.hdf_prefix + "Parts.%d.Size" % i] = part["size"]
self.hdf[self.hdf_prefix + "Parts.%d.Type" % i] = part["type"]
availSize -= part["size"]
avail_size -= part["size"]
i += 1
self.hdf[self.hdf_prefix + "availSize"] = availSize
if self.withConfigPartition:
self.hdf[self.hdf_prefix + "availSize"] = avail_size
if self.with_config_partition:
self.hdf[self.hdf_prefix + "CreateConfigPartition"] = "1"
for t in self.PartTypes.keys():
self.hdf[self.hdf_prefix + "Types.%s" % t] = t
for ptype in PARTTYPES.keys():
self.hdf[self.hdf_prefix + "Types.%s" % ptype] = ptype
## store the currently existing partitions of the choosen block device
current_containers = [ e for e in self.cbox.getContainerList() if cbxTools.isPartOfBlockDevice(self.device, e.getDevice()) ]
for (index, t) in enumerate(current_containers):
self.hdf[self.hdf_prefix + "ExistingContainers.%d.name" % index] = t.getName()
self.hdf[self.hdf_prefix + "ExistingContainers.%d.size" % index] = cbxTools.getBlockDeviceSizeHumanly(t.getDevice())
current_containers = [ e for e in self.cbox.get_container_list()
if cbxTools.is_part_of_blockdevice(self.device, e.get_device()) ]
for (index, cont) in enumerate(current_containers):
self.hdf[self.hdf_prefix + "ExistingContainers.%d.name" % index] = \
cont.get_name()
self.hdf[self.hdf_prefix + "ExistingContainers.%d.size" % index] = \
cbxTools.get_blockdevice_size_humanly(cont.get_device())
def __getPartitionsFromArgs(self, args):
def __get_partitions_from_args(self, args):
"""Filter the given arguments and construct a partition table.
"""
parts = []
done = False
availSize = self.deviceSize
avail_size = self.device_size
i = -1
while not done:
i += 1
try:
size = int(args["part%d_size" % i])
partType = args["part%d_type" % i]
if int(size) > availSize:
part_type = args["part%d_type" % i]
if int(size) > avail_size:
self.hdf["Data.Warning"] = "Plugins.partition.PartitionTooBig"
continue
if int(size) < 10:
self.hdf["Data.Warning"] = "Plugins.partition.PartitionTooSmall"
continue
if not partType in self.PartTypes.keys(): continue
parts.append({"size":size, "type":partType})
availSize -= size
if not part_type in PARTTYPES.keys():
continue
parts.append({"size":size, "type":part_type})
avail_size -= size
except TypeError:
pass
except KeyError:
@ -280,33 +333,40 @@ class partition(cryptobox.plugins.base.CryptoBoxPlugin):
return parts
def __getAvailableDeviceSize(self, device):
def __get_available_device_size(self, device):
"""calculate the available size (MB) of the device
also consider a (possible) configuration partition"""
deviceSize = cbxTools.getBlockDeviceSize(device)
if deviceSize < 0: return 0
if self.withConfigPartition:
deviceSize -= self.ConfigPartition["size"]
return deviceSize
also consider a (possible) configuration partition
"""
device_size = cbxTools.get_blockdevice_size(device)
if device_size < 0:
return 0
if self.with_config_partition:
device_size -= CONFIGPARTITION["size"]
return device_size
def __isWithConfigPartition(self):
"""check if we have to create a configuration partition"""
if self.cbox.prefs.requiresPartition():
active = self.cbox.prefs.getActivePartition()
def __is_with_config_partition(self):
"""check if we have to create a configuration partition
"""
if self.cbox.prefs.requires_partition():
active = self.cbox.prefs.get_active_partition()
## we need a partition, if there is no active one
if not active: return True
if not active:
return True
## check if the active one is part of the current device
return cbxTools.isPartOfBlockDevice(self.device, active)
return cbxTools.is_part_of_blockdevice(self.device, active)
return False
def __runFDisk(self, parts):
def __run_fdisk(self, parts):
"""Call fdisk to partition the device.
"""
## check if the device is completely filled (to avoid some empty last blocks)
avail_size = self.deviceSize
for d in parts: avail_size -= d["size"]
avail_size = self.device_size
for one_part in parts:
avail_size -= one_part["size"]
self.cbox.log.debug("remaining size: %d" % avail_size)
isFilled = avail_size == 0
is_filled = avail_size == 0
proc = subprocess.Popen(
shell = False,
stdin = subprocess.PIPE,
@ -316,81 +376,91 @@ class partition(cryptobox.plugins.base.CryptoBoxPlugin):
self.cbox.prefs["Programs"]["super"],
self.cbox.prefs["Programs"]["CryptoBoxRootActions"],
"plugin",
os.path.join(self.pluginDir, "root_action.py"),
os.path.join(self.plugin_dir, "root_action.py"),
"partition",
self.device])
for line in self.__getSFDiskLayout(parts, isFilled):
for line in self.__get_sfdisk_layout(parts, is_filled):
proc.stdin.write(line + "\n")
(output, error) = proc.communicate()
if proc.returncode != 0: self.cbox.log.debug("partitioning failed: %s" % error)
if proc.returncode != 0:
self.cbox.log.debug("partitioning failed: %s" % error)
return proc.returncode == 0
def __getSFDiskLayout(self, paramParts, isFilled):
"""this generator returns the input lines for sfdisk"""
parts = paramParts[:]
def __get_sfdisk_layout(self, param_parts, is_filled):
"""this generator returns the input lines for sfdisk
"""
parts = param_parts[:]
## first a (possible) configuration partition - so it will be reusable
if self.withConfigPartition:
if self.with_config_partition:
## fill the main table (including a config partition)
yield ",%d,%s" % (self.ConfigPartition["size"], self.ConfigPartition["type"])
yield ",%d,%s" % (CONFIGPARTITION["size"], CONFIGPARTITION["type"])
## one primary partition
if isFilled and (len(parts) == 1):
if is_filled and (len(parts) == 1):
## fill the rest of the device
yield ",,%s,*" % self.PartTypes[parts[0]["type"]][0]
yield ",,%s,*" % PARTTYPES[parts[0]["type"]][0]
else:
## only use the specified size
yield ",%d,%s,*" % (parts[0]["size"], self.PartTypes[parts[0]["type"]][0])
yield ",%d,%s,*" % (parts[0]["size"], PARTTYPES[parts[0]["type"]][0])
del parts[0]
## no extended partition, if there is only one disk
if not parts: return
if not parts:
return
## an extended container for the rest
yield ",,E"
## an empty partition in main table
yield ";"
## maybe another empty partition if there is no config partition
if not self.withConfigPartition: yield ";"
if not self.with_config_partition:
yield ";"
while parts:
if isFilled and (len(parts) == 1):
yield ",,%s" % (self.PartTypes[parts[0]["type"]][0],)
if is_filled and (len(parts) == 1):
yield ",,%s" % (PARTTYPES[parts[0]["type"]][0],)
else:
yield ",%d,%s" % (parts[0]["size"], self.PartTypes[parts[0]["type"]][0])
yield ",%d,%s" % (parts[0]["size"], PARTTYPES[parts[0]["type"]][0])
del parts[0]
def __formatPartitions(self, paramParts):
parts = paramParts[:]
def __format_partitions(self, param_parts):
"""Format all partitions of the device.
"""
parts = param_parts[:]
part_num = 1
## maybe a config partition?
if self.withConfigPartition:
if self.with_config_partition:
dev_name = self.device + str(part_num)
self.cbox.log.info("formatting config partition (%s)" % dev_name)
if self.__formatOnePartition(dev_name, self.ConfigPartition["fs"]):
self.__setLabelOfPartition(dev_name, self.cbox.prefs["Main"]["ConfigVolumeLabel"])
if self.__format_one_partition(dev_name, CONFIGPARTITION["fs"]):
self.__set_label_of_partition(dev_name,
self.cbox.prefs["Main"]["ConfigVolumeLabel"])
part_num += 1
## the first data partition
dev_name = self.device + str(part_num)
partType = self.PartTypes[parts[0]["type"]][1]
self.cbox.log.info("formatting partition (%s) as '%s'" % (dev_name, partType))
part_type = PARTTYPES[parts[0]["type"]][1]
self.cbox.log.info("formatting partition (%s) as '%s'" % (dev_name, part_type))
yield dev_name
yield self.__formatOnePartition(dev_name, partType)
yield self.__format_one_partition(dev_name, part_type)
del parts[0]
## other data partitions
part_num = 5
while parts:
dev_name = self.device + str(part_num)
partType = self.PartTypes[parts[0]["type"]][1]
self.cbox.log.info("formatting partition (%s) as '%s'" % (dev_name, partType))
part_type = PARTTYPES[parts[0]["type"]][1]
self.cbox.log.info("formatting partition (%s) as '%s'" % \
(dev_name, part_type))
yield dev_name
yield self.__formatOnePartition(dev_name, partType)
yield self.__format_one_partition(dev_name, part_type)
part_num += 1
del parts[0]
return
def __formatOnePartition(self, dev_name, type):
def __format_one_partition(self, dev_name, fs_type):
"""Format a single partition
"""
## first: retrieve UUID - it can be removed from the database afterwards
volDB = self.cbox.prefs.volumesDB
prev_name = [e.getName() for e in self.cbox.getContainerList() if e.getDevice() == dev_name]
prev_name = [e.get_name() for e in self.cbox.get_container_list()
if e.get_device() == dev_name]
## call "mkfs"
proc = subprocess.Popen(
shell = False,
@ -398,10 +468,10 @@ class partition(cryptobox.plugins.base.CryptoBoxPlugin):
self.cbox.prefs["Programs"]["super"],
self.cbox.prefs["Programs"]["CryptoBoxRootActions"],
"plugin",
os.path.join(self.pluginDir, "root_action.py"),
os.path.join(self.plugin_dir, "root_action.py"),
"format",
dev_name,
type])
fs_type])
(output, error) = proc.communicate()
if proc.returncode != 0:
self.cbox.log.warn("failed to create filesystem on %s: %s" % (dev_name, error))
@ -409,11 +479,13 @@ class partition(cryptobox.plugins.base.CryptoBoxPlugin):
else:
## remove unused volume entry
if prev_name:
self.cbox.prefs.volumesDB[prev_name[0]]
del self.cbox.prefs.volumes_db[prev_name[0]]
return True
def __setLabelOfPartition(self, dev_name, label):
def __set_label_of_partition(self, dev_name, label):
"""Set the label of a partition - useful for the config partition.
"""
proc = subprocess.Popen(
shell = False,
stdout = subprocess.PIPE,
@ -422,7 +494,7 @@ class partition(cryptobox.plugins.base.CryptoBoxPlugin):
self.cbox.prefs["Programs"]["super"],
self.cbox.prefs["Programs"]["CryptoBoxRootActions"],
"plugin",
os.path.join(self.pluginDir, "root_action.py"),
os.path.join(self.plugin_dir, "root_action.py"),
"label",
dev_name,
label])

View File

@ -19,6 +19,8 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
__revision__ = "$Id"
## necessary: otherwise CryptoBoxRootActions.py will refuse to execute this script
PLUGIN_TYPE = "cryptobox"

View File

@ -18,12 +18,14 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
__revision__ = "$Id"
import cryptobox.web.testclass
class unittests(cryptobox.web.testclass.WebInterfaceTestClass):
def test_read_form(self):
url = self.URL + "partition?weblang=en"
url = self.url + "partition?weblang=en"
self.register_auth(url)
self.cmd.go(url)
self.cmd.find('VERY careful')

View File

@ -7,7 +7,7 @@ The following directory structure is required:
Python code interface:
- create a class with the same name as the plugin - it must inherit CryptoBoxPlugin
- function "doAction":
- function "do_action":
- this function will get called whenever this plugins is involved in a request
- all arguments should be optional (e.g. for displaying a form without previous input values)
- the argument "store" should be used to process a form submission (just a recommendation)
@ -27,15 +27,15 @@ Python code interface:
* values: a dictionary of variables that should be defined for this plugin
- an empty (e.g. None) return value can be used to go to the default page ("disks"
or "volume_mount" (for volume plugins))
- function "getStatus":
- function "get_status":
- returns a string, that describes a state connected to this plugin (e.g. the current date and
time (for the "date" plugin))
- the class variable "pluginCapabilities" must be an array of strings (supported: "system" and
- the class variable "plugin_capabilities" must be an array of strings (supported: "system" and
"volume")
- the class variable "pluginVisibility" may contain one or more of the following items:
menu/preferences/volume. This obviously should fit to the 'pluginCapabilities' variable.
An empty list is interpreted as a disabled plugin.
- the class variable "requestAuth" is boolean and defines, if admin authentication is necessary
- the class variable "plugin_visibility" may contain one or more of the following items:
menu/preferences/volume. This should fit to the 'plugin_capabilities' variable.
An empty list is interpreted as an invisible plugin.
- the class variable "request_auth" is boolean and defines, if admin authentication is necessary
for this plugin
- the class variable "rank" is an integer in the range of 0..100 - it determines the order
of plugins in listings (lower value -> higher priority)

View File

@ -18,27 +18,31 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
__revision__ = "$Id"
import cryptobox.plugins.base
import cryptobox.plugins.manage
class plugin_manager(cryptobox.plugins.base.CryptoBoxPlugin):
pluginCapabilities = [ "system" ]
pluginVisibility = [ "preferences" ]
requestAuth = True
plugin_capabilities = [ "system" ]
plugin_visibility = [ "preferences" ]
request_auth = True
rank = 90
def doAction(self, store=None, action=None, plugin_name=None, **args):
def do_action(self, store=None, action=None, plugin_name=None, **args):
import re
if plugin_name:
## check for invalid characters
if re.search(u'\W', plugin_name): return "plugin_list"
pluginList = cryptobox.plugins.manage.PluginManager(self.cbox, self.cbox.prefs["Locations"]["PluginDir"])
plugin = pluginList.getPlugin(plugin_name)
plugin_manager = cryptobox.plugins.manage.PluginManager(
self.cbox, self.cbox.prefs["Locations"]["PluginDir"])
plugin = plugin_manager.get_plugin(plugin_name)
if not plugin: return "plugin_list"
## take only plugins, that are of the same type as the choosen one
self.plugins = [e for e in pluginList.getPlugins() if e.pluginCapabilities == plugin.pluginCapabilities]
self.plugins = [e for e in plugin_manager.get_plugins()
if e.plugin_capabilities == plugin.plugin_capabilities]
if action == "up":
self.__move_up(plugin)
elif action == "down":
@ -50,44 +54,48 @@ class plugin_manager(cryptobox.plugins.base.CryptoBoxPlugin):
if not re.search(u'\W',key):
self.__setConfig(key[:-7], args)
else:
self.cbox.log.info("plugin_manager: invalid plugin name (%s)" % str(key[:-7]))
self.cbox.log.info("plugin_manager: invalid plugin name (%s)" % \
str(key[:-7]))
try:
self.cbox.prefs.pluginConf.write()
self.cbox.prefs.plugin_conf.write()
except IOError:
self.cbox.log.warn("failed to write plugin configuration")
return "plugin_list"
def getStatus(self):
def get_status(self):
return "no status"
def __sortPlugins(self):
def __sort_plugins(self):
"""sort all plugins in the list according to their rank"""
def cmp_func(x,y):
xRank = x.getRank()
yRank = y.getRank()
if xRank < yRank: return -1
elif xRank == yRank: return 0
else: return 1
x_rank = x.get_rank()
y_rank = y.get_rank()
if x_rank < y_rank:
return -1
elif x_rank == y_rank:
return 0
else:
return 1
self.plugins.sort(cmp = cmp_func)
def __distributeRanks(self):
def __distribute_ranks(self):
"""evenly distribute the 'rank' values according to the current order of
the list"""
dist = 100/len(self.plugins)
for index,pl in enumerate(self.plugins):
try:
self.cbox.prefs.pluginConf[pl.getName()]["rank"] = dist*index
self.cbox.prefs.plugin_conf[pl.get_name()]["rank"] = dist*index
except KeyError:
self.cbox.prefs.pluginConf[pl.getName()] = {}
self.cbox.prefs.pluginConf[pl.getName()]["rank"] = dist*index
self.cbox.prefs.pluginConf.write()
self.cbox.prefs.plugin_conf[pl.get_name()] = {}
self.cbox.prefs.plugin_conf[pl.get_name()]["rank"] = dist*index
self.cbox.prefs.plugin_conf.write()
def __move_up(self, plugin):
self.__sortPlugins()
self.__sort_plugins()
try:
index = self.plugins.index(plugin)
## first elements may not move up
@ -97,11 +105,11 @@ class plugin_manager(cryptobox.plugins.base.CryptoBoxPlugin):
return
self.plugins.remove(plugin)
self.plugins.insert(index-1, plugin)
self.__distributeRanks()
self.__distribute_ranks()
def __move_down(self, plugin):
self.__sortPlugins()
self.__sort_plugins()
try:
index = self.plugins.index(plugin)
## last elements may not move down
@ -111,7 +119,7 @@ class plugin_manager(cryptobox.plugins.base.CryptoBoxPlugin):
return
self.plugins.remove(plugin)
self.plugins.insert(index+1, plugin)
self.__distributeRanks()
self.__distribute_ranks()
def __setConfig(self, name, args):
@ -137,5 +145,5 @@ class plugin_manager(cryptobox.plugins.base.CryptoBoxPlugin):
setting["requestAuth"] = True
except (KeyError, ValueError):
pass
self.cbox.prefs.pluginConf[name] = setting
self.cbox.prefs.plugin_conf[name] = setting

View File

@ -18,19 +18,21 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
__revision__ = "$Id"
import cryptobox.web.testclass
class unittests(cryptobox.web.testclass.WebInterfaceTestClass):
def test_read_form(self):
url = self.URL + "plugin_manager?weblang=en"
url = self.url + "plugin_manager?weblang=en"
self.register_auth(url)
self.cmd.go(url)
self.cmd.find('Plugin Manager')
def test_set_options(self):
url = self.URL + "plugin_manager"
url = self.url + "plugin_manager"
self.register_auth(url)
self.cmd.go(url + u"?plugin_name=t/-!")
self.cmd.find('Plugin Manager')
@ -55,8 +57,8 @@ class unittests(cryptobox.web.testclass.WebInterfaceTestClass):
def test_move_up(self):
## TODO: if we want to be perfect, then we should check the change of the rank
url = self.URL + "plugin_manager"
#TODO: if we want to be perfect, then we should check the change of the rank
url = self.url + "plugin_manager"
self.register_auth(url)
self.cmd.go(url + u"?plugin_name=disks&action=up")
self.cmd.find('Plugin Manager')
@ -68,7 +70,7 @@ class unittests(cryptobox.web.testclass.WebInterfaceTestClass):
def test_move_down(self):
## TODO: if we want to be perfect, then we should check the change of the rank
url = self.URL + "plugin_manager"
url = self.url + "plugin_manager"
self.register_auth(url)
self.cmd.go(url + u"?plugin_name=disks&action=down")
self.cmd.find('Plugin Manager')

View File

@ -19,6 +19,8 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
__revision__ = "$Id"
## necessary: otherwise CryptoBoxRootActions.py will refuse to execute this script
PLUGIN_TYPE = "cryptobox"

View File

@ -18,29 +18,31 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
__revision__ = "$Id"
import cryptobox.plugins.base
REDIRECT_DELAY = 180
class shutdown(cryptobox.plugins.base.CryptoBoxPlugin):
pluginCapabilities = [ "system" ]
pluginVisibility = [ "preferences", "menu" ]
requestAuth = False
plugin_capabilities = [ "system" ]
plugin_visibility = [ "preferences", "menu" ]
request_auth = False
rank = 90
def doAction(self, type=None):
def do_action(self, type=None):
if not type:
return "form_shutdown"
elif type == "shutdown":
if self.__doShutdown("shutdown"):
if self.__do_shutdown("shutdown"):
self.hdf["Data.Success"] = "Plugins.shutdown.Shutdown"
return "progress_shutdown"
else:
self.hdf["Data.Warning"] = "Plugins.shutdown.ShutdownFailed"
return "form_shutdown"
elif type == "reboot":
if self.__doShutdown("reboot"):
if self.__do_shutdown("reboot"):
self.hdf["Data.Success"] = "Plugins.shutdown.Reboot"
self.hdf["Data.Redirect.URL"] = ""
self.hdf["Data.Redirect.Delay"] = REDIRECT_DELAY
@ -52,11 +54,11 @@ class shutdown(cryptobox.plugins.base.CryptoBoxPlugin):
return "form_shutdown"
def getStatus(self):
def get_status(self):
return "the box is up'n'running"
def __doShutdown(self, action):
def __do_shutdown(self, action):
import subprocess
import os
proc = subprocess.Popen(
@ -65,7 +67,7 @@ class shutdown(cryptobox.plugins.base.CryptoBoxPlugin):
self.cbox.prefs["Programs"]["super"],
self.cbox.prefs["Programs"]["CryptoBoxRootActions"],
"plugin",
os.path.join(self.pluginDir, "root_action.py"),
os.path.join(self.plugin_dir, "root_action.py"),
action])
proc.wait()
return proc.returncode == 0

View File

@ -18,13 +18,15 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
__revision__ = "$Id"
import cryptobox.web.testclass
class unittests(cryptobox.web.testclass.WebInterfaceTestClass):
def test_read_form(self):
"""just read the form - I do not know of a way how to check success"""
url = self.URL + "shutdown"
url = self.url + "shutdown"
self.register_auth(url)
self.cmd.go(url)
self.cmd.find('shutdown')

View File

@ -18,20 +18,23 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
__revision__ = "$Id"
import cryptobox.plugins.base
class system_preferences(cryptobox.plugins.base.CryptoBoxPlugin):
pluginCapabilities = [ "system" ]
pluginVisibility = [ "menu" ]
requestAuth = False
plugin_capabilities = [ "system" ]
plugin_visibility = [ "menu" ]
request_auth = False
rank = 20
def doAction(self):
def do_action(self):
return "show_plugins"
def getStatus(self):
return ":".join([p.getName() for p in self.site.pluginList.getPlugins()])
def get_status(self):
plugin_manager = cryptobox.plugins.manage.PluginManager(
self.cbox, self.cbox.prefs["Locations"]["PluginDir"])
return ":".join([p.get_name() for p in plugin_manager.get_plugins()])

View File

@ -18,17 +18,19 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
__revision__ = "$Id"
import cryptobox.web.testclass
class unittests(cryptobox.web.testclass.WebInterfaceTestClass):
def test_read_form(self):
self.cmd.go(self.URL + "system_preferences")
self.cmd.go(self.url + "system_preferences")
self.cmd.find("Preferences")
def test_check_plugins(self):
self.cmd.go(self.URL + "system_preferences")
self.cmd.go(self.url + "system_preferences")
self.cmd.find(u'Data.Status.Plugins.system_preferences=(.*)$', "m")
plugins = self.locals["__match__"].split(":")
self.assertTrue(len(plugins) > 1)

View File

@ -18,6 +18,8 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
__revision__ = "$Id"
import cryptobox.web.testclass
## this user may not be removed
@ -35,7 +37,7 @@ class unittests(cryptobox.web.testclass.WebInterfaceTestClass):
def test_test_wrong_credentials(self):
"""check if the user_manager is protected"""
url = self.URL + "user_manager"
url = self.url + "user_manager"
self.register_auth(url,"foo","bar")
self.cmd.go(url)
self.cmd.notfind("Manage users")
@ -43,7 +45,7 @@ class unittests(cryptobox.web.testclass.WebInterfaceTestClass):
def test_add_existing_user(self):
"""adding an existing user should fail"""
url = self.URL + "user_manager"
url = self.url + "user_manager"
self.register_auth(url)
self._add_user("admin","foo","foo")
self.cmd.find("The choosen username does already exist")
@ -51,7 +53,7 @@ class unittests(cryptobox.web.testclass.WebInterfaceTestClass):
def test_add_invalid_username(self):
"""adding an invalid username should fail"""
url = self.URL + "user_manager"
url = self.url + "user_manager"
self.register_auth(url)
self._add_user("foo/bar","foo","foo")
self.cmd.find("Invalid username")
@ -60,7 +62,7 @@ class unittests(cryptobox.web.testclass.WebInterfaceTestClass):
def test_add_without_password(self):
"""adding a user without password should fail"""
url = self.URL + "user_manager"
url = self.url + "user_manager"
self.register_auth(url)
self.assertFalse("foo" in self._getUsers())
self._add_user("foo","","foo")
@ -70,7 +72,7 @@ class unittests(cryptobox.web.testclass.WebInterfaceTestClass):
def test_add_with_different_passwords(self):
"""adding a user with different passwords should fail"""
url = self.URL + "user_manager"
url = self.url + "user_manager"
self.register_auth(url)
self.assertFalse("foo" in self._getUsers())
self._add_user("foo","bar","foo")
@ -80,7 +82,7 @@ class unittests(cryptobox.web.testclass.WebInterfaceTestClass):
def test_change_pw_for_invalid_user(self):
"""changing a password of a non existing user should fail"""
url = self.URL + "user_manager"
url = self.url + "user_manager"
self.register_auth(url)
self.assertFalse("barfoo" in self._getUsers())
self.cmd.go(url + "?store=change_password&user=foobar&new_pw=foo&new_pw2=foo")
@ -89,7 +91,7 @@ class unittests(cryptobox.web.testclass.WebInterfaceTestClass):
def test_change_pw_without_password(self):
"""changing a password without a new password should fail"""
url = self.URL + "user_manager"
url = self.url + "user_manager"
self.register_auth(url)
self.assertFalse("foo" in self._getUsers())
self._add_user("foo","bar","bar")
@ -102,7 +104,7 @@ class unittests(cryptobox.web.testclass.WebInterfaceTestClass):
def test_change_pw_wit_different_passwords(self):
"""changing a password while supplying different passwords should fail"""
url = self.URL + "user_manager"
url = self.url + "user_manager"
self.register_auth(url)
self.assertFalse("foo" in self._getUsers())
self._add_user("foo","bar","bar")
@ -115,7 +117,7 @@ class unittests(cryptobox.web.testclass.WebInterfaceTestClass):
def _remove_reserved_user(self):
"""removing a reserved user should fail"""
url = self.URL + "user_manager"
url = self.url + "user_manager"
self.register_auth(url)
self.assertTrue("admin" in self._getUsers())
self._del_user("admin")
@ -125,7 +127,7 @@ class unittests(cryptobox.web.testclass.WebInterfaceTestClass):
def _remove_non_existing_user(self):
"""removing a non-existing user should fail"""
url = self.URL + "user_manager"
url = self.url + "user_manager"
self.register_auth(url)
self.assertFalse("barfoo" in self._getUsers())
self._del_user("barfoo")
@ -134,7 +136,7 @@ class unittests(cryptobox.web.testclass.WebInterfaceTestClass):
def test_manage_users(self):
"""add a new user, change its password and remove the user afterwards"""
url = self.URL + "user_manager"
url = self.url + "user_manager"
self.register_auth(url)
## remove the user that should be added - just in case a previous run was unclean
## check its existence before
@ -158,13 +160,13 @@ class unittests(cryptobox.web.testclass.WebInterfaceTestClass):
def test_invalid_input(self):
"""check all combinations of invalid input"""
url = self.URL + "user_manager"
url = self.url + "user_manager"
self.register_auth(url)
self.cmd.go(url + "?store=foobar")
def _add_user(self, username, pw, pw2):
self.cmd.go(self.URL + "user_manager")
self.cmd.go(self.url + "user_manager")
self.cmd.formvalue("add_user","user",username)
self.cmd.formvalue("add_user","new_pw",pw)
self.cmd.formvalue("add_user","new_pw2",pw2)
@ -172,13 +174,13 @@ class unittests(cryptobox.web.testclass.WebInterfaceTestClass):
def _del_user(self, username):
self.cmd.go(self.URL + "user_manager")
self.cmd.go(self.url + "user_manager")
self.cmd.formvalue("del_user","user",username)
self.cmd.submit()
def _change_password(self, username, pw, pw2):
self.cmd.go(self.URL + "user_manager")
self.cmd.go(self.url + "user_manager")
self.cmd.formvalue("change_password","user",username)
self.cmd.formvalue("change_password","new_pw",pw)
self.cmd.formvalue("change_password","new_pw2",pw2)
@ -186,7 +188,7 @@ class unittests(cryptobox.web.testclass.WebInterfaceTestClass):
def _getUsers(self):
url = self.URL + "user_manager"
url = self.url + "user_manager"
self.register_auth(url)
self.cmd.go(url)
self.cmd.find("Data.Status.Plugins.user_manager=([\w:]+)")

View File

@ -18,21 +18,23 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
__revision__ = "$Id"
import cryptobox.plugins.base
RESERVED_USERS = [ "admin" ]
class user_manager(cryptobox.plugins.base.CryptoBoxPlugin):
pluginCapabilities = [ "system" ]
pluginVisibility = [ "preferences" ]
requestAuth = True
plugin_capabilities = [ "system" ]
plugin_visibility = [ "preferences" ]
request_auth = True
rank = 45
def doAction(self, store=None, user=None, new_pw=None, new_pw2=None):
def do_action(self, store=None, user=None, new_pw=None, new_pw2=None):
import re
adminDict = self.cbox.prefs.userDB["admins"]
self.__cleanHDF()
admin_dict = self.cbox.prefs.user_db["admins"]
self.__clean_hdf()
if store is None:
pass
elif store == "add_user":
@ -42,13 +44,13 @@ class user_manager(cryptobox.plugins.base.CryptoBoxPlugin):
self.hdf["Data.Warning"] = "EmptyNewPassword"
elif new_pw != new_pw2:
self.hdf["Data.Warning"] = "DifferentPasswords"
elif user in adminDict.keys():
elif user in admin_dict.keys():
self.hdf["Data.Warning"] = "Plugins.user_manager.UserAlreadyExists"
else:
adminDict[user] = self.cbox.prefs.userDB.getDigest(new_pw)
admin_dict[user] = self.cbox.prefs.user_db.get_digest(new_pw)
self.hdf["Data.Success"] = "Plugins.user_manager.UserAdded"
try:
self.cbox.prefs.userDB.write()
self.cbox.prefs.user_db.write()
except IOError:
self.cbox.log.warn("failed to write user database")
elif store == "change_password":
@ -56,11 +58,11 @@ class user_manager(cryptobox.plugins.base.CryptoBoxPlugin):
self.hdf["Data.Warning"] = "EmptyNewPassword"
elif new_pw != new_pw2:
self.hdf["Data.Warning"] = "DifferentPasswords"
elif user in adminDict.keys():
adminDict[user] = self.cbox.prefs.userDB.getDigest(new_pw)
elif user in admin_dict.keys():
admin_dict[user] = self.cbox.prefs.user_db.get_digest(new_pw)
self.hdf["Data.Success"] = "Plugins.user_manager.PasswordChanged"
try:
self.cbox.prefs.userDB.write()
self.cbox.prefs.user_db.write()
except IOError:
self.cbox.log.warn("failed to write user database")
else:
@ -69,31 +71,31 @@ class user_manager(cryptobox.plugins.base.CryptoBoxPlugin):
if user in RESERVED_USERS:
self.cbox.log.info("user_manager: tried to remove reserved user (%s)" % user)
self.hdf["Data.Warning"] = "NeverRemoveReservedUser"
elif user in adminDict.keys():
del adminDict[user]
elif user in admin_dict.keys():
del admin_dict[user]
self.hdf["Data.Success"] = "Plugins.user_manager.UserRemoved"
try:
self.cbox.prefs.userDB.write()
self.cbox.prefs.user_db.write()
except IOError:
self.cbox.log.warn("failed to write user database")
else:
self.cbox.log.info("user_manager: tried to remove non-existing user (%s)" % str(user))
else:
self.cbox.log.info("user_manager: invalid value of 'store' (%s)" % store)
self.__prepareHDF(adminDict)
self.__prepare_hdf(admin_dict)
return "user_list"
def getStatus(self):
return ":".join(self.cbox.prefs.userDB["admins"].keys())
def get_status(self):
return ":".join(self.cbox.prefs.user_db["admins"].keys())
def __cleanHDF(self):
def __clean_hdf(self):
for key in self.hdf.keys():
del self.hdf[key]
def __prepareHDF(self, dict):
def __prepare_hdf(self, dict):
## sort by name
users = dict.keys()
users.sort()

View File

@ -18,13 +18,15 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
__revision__ = "$Id"
import cryptobox.web.testclass
class unittests(cryptobox.web.testclass.WebInterfaceTestClass):
def test_read_form(self):
"""try to read automount form"""
url = self.URL + "volume_automount?weblang=en&device=%2Fdev%2F" + self.device + "1"
url = self.url + "volume_automount?weblang=en&device=%2Fdev%2F" + self.device
self.register_auth(url)
self.cmd.go(url)
self.cmd.find('Activate during startup')
@ -32,21 +34,21 @@ class unittests(cryptobox.web.testclass.WebInterfaceTestClass):
def test_toggle(self):
"""try to toggle automount property"""
url = self.URL + "volume_automount"
url = self.url + "volume_automount"
self.register_auth(url)
self.cmd.go(url + "?device=%%2Fdev%%2F%s1&action=disable" % self.device)
self.cmd.go(url + "?device=%%2Fdev%%2F%s&action=disable" % self.device)
self.cmd.find("Automatic activation disabled")
self.cmd.find("is disabled")
self.cmd.go(url + "?device=%%2Fdev%%2F%s1&action=enable" % self.device)
self.cmd.go(url + "?device=%%2Fdev%%2F%s&action=enable" % self.device)
self.cmd.find("Automatic activation enabled")
self.cmd.notfind("is disabled")
def test_invalid_input(self):
"""check invalid inputs"""
url = self.URL + "volume_automount"
url = self.url + "volume_automount"
self.register_auth(url)
self.cmd.go(url + "?device=%%2Fdev%%2F%s1&action=foobar" % self.device)
self.cmd.go(url + "?device=%%2Fdev%%2F%s&action=foobar" % self.device)
self.cmd.notfind("Automatic activation disabled")
self.cmd.notfind("Automatic activation enabled")

View File

@ -18,58 +18,60 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
__revision__ = "$Id"
import cryptobox.plugins.base
from cryptobox.core.exceptions import *
class volume_automount(cryptobox.plugins.base.CryptoBoxPlugin):
pluginCapabilities = [ "volume" ]
pluginVisibility = [ "properties" ]
requestAuth = False
plugin_capabilities = [ "volume" ]
plugin_visibility = [ "properties" ]
request_auth = False
rank = 80
trueString = "yes"
falseString = "no"
true_string = "yes"
false_string = "no"
def doAction(self, action=None):
container = self.cbox.getContainer(self.device)
def do_action(self, action=None):
container = self.cbox.get_container(self.device)
if action is None:
pass
elif action == "enable":
container.attributes["automount"] = self.trueString
container.attributes["automount"] = self.true_string
self.hdf["Data.Success"] = "Plugins.volume_automount.AutoMountEnabled"
self.cbox.log.info("volume_automount: enabled for device '%s'" % self.device)
self.cbox.prefs.volumesDB.write()
self.cbox.prefs.volumes_db.write()
elif action == "disable":
container.attributes["automount"] = self.falseString
container.attributes["automount"] = self.false_string
self.hdf["Data.Success"] = "Plugins.volume_automount.AutoMountDisabled"
self.cbox.log.info("volume_automount: disabled for device '%s'" % self.device)
self.cbox.prefs.volumesDB.write()
self.cbox.prefs.volumes_db.write()
else:
self.cbox.log.info("volume_automount: invalid action (%s)" % str(action))
self.__prepareHDF()
self.__prepare_hdf()
return "volume_automount"
def getStatus(self):
return str(self.__isAutoMount())
def get_status(self):
return str(self.__is_auto_mount())
def __prepareHDF(self):
if self.__isAutoMount():
def __prepare_hdf(self):
if self.__is_auto_mount():
self.hdf[self.hdf_prefix + "automount_setting"] = "1"
else:
self.hdf[self.hdf_prefix + "automount_setting"] = "0"
def __isAutoMount(self):
container = self.cbox.getContainer(self.device)
def __is_auto_mount(self):
container = self.cbox.get_container(self.device)
if not container:
return False
if container.attributes.has_key("automount"):
return container.attributes["automount"] == self.trueString
return container.attributes["automount"] == self.true_string
else:
return False

View File

@ -18,12 +18,14 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
__revision__ = "$Id"
import cryptobox.web.testclass
class unittests(cryptobox.web.testclass.WebInterfaceTestClass):
def test_read_form(self):
url = self.URL + "volume_chpasswd?weblang=en&device=%2Fdev%2F" + self.device + "1"
url = self.url + "volume_chpasswd?weblang=en&device=%2Fdev%2F" + self.device
self.register_auth(url)
self.cmd.go(url)
self.cmd.find('hange')

View File

@ -18,24 +18,26 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
__revision__ = "$Id"
import cryptobox.plugins.base
from cryptobox.core.exceptions import *
class volume_chpasswd(cryptobox.plugins.base.CryptoBoxPlugin):
pluginCapabilities = [ "volume" ]
pluginVisibility = [ "properties" ]
requestAuth = False
plugin_capabilities = [ "volume" ]
plugin_visibility = [ "properties" ]
request_auth = False
rank = 70
def doAction(self, store=None, old_pw=None, new_pw=None, new_pw2=None):
self.container = self.cbox.getContainer(self.device)
def do_action(self, store=None, old_pw=None, new_pw=None, new_pw2=None):
self.container = self.cbox.get_container(self.device)
if not self.container:
return None
elif store == "change_pw":
return self.__changePassword(old_pw, new_pw, new_pw2)
return self.__change_password(old_pw, new_pw, new_pw2)
elif not store:
return "volume_chpasswd"
else:
@ -43,11 +45,11 @@ class volume_chpasswd(cryptobox.plugins.base.CryptoBoxPlugin):
return "volume_chpasswd"
def getStatus(self):
def get_status(self):
return "TODO"
def __changePassword(self, old_pw, new_pw, new_pw2):
def __change_password(self, old_pw, new_pw, new_pw2):
if not old_pw:
self.hdf["Data.Warning"] = "EmptyPassword"
elif not new_pw:
@ -59,13 +61,13 @@ class volume_chpasswd(cryptobox.plugins.base.CryptoBoxPlugin):
pass
else:
try:
self.container.changePassword(old_pw, new_pw)
except CBInvalidType, errMsg:
self.cbox.log.info("plugin 'volume_chpasswd' - cannot change passphrase for non-encrypted container (%s): %s" % (self.device, errMsg))
self.container.change_password(old_pw, new_pw)
except CBInvalidType, err_msg:
self.cbox.log.info("plugin 'volume_chpasswd' - cannot change passphrase for non-encrypted container (%s): %s" % (self.device, err_msg))
except CBVolumeIsActive:
self.hdf["Data.Warning"] = "VolumeMayNotBeMounted"
except CBChangePasswordError, errMsg:
self.cbox.log.warn("plugin 'volume_chpasswd' - cannot change password for device (%s): %s" % (self.device, errMsg))
except CBChangePasswordError, err_msg:
self.cbox.log.warn("plugin 'volume_chpasswd' - cannot change password for device (%s): %s" % (self.device, err_msg))
self.hdf["Data.Warning"] = "Plugins.volume_chpasswd.PasswordChange"
else:
self.hdf["Data.Success"] = "Plugins.volume_chpasswd.PasswordChange"

View File

@ -18,12 +18,14 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
__revision__ = "$Id"
import cryptobox.web.testclass
class unittests(cryptobox.web.testclass.WebInterfaceTestClass):
def test_read_form(self):
url = self.URL + "volume_details?weblang=en&device=%2Fdev%2F" + self.device + "1"
url = self.url + "volume_details?weblang=en&device=%2Fdev%2F" + self.device
self.register_auth(url)
self.cmd.go(url)
self.cmd.find('Technical details')

View File

@ -18,22 +18,24 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
__revision__ = "$Id"
import cryptobox.plugins.base
class volume_details(cryptobox.plugins.base.CryptoBoxPlugin):
pluginCapabilities = [ "volume" ]
pluginVisibility = [ "volume" ]
requestAuth = False
plugin_capabilities = [ "volume" ]
plugin_visibility = [ "volume" ]
request_auth = False
rank = 100
def doAction(self):
def do_action(self):
## all variables are already set somewhere else
return "volume_details"
def getStatus(self):
def get_status(self):
return "no status"

View File

@ -18,12 +18,14 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
__revision__ = "$Id"
import cryptobox.web.testclass
class unittests(cryptobox.web.testclass.WebInterfaceTestClass):
def test_read_form(self):
url = self.URL + "volume_format_fs?weblang=en&device=%2Fdev%2F" + self.device + "1"
url = self.url + "volume_format_fs?weblang=en&device=%2Fdev%2F" + self.device
self.register_auth(url)
self.cmd.go(url)
self.cmd.find('Initializing filesystem')

View File

@ -18,32 +18,33 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
__revision__ = "$Id"
import cryptobox.plugins.base
from cryptobox.core.exceptions import *
import cryptobox.core.container as cbxContainer
import cryptobox.core.container as cbx_container
## map filesystem types to the appropriate arguments for 'mkfs'
FSTYPES = {
"windows": "vfat",
"linux": "ext3" }
class volume_format_fs(cryptobox.plugins.base.CryptoBoxPlugin):
pluginCapabilities = [ "volume" ]
pluginVisibility = [ "volume" ]
requestAuth = True
plugin_capabilities = [ "volume" ]
plugin_visibility = [ "volume" ]
request_auth = True
rank = 60
## map filesystem types to the appropriate arguments for 'mkfs'
fsTypes = {
"windows": "vfat",
"linux": "ext3" }
containerTypes = [ "luks", "plain" ]
def doAction(self, store=None, fs_type="windows", container_type="luks", crypto_password=None, crypto_password2=None, confirm=None):
if not fs_type in self.fsTypes.keys():
def do_action(self, store=None, fs_type="windows", container_type="luks", crypto_password=None, crypto_password2=None, confirm=None):
if not fs_type in FSTYPES.keys():
self.cbox.info
return "format_volume"
self.hdf[self.hdf_prefix + "fs_type"] = fs_type
self.hdf[self.hdf_prefix + "container_type"] = container_type
for t in self.fsTypes.keys():
for t in FSTYPES.keys():
self.hdf[self.hdf_prefix + "fs_types." + t] = t
if store == "step1":
if not confirm:
@ -67,14 +68,14 @@ class volume_format_fs(cryptobox.plugins.base.CryptoBoxPlugin):
return "volume_format"
def getStatus(self):
def get_status(self):
return "no status"
def __format_plain(self, fsType):
try:
container = self.cbox.getContainer(self.device)
container.create(cbxContainer.ContainerTypes["plain"])
container = self.cbox.get_container(self.device)
container.create(cbx_container.CONTAINERTYPES["plain"])
except CBVolumeIsActive:
self.hdf["Data.Warning"] = "VolumeMayNotBeMounted"
self.cbox.log.info("initialization is not possible as long as the device (%s) is mounted" % self.device)
@ -98,9 +99,9 @@ class volume_format_fs(cryptobox.plugins.base.CryptoBoxPlugin):
self.hdf["Data.Warning"] = "DifferentPasswords"
self.cbox.log.warn("the crypto password was not repeated correctly for initialization of device '%s'" % self.device)
return "volume_format"
container = self.cbox.getContainer(self.device)
container = self.cbox.get_container(self.device)
try:
container.create(cbxContainer.ContainerTypes["luks"], pw)
container.create(cbx_container.CONTAINERTYPES["luks"], pw)
except CBVolumeIsActive:
self.hdf["Data.Warning"] = "VolumeMayNotBeMounted"
self.cbox.log.info("initialization is not possible as long as the device (%s) is mounted" % self.device)

View File

@ -18,12 +18,14 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
__revision__ = "$Id"
import cryptobox.web.testclass
class unittests(cryptobox.web.testclass.WebInterfaceTestClass):
def test_read_form(self):
url = self.URL + "volume_mount?weblang=en&device=%2Fdev%2F" + self.device + "1"
url = self.url + "volume_mount?weblang=en&device=%2Fdev%2F" + self.device
self.register_auth(url)
self.cmd.go(url)
self.cmd.find('ctivate volume')

View File

@ -18,6 +18,8 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
__revision__ = "$Id"
import cryptobox.plugins.base
from cryptobox.core.exceptions import *
import cryptobox.core.container as cbxContainer
@ -25,20 +27,20 @@ import cryptobox.core.container as cbxContainer
class volume_mount(cryptobox.plugins.base.CryptoBoxPlugin):
pluginCapabilities = [ "volume" ]
pluginVisibility = [ "volume" ]
requestAuth = False
plugin_capabilities = [ "volume" ]
plugin_visibility = [ "volume" ]
request_auth = False
rank = 0
def doAction(self, action=None, pw=None):
self.container = self.cbox.getContainer(self.device)
def do_action(self, action=None, pw=None):
self.container = self.cbox.get_container(self.device)
if action == "mount_plain":
return self.__doMountPlain()
return self.__do_mount_plain()
elif action == "mount_luks":
return self.__doMountLuks(pw)
return self.__do_mount_luks(pw)
elif action == "umount":
return self.__doUmount()
return self.__do_umount()
elif not action:
return "volume_status"
else:
@ -46,43 +48,43 @@ class volume_mount(cryptobox.plugins.base.CryptoBoxPlugin):
return None
def getStatus(self):
container = self.cbox.getContainer(self.device)
def get_status(self):
container = self.cbox.get_container(self.device)
if not self.container:
return "invalid device"
if container.isMounted():
if container.is_mounted():
return "active"
else:
return "passive"
def __doMountPlain(self):
if self.container.isMounted():
def __do_mount_plain(self):
if self.container.is_mounted():
self.hdf["Data.Warning"] = "Plugins.volume_mount.IsAlreadyMounted"
self.cbox.log.info("the device (%s) is already mounted" % self.device)
return "volume_status"
if self.container.getType() != cbxContainer.ContainerTypes["plain"]:
if self.container.get_type() != cbxContainer.CONTAINERTYPES["plain"]:
## not a plain container
self.cbox.log.info("plugin 'volume_mount' - invalid container type")
self.hdf["Data.Warning"] = "Plugins.volume_mount.InvalidContainerType"
return "volume_status"
try:
self.container.mount()
except CBMountError, errMsg:
except CBMountError, err_msg:
self.hdf["Data.Warning"] = "Plugins.volume_mount.MountFailed"
self.cbox.log.warn("failed to mount the device (%s): %s" % (self.device, errMsg))
self.cbox.log.warn("failed to mount the device (%s): %s" % (self.device, err_msg))
return "volume_status"
except CBContainerError, errMsg:
except CBContainerError, err_msg:
self.hdf["Data.Warning"] = "Plugins.volume_mount.MountFailed"
self.cbox.log.warn("failed to mount the device (%s): %s" % (self.device, errMsg))
self.cbox.log.warn("failed to mount the device (%s): %s" % (self.device, err_msg))
return "volume_status"
self.cbox.log.info("successfully mounted the volume: %s" % self.device)
self.hdf["Data.Success"] = "Plugins.volume_mount.MountDone"
return "volume_status"
def __doMountLuks(self, pw):
if self.container.isMounted():
def __do_mount_luks(self, pw):
if self.container.is_mounted():
self.hdf["Data.Warning"] = "Plugins.volume_mount.IsAlreadyMounted"
self.cbox.log.info("the device (%s) is already mounted" % self.device)
return "volume_status"
@ -90,35 +92,35 @@ class volume_mount(cryptobox.plugins.base.CryptoBoxPlugin):
self.dataset["Data.Warning"] = "EmptyPassword"
self.log.info("no password was supplied for mounting of device: '%s'" % self.device)
return "volume_status"
if self.container.getType() != cbxContainer.ContainerTypes["luks"]:
if self.container.get_type() != cbxContainer.CONTAINERTYPES["luks"]:
## not a luks container - fail silently
self.cbox.log.info("plugin 'volume_mount' - invalid container type")
return "volume_status"
try:
self.container.mount(pw)
except CBMountError, errMsg:
except CBMountError, err_msg:
self.hdf["Data.Warning"] = "Plugins.volume_mount.MountCryptoFailed"
self.cbox.log.warn("failed to mount the device (%s): %s" % (self.device, errMsg))
self.cbox.log.warn("failed to mount the device (%s): %s" % (self.device, err_msg))
return "volume_status"
except CBContainerError, errMsg:
except CBContainerError, err_msg:
self.hdf["Data.Warning"] = "Plugins.volume_mount.MountCryptoFailed"
self.cbox.log.warn("failed to mount the device (%s): %s" % (self.device, errMsg))
self.cbox.log.warn("failed to mount the device (%s): %s" % (self.device, err_msg))
return "volume_status"
self.cbox.log.info("successfully mounted the volume: %s" % self.device)
self.hdf["Data.Success"] = "Plugins.volume_mount.MountDone"
return "volume_status"
def __doUmount(self):
if not self.container.isMounted():
def __do_umount(self):
if not self.container.is_mounted():
self.hdf["Data.Warning"] = "Plugins.volume_mount.IsNotMounted"
self.cbox.log.info("the device (%s) is currently not mounted" % self.device)
return "volume_status"
try:
self.container.umount()
except CBUmountError, errMsg:
except CBUmountError, err_msg:
self.hdf["Data.Warning"] = "UmountFailed"
self.cbox.log.warn("could not umount the volume (%s): %s" % (self.device, errMsg))
self.cbox.log.warn("could not umount the volume (%s): %s" % (self.device, err_msg))
return "volume_status"
self.cbox.log.info("successfully unmounted the container: %s" % self.device)
self.hdf["Data.Success"] = "Plugins.volume_mount.UmountDone"

View File

@ -18,12 +18,14 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
__revision__ = "$Id"
import cryptobox.web.testclass
class unittests(cryptobox.web.testclass.WebInterfaceTestClass):
def test_read_form(self):
url = self.URL + "volume_props?weblang=en&device=%2Fdev%2F" + self.device + "1"
url = self.url + "volume_props?weblang=en&device=%2Fdev%2F" + self.device
self.register_auth(url)
self.cmd.go(url)
self.cmd.find('Properties')

View File

@ -18,6 +18,8 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
__revision__ = "$Id"
import cryptobox.plugins.base
import cryptobox.plugins.manage
from cryptobox.core.exceptions import *
@ -25,51 +27,54 @@ from cryptobox.core.exceptions import *
class volume_props(cryptobox.plugins.base.CryptoBoxPlugin):
pluginCapabilities = [ "volume" ]
pluginVisibility = [ "volume" ]
requestAuth = False
plugin_capabilities = [ "volume" ]
plugin_visibility = [ "volume" ]
request_auth = False
rank = 30
def doAction(self, **args):
def do_action(self, **args):
import os
self.props_plugins = [e for e in cryptobox.plugins.manage.PluginManager(self.cbox, self.cbox.prefs["Locations"]["PluginDir"]).getPlugins() if "properties" in e.getVisibility()]
self.props_plugins = [e for e in cryptobox.plugins.manage.PluginManager(self.cbox,
self.cbox.prefs["Locations"]["PluginDir"]).get_plugins()
if "properties" in e.get_visibility()]
## sort plugins by rank
self.props_plugins.sort(cmp = self.__cmpPluginsRank)
self.props_plugins.sort(cmp = self.__cmp_plugins_rank)
## set the name of the templates for every plugin
loadString = ""
load_string = ""
for p in self.props_plugins:
p.device = self.device
plfname = os.path.join(p.pluginDir, str(p.doAction(**args)) + ".cs")
loadString += "<?cs include:'%s' ?>" % plfname
plfname = os.path.join(p.plugin_dir, str(p.do_action(**args)) + ".cs")
load_string += "<?cs include:'%s' ?>" % plfname
## this is a little bit ugly: as it is not possible, to load cs files via
## 'linclude' (see clearsilver doc) if they use previously defined macros (see
## clearsilver mailing list thread 'linclude file which calls a macro' - 27th
## December 02005)
## our workaround: define the appropriate "include" (not 'linclude') commands
## as a hdf variable - then we can include it via 'evar'
self.hdf[self.hdf_prefix + 'includePlugins'] = loadString
self.hdf[self.hdf_prefix + 'includePlugins'] = load_string
return "volume_properties"
def getStatus(self):
def get_status(self):
return "TODO"
def loadDataSet(self, hdf):
def load_dataset(self, hdf):
"""override the parent's function
we have to get the data from all included plugins"""
for p in self.props_plugins:
p.loadDataSet(hdf)
for plugin in self.props_plugins:
plugin.load_dataset(hdf)
## call our parent's method
cryptobox.plugins.base.CryptoBoxPlugin.loadDataSet(self, hdf)
cryptobox.plugins.base.CryptoBoxPlugin.load_dataset(self, hdf)
def __cmpPluginsRank(self, p1, p2):
order = p1.getRank() - p2.getRank()
def __cmp_plugins_rank(self, p1, p2):
order = p1.get_rank() - p2.get_rank()
if order < 0:
return -1
elif order == 0:
return 0
else:
return 1

View File

@ -18,12 +18,14 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
__revision__ = "$Id"
import cryptobox.web.testclass
class unittests(cryptobox.web.testclass.WebInterfaceTestClass):
def test_read_form(self):
url = self.URL + "volume_rename?weblang=en&device=%2Fdev%2F" + self.device + "1"
url = self.url + "volume_rename?weblang=en&device=%2Fdev%2F" + self.device
self.register_auth(url)
self.cmd.go(url)
self.cmd.find('name')

View File

@ -18,49 +18,51 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
__revision__ = "$Id"
import cryptobox.plugins.base
from cryptobox.core.exceptions import *
class volume_rename(cryptobox.plugins.base.CryptoBoxPlugin):
pluginCapabilities = [ "volume" ]
pluginVisibility = [ "properties" ]
requestAuth = False
plugin_capabilities = [ "volume" ]
plugin_visibility = [ "properties" ]
request_auth = False
rank = 60
def doAction(self, store=None, vol_name=None):
self.container = self.cbox.getContainer(self.device)
def do_action(self, store=None, vol_name=None):
self.container = self.cbox.get_container(self.device)
if not self.container:
return None
self.__prepareHDF()
self.__prepare_hdf()
if store and vol_name:
return self.__setVolumeName(vol_name)
return self.__set_volume_name(vol_name)
else:
return "volume_rename"
def getStatus(self):
self.container = self.cbox.getContainer(self.device)
def get_status(self):
self.container = self.cbox.get_container(self.device)
if not self.container:
return "invalid device"
return "name=%s" % self.container.getName()
return "name=%s" % self.container.get_name()
def __prepareHDF(self):
self.hdf[self.hdf_prefix + "vol_name"] = self.container.getName()
def __prepare_hdf(self):
self.hdf[self.hdf_prefix + "vol_name"] = self.container.get_name()
def __setVolumeName(self, vol_name):
def __set_volume_name(self, vol_name):
if not vol_name:
self.hdf["Data.Warning"] = "Plugins.volume_rename.InvalidVolumeName"
return "volume_rename"
if vol_name == self.container.getName():
if vol_name == self.container.get_name():
## nothing has to be done
return "volume_rename"
try:
self.container.setName(vol_name)
self.container.set_name(vol_name)
self.hdf["Data.Success"] = "Plugins.volume_rename.VolumeNameChanged"
except CBVolumeIsActive:
self.hdf["Data.Warning"] = "Plugins.volume_rename.NoRenameIfActive"
@ -71,6 +73,6 @@ class volume_rename(cryptobox.plugins.base.CryptoBoxPlugin):
except CBContainerError:
self.hdf["Data.Warning"] = "Plugins.volume_rename.SetVolumeNameFailed"
## reread the volume name
self.__prepareHDF()
self.__prepare_hdf()
return "volume_rename"

View File

@ -1 +1,13 @@
__all__ = ['core','web','plugins','tests']
"""CryptoBox package
The CryptoBox is a webserver. It enables you to control your encrypted
(cryptsetup-luks) and plaintext disks via an easy to use web interface.
The CryptoBox is especially suitable for non-desktop fileservers with
encrypted partitions.
"""
__all__ = ['core', 'web', 'plugins', 'tests']
__revision__ = "$Id$"
__version__ = "0.2.99~1"

View File

@ -0,0 +1,7 @@
"""Core management functions of the CryptoBox.
"""
__revision__ = "$Id"
__all__ = [ 'main', 'container', 'exceptions', 'tools', 'settings' ]

View File

@ -18,21 +18,18 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
## 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)
"""Manage a single container of the CryptoBox
"""
__revision__ = "$Id"
import subprocess
import os
import re
import logging
from cryptobox.core.exceptions import *
ContainerTypes = {
CONTAINERTYPES = {
"unused":0,
"plain":1,
"luks":2,
@ -46,11 +43,12 @@ MOUNT_DIR_MARKER = '_cryptobox_mount_dir_'
class CryptoBoxContainer:
"""Manage a container of the CryptoBox
"""
__fsTypes = {
"plain":["ext3", "ext2", "vfat", "reiser"],
"plain":["ext3", "ext2", "vfat", "reiserfs"],
"swap":["swap"]}
# TODO: more filesystem types? / check 'reiser'
__dmDir = "/dev/mapper"
@ -58,41 +56,58 @@ class CryptoBoxContainer:
def __init__(self, device, cbox):
self.device = device
self.cbox = cbox
self.log = logging.getLogger("CryptoBox")
self.resetObject()
self.uuid = None
self.name = None
self.cont_type = None
self.mount = None
self.umount = None
self.attributes = None
self.reset_object()
def getName(self):
def get_name(self):
"""Return a humanly readable name for the container.
"""
return self.name
def __setAttributes(self):
def __set_attributes(self):
"""Define the default attributes of a container.
At least there should be a uuid.
Other attributes may be added by features (e.g. automount).
"""
try:
## is there already an entry in the database?
self.attributes = self.cbox.prefs.volumesDB[self.getName()]
self.attributes = self.cbox.prefs.volumes_db[self.get_name()]
self.attributes["uuid"] = self.uuid
except KeyError:
## set default values
self.attributes = { "uuid": self.uuid }
self.cbox.prefs.volumesDB[self.getName()] = self.attributes
self.cbox.prefs.volumes_db[self.get_name()] = self.attributes
def setName(self, new_name):
old_name = self.getName()
if new_name == self.name: return
def set_name(self, new_name):
"""Define a humanly readable name of this container.
this also manages the name database
"""
old_name = self.get_name()
if new_name == self.name:
return
## renaming is not possible, if the volume is active, as the mountpoint name
## is the same as the volume name
if self.isMounted():
if self.is_mounted():
raise CBVolumeIsActive("the container must not be active during renaming")
if not re.search(r'^[a-zA-Z0-9_\.\- ]+$', new_name):
raise CBInvalidName("the supplied new name contains illegal characters")
## check for another partitions with the same name
if self.cbox.getContainerList(filterName=new_name):
if self.cbox.get_container_list(filter_name=new_name):
raise CBNameIsInUse("the supplied new name is already in use for anonther partition")
## maybe there a is an entry in the volumes database (but the partition is not active
try:
## remove possibly existing inactive database item
del self.cbox.prefs.volumesDB[new_name]
del self.cbox.prefs.volumes_db[new_name]
except KeyError:
## no entry - so nothing happens
pass
@ -100,99 +115,116 @@ class CryptoBoxContainer:
self.name = new_name
## remove old database entry
try:
del self.cbox.prefs.volumesDB[old_name]
del self.cbox.prefs.volumes_db[old_name]
except KeyError:
pass
## set new volumes database entry
self.cbox.prefs.volumesDB[new_name] = self.attributes
self.cbox.prefs.volumesDB.write()
self.cbox.prefs.volumes_db[new_name] = self.attributes
self.cbox.prefs.volumes_db.write()
def getDevice(self):
def get_device(self):
"""Return the device name of the container
e.g.: /dev/hdc1
"""
return self.device
def getType(self):
return self.type
def get_type(self):
"""Return the type (int) of this container.
"""
return self.cont_type
def isMounted(self):
return os.path.ismount(self.__getMountPoint())
def is_mounted(self):
"""Check if the container is currently mounted.
"""
return os.path.ismount(self.__get_mount_point())
def getCapacity(self):
"""return the current capacity state of the volume
def get_capacity(self):
"""Return the current capacity state of the volume.
the volume may not be mounted
the result is a tuple of values in megabyte:
(size, available, used)
"""
info = os.statvfs(self.__getMountPoint())
info = os.statvfs(self.__get_mount_point())
return (
int(info.f_bsize*info.f_blocks/1024/1024),
int(info.f_bsize*info.f_bavail/1024/1024),
int(info.f_bsize*(info.f_blocks-info.f_bavail)/1024/1024))
def getSize(self):
def get_size(self):
"""return the size of the block device (_not_ of the filesystem)
the result is a value in megabyte
an error is indicated by "-1"
"""
import cryptobox.core.tools as cbxtools
return cbxtools.getBlockDeviceSize(self.device)
return cbxtools.get_blockdevice_size(self.device)
def resetObject(self):
def reset_object(self):
""" recheck the information about this container
this is especially useful after changing the type via 'create' """
self.uuid = self.__getUUID()
self.type = self.__getTypeOfPartition()
self.name = self.__getNameOfContainer()
self.__setAttributes()
if self.type == ContainerTypes["luks"]:
self.mount = self.__mountLuks
self.umount = self.__umountLuks
elif self.type == ContainerTypes["plain"]:
self.mount = self.__mountPlain
self.umount = self.__umountPlain
self.uuid = self.__get_uuid()
self.cont_type = self.__get_type_of_partition()
self.name = self.__get_name_of_container()
self.__set_attributes()
if self.cont_type == CONTAINERTYPES["luks"]:
self.mount = self.__mount_luks
self.umount = self.__umount_luks
elif self.cont_type == CONTAINERTYPES["plain"]:
self.mount = self.__mount_plain
self.umount = self.__umount_plain
def create(self, type, password=None):
old_name = self.getName()
if type == ContainerTypes["luks"]:
self.__createLuks(password)
elif type == ContainerTypes["plain"]:
self.__createPlain()
def create(self, cont_type, password=None):
"""Format a container.
Also set a password for encrypted container.
"""
old_name = self.get_name()
if cont_type == CONTAINERTYPES["luks"]:
self.__create_luks(password)
elif cont_type == CONTAINERTYPES["plain"]:
self.__create_plain()
else:
raise CBInvalidType("invalid container type (%d) supplied" % (type, ))
raise CBInvalidType("invalid container type (%d) supplied" % (cont_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)
self.reset_object()
## restore the old name (must be after reset_object)
self.set_name(old_name)
def changePassword(self, oldpw, newpw):
if self.type != ContainerTypes["luks"]:
def change_password(self, oldpw, newpw):
"""Change the password of an encrypted container.
Raises an exception for plaintext container.
"""
if self.cont_type != CONTAINERTYPES["luks"]:
raise CBInvalidType("changing of password is possible only for luks containers")
if not oldpw:
raise CBInvalidPassword("no old password supplied for password change")
if not newpw:
raise CBInvalidPassword("no new password supplied for password change")
"return if new and old passwords are the same"
if oldpw == newpw: return
if self.isMounted():
## return if new and old passwords are the same
if oldpw == newpw:
return
if self.is_mounted():
raise CBVolumeIsActive("this container is currently active")
devnull = None
try:
devnull = open(os.devnull, "w")
except IOError:
self.log.warn("Could not open %s" % (os.devnull, ))
"remove any potential open luks mapping"
self.__umountLuks()
"create the luks header"
self.cbox.log.warn("Could not open %s" % (os.devnull, ))
## remove any potential open luks mapping
self.__umount_luks()
## create the luks header
proc = subprocess.Popen(
shell = False,
stdin = subprocess.PIPE,
@ -208,16 +240,17 @@ class CryptoBoxContainer:
proc.stdin.write("%s\n%s" % (oldpw, newpw))
(output, errout) = proc.communicate()
if proc.returncode != 0:
errorMsg = "Could not add a new luks key: %s - %s" % (output.strip(), errout.strip(), )
self.log.error(errorMsg)
raise CBChangePasswordError(errorMsg)
error_msg = "Could not add a new luks key: %s - %s" \
% (output.strip(), errout.strip(), )
self.cbox.log.error(error_msg)
raise CBChangePasswordError(error_msg)
## retrieve the key slot we used for unlocking
keys_found = re.search(r'key slot (\d{1,3}) unlocked', output).groups()
if keys_found:
keyslot = int(keys_found[0])
else:
raise CBChangePasswordError("could not get the old key slot")
"remove the old key"
## remove the old key
proc = subprocess.Popen(
shell = False,
stdin = None,
@ -231,40 +264,43 @@ class CryptoBoxContainer:
"%d" % (keyslot, )])
proc.wait()
if proc.returncode != 0:
errorMsg = "Could not remove the old luks key: %s" % (proc.stderr.read().strip(), )
self.log.error(errorMsg)
raise CBChangePasswordError(errorMsg)
error_msg = "Could not remove the old luks key: %s" % (proc.stderr.read().strip(), )
self.cbox.log.error(error_msg)
raise CBChangePasswordError(error_msg)
" ****************** internal stuff ********************* "
## ****************** internal stuff *********************
def __getNameOfContainer(self):
def __get_name_of_container(self):
"""retrieve the name of the container by querying the database
call this function only for the initial setup of the container object"""
found_name = None
for key in self.cbox.prefs.volumesDB.keys():
if self.cbox.prefs.volumesDB[key]["uuid"] == self.uuid:
for key in self.cbox.prefs.volumes_db.keys():
if self.cbox.prefs.volumes_db[key]["uuid"] == self.uuid:
found_name = key
if found_name: return found_name
if found_name:
return found_name
## there is no name defined for this uuid - we will propose a good one
prefix = self.cbox.prefs["Main"]["DefaultVolumePrefix"]
unused_found = False
counter = 1
while not unused_found:
guess = prefix + str(counter)
if self.cbox.prefs.volumesDB.has_key(guess):
if self.cbox.prefs.volumes_db.has_key(guess):
counter += 1
else:
unused_found = True
return guess
def __getUUID(self):
if self.__getTypeOfPartition() == ContainerTypes["luks"]:
guess = self.__getLuksUUID()
def __get_uuid(self):
"""Retrieve the uuid of the container device.
"""
if self.__get_type_of_partition() == CONTAINERTYPES["luks"]:
guess = self.__get_luks_uuid()
else:
guess = self.__getNonLuksUUID()
guess = self.__get_non_luks_uuid()
## did we get a valid value?
if guess:
return guess
@ -273,7 +309,7 @@ class CryptoBoxContainer:
return self.device.replace(os.path.sep, "_")
def __getLuksUUID(self):
def __get_luks_uuid(self):
"""get uuid for luks devices"""
proc = subprocess.Popen(
shell = False,
@ -284,17 +320,18 @@ class CryptoBoxContainer:
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()))
self.cbox.log.info("could not retrieve luks uuid (%s): %s",
(self.device, stderr.strip()))
return None
return stdout.strip()
def __getNonLuksUUID(self):
def __get_non_luks_uuid(self):
"""return UUID for ext2/3 and vfat filesystems"""
try:
devnull = open(os.devnull, "w")
except IOError:
self.warn("Could not open %s" % (os.devnull, ))
self.cbox.log.warn("Could not open %s" % (os.devnull, ))
proc = subprocess.Popen(
shell=False,
stdout=subprocess.PIPE,
@ -309,30 +346,36 @@ class CryptoBoxContainer:
devnull.close()
## execution failed?
if proc.returncode != 0:
self.log.info("retrieving of partition type (%s) via 'blkid' failed: %s - maybe it is encrypted?" % (self.device, stderr.strip()))
self.cbox.log.info("retrieving of partition type (" + str(self.device) \
+ ") via 'blkid' failed: " + str(stderr.strip()) \
+ " - maybe it is encrypted?")
return None
## return output of blkid
return stdout.strip()
def __getTypeOfPartition(self):
"retrieve the type of the given partition (see cryptobox.core.container.ContainerTypes)"
if self.__isLuksPartition(): return ContainerTypes["luks"]
typeOfPartition = self.__getTypeIdOfPartition()
if typeOfPartition in self.__fsTypes["plain"]:
return ContainerTypes["plain"]
if typeOfPartition in self.__fsTypes["swap"]:
return ContainerTypes["swap"]
return ContainerTypes["unused"]
def __get_type_of_partition(self):
"""Retrieve the type of the given partition.
see cryptobox.core.container.CONTAINERTYPES
"""
if self.__is_luks_partition():
return CONTAINERTYPES["luks"]
type_of_partition = self.__get_type_id_of_partition()
if type_of_partition in self.__fsTypes["plain"]:
return CONTAINERTYPES["plain"]
if type_of_partition in self.__fsTypes["swap"]:
return CONTAINERTYPES["swap"]
return CONTAINERTYPES["unused"]
def __getTypeIdOfPartition(self):
def __get_type_id_of_partition(self):
"returns the type of the partition (see 'man blkid')"
devnull = None
try:
devnull = open(os.devnull, "w")
except IOError:
self.log.warn("Could not open %s" % (os.devnull, ))
self.cbox.log.warn("Could not open %s" % (os.devnull, ))
proc = subprocess.Popen(
shell=False,
stdin=None,
@ -347,19 +390,20 @@ class CryptoBoxContainer:
proc.wait()
output = proc.stdout.read().strip()
if proc.returncode != 0:
self.log.warn("retrieving of partition type via 'blkid' failed: %s" % (proc.stderr.read().strip(), ))
self.cbox.log.warn("retrieving of partition type via 'blkid' failed: %s" % \
(proc.stderr.read().strip(), ))
return None
devnull.close()
return output
def __isLuksPartition(self):
def __is_luks_partition(self):
"check if the given device is a luks partition"
devnull = None
try:
devnull = open(os.devnull, "w")
except IOError:
self.log.warn("Could not open %s" % (os.devnull, ))
self.cbox.log.warn("Could not open %s" % (os.devnull, ))
proc = subprocess.Popen(
shell = False,
stdin = None,
@ -375,29 +419,30 @@ class CryptoBoxContainer:
return proc.returncode == 0
def __getMountPoint(self):
def __get_mount_point(self):
"return the name of the mountpoint of this volume"
return os.path.join(self.cbox.prefs["Locations"]["MountParentDir"], self.name)
def __mountLuks(self, password):
def __mount_luks(self, password):
"mount a luks partition"
if not password:
raise CBInvalidPassword("no password supplied for luksOpen")
if self.isMounted(): raise CBVolumeIsActive("this container is already active")
self.__umountLuks()
if self.is_mounted():
raise CBVolumeIsActive("this container is already active")
self.__umount_luks()
try:
devnull = open(os.devnull, "w")
except IOError:
self.log.warn("Could not open %s" % (os.devnull, ))
self.__cleanMountDirs()
if not os.path.exists(self.__getMountPoint()):
self.__createMountDirectory(self.__getMountPoint())
if not os.path.exists(self.__getMountPoint()):
errorMsg = "Could not create mountpoint (%s)" % (self.__getMountPoint(), )
self.log.error(errorMsg)
raise CBMountError(errorMsg)
self.cbox.sendEventNotification("premount", self.__getEventArgs())
self.cbox.log.warn("Could not open %s" % (os.devnull, ))
self.__clean_mount_dirs()
if not os.path.exists(self.__get_mount_point()):
self.__create_mount_directory(self.__get_mount_point())
if not os.path.exists(self.__get_mount_point()):
err_msg = "Could not create mountpoint (%s)" % (self.__get_mount_point(), )
self.cbox.log.error(err_msg)
raise CBMountError(err_msg)
self.cbox.send_event_notification("premount", self.__get_event_args())
proc = subprocess.Popen(
shell = False,
stdin = subprocess.PIPE,
@ -414,9 +459,9 @@ class CryptoBoxContainer:
proc.stdin.write(password)
(output, errout) = proc.communicate()
if proc.returncode != 0:
errorMsg = "Could not open the luks mapping: %s" % (errout.strip(), )
self.log.warn(errorMsg)
raise CBMountError(errorMsg)
err_msg = "Could not open the luks mapping: %s" % (errout.strip(), )
self.cbox.log.warn(err_msg)
raise CBMountError(err_msg)
proc = subprocess.Popen(
shell = False,
stdin = None,
@ -427,25 +472,25 @@ class CryptoBoxContainer:
self.cbox.prefs["Programs"]["CryptoBoxRootActions"],
"mount",
os.path.join(self.__dmDir, self.name),
self.__getMountPoint()])
self.__get_mount_point()])
proc.wait()
if proc.returncode != 0:
errorMsg = "Could not mount the filesystem: %s" % (proc.stderr.read().strip(), )
self.log.warn(errorMsg)
raise CBMountError(errorMsg)
err_msg = "Could not mount the filesystem: %s" % (proc.stderr.read().strip(), )
self.cbox.log.warn(err_msg)
raise CBMountError(err_msg)
devnull.close()
self.cbox.sendEventNotification("postmount", self.__getEventArgs())
self.cbox.send_event_notification("postmount", self.__get_event_args())
def __umountLuks(self):
def __umount_luks(self):
"umount a luks partition"
devnull = None
try:
devnull = open(os.devnull, "w")
except IOError:
self.log.warn("Could not open %s" % (os.devnull, ))
self.cbox.sendEventNotification("preumount", self.__getEventArgs())
if self.isMounted():
self.cbox.log.warn("Could not open %s" % (os.devnull, ))
self.cbox.send_event_notification("preumount", self.__get_event_args())
if self.is_mounted():
proc = subprocess.Popen(
shell = False,
stdin = None,
@ -455,12 +500,12 @@ class CryptoBoxContainer:
self.cbox.prefs["Programs"]["super"],
self.cbox.prefs["Programs"]["CryptoBoxRootActions"],
"umount",
self.__getMountPoint()])
self.__get_mount_point()])
proc.wait()
if proc.returncode != 0:
errorMsg = "Could not umount the filesystem: %s" % (proc.stderr.read().strip(), )
self.log.warn(errorMsg)
raise CBUmountError(errorMsg)
err_msg = "Could not umount the filesystem: %s" % (proc.stderr.read().strip(), )
self.cbox.log.warn(err_msg)
raise CBUmountError(err_msg)
if os.path.exists(os.path.join(self.__dmDir, self.name)):
proc = subprocess.Popen(
shell = False,
@ -476,29 +521,30 @@ class CryptoBoxContainer:
"--batch-mode"])
proc.wait()
if proc.returncode != 0:
errorMsg = "Could not remove the luks mapping: %s" % (proc.stderr.read().strip(), )
self.log.warn(errorMsg)
raise CBUmountError(errorMsg)
err_msg = "Could not remove the luks mapping: %s" % (proc.stderr.read().strip(), )
self.cbox.log.warn(err_msg)
raise CBUmountError(err_msg)
devnull.close()
self.cbox.sendEventNotification("postumount", self.__getEventArgs())
self.cbox.send_event_notification("postumount", self.__get_event_args())
def __mountPlain(self):
def __mount_plain(self):
"mount a plaintext partition"
if self.isMounted(): raise CBVolumeIsActive("this container is already active")
if self.is_mounted():
raise CBVolumeIsActive("this container is already active")
devnull = None
try:
devnull = open(os.devnull, "w")
except IOError:
self.log.warn("Could not open %s" % (os.devnull, ))
self.__cleanMountDirs()
if not os.path.exists(self.__getMountPoint()):
self.__createMountDirectory(self.__getMountPoint())
if not os.path.exists(self.__getMountPoint()):
errorMsg = "Could not create mountpoint (%s)" % (self.__getMountPoint(), )
self.log.error(errorMsg)
raise CBMountError(errorMsg)
self.cbox.sendEventNotification("premount", self.__getEventArgs())
self.cbox.log.warn("Could not open %s" % (os.devnull, ))
self.__clean_mount_dirs()
if not os.path.exists(self.__get_mount_point()):
self.__create_mount_directory(self.__get_mount_point())
if not os.path.exists(self.__get_mount_point()):
err_msg = "Could not create mountpoint (%s)" % (self.__get_mount_point(), )
self.cbox.log.error(err_msg)
raise CBMountError(err_msg)
self.cbox.send_event_notification("premount", self.__get_event_args())
proc = subprocess.Popen(
shell = False,
stdin = None,
@ -509,27 +555,28 @@ class CryptoBoxContainer:
self.cbox.prefs["Programs"]["CryptoBoxRootActions"],
"mount",
self.device,
self.__getMountPoint()])
self.__get_mount_point()])
proc.wait()
if proc.returncode != 0:
errorMsg = "Could not mount the filesystem: %s" % (proc.stderr.read().strip(), )
self.log.warn(errorMsg)
raise CBMountError(errorMsg)
err_msg = "Could not mount the filesystem: %s" % (proc.stderr.read().strip(), )
self.cbox.log.warn(err_msg)
raise CBMountError(err_msg)
devnull.close()
self.cbox.sendEventNotification("postmount", self.__getEventArgs())
self.cbox.send_event_notification("postmount", self.__get_event_args())
def __umountPlain(self):
def __umount_plain(self):
"umount a plaintext partition"
if not self.isMounted():
self.cbox.log.info("trying to umount while volume (%s) is mounted" % self.getDevice())
if not self.is_mounted():
self.cbox.log.info("trying to umount while volume (%s) is mounted" % \
self.get_device())
return
devnull = None
try:
devnull = open(os.devnull, "w")
except IOError:
self.log.warn("Could not open %s" % (os.devnull, ))
self.cbox.sendEventNotification("preumount", self.__getEventArgs())
self.cbox.log.warn("Could not open %s" % (os.devnull, ))
self.cbox.send_event_notification("preumount", self.__get_event_args())
proc = subprocess.Popen(
shell = False,
stdin = None,
@ -539,25 +586,25 @@ class CryptoBoxContainer:
self.cbox.prefs["Programs"]["super"],
self.cbox.prefs["Programs"]["CryptoBoxRootActions"],
"umount",
self.__getMountPoint()])
self.__get_mount_point()])
proc.wait()
if proc.returncode != 0:
errorMsg = "Could not umount the filesystem: %s" % (proc.stderr.read().strip(), )
self.log.warn(errorMsg)
raise CBUmountError(errorMsg)
err_msg = "Could not umount the filesystem: %s" % (proc.stderr.read().strip(), )
self.cbox.log.warn(err_msg)
raise CBUmountError(err_msg)
devnull.close()
self.cbox.sendEventNotification("postumount", self.__getEventArgs())
self.cbox.send_event_notification("postumount", self.__get_event_args())
def __createPlain(self):
def __create_plain(self):
"make a plaintext partition"
if self.isMounted():
if self.is_mounted():
raise CBVolumeIsActive("deactivate the partition before filesystem initialization")
devnull = None
try:
devnull = open(os.devnull, "w")
except IOError:
self.log.warn("Could not open %s" % (os.devnull, ))
self.cbox.log.warn("Could not open %s" % (os.devnull, ))
proc = subprocess.Popen(
shell = False,
stdin = None,
@ -568,26 +615,27 @@ class CryptoBoxContainer:
self.device])
proc.wait()
if proc.returncode != 0:
errorMsg = "Could not create the filesystem: %s" % (proc.stderr.read().strip(), )
self.log.error(errorMsg)
raise CBCreateError(errorMsg)
err_msg = "Could not create the filesystem: %s" % (proc.stderr.read().strip(), )
self.cbox.log.error(err_msg)
raise CBCreateError(err_msg)
devnull.close()
def __createLuks(self, password):
"make a luks partition"
def __create_luks(self, password):
"""Create a luks partition.
"""
if not password:
raise CBInvalidPassword("no password supplied for new luks mapping")
if self.isMounted():
if self.is_mounted():
raise CBVolumeIsActive("deactivate the partition before filesystem initialization")
devnull = None
try:
devnull = open(os.devnull, "w")
except IOError:
self.log.warn("Could not open %s" % (os.devnull, ))
"remove any potential open luks mapping"
self.__umountLuks()
"create the luks header"
self.cbox.log.warn("Could not open %s" % (os.devnull, ))
## remove any potential open luks mapping
self.__umount_luks()
## create the luks header
proc = subprocess.Popen(
shell = False,
stdin = subprocess.PIPE,
@ -605,10 +653,10 @@ class CryptoBoxContainer:
proc.stdin.write(password)
(output, errout) = proc.communicate()
if proc.returncode != 0:
errorMsg = "Could not create the luks header: %s" % (errout.strip(), )
self.log.error(errorMsg)
raise CBCreateError(errorMsg)
"open the luks container for mkfs"
err_msg = "Could not create the luks header: %s" % (errout.strip(), )
self.cbox.log.error(err_msg)
raise CBCreateError(err_msg)
## open the luks container for mkfs
proc = subprocess.Popen(
shell = False,
stdin = subprocess.PIPE,
@ -625,10 +673,10 @@ class CryptoBoxContainer:
proc.stdin.write(password)
(output, errout) = proc.communicate()
if proc.returncode != 0:
errorMsg = "Could not open the new luks mapping: %s" % (errout.strip(), )
self.log.error(errorMsg)
raise CBCreateError(errorMsg)
"make the filesystem"
err_msg = "Could not open the new luks mapping: %s" % (errout.strip(), )
self.cbox.log.error(err_msg)
raise CBCreateError(err_msg)
## make the filesystem
proc = subprocess.Popen(
shell = False,
stdin = None,
@ -638,50 +686,54 @@ class CryptoBoxContainer:
self.cbox.prefs["Programs"]["mkfs-data"],
os.path.join(self.__dmDir, self.name)])
proc.wait()
"remove the mapping - for every exit status"
self.__umountLuks()
## remove the mapping - for every exit status
self.__umount_luks()
if proc.returncode != 0:
errorMsg = "Could not create the filesystem: %s" % (proc.stderr.read().strip(), )
self.log.error(errorMsg)
"remove the luks mapping"
raise CBCreateError(errorMsg)
err_msg = "Could not create the filesystem: %s" % (proc.stderr.read().strip(), )
self.cbox.log.error(err_msg)
## remove the luks mapping
raise CBCreateError(err_msg)
devnull.close()
def __cleanMountDirs(self):
def __clean_mount_dirs(self):
""" remove all unnecessary subdirs of the mount parent directory
this should be called for every (u)mount """
subdirs = os.listdir(self.cbox.prefs["Locations"]["MountParentDir"])
for d in subdirs:
abs_dir = os.path.join(self.cbox.prefs["Locations"]["MountParentDir"], d)
for one_dir in subdirs:
abs_dir = os.path.join(self.cbox.prefs["Locations"]["MountParentDir"], one_dir)
if (not os.path.islink(abs_dir)) \
and os.path.isdir(abs_dir) \
and (not os.path.ismount(abs_dir)) \
and (os.path.isfile(os.path.join(abs_dir,MOUNT_DIR_MARKER))) \
and (len(os.listdir(abs_dir)) == 1):
try:
os.remove(os.path.join(abs_dir,MOUNT_DIR_MARKER))
os.remove(os.path.join(abs_dir, MOUNT_DIR_MARKER))
os.rmdir(abs_dir)
except OSError,errMsg:
except OSError, err_msg:
## we do not care too much about unclean cleaning ...
self.log.info("failed to clean a mountpoint (%s): %s" % (abs_dir,str(errMsg)))
self.cbox.log.info("failed to clean a mountpoint (%s): %s" % \
(abs_dir, str(err_msg)))
def __createMountDirectory(self,dirname):
def __create_mount_directory(self, dirname):
"""create and mark a mount directory
this marking helps to remove old mountdirs safely"""
os.mkdir(dirname)
try:
f = file(os.path.join(dirname,MOUNT_DIR_MARKER),"w")
f.close()
except OSError,errMsg:
mark_file = file(os.path.join(dirname, MOUNT_DIR_MARKER), "w")
mark_file.close()
except OSError, err_msg:
## we do not care too much about the marking
self.log.info("failed to mark a mountpoint (%s): %s" % (dirname,str(errMsg)))
self.cbox.log.info("failed to mark a mountpoint (%s): %s" % (dirname, str(err_msg)))
def __getEventArgs(self):
"""return an array of arguments for event scripts handling pre/post-mount/umount
events"""
typeText = [e for e in ContainerTypes.keys() if ContainerTypes[e] == self.getType()][0]
return [self.getDevice(), self.getName(), typeText, self.__getMountPoint()]
def __get_event_args(self):
"""Return an array of arguments for event scripts.
for now supported: pre/post-mount/umount events
"""
type_text = [e for e in CONTAINERTYPES.keys()
if CONTAINERTYPES[e] == self.get_type()][0]
return [self.get_device(), self.get_name(), type_text, self.__get_mount_point()]

View File

@ -22,6 +22,8 @@
exceptions of the cryptobox package
"""
__revision__ = "$Id"
class CBError(Exception):
"""base class for exceptions of the cryptobox"""
@ -54,13 +56,16 @@ class CBConfigUndefinedError(CBConfigError):
self.name = name
def __str__(self):
# is it a settings or a section?
"""Output the appropriate string: for a setting or a section.
"""
if self.name:
# setting
return "undefined configuration setting: [%s]->%s - please check your configuration file" % (self.section, self.name)
return "undefined configuration setting: [" + str(self.section) \
+ "]->" + str(self.name) + " - please check your configuration file"
else:
# section
return "undefined configuration section: [%s] - please check your configuration file" % (self.section, )
return "undefined configuration section: [" + str(self.section) \
+ "] - please check your configuration file"
@ -73,8 +78,12 @@ class CBConfigInvalidValueError(CBConfigError):
self.value = value
self.reason = reason
def __str__(self):
return "invalid configuration setting [%s]->%s (%s): %s" % (self.section, self.name, self.value, self.reason)
"""Return the error description.
"""
return "invalid configuration setting [%s]->%s (%s): %s" % \
(self.section, self.name, self.value, self.reason)
class CBEnvironmentError(CBError):
@ -86,42 +95,58 @@ class CBEnvironmentError(CBError):
self.desc = desc
def __str__(self):
"""Return the error description.
"""
return "misconfiguration detected: %s" % self.desc
class CBContainerError(CBError):
"""any error raised while manipulating a cryptobox container"""
"""Any error raised while manipulating a cryptobox container.
"""
def __init__(self, desc):
self.desc = desc
def __str__(self):
return self.desc
class CBCreateError(CBContainerError):
"""Raised if a container could not be created (formatted).
"""
pass
class CBVolumeIsActive(CBContainerError):
"""Raised if a container was active even if it may not for a specific action.
"""
pass
class CBInvalidName(CBContainerError):
"""Raised if someone tried to set an invalid container name.
"""
pass
class CBNameIsInUse(CBContainerError):
"""Raised if the new name of a container is already in use.
"""
pass
class CBInvalidType(CBContainerError):
"""Raised if a container is of an invalid type for a choosen action.
"""
pass
class CBInvalidPassword(CBContainerError):
"""Someone tried to open an ecnrypted container with the wrong password.
"""
pass
class CBChangePasswordError(CBContainerError):
"""Changing of the password of an encrypted container failed.
"""
pass
class CBMountError(CBContainerError):
"""Failed to mount a container.
"""
pass
class CBUmountError(CBContainerError):
"""Failed to umount a container.
"""
pass

View File

@ -22,23 +22,17 @@
This is the web interface for a fileserver managing encrypted filesystems.
'''
# 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)
__revision__ = "$Id"
import sys
import cryptobox.core.container as cbxContainer
from cryptobox.core.exceptions import *
from cryptobox.core.exceptions import CBEnvironmentError, CBConfigUndefinedError
import re
import os
import cryptobox.core.tools as cbxTools
import subprocess
VERSION = "0.3~1"
class CryptoBox:
'''this class rules them all!
@ -48,14 +42,15 @@ class CryptoBox:
def __init__(self, config_file=None):
import cryptobox.core.settings as cbxSettings
self.log = self.__getStartupLogger()
self.log = self.__get_startup_logger()
self.prefs = cbxSettings.CryptoBoxSettings(config_file)
self.__runTests()
self.__run_tests()
self.__containers = []
self.reread_container_list()
def __getStartupLogger(self):
import logging
'''initialises the logging system
def __get_startup_logger(self):
"""Initialize the configured logging facility of the CryptoBox.
use it with: 'self.log.[debug|info|warning|error|critical](logmessage)'
all classes should get the logging instance during __init__:
@ -63,13 +58,15 @@ class CryptoBox:
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'''
to the configured destination
"""
import logging
## basicConfig(...) needs python >= 2.4
try:
log_handler = logging.getLogger("CryptoBox")
logging.basicConfig(
format='%(asctime)s CryptoBox %(levelname)s: %(message)s',
stderr=sys.stderr)
format = '%(asctime)s CryptoBox %(levelname)s: %(message)s',
stderr = sys.stderr)
log_handler.setLevel(logging.ERROR)
log_handler.info("loggingsystem is up'n running")
## from now on everything can be logged via self.log...
@ -78,19 +75,15 @@ class CryptoBox:
return log_handler
# do some initial checks
def __runTests(self):
#self.__runTestUID()
self.__runTestRootPriv()
def __run_tests(self):
"""Do some initial tests.
"""
self.__run_test_root_priv()
def __runTestUID(self):
if os.geteuid() == 0:
raise CBEnvironmentError("you may not run the cryptobox as root")
def __runTestRootPriv(self):
"""try to run 'super' with 'CryptoBoxRootActions'"""
def __run_test_root_priv(self):
"""Try to run 'super' with 'CryptoBoxRootActions'.
"""
try:
devnull = open(os.devnull, "w")
except IOError:
@ -110,46 +103,39 @@ class CryptoBox:
stderr = devnull,
args = [prog_super, prog_rootactions, "check"])
except OSError:
raise CBEnvironmentError("failed to execute 'super' (%s)" % self.prefs["Programs"]["super"])
raise CBEnvironmentError(
"failed to execute 'super' (%s)" % self.prefs["Programs"]["super"])
proc.wait()
if proc.returncode != 0:
raise CBEnvironmentError("failed to call CryptoBoxRootActions (%s) via 'super' - maybe you did not add the appropriate line to /etc/super.tab?" % prog_rootactions)
raise CBEnvironmentError("failed to call CryptoBoxRootActions ("
+ prog_rootactions + ") via 'super' - maybe you did not add the "
+ "appropriate line to '/etc/super.tab'?")
# this method just demonstrates inheritance effects - may be removed
def cbx_inheritance_test(self, string="you lucky widow"):
self.log.info(string)
def reread_container_list(self):
"""Reinitialize the list of available containers.
# RFC: why should CryptoBoxProps inherit CryptoBox? [l]
# RFC: shouldn't we move all useful functions of CryptoBoxProps to CryptoBox? [l]
class CryptoBoxProps(CryptoBox):
'''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.
'''
def __init__(self, config_file=None):
'''read config and fill class variables'''
CryptoBox.__init__(self, config_file)
self.reReadContainerList()
def reReadContainerList(self):
This should be called whenever the available containers may have changed.
E.g.: after partitioning and after device addition/removal
"""
self.log.debug("rereading container list")
self.containers = []
for device in cbxTools.getAvailablePartitions():
if self.isDeviceAllowed(device) and not self.isConfigPartition(device):
self.containers.append(cbxContainer.CryptoBoxContainer(device, self))
self.__containers = []
for device in cbxTools.get_available_partitions():
if self.is_device_allowed(device) and not self.is_config_partition(device):
self.__containers.append(cbxContainer.CryptoBoxContainer(device, self))
## sort by container name
self.containers.sort(cmp = lambda x,y: x.getName() < y.getName() and -1 or 1)
self.__containers.sort(cmp = lambda x, y: x.get_name() < y.get_name() and -1 or 1)
def isConfigPartition(self, device):
def is_config_partition(self, device):
"""Check if a given partition contains configuration informations.
The check is done by comparing the label of the filesystem with a string.
"""
proc = subprocess.Popen(
shell = False,
stdout = subprocess.PIPE,
stderr = subprocess.PIPE,
args = [
self.prefs["Programs"]["blkid"],
"-c", os.path.devnull,
@ -160,70 +146,77 @@ class CryptoBoxProps(CryptoBox):
return output.strip() == self.prefs["Main"]["ConfigVolumeLabel"]
def isDeviceAllowed(self, devicename):
def is_device_allowed(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]
if type(allowed) == types.StringType:
allowed = [allowed]
for a_dev in allowed:
"remove double dots and so on ..."
## remove double dots and so on ...
real_device = os.path.realpath(devicename)
if a_dev and re.search('^' + a_dev, real_device): return True
if a_dev and re.search('^' + a_dev, real_device):
return True
return False
def getLogData(self, lines=None, maxSize=None):
def get_log_data(self, lines=None, max_size=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'
the maximum number and size of these entries can be limited by
'lines' and 'max_size'
"""
# return nothing if the currently selected log output is not a file
try:
if self.prefs["Log"]["Destination"].upper() != "FILE": return []
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")
self.log.error("could not evaluate one of the following config settings: "
+ "[Log]->Destination or [Log]->Details")
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()
fdesc = open(log_file, "r")
if max_size:
fdesc.seek(-max_size, 2) # seek relative to the end of the file
content = fdesc.readlines()
fdesc.close()
except IOError:
self.log.warn("failed to read the log file (%s)" % log_file)
return []
if lines: content = content[-lines:]
if lines:
content = content[-lines:]
content.reverse()
return content
def getContainerList(self, filterType=None, filterName=None):
def get_container_list(self, filter_type=None, filter_name=None):
"retrieve the list of all containers of this cryptobox"
try:
result = self.containers[:]
if filterType != None:
if filterType in range(len(cbxContainer.ContainerTypes)):
return [e for e in self.containers if e.getType() == filterType]
result = self.__containers[:]
if filter_type != None:
if filter_type in range(len(cbxContainer.CONTAINERTYPES)):
return [e for e in self.__containers if e.get_type() == filter_type]
else:
self.log.info("invalid filterType (%d)" % filterType)
self.log.info("invalid filter_type (%d)" % filter_type)
result.clear()
if filterName != None:
result = [e for e in self.containers if e.getName() == filterName]
if filter_name != None:
result = [e for e in self.__containers if e.get_name() == filter_name]
return result
except AttributeError:
return []
def getContainer(self, device):
def get_container(self, device):
"retrieve the container element for this device"
all = [e for e in self.getContainerList() if e.device == device]
all = [e for e in self.get_container_list() if e.device == device]
if all:
return all[0]
else:
return None
def sendEventNotification(self, event, event_infos):
def send_event_notification(self, event, event_infos):
"""call all available scripts in the event directory with some event information"""
event_dir = self.prefs["Locations"]["EventDir"]
for fname in os.listdir(event_dir):
@ -240,11 +233,14 @@ class CryptoBoxProps(CryptoBox):
args = cmd_args)
(stdout, stderr) = proc.communicate()
if proc.returncode != 0:
self.log.warn("an event script (%s) failed (exitcode=%d) to handle an event (%s): %s" % (real_fname, proc.returncode, event, stderr.strip()))
self.log.warn(
"an event script (%s) failed (exitcode=%d) to handle an event (%s): %s" %
(real_fname, proc.returncode, event, stderr.strip()))
else:
self.log.info("event handler (%s) finished successfully: %s" % (real_fname, event))
self.log.info("event handler (%s) finished successfully: %s" %
(real_fname, event))
if __name__ == "__main__":
cb = CryptoBoxProps()
CryptoBox()

View File

@ -18,22 +18,21 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
"""Manage the configuration of a CryptoBox
"""
__revision__ = "$Id"
from cryptobox.core.exceptions import *
import logging
try:
import validate
except:
raise CBEnvironmentError("couldn't import 'validate'! Try 'apt-get install python-formencode'.")
import os
import subprocess
try:
import configobj ## needed for reading and writing of the config file
except:
raise CBEnvironmentError("couldn't import 'configobj'! Try 'apt-get install python-configobj'.")
import os
import configobj, validate
class CryptoBoxSettings:
"""Manage the various configuration files of the CryptoBox
"""
CONF_LOCATIONS = [
"./cryptobox.conf",
@ -47,76 +46,85 @@ class CryptoBoxSettings:
def __init__(self, config_file=None):
self.log = logging.getLogger("CryptoBox")
config_file = self.__getConfigFileName(config_file)
config_file = self.__get_config_filename(config_file)
self.log.info("loading config file: %s" % config_file)
self.prefs = self.__getPreferences(config_file)
self.__validateConfig()
self.__configureLogHandler()
self.__checkUnknownPreferences()
self.preparePartition()
self.volumesDB = self.__getVolumesDatabase()
self.pluginConf = self.__getPluginConfig()
self.userDB = self.__getUserDB()
self.misc_files = self.__getMiscFiles()
self.prefs = self.__get_preferences(config_file)
self.__validate_config()
self.__configure_log_handler()
self.__check_unknown_preferences()
self.prepare_partition()
self.volumes_db = self.__get_volumes_database()
self.plugin_conf = self.__get_plugin_config()
self.user_db = self.__get_user_db()
self.misc_files = self.__get_misc_files()
def write(self):
"""
write all local setting files including the content of the "misc" subdirectory
"""
ok = True
status = True
try:
self.volumesDB.write()
self.volumes_db.write()
except IOError:
self.log.warn("could not save the volume database")
ok = False
status = False
try:
self.pluginConf.write()
self.plugin_conf.write()
except IOError:
self.log.warn("could not save the plugin configuration")
ok = False
status = False
try:
self.userDB.write()
self.user_db.write()
except IOError:
self.log.warn("could not save the user database")
ok = False
status = False
for misc_file in self.misc_files:
if not misc_file.save():
self.log.warn("could not save a misc setting file (%s)" % misc_file.filename)
ok = False
return ok
status = False
return status
def requiresPartition(self):
def requires_partition(self):
return bool(self.prefs["Main"]["UseConfigPartition"])
def getActivePartition(self):
def get_active_partition(self):
"""Return the currently active cnfiguration partition.
"""
settings_dir = self.prefs["Locations"]["SettingsDir"]
if not os.path.ismount(settings_dir): return None
if not os.path.ismount(settings_dir):
return None
for line in file("/proc/mounts"):
fields = line.split(" ")
mount_dir = fields[1]
try:
if os.path.samefile(mount_dir, settings_dir): return fields[0]
if os.path.samefile(mount_dir, settings_dir):
return fields[0]
except OSError:
pass
## no matching entry found
return None
def mountPartition(self):
def mount_partition(self):
"""Mount a config partition.
"""
self.log.debug("trying to mount configuration partition")
if not self.requiresPartition():
self.log.warn("mountConfigPartition: configuration partition is not required - mounting anyway")
if self.getActivePartition():
self.log.warn("mountConfigPartition: configuration partition already mounted - not mounting again")
if not self.requires_partition():
self.log.warn("mountConfigPartition: configuration partition is "
+ "not required - mounting anyway")
if self.get_active_partition():
self.log.warn("mountConfigPartition: configuration partition already "
+ "mounted - not mounting again")
return False
confPartitions = self.getAvailablePartitions()
if not confPartitions:
self.log.error("no configuration partitions found - you have to create it first")
conf_partitions = self.get_available_partitions()
if not conf_partitions:
self.log.error("no configuration partition found - you have to create "
+ "it first")
return False
partition = confPartitions[0]
partition = conf_partitions[0]
proc = subprocess.Popen(
shell = False,
stdout = subprocess.PIPE,
@ -136,8 +144,10 @@ class CryptoBoxSettings:
return True
def umountPartition(self):
if not self.getActivePartition():
def umount_partition(self):
"""Umount the currently active configuration partition.
"""
if not self.get_active_partition():
self.log.warn("umountConfigPartition: no configuration partition mounted")
return False
proc = subprocess.Popen(
@ -158,7 +168,7 @@ class CryptoBoxSettings:
return True
def getAvailablePartitions(self):
def get_available_partitions(self):
"""returns a sequence of found config partitions"""
proc = subprocess.Popen(
shell = False,
@ -169,14 +179,16 @@ class CryptoBoxSettings:
"-t", "LABEL=%s" % self.prefs["Main"]["ConfigVolumeLabel"] ])
(output, error) = proc.communicate()
if output:
return [e.strip().split(":",1)[0] for e in output.splitlines()]
return [e.strip().split(":", 1)[0] for e in output.splitlines()]
else:
return []
def preparePartition(self):
if self.requiresPartition() and not self.getActivePartition():
self.mountPartition()
def prepare_partition(self):
"""Mount a config partition if necessary.
"""
if self.requires_partition() and not self.get_active_partition():
self.mount_partition()
def __getitem__(self, key):
@ -184,7 +196,9 @@ class CryptoBoxSettings:
return self.prefs[key]
def __getPreferences(self, config_file):
def __get_preferences(self, config_file):
"""Load the CryptoBox configuration.
"""
import StringIO
config_rules = StringIO.StringIO(self.validation_spec)
try:
@ -192,86 +206,111 @@ class CryptoBoxSettings:
if prefs:
self.log.info("found config: %s" % prefs.items())
else:
raise CBConfigUnavailableError("failed to load the config file: %s" % config_file)
raise CBConfigUnavailableError(
"failed to load the config file: %s" % config_file)
except IOError:
raise CBConfigUnavailableError("unable to open the config file: %s" % config_file)
raise CBConfigUnavailableError(
"unable to open the config file: %s" % config_file)
return prefs
def __validateConfig(self):
def __validate_config(self):
"""Check the configuration settings and cast value types.
"""
result = self.prefs.validate(CryptoBoxSettingsValidator(), preserve_errors=True)
error_list = configobj.flatten_errors(self.prefs, result)
if not error_list: return
errorMsgs = []
if not error_list:
return
error_msgs = []
for sections, key, text in error_list:
section_name = "->".join(sections)
if not text:
errorMsg = "undefined configuration value (%s) in section '%s'" % (key, section_name)
error_msg = "undefined configuration value (%s) in section '%s'" % \
(key, section_name)
else:
errorMsg = "invalid configuration value (%s) in section '%s': %s" % (key, section_name, text)
errorMsgs.append(errorMsg)
raise CBConfigError, "\n".join(errorMsgs)
error_msg = "invalid configuration value (%s) in section '%s': %s" % \
(key, section_name, text)
error_msgs.append(error_msg)
raise CBConfigError, "\n".join(error_msgs)
def __checkUnknownPreferences(self):
def __check_unknown_preferences(self):
"""Check the configuration file for unknown settings to avoid spelling mistakes.
"""
import StringIO
config_rules = configobj.ConfigObj(StringIO.StringIO(self.validation_spec), list_values=False)
self.__recursiveConfigSectionCheck("", self.prefs, config_rules)
config_rules = configobj.ConfigObj(StringIO.StringIO(self.validation_spec),
list_values=False)
self.__recursive_section_check("", self.prefs, config_rules)
def __recursiveConfigSectionCheck(self, section_path, section_config, section_rules):
"""should be called by '__checkUnknownPreferences' for every section
def __recursive_section_check(self, section_path, section_config, section_rules):
"""should be called by '__check_unknown_preferences' for every section
sends a warning message to the logger for every undefined (see validation_spec)
configuration setting
"""
for e in section_config.keys():
element_path = section_path + e
if e in section_rules.keys():
if isinstance(section_config[e], configobj.Section):
if isinstance(section_rules[e], configobj.Section):
self.__recursiveConfigSectionCheck(element_path + "->", section_config[e], section_rules[e])
for section in section_config.keys():
element_path = section_path + section
if section in section_rules.keys():
if isinstance(section_config[section], configobj.Section):
if isinstance(section_rules[section], configobj.Section):
self.__recursive_section_check(element_path + "->",
section_config[section], section_rules[section])
else:
self.log.warn("configuration setting should be a value instead of a section name: %s" % element_path)
self.log.warn("configuration setting should be a value "
+ "instead of a section name: %s" % element_path)
else:
if not isinstance(section_rules[e], configobj.Section):
if not isinstance(section_rules[section], configobj.Section):
pass # good - the setting is valid
else:
self.log.warn("configuration setting should be a section name instead of a value: %s" % element_path)
self.log.warn("configuration setting should be a section "
+ "name instead of a value: %s" % element_path)
else:
self.log.warn("unknown configuration setting: %s" % element_path)
def __getPluginConfig(self):
def __get_plugin_config(self):
"""Load the plugin configuration file if it exists.
"""
import StringIO
plugin_rules = StringIO.StringIO(self.pluginValidationSpec)
try:
try:
pluginConf_file = os.path.join(self.prefs["Locations"]["SettingsDir"], self.PLUGINCONF_FILE)
plugin_conf_file = os.path.join(
self.prefs["Locations"]["SettingsDir"], self.PLUGINCONF_FILE)
except KeyError:
raise CBConfigUndefinedError("Locations", "SettingsDir")
except SyntaxError:
raise CBConfigInvalidValueError("Locations", "SettingsDir", pluginConf_file, "failed to interprete the filename of the plugin config file correctly (%s)" % pluginConf_file)
## create pluginConf_file if necessary
if os.path.exists(pluginConf_file):
pluginConf = configobj.ConfigObj(pluginConf_file, configspec=plugin_rules)
raise CBConfigInvalidValueError("Locations", "SettingsDir", plugin_conf_file,
"failed to interprete the filename of the plugin config file "
+ "correctly (%s)" % plugin_conf_file)
## create plugin_conf_file if necessary
if os.path.exists(plugin_conf_file):
plugin_conf = configobj.ConfigObj(plugin_conf_file, configspec=plugin_rules)
else:
pluginConf = configobj.ConfigObj(pluginConf_file, configspec=plugin_rules, create_empty=True)
plugin_conf = configobj.ConfigObj(plugin_conf_file, configspec=plugin_rules,
create_empty=True)
## validate and convert values according to the spec
pluginConf.validate(validate.Validator())
## check if pluginConf_file file was created successfully?
if not os.path.exists(pluginConf_file):
raise CBEnvironmentError("failed to create plugin configuration file (%s)" % pluginConf_file)
return pluginConf
plugin_conf.validate(validate.Validator())
## check if plugin_conf_file file was created successfully?
if not os.path.exists(plugin_conf_file):
raise CBEnvironmentError(
"failed to create plugin configuration file (%s)" % plugin_conf_file)
return plugin_conf
def __getVolumesDatabase(self):
def __get_volumes_database(self):
"""Load the volume database file if it exists.
"""
try:
try:
conf_file = os.path.join(self.prefs["Locations"]["SettingsDir"], self.VOLUMESDB_FILE)
conf_file = os.path.join(
self.prefs["Locations"]["SettingsDir"], self.VOLUMESDB_FILE)
except KeyError:
raise CBConfigUndefinedError("Locations", "SettingsDir")
except SyntaxError:
raise CBConfigInvalidValueError("Locations", "SettingsDir", conf_file, "failed to interprete the filename of the volume database correctly (%s)" % conf_file)
raise CBConfigInvalidValueError("Locations", "SettingsDir", conf_file,
"failed to interprete the filename of the volume database "
+ "correctly (%s)" % conf_file)
## create conf_file if necessary
if os.path.exists(conf_file):
conf = configobj.ConfigObj(conf_file)
@ -279,36 +318,48 @@ class CryptoBoxSettings:
conf = configobj.ConfigObj(conf_file, create_empty=True)
## check if conf_file file was created successfully?
if not os.path.exists(conf_file):
raise CBEnvironmentError("failed to create volume database file (%s)" % conf_file)
raise CBEnvironmentError(
"failed to create volume database file (%s)" % conf_file)
return conf
def __getUserDB(self):
def __get_user_db(self):
"""Load the user database file if it exists.
"""
import StringIO, sha
userDB_rules = StringIO.StringIO(self.userDatabaseSpec)
user_db_rules = StringIO.StringIO(self.userDatabaseSpec)
try:
try:
userDB_file = os.path.join(self.prefs["Locations"]["SettingsDir"], self.USERDB_FILE)
user_db_file = os.path.join(
self.prefs["Locations"]["SettingsDir"], self.USERDB_FILE)
except KeyError:
raise CBConfigUndefinedError("Locations", "SettingsDir")
except SyntaxError:
raise CBConfigInvalidValueError("Locations", "SettingsDir", userDB_file, "failed to interprete the filename of the users database file correctly (%s)" % userDB_file)
## create userDB_file if necessary
if os.path.exists(userDB_file):
userDB = configobj.ConfigObj(userDB_file, configspec=userDB_rules)
raise CBConfigInvalidValueError("Locations", "SettingsDir", user_db_file,
"failed to interprete the filename of the users database file "
+ "correctly (%s)" % user_db_file)
## create user_db_file if necessary
if os.path.exists(user_db_file):
user_db = configobj.ConfigObj(user_db_file, configspec=user_db_rules)
else:
userDB = configobj.ConfigObj(userDB_file, configspec=userDB_rules, create_empty=True)
user_db = configobj.ConfigObj(user_db_file, configspec=user_db_rules,
create_empty=True)
## validate and set default value for "admin" user
userDB.validate(validate.Validator())
## check if userDB file was created successfully?
if not os.path.exists(userDB_file):
raise CBEnvironmentError("failed to create user database file (%s)" % userDB_file)
user_db.validate(validate.Validator())
## check if user_db file was created successfully?
if not os.path.exists(user_db_file):
raise CBEnvironmentError(
"failed to create user database file (%s)" % user_db_file)
## define password hash function - never use "sha" directly - SPOT
userDB.getDigest = lambda password: sha.new(password).hexdigest()
return userDB
user_db.get_digest = lambda password: sha.new(password).hexdigest()
return user_db
def __getMiscFiles(self):
def __get_misc_files(self):
"""Load miscelleanous configuration files.
e.g.: an ssl certificate, ...
"""
misc_dir = os.path.join(self.prefs["Locations"]["SettingsDir"], "misc")
if (not os.path.isdir(misc_dir)) or (not os.access(misc_dir, os.X_OK)):
return []
@ -317,8 +368,9 @@ class CryptoBoxSettings:
if os.path.isfile(os.path.join(misc_dir, f))]
def __getConfigFileName(self, config_file):
# search for the configuration file
def __get_config_filename(self, config_file):
"""Search for the configuration file.
"""
import types
if config_file is None:
# no config file was specified - we will look for it in the ususal locations
@ -332,13 +384,17 @@ class CryptoBoxSettings:
else:
# a config file was specified (e.g. via command line)
if type(config_file) != types.StringType:
raise CBConfigUnavailableError("invalid config file specified: %s" % config_file)
raise CBConfigUnavailableError(
"invalid config file specified: %s" % config_file)
if not os.path.exists(config_file):
raise CBConfigUnavailableError("could not find the specified configuration file (%s)" % config_file)
raise CBConfigUnavailableError(
"could not find the specified configuration file (%s)" % config_file)
return config_file
def __configureLogHandler(self):
def __configure_log_handler(self):
"""Configure the log handler of the CryptoBox according to the config.
"""
try:
log_level = self.prefs["Log"]["Level"].upper()
log_level_avail = ["DEBUG", "INFO", "WARN", "ERROR"]
@ -347,15 +403,18 @@ class CryptoBoxSettings:
except KeyError:
raise CBConfigUndefinedError("Log", "Level")
except TypeError:
raise CBConfigInvalidValueError("Log", "Level", log_level, "invalid log level: only %s are allowed" % log_level_avail)
raise CBConfigInvalidValueError("Log", "Level", log_level,
"invalid log level: only %s are allowed" % log_level_avail)
try:
try:
log_handler = logging.FileHandler(self.prefs["Log"]["Details"])
except KeyError:
raise CBConfigUndefinedError("Log", "Details")
except IOError:
raise CBEnvironmentError("could not create the log file (%s)" % self.prefs["Log"]["Details"])
log_handler.setFormatter(logging.Formatter('%(asctime)s CryptoBox %(levelname)s: %(message)s'))
raise CBEnvironmentError("could not create the log file (%s)" % \
self.prefs["Log"]["Details"])
log_handler.setFormatter(
logging.Formatter('%(asctime)s CryptoBox %(levelname)s: %(message)s'))
cbox_log = logging.getLogger("CryptoBox")
## remove previous handlers
cbox_log.handlers = []
@ -364,7 +423,7 @@ class CryptoBoxSettings:
## do not call parent's handlers
cbox_log.propagate = False
## 'log_level' is a string -> use 'getattr'
cbox_log.setLevel(getattr(logging,log_level))
cbox_log.setLevel(getattr(logging, log_level))
## the logger named "CryptoBox" is configured now
@ -420,16 +479,20 @@ admin = string(default=d033e22ae348aeb5660fc2140aec35850c4da997)
class CryptoBoxSettingsValidator(validate.Validator):
"""Some custom configuration check functions.
"""
def __init__(self):
validate.Validator.__init__(self)
self.functions["directoryExists"] = self.check_directoryExists
self.functions["fileExecutable"] = self.check_fileExecutable
self.functions["fileWriteable"] = self.check_fileWriteable
self.functions["listOfExistingDirectories"] = self.check_listOfExistingDirectories
self.functions["directoryExists"] = self.check_directory_exists
self.functions["fileExecutable"] = self.check_file_executable
self.functions["fileWriteable"] = self.check_file_writeable
self.functions["listOfExistingDirectories"] = self.check_existing_directories
def check_directoryExists(self, value):
def check_directory_exists(self, value):
"""Is the directory accessible?
"""
dir_path = os.path.abspath(value)
if not os.path.isdir(dir_path):
raise validate.VdtValueError("%s (not found)" % value)
@ -438,7 +501,9 @@ class CryptoBoxSettingsValidator(validate.Validator):
return dir_path
def check_fileExecutable(self, value):
def check_file_executable(self, value):
"""Is the file executable?
"""
file_path = os.path.abspath(value)
if not os.path.isfile(file_path):
raise validate.VdtValueError("%s (not found)" % value)
@ -447,7 +512,9 @@ class CryptoBoxSettingsValidator(validate.Validator):
return file_path
def check_fileWriteable(self, value):
def check_file_writeable(self, value):
"""Is the file writeable?
"""
file_path = os.path.abspath(value)
if os.path.isfile(file_path):
if not os.access(file_path, os.W_OK):
@ -460,18 +527,22 @@ class CryptoBoxSettingsValidator(validate.Validator):
return file_path
def check_listOfExistingDirectories(self, value):
def check_existing_directories(self, value):
"""Are these directories accessible?
"""
if not value:
raise validate.VdtValueError("no plugin directory specified")
if not isinstance(value,list):
if not isinstance(value, list):
value = [value]
result = []
for d in value:
dir_path = os.path.abspath(d)
for one_dir in value:
dir_path = os.path.abspath(one_dir)
if not os.path.isdir(dir_path):
raise validate.VdtValueError("%s (plugin directory not found)" % d)
raise validate.VdtValueError(
"%s (plugin directory not found)" % one_dir)
if not os.access(dir_path, os.X_OK):
raise validate.VdtValueError("%s (access denied for plugin directory)" % d)
raise validate.VdtValueError(
"%s (access denied for plugin directory)" % one_dir)
result.append(dir_path)
return result
@ -484,19 +555,25 @@ class MiscConfigFile:
def __init__(self, filename, logger):
self.filename = filename
self.log = logger
self.content = None
self.load()
def load(self):
fd = open(self.filename, "rb")
"""Load a configuration file into memory.
"""
fdesc = open(self.filename, "rb")
## limit the maximum size
self.content = fd.read(self.maxSize)
if fd.tell() == self.maxSize:
self.log.warn("file in misc settings directory (%s) is bigger than allowed (%s)" % (self.filename, self.maxSize))
fd.close()
self.content = fdesc.read(self.maxSize)
if fdesc.tell() == self.maxSize:
self.log.warn("file in misc settings directory (" + str(self.filename) \
+ ") is bigger than allowed (" + str(self.maxSize) + ")")
fdesc.close()
def save(self):
"""Save a configuration file to disk.
"""
save_dir = os.path.dirname(self.filename)
## create the directory, if necessary
if not os.path.isdir(save_dir):
@ -506,14 +583,14 @@ class MiscConfigFile:
return False
## save the content of the file
try:
fd = open(self.filename, "wb")
fdesc = open(self.filename, "wb")
except IOError:
return False
try:
fd.write(self.content)
fd.close()
fdesc.write(self.content)
fdesc.close()
return True
except IOError:
fd.close()
fdesc.close()
return False

View File

@ -18,137 +18,169 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
"""Some useful functions for the CryptoBox.
"""
__revision__ = "$Id"
import logging
import os
import re
logger = logging.getLogger("CryptoBox")
LOGGER = logging.getLogger("CryptoBox")
def getAvailablePartitions():
def get_available_partitions():
"retrieve a list of all available containers"
ret_list = []
try:
"the following reads all lines of /proc/partitions and adds the mentioned devices"
## the following reads all lines of /proc/partitions and adds the mentioned devices
fpart = open("/proc/partitions", "r")
try:
line = fpart.readline()
while line:
p_details = line.split()
if (len(p_details) == 4):
"the following code prevents double entries like /dev/hda and /dev/hda1"
## the following code prevents double entries like /dev/hda and /dev/hda1
(p_major, p_minor, p_size, p_device) = p_details
## ignore lines with: invalid minor/major or extend partitions (size=1)
if re.search('^[0-9]*$', p_major) and re.search('^[0-9]*$', p_minor) and (p_size != "1"):
if re.search('^[0-9]*$', p_major) and \
re.search('^[0-9]*$', p_minor) and (p_size != "1"):
p_parent = re.sub('[1-9]?[0-9]$', '', p_device)
if p_parent == p_device:
if [e for e in ret_list if re.search('^' + p_parent + '[1-9]?[0-9]$', e)]:
"major partition - its children are already in the list"
if [e for e in ret_list
if re.search('^' + p_parent + '[1-9]?[0-9]$', e)]:
## major partition - its children are already in the list
pass
else:
"major partition - but there are no children for now"
## major partition - but there are no children for now
ret_list.append(p_device)
else:
"minor partition - remove parent if necessary"
if p_parent in ret_list: ret_list.remove(p_parent)
## minor partition - remove parent if necessary
if p_parent in ret_list:
ret_list.remove(p_parent)
ret_list.append(p_device)
line = fpart.readline()
finally:
fpart.close()
return map(getAbsoluteDeviceName, ret_list)
return [ get_absolute_devicename(e) for e in ret_list ]
except IOError:
logger.warning("Could not read /proc/partitions")
LOGGER.warning("Could not read /proc/partitions")
return []
def getAbsoluteDeviceName(shortname):
def get_absolute_devicename(shortname):
""" returns the absolute file name of a device (e.g.: "hda1" -> "/dev/hda1")
this does also work for device mapper devices
if the result is non-unique, one arbitrary value is returned"""
if re.search('^/', shortname): return shortname
if the result is non-unique, one arbitrary value is returned
"""
if re.search('^/', shortname):
return shortname
default = os.path.join("/dev", shortname)
if os.path.exists(default): return default
result = findMajorMinorOfDevice(shortname)
"if no valid major/minor was found -> exit"
if not result: return default
if os.path.exists(default):
return default
result = find_major_minor_of_device(shortname)
## if no valid major/minor was found -> exit
if not result:
return default
(major, minor) = result
"for device-mapper devices (major == 254) ..."
## for device-mapper devices (major == 254) ...
if major == 254:
result = findMajorMinorDeviceName("/dev/mapper", major, minor)
if result: return result[0]
"now check all files in /dev"
result = findMajorMinorDeviceName("/dev", major, minor)
if result: return result[0]
result = find_major_minor_device("/dev/mapper", major, minor)
if result:
return result[0]
## now check all files in /dev
result = find_major_minor_device("/dev", major, minor)
if result:
return result[0]
return default
def findMajorMinorOfDevice(device):
"return the major/minor numbers of a block device"
if re.match("/", device) or not os.path.exists(os.path.join(os.path.sep,"sys","block",device)):
def find_major_minor_of_device(device):
"""Return the major/minor numbers of a block device.
"""
if re.match("/", device) or \
not os.path.exists(os.path.join(os.path.sep, "sys", "block", device)):
## maybe it is an absolute device name
if not os.path.exists(device): return None
if not os.path.exists(device):
return None
## okay - it seems to to a device node
rdev = os.stat(device).st_rdev
return (os.major(rdev), os.minor(rdev))
blockdev_info_file = os.path.join(os.path.join(os.path.sep,"sys","block", device), "dev")
blockdev_info_file = os.path.join(os.path.join(
os.path.sep,"sys","block", device), "dev")
try:
f_blockdev_info = open(blockdev_info_file, "r")
blockdev_info = f_blockdev_info.read()
f_blockdev_info.close()
(str_major, str_minor) = blockdev_info.split(":")
"numeric conversion"
## numeric conversion
try:
major = int(str_major)
minor = int(str_minor)
return (major, minor)
except ValueError:
"unknown device numbers -> stop guessing"
## unknown device numbers -> stop guessing
return None
except IOError:
pass
return None
def findMajorMinorDeviceName(dir, major, minor):
"returns the names of devices with the specified major and minor number"
def find_major_minor_device(dirpath, major, minor):
"""Returns the names of devices with the specified major and minor number.
"""
collected = []
try:
subdirs = [os.path.join(dir, e) for e in os.listdir(dir) if (not os.path.islink(os.path.join(dir, e))) and os.path.isdir(os.path.join(dir, e))]
"do a recursive call to parse the directory tree"
subdirs = [os.path.join(dirpath, e) for e in os.listdir(dirpath)
if (not os.path.islink(os.path.join(dirpath, e))) and \
os.path.isdir(os.path.join(dirpath, e))]
## do a recursive call to parse the directory tree
for dirs in subdirs:
collected.extend(findMajorMinorDeviceName(dirs, major, minor))
"filter all device inodes in this directory"
collected.extend([os.path.realpath(os.path.join(dir, e)) for e in os.listdir(dir) if (os.major(os.stat(os.path.join(dir, e)).st_rdev) == major) and (os.minor(os.stat(os.path.join(dir, e)).st_rdev) == minor)])
collected.extend(find_major_minor_device(dirs, major, minor))
## filter all device inodes in this directory
collected.extend([os.path.realpath(os.path.join(dirpath, e))
for e in os.listdir(dirpath)
if (os.major(os.stat(os.path.join(dirpath, e)).st_rdev) == major) \
and (os.minor(os.stat(os.path.join(dirpath, e)).st_rdev) == minor)])
## remove double entries
result = []
for e in collected:
if e not in result: result.append(e)
for item in collected:
if item not in result:
result.append(item)
return result
except OSError:
return []
def getParentBlockDevices():
def get_parent_blockdevices():
"""Return a list of all block devices that contain other devices.
"""
devs = []
for line in file("/proc/partitions"):
p_details = line.split()
## we expect four values - otherwise continue with next iteration
if len(p_details) != 4: continue
if len(p_details) != 4:
continue
(p_major, p_minor, p_size, p_device) = p_details
## we expect numeric values in the first two columns
if re.search(u'\D',p_major) or re.search(u'\D',p_minor): continue
if re.search(u'\D', p_major) or re.search(u'\D', p_minor):
continue
## now let us check, if it is a (parent) block device or a partition
if not os.path.isdir(os.path.join(os.path.sep, "sys", "block", p_device)): continue
if not os.path.isdir(os.path.join(os.path.sep, "sys", "block", p_device)):
continue
devs.append(p_device)
return map(getAbsoluteDeviceName, devs)
return [ get_absolute_devicename(e) for e in devs ]
def isPartOfBlockDevice(parent, subdevice):
"""check if the given block device is a parent of 'subdevice'
e.g. for checking if a partition belongs to a block device"""
def is_part_of_blockdevice(parent, subdevice):
"""Check if the given block device is a parent of 'subdevice'.
e.g. for checking if a partition belongs to a block device
"""
try:
(par_major, par_minor) = findMajorMinorOfDevice(parent)
(sub_major, sub_minor) = findMajorMinorOfDevice(subdevice)
(par_major, par_minor) = find_major_minor_of_device(parent)
(sub_major, sub_minor) = find_major_minor_of_device(subdevice)
except TypeError:
## at least one of these devices did not return a valid major/minor combination
return False
@ -158,7 +190,8 @@ def isPartOfBlockDevice(parent, subdevice):
blpath = os.path.join(root, bldev, 'dev')
if os.access(blpath, os.R_OK):
try:
if (str(par_major), str(par_minor)) == tuple([e for e in file(blpath)][0].strip().split(":",1)):
if (str(par_major), str(par_minor)) == tuple([e
for e in file(blpath)][0].strip().split(":",1)):
parent_path = os.path.join(root, bldev)
break
except (IndexError, OSError):
@ -170,7 +203,8 @@ def isPartOfBlockDevice(parent, subdevice):
subblpath = os.path.join(parent_path, subbldev, "dev")
if os.access(subblpath, os.R_OK):
try:
if (str(sub_major), str(sub_minor)) == tuple([e for e in file(subblpath)][0].strip().split(":",1)):
if (str(sub_major), str(sub_minor)) == tuple([e
for e in file(subblpath)][0].strip().split(":",1)):
## the name of the subdevice node is not important - we found it!
return True
except (IndexError, OSError):
@ -178,18 +212,22 @@ def isPartOfBlockDevice(parent, subdevice):
return False
def getBlockDeviceSize(device):
if not device: return -1
def get_blockdevice_size(device):
"""Return the size of a blockdevice.
"""
if not device:
return -1
try:
rdev = os.stat(device).st_rdev
except OSError:
return -1
minor = os.minor(rdev)
major = os.major(rdev)
for f in file("/proc/partitions"):
for line in file("/proc/partitions"):
try:
elements = f.split()
if len(elements) != 4: continue
elements = line.split()
if len(elements) != 4:
continue
if (int(elements[0]) == major) and (int(elements[1]) == minor):
return int(elements[2])/1024
except ValueError:
@ -197,8 +235,10 @@ def getBlockDeviceSize(device):
return -1
def getBlockDeviceSizeHumanly(device):
size = getBlockDeviceSize(device)
def get_blockdevice_size_humanly(device):
"""Return a human readable size of a blockdevice.
"""
size = get_blockdevice_size(device)
if size > 5120:
return "%sGB" % size/1024
else:

View File

@ -0,0 +1,7 @@
"""Features may be easily added to the CryptoBox.
"""
__revision__ = "$Id"
__all__ = [ 'base', 'manage' ]

View File

@ -21,161 +21,189 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
"""All features should inherit from this class.
"""
__revision__ = "$Id"
import os
import cherrypy
class CryptoBoxPlugin:
"""The base class of all features.
"""
## default capability is "system" - the other supported capability is: "volume"
pluginCapabilities = [ "system" ]
plugin_capabilities = [ "system" ]
## where should the plugin be visible by default?
pluginVisibility = [ "preferences" ]
plugin_visibility = [ "preferences" ]
## does this plugin require admin authentification?
requestAuth = False
request_auth = False
## default rank (0..100) of the plugin in listings (lower value means higher priority)
rank = 80
## default icon of this plugin (relative path)
defaultIconFileName = "plugin_icon.gif"
default_icon_filename = "plugin_icon.gif"
## fallback icon file (in the common plugin directory)
fallbackIconFileName = "plugin_icon_unknown.gif"
fallback_icon_filename = "plugin_icon_unknown.gif"
def __init__(self, cbox, pluginDir, siteClass=None):
def __init__(self, cbox, plugin_dir, site_class=None):
self.cbox = cbox
self.hdf = {}
self.pluginDir = pluginDir
self.hdf_prefix = "Data.Plugins.%s." % self.getName()
self.site = siteClass
self.plugin_dir = plugin_dir
self.hdf_prefix = "Data.Plugins.%s." % self.get_name()
self.site = site_class
def doAction(self, **args):
"""override doAction with your plugin code"""
raise Exception, "undefined action handler ('doAction') in plugin '%'" % self.getName()
def do_action(self, **args):
"""Override do_action with your plugin code
"""
raise Exception, \
"undefined action handler ('do_action') in plugin '%s'" % self.get_name()
def getStatus(self):
"""you should override this, to supply useful state information"""
raise Exception, "undefined state handler ('getStatus') in plugin '%'" % self.getName()
def get_status(self):
"""you should override this, to supply useful state information
"""
raise Exception, \
"undefined state handler ('get_status') in plugin '%s'" % self.get_name()
def getName(self):
"""the name of the python file (module) should be the name of the plugin"""
def get_name(self):
"""the name of the python file (module) should be the name of the plugin
"""
return self.__module__
@cherrypy.expose
def getIcon(self, image=None, **kargs):
def get_icon(self, image=None, **kargs):
"""return the image data of the icon of the plugin
the parameter 'image' may be used for alternative image locations (relative
to the directory of the plugin)
'**kargs' is necessary, as a 'weblang' attribute may be specified (and ignored)"""
import cherrypy, re
if (image is None): # or (re.search(u'[\w-\.]', image)):
plugin_icon_file = os.path.join(self.pluginDir, self.defaultIconFileName)
'**kargs' is necessary, as a 'weblang' attribute may be specified (and ignored)
"""
import re
if (image is None) or (not re.match(u'[\w\-\.]*$', image)):
plugin_icon_file = os.path.join(self.plugin_dir, self.default_icon_filename)
else:
plugin_icon_file = os.path.join(self.pluginDir, image)
plugin_icon_file = os.path.join(self.plugin_dir, image)
if not os.access(plugin_icon_file, os.R_OK):
plugin_icon_file = os.path.join(self.cbox.prefs["Locations"]["PluginDir"], self.fallbackIconFileName)
plugin_icon_file = os.path.join(
self.cbox.prefs["Locations"]["PluginDir"], self.fallback_icon_filename)
return cherrypy.lib.cptools.serveFile(plugin_icon_file)
def getTemplateFileName(self, template_name):
def get_template_filename(self, template_name):
"""return the filename of the template, if it is part of this plugin
use this function to check, if the plugin provides the specified template
"""
result_file = os.path.join(self.pluginDir, template_name + ".cs")
result_file = os.path.join(self.plugin_dir, template_name + ".cs")
if os.access(result_file, os.R_OK) and os.path.isfile(result_file):
return result_file
else:
return None
def getLanguageData(self):
def get_language_data(self):
"""Retrieve the language data of the feature.
Typically this is the content of the language.hdf file as a HDF object.
"""
import neo_cgi, neo_util
lang_hdf = neo_util.HDF()
langFile = os.path.join(self.pluginDir, 'language.hdf')
lang_file = os.path.join(self.plugin_dir, 'language.hdf')
try:
lang_hdf.readFile(langFile)
except (neo_util.Error, neo_util.ParseError), errMsg:
self.cbox.log.error("failed to load language file (%s) of plugin (%s):" % (langFile,self.getName()))
lang_hdf.readFile(lang_file)
except (neo_util.Error, neo_util.ParseError):
self.cbox.log.error("failed to load language file (%s) of plugin (%s):" % \
(lang_file, self.get_name()))
return lang_hdf
def loadDataSet(self, hdf):
def load_dataset(self, hdf):
"""Add the local values of the feature to the hdf dataset.
"""
for (key, value) in self.hdf.items():
hdf.setValue(key, str(value))
def isAuthRequired(self):
def is_auth_required(self):
"""check if this plugin requires authentication
first step: check plugin configuration
second step: check default value of plugin"""
second step: check default value of plugin
"""
try:
if self.cbox.prefs.pluginConf[self.getName()]["requestAuth"] is None:
return self.requestAuth
if self.cbox.prefs.pluginConf[self.getName()]["requestAuth"]:
if self.cbox.prefs.plugin_conf[self.get_name()]["requestAuth"] is None:
return self.request_auth
if self.cbox.prefs.plugin_conf[self.get_name()]["requestAuth"]:
return True
else:
return False
except KeyError:
return self.requestAuth
return self.request_auth
def isEnabled(self):
def is_enabled(self):
"""check if this plugin is enabled
first step: check plugin configuration
second step: check default value of plugin"""
fallback = bool(self.pluginVisibility)
second step: check default value of plugin
"""
fallback = bool(self.plugin_visibility)
try:
if self.cbox.prefs.pluginConf[self.getName()]["visibility"] is None:
if self.cbox.prefs.plugin_conf[self.get_name()]["visibility"] is None:
return fallback
return bool(self.cbox.prefs.pluginConf[self.getName()]["visibility"])
return bool(self.cbox.prefs.plugin_conf[self.get_name()]["visibility"])
except KeyError:
return fallback
def getRank(self):
def get_rank(self):
"""check the rank of this plugin
first step: check plugin configuration
second step: check default value of plugin"""
second step: check default value of plugin
"""
try:
if self.cbox.prefs.pluginConf[self.getName()]["rank"] is None:
if self.cbox.prefs.plugin_conf[self.get_name()]["rank"] is None:
return self.rank
return int(self.cbox.prefs.pluginConf[self.getName()]["rank"])
return int(self.cbox.prefs.plugin_conf[self.get_name()]["rank"])
except (KeyError, TypeError):
return self.rank
def getVisibility(self):
def get_visibility(self):
"""Check which visibility flags of the feature are set.
"""
try:
if self.cbox.prefs.pluginConf[self.getName()]["visibility"] is None:
return self.pluginVisibility[:]
return self.cbox.prefs.pluginConf[self.getName()]["visibility"]
if self.cbox.prefs.plugin_conf[self.get_name()]["visibility"] is None:
return self.plugin_visibility[:]
return self.cbox.prefs.plugin_conf[self.get_name()]["visibility"]
except KeyError:
return self.pluginVisibility
return self.plugin_visibility
def getTestClass(self):
def get_test_class(self):
"""Return the unittest class of the feature.
"""
import imp
pl_file = os.path.join(self.pluginDir, "unittests.py")
pl_file = os.path.join(self.plugin_dir, "unittests.py")
if os.access(pl_file, os.R_OK) and os.path.isfile(pl_file):
try:
return getattr(imp.load_source("unittests_%s" % self.getName(), pl_file), "unittests")
return imp.load_source("unittests_%s" % self.get_name(), pl_file).unittests
except AttributeError:
pass
try:
self.cbox.log.info("could not load unittests for plugin: %s" % self.getName())
self.cbox.log.info("could not load unittests for plugin: %s" % \
self.get_name())
except AttributeError:
pass
return None

View File

@ -19,46 +19,56 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
"""Manages the pluggable features of the CryptoBox.
"""
__revision__ = "$Id"
import imp
import os
import logging
class PluginManager:
"""manage available plugins"""
def __init__(self, cbox, plugin_dirs=".", siteClass=None):
def __init__(self, cbox, plugin_dirs=".", site_class=None):
self.cbox = cbox
self.log = logging.getLogger("CryptoBox")
self.site = siteClass
self.site = site_class
if hasattr(plugin_dirs, "__iter__"):
self.plugin_dirs = [os.path.abspath(dir) for dir in plugin_dirs]
self.plugin_dirs = [os.path.abspath(d) for d in plugin_dirs]
else:
self.plugin_dirs = [os.path.abspath(plugin_dirs)]
self.pluginList = self.__getAllPlugins()
self.plugin_list = self.__get_all_plugins()
def getPlugins(self):
return self.pluginList[:]
def get_plugins(self):
"""Return a list of all feature instances.
"""
return self.plugin_list[:]
def getPlugin(self, name):
for p in self.pluginList[:]:
if p.getName() == name:
return p
def get_plugin(self, name):
"""Return the specified feature as an instance.
"""
for plugin in self.plugin_list[:]:
if plugin.get_name() == name:
return plugin
return None
def __getAllPlugins(self):
list = []
for plfile in self.__getPluginFiles():
list.append(self.__getPluginClass(os.path.basename(plfile)[:-3]))
return list
def __get_all_plugins(self):
"""Return all available features as instances.
"""
plist = []
for plfile in self.__get_plugin_files():
plist.append(self.__get_plugin_class(os.path.basename(plfile)[:-3]))
return plist
def __getPluginClass(self, name):
for plfile in self.__getPluginFiles():
def __get_plugin_class(self, name):
"""Return a instance object of the give feature.
"""
import imp
for plfile in self.__get_plugin_files():
if name == os.path.basename(plfile)[:-3]:
try:
pl_class = getattr(imp.load_source(name, plfile), name)
@ -69,18 +79,23 @@ class PluginManager:
return None
def __getPluginFiles(self):
def __get_plugin_files(self):
"""Retrieve all python files that may potentially be a feature.
"""
result = []
if self.cbox and self.cbox.prefs["Main"]["DisabledPlugins"]:
disabled = self.cbox.prefs["Main"]["DisabledPlugins"]
else:
disabled = []
for dir in [os.path.abspath(e) for e in self.plugin_dirs if os.access(e, os.R_OK) and os.path.isdir(e)]:
for plname in [f for f in os.listdir(dir)]:
for pdir in [os.path.abspath(e) for e in self.plugin_dirs
if os.access(e, os.R_OK) and os.path.isdir(e)]:
for plname in [f for f in os.listdir(pdir)]:
if plname in disabled:
if self.cbox: self.cbox.log.info("skipped plugin '%s' (disabled via config)" % plname)
if self.cbox:
self.cbox.log.info(
"skipped plugin '%s' (disabled via config)" % plname)
continue
pldir = os.path.join(dir, plname)
pldir = os.path.join(pdir, plname)
plfile = os.path.join(pldir, plname + ".py")
if os.path.isfile(plfile) and os.access(plfile, os.R_OK):
result.append(plfile)
@ -88,8 +103,8 @@ class PluginManager:
if __name__ == "__main__":
x = PluginManager(None, "../plugins")
for a in x.getPlugins():
if not a is None:
print "Plugin: %s" % a.getName()
MANAGER = PluginManager(None, "../plugins")
for one_plugin in MANAGER.get_plugins():
if not one_plugin is None:
print "Plugin: %s" % one_plugin.get_name()

View File

@ -0,0 +1,7 @@
"""Some unittests for the CryptoBox.
"""
__revision__ = "$Id"
__all__ = [ 'test.cryptobox', 'test.cryptoboxtools', 'test.plugins', 'test.websites' ]

View File

@ -19,33 +19,41 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
"""Some unittests for the core CryptoBox modules.
"""
__revision__ = "$Id"
import unittest
import sys
import cryptobox.core.main
from cryptobox.core.exceptions import *
import cryptobox.core.settings
import os
class CryptoBoxPropsDeviceTests(unittest.TestCase):
cb = cryptobox.core.main.CryptoBoxProps()
class CryptoBoxDeviceTests(unittest.TestCase):
"""Some unittests for the CryptoBox
"""
cb = cryptobox.core.main.CryptoBox()
def testAllowedDevices(self):
'''isDeviceAllowed should accept permitted devices'''
self.assertTrue(self.cb.isDeviceAllowed("/dev/loop"))
self.assertTrue(self.cb.isDeviceAllowed("/dev/loop1"))
self.assertTrue(self.cb.isDeviceAllowed("/dev/loop/urgd"))
self.assertTrue(self.cb.isDeviceAllowed("/dev/usb/../loop1"))
def test_allowed_devices(self):
'''is_device_allowed should accept permitted devices'''
self.assertTrue(self.cb.is_device_allowed("/dev/loop"))
self.assertTrue(self.cb.is_device_allowed("/dev/loop1"))
self.assertTrue(self.cb.is_device_allowed("/dev/loop/urgd"))
self.assertTrue(self.cb.is_device_allowed("/dev/usb/../loop1"))
def testDeniedDevices(self):
'''isDeviceAllowed should fail with not explicitly allowed devices'''
self.assertFalse(self.cb.isDeviceAllowed("/dev/hda"))
self.assertFalse(self.cb.isDeviceAllowed("/dev/loopa/../hda"))
self.assertFalse(self.cb.isDeviceAllowed("/"))
def test_denied_devices(self):
'''is_device_allowed should fail with not explicitly allowed devices'''
self.assertFalse(self.cb.is_device_allowed("/dev/hda"))
self.assertFalse(self.cb.is_device_allowed("/dev/loopa/../hda"))
self.assertFalse(self.cb.is_device_allowed("/"))
class CryptoBoxPropsConfigTests(unittest.TestCase):
class CryptoBoxConfigTests(unittest.TestCase):
'''test here if everything with the config turns right'''
import os
files = {
"configFileOK" : "cbox-test_ok.conf",
"configFileBroken" : "cbox-test_broken.conf",
@ -86,71 +94,89 @@ CryptoBoxRootActions = CryptoBoxRootActions
def setUp(self):
'''generate all files in tmp and remember the names'''
import tempfile
os = self.os
self.tmpdirname = tempfile.mkdtemp(prefix="cbox-")
for file in self.files.keys():
self.filenames[file] = os.path.join(self.tmpdirname, self.files[file])
self.writeConfig()
for tfile in self.files.keys():
self.filenames[tfile] = os.path.join(self.tmpdirname, self.files[tfile])
self.write_config()
def tearDown(self):
'''remove the created tmpfiles'''
os = self.os
# remove temp files
for file in self.filenames.values():
compl_name = os.path.join(self.tmpdirname, file)
for tfile in self.filenames.values():
compl_name = os.path.join(self.tmpdirname, tfile)
if os.path.exists(compl_name):
os.remove(compl_name)
# remove temp dir
os.rmdir(self.tmpdirname)
def testConfigInit(self):
def test_config_init(self):
'''Check various branches of config file loading'''
import os
self.assertRaises(CBConfigUnavailableError, cryptobox.core.main.CryptoBoxProps,"/invalid/path/to/config/file")
self.assertRaises(CBConfigUnavailableError, cryptobox.core.main.CryptoBoxProps,"/etc/shadow")
""" check one of the following things:
1) are we successfully using an existing config file?
2) do we break, if no config file is there?
depending on the existence of a config file, only one of these conditions
can be checked - hints for more comprehensive tests are appreciated :) """
for a in ['cryptobox.conf']:
if os.path.exists(a):
cryptobox.core.main.CryptoBoxProps()
self.assertRaises(CBConfigUnavailableError,
cryptobox.core.main.CryptoBox,"/invalid/path/to/config/file")
self.assertRaises(CBConfigUnavailableError,
cryptobox.core.main.CryptoBox,"/etc/shadow")
## check one of the following things:
## 1) are we successfully using an existing config file?
## 2) do we break, if no config file is there?
## depending on the existence of a config file, only one of these conditions
## can be checked - hints for more comprehensive tests are appreciated :)
for cfile in ['cryptobox.conf']:
if os.path.exists(cfile):
cryptobox.core.main.CryptoBox()
break # this skips the 'else' clause
else: self.assertRaises(CBConfigUnavailableError, cryptobox.core.main.CryptoBoxProps)
self.assertRaises(CBConfigUnavailableError, cryptobox.core.main.CryptoBoxProps,[])
else:
self.assertRaises(CBConfigUnavailableError,
cryptobox.core.main.CryptoBox)
self.assertRaises(CBConfigUnavailableError,
cryptobox.core.main.CryptoBox,[])
def testBrokenConfigs(self):
"""Check various broken configurations"""
self.writeConfig("SettingsDir", "SettingsDir=/foo/bar", filename=self.filenames["configFileBroken"])
self.assertRaises(CBConfigError, cryptobox.core.main.CryptoBoxProps,self.filenames["configFileBroken"])
self.writeConfig("Level", "Level = ho", filename=self.filenames["configFileBroken"])
self.assertRaises(CBConfigError, cryptobox.core.main.CryptoBoxProps,self.filenames["configFileBroken"])
self.writeConfig("Details", "#out", filename=self.filenames["configFileBroken"])
self.assertRaises(CBConfigError, cryptobox.core.main.CryptoBoxProps,self.filenames["configFileBroken"])
self.writeConfig("super", "super=/bin/invalid/no", filename=self.filenames["configFileBroken"])
self.assertRaises(CBConfigError, cryptobox.core.main.CryptoBoxProps,self.filenames["configFileBroken"])
self.writeConfig("CryptoBoxRootActions", "#not here", filename=self.filenames["configFileBroken"])
self.assertRaises(CBConfigError, cryptobox.core.main.CryptoBoxProps,self.filenames["configFileBroken"])
self.writeConfig("CryptoBoxRootActions", "CryptoBoxRootActions = /bin/false", filename=self.filenames["configFileBroken"])
self.assertRaises(CBEnvironmentError, cryptobox.core.main.CryptoBoxProps,self.filenames["configFileBroken"])
def test_broken_configs(self):
"""Check various broken configurations
"""
self.write_config("SettingsDir", "SettingsDir=/foo/bar",
filename=self.filenames["configFileBroken"])
self.assertRaises(CBConfigError, cryptobox.core.main.CryptoBox,
self.filenames["configFileBroken"])
self.write_config("Level", "Level = ho",
filename=self.filenames["configFileBroken"])
self.assertRaises(CBConfigError, cryptobox.core.main.CryptoBox,
self.filenames["configFileBroken"])
self.write_config("Details", "#out",
filename=self.filenames["configFileBroken"])
self.assertRaises(CBConfigError, cryptobox.core.main.CryptoBox,
self.filenames["configFileBroken"])
self.write_config("super", "super=/bin/invalid/no",
filename=self.filenames["configFileBroken"])
self.assertRaises(CBConfigError, cryptobox.core.main.CryptoBox,
self.filenames["configFileBroken"])
self.write_config("CryptoBoxRootActions", "#not here",
filename=self.filenames["configFileBroken"])
self.assertRaises(CBConfigError, cryptobox.core.main.CryptoBox,
self.filenames["configFileBroken"])
self.write_config("CryptoBoxRootActions", "CryptoBoxRootActions = /bin/false",
filename=self.filenames["configFileBroken"])
self.assertRaises(CBEnvironmentError, cryptobox.core.main.CryptoBox,
self.filenames["configFileBroken"])
def writeConfig(self, replace=None, newline=None, filename=None):
def write_config(self, replace=None, newline=None, filename=None):
"""write a config file and (optional) replace a line in it"""
import re
if not filename: filename = self.filenames["configFileOK"]
content = self.configContentOK % (self.tmpdirname, self.tmpdirname, self.tmpdirname)
if not filename:
filename = self.filenames["configFileOK"]
content = self.configContentOK % \
(self.tmpdirname, self.tmpdirname, self.tmpdirname)
if replace:
pattern = re.compile('^' + replace + '\\s*=.*$', flags=re.M)
content = re.sub(pattern, newline, content)
cf = open(filename, "w")
cf.write(content)
cf.close()
cfile = open(filename, "w")
cfile.write(content)
cfile.close()
if __name__ == "__main__":
unittest.main()

View File

@ -19,9 +19,14 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
"""Unittests for cryptobox.core.tools
"""
__revision__ = "$Id"
import unittest
import cryptobox.core.tools as cbxTools
import cryptobox.core.tools as cbx_tools
import os
## use /dev/ubd? if possible - otherwise /dev/hd?
@ -36,30 +41,40 @@ else:
class CryptoBoxToolsTests(unittest.TestCase):
"""All unittests for cryptoboxtools
"""
def testGetAbsoluteDeviceName(self):
func = cbxTools.getAbsoluteDeviceName
def test_get_absolute_devicename(self):
"""check the get_absolute_devicename function
"""
func = cbx_tools.get_absolute_devicename
self.assertTrue(func(device) == "/dev/%s" % device)
self.assertTrue(func("loop0") == "/dev/loop0")
self.assertTrue(func(os.path.devnull) == os.path.devnull)
def testFindMajorMinorOfDevice(self):
func = cbxTools.findMajorMinorOfDevice
self.assertTrue(func(os.path.devnull) == (1,3))
def test_find_major_minor_of_device(self):
"""check the find_major_minor_of_device function
"""
func = cbx_tools.find_major_minor_of_device
self.assertTrue(func(os.path.devnull) == (1, 3))
self.assertTrue(func("/dev/nothere") is None)
def testFindMajorMinorDeviceName(self):
func = cbxTools.findMajorMinorDeviceName
dir = os.path.join(os.path.sep, "dev")
self.assertTrue(os.path.devnull in func(dir,1,3))
self.assertFalse(os.path.devnull in func(dir,2,3))
self.assertFalse(None in func(dir,17,23))
def test_find_major_minor_device(self):
"""check the find_major_minor_device function
"""
func = cbx_tools.find_major_minor_device
path = os.path.join(os.path.sep, "dev")
self.assertTrue(os.path.devnull in func(path, 1, 3))
self.assertFalse(os.path.devnull in func(path, 2, 3))
self.assertFalse(None in func(path, 17, 23))
def testIsPartOfBlockDevice(self):
func = cbxTools.isPartOfBlockDevice
def test_is_part_of_blockdevice(self):
"""check the is_part_of_blockdevice function
"""
func = cbx_tools.is_part_of_blockdevice
self.assertTrue(func("/dev/%s" % device, "/dev/%s1" % device))
self.assertFalse(func("/dev/%s" % device, "/dev/%s" % device))
self.assertFalse(func("/dev/%s1" % device, "/dev/%s" % device))

View File

@ -19,6 +19,10 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
"""This module handles the unittests of all features.
"""
__revision__ = "$Id"
import unittest
import cryptobox.plugins.manage
@ -28,20 +32,21 @@ class CheckForUndefinedTestCases(unittest.TestCase):
def create_testcases():
plugins = cryptobox.plugins.manage.PluginManager(None, "../plugins").getPlugins()
"""Create functions that execute unittests for all features.
"""
plugins = cryptobox.plugins.manage.PluginManager(None, "../plugins").get_plugins()
glob_dict = globals()
loc_dict = locals()
for pl in plugins:
test_class = pl.getTestClass()
for plugin in plugins:
test_class = plugin.get_test_class()
if test_class:
## add the testclass to the global dictionary
glob_dict["unittest" + pl.getName()] = test_class
glob_dict["unittest" + plugin.get_name()] = test_class
else:
subname = "test_existence_%s" % pl.getName()
subname = "test_existence_%s" % plugin.get_name()
def test_existence(self):
"""check if the plugin (%s) contains tests""" % pl.getName()
self.fail("no tests defined for plugin: %s" % pl.getName())
"""check if the plugin (%s) contains tests""" % plugin.get_name()
self.fail("no tests defined for plugin: %s" % plugin.get_name())
## add this function to the class above
setattr(CheckForUndefinedTestCases, subname, test_existence)
#FIXME: the failure output always contains the same name for all plugins

View File

@ -19,6 +19,12 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
"""Base class for all unittests involving the webserver.
This class uses twill.
"""
__revision__ = "$Id"
import unittest
@ -32,28 +38,32 @@ import cryptobox.web.testclass
class WebServer(cryptobox.web.testclass.WebInterfaceTestClass):
"""Basic tests for the webserver.
"""
def test_is_server_running(self):
'''the server should run under given name and port'''
self.register_auth(self.URL)
self.cmd.go(self.URL)
self.register_auth(self.url)
self.cmd.go(self.url)
self.cmd.find("CBOX-STATUS")
## other URLs must not be checked, as we do not know, if they are valid
class BuiltinPages(cryptobox.web.testclass.WebInterfaceTestClass):
"""Basic test of builtin pages (no features).
"""
def test_goto_index(self):
'''display all devices'''
self.register_auth(self.URL)
self.cmd.go(self.URL)
self.register_auth(self.url)
self.cmd.go(self.url)
self.cmd.find("The CryptoBox")
self.cmd.go(self.URL + "?weblang=de")
self.cmd.go(self.url + "?weblang=de")
self.cmd.find("Die CryptoBox")
self.cmd.go(self.URL + "?weblang=sl")
self.cmd.go(self.url + "?weblang=sl")
self.cmd.find("Privatnost v vsako vas")
self.cmd.go(self.URL + "?weblang=fr")
self.cmd.go(self.url + "?weblang=fr")
self.cmd.find("La CryptoBox")

View File

@ -0,0 +1,7 @@
"""The webinterface of the CryptoBox.
"""
__revision__ = "$Id"
__all__ = [ 'dataset', 'languages', 'sites', 'testclass' ]

View File

@ -18,8 +18,12 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
"""Manage the hdf dataset of the cryptobox web sites.
"""
__revision__ = "$Id"
import os
from cryptobox.core.exceptions import *
import cryptobox.core.container as cbxContainer
import cryptobox.core.tools as cbxTools
@ -30,20 +34,24 @@ class WebInterfaceDataset(dict):
"""
def __init__(self, cbox, prefs, plugins):
super(WebInterfaceDataset, self).__init__()
self.prefs = prefs
self.cbox = cbox
self.__setConfigValues()
self.__set_config_values()
self.plugins = plugins
self.setCryptoBoxState()
self.setPluginData()
self.setContainersState()
self.set_crypto_box_state()
self.set_plugin_data()
self.set_containers_state()
def setCryptoBoxState(self):
def set_crypto_box_state(self):
"""Set some hdf values according to the cryptobox as a whole.
"""
import cherrypy
import cryptobox.core.main
import cryptobox.web.languages
self["Data.Version"] = cryptobox.core.main.VERSION
import cryptobox
self["Data.Version"] = cryptobox.__version__
langs = self.cbox.prefs["WebSettings"]["Languages"][:]
langs.sort()
for (index, lang) in enumerate(langs):
@ -58,10 +66,11 @@ class WebInterfaceDataset(dict):
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"])
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])
port = int(host.split(":", 1)[1])
complete_url += ":%s" % port
except (IndexError, ValueError):
if cherrypy.request.scheme == "http":
@ -70,7 +79,8 @@ class WebInterfaceDataset(dict):
port = 443
else:
## unknown scheme -> port 0
self.cbox.log.info("unknown protocol scheme used: %s" % (cherrypy.request.scheme,))
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)
@ -83,20 +93,25 @@ class WebInterfaceDataset(dict):
self["Data.ScriptURL"] = ""
def setCurrentDiskState(self, device):
for container in self.cbox.getContainerList():
if container.getDevice() == device:
isEncrypted = (container.getType() == cbxContainer.ContainerTypes["luks"]) and 1 or 0
isPlain = (container.getType() == cbxContainer.ContainerTypes["plain"]) and 1 or 0
isMounted = container.isMounted() and 1 or 0
self["Data.CurrentDisk.device"] = container.getDevice()
self["Data.CurrentDisk.name"] = container.getName()
self["Data.CurrentDisk.encryption"] = isEncrypted
self["Data.CurrentDisk.plaintext"] = isPlain
self["Data.CurrentDisk.active"] = isMounted
self["Data.CurrentDisk.size"] = cbxTools.getBlockDeviceSizeHumanly(container.getDevice())
if isMounted:
(size, avail, used) = container.getCapacity()
def set_current_disk_state(self, device):
"""Set some hdf values according to the currently active disk.
"""
for container in self.cbox.get_container_list():
if container.get_device() == device:
is_encrypted = (container.get_type() == \
cbxContainer.CONTAINERTYPES["luks"]) and 1 or 0
is_plain = (container.get_type() == \
cbxContainer.CONTAINERTYPES["plain"]) and 1 or 0
is_mounted = container.is_mounted() and 1 or 0
self["Data.CurrentDisk.device"] = container.get_device()
self["Data.CurrentDisk.name"] = container.get_name()
self["Data.CurrentDisk.encryption"] = is_encrypted
self["Data.CurrentDisk.plaintext"] = is_plain
self["Data.CurrentDisk.active"] = is_mounted
self["Data.CurrentDisk.size"] = cbxTools.get_blockdevice_size_humanly(
container.get_device())
if is_mounted:
(size, avail, used) = container.get_capacity()
percent = used / size
self["Data.CurrentDisk.capacity.used"] = used
self["Data.CurrentDisk.capacity.free"] = avail
@ -105,51 +120,63 @@ class WebInterfaceDataset(dict):
self["Settings.LinkAttrs.device"] = device
def setContainersState(self):
def set_containers_state(self):
"""Set some hdf values according to the list of available containers.
"""
avail_counter = 0
active_counter = 0
for container in self.cbox.getContainerList():
for container in self.cbox.get_container_list():
## useful if the container was changed during an action
container.resetObject()
isEncrypted = (container.getType() == cbxContainer.ContainerTypes["luks"]) and 1 or 0
isPlain = (container.getType() == cbxContainer.ContainerTypes["plain"]) and 1 or 0
isMounted = container.isMounted() and 1 or 0
self["Data.Disks.%d.device" % avail_counter] = container.getDevice()
self["Data.Disks.%d.name" % avail_counter] = container.getName()
self["Data.Disks.%d.encryption" % avail_counter] = isEncrypted
self["Data.Disks.%d.plaintext" % avail_counter] = isPlain
self["Data.Disks.%d.active" % avail_counter] = isMounted
self["Data.Disks.%d.size" % avail_counter] = cbxTools.getBlockDeviceSizeHumanly(container.getDevice())
if isMounted: active_counter += 1
container.reset_object()
is_encrypted = (container.get_type() == \
cbxContainer.CONTAINERTYPES["luks"]) and 1 or 0
is_plain = (container.get_type() == \
cbxContainer.CONTAINERTYPES["plain"]) and 1 or 0
is_mounted = container.is_mounted() and 1 or 0
self["Data.Disks.%d.device" % avail_counter] = container.get_device()
self["Data.Disks.%d.name" % avail_counter] = container.get_name()
self["Data.Disks.%d.encryption" % avail_counter] = is_encrypted
self["Data.Disks.%d.plaintext" % avail_counter] = is_plain
self["Data.Disks.%d.active" % avail_counter] = is_mounted
self["Data.Disks.%d.size" % avail_counter] = \
cbxTools.get_blockdevice_size_humanly(container.get_device())
if is_mounted:
active_counter += 1
avail_counter += 1
self["Data.activeDisksCount"] = active_counter
def setPluginData(self):
for p in self.plugins:
entryName = "Settings.PluginList." + p.getName()
def set_plugin_data(self):
"""Set some hdf values according to the available features.
"""
for plugin in self.plugins:
entry_name = "Settings.PluginList." + plugin.get_name()
## first: remove all existing settings of this plugin
for key in self.keys():
if key.startswith(entryName): del self[key]
lang_data = p.getLanguageData()
self[entryName] = p.getName()
self[entryName + ".Name"] = lang_data.getValue("Name", 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"
for a in p.pluginCapabilities:
self[entryName + ".Types." + a] = "1"
for a in p.getVisibility():
self[entryName + ".Visible." + a] = "1"
if key.startswith(entry_name):
del self[key]
lang_data = plugin.get_language_data()
self[entry_name] = plugin.get_name()
self[entry_name + ".Name"] = lang_data.getValue("Name", plugin.get_name())
self[entry_name + ".Link"] = lang_data.getValue("Link", plugin.get_name())
self[entry_name + ".Rank"] = plugin.get_rank()
self[entry_name + ".RequestAuth"] = plugin.is_auth_required() and "1" or "0"
for capy in plugin.plugin_capabilities:
self[entry_name + ".Types." + capy] = "1"
for visi in plugin.get_visibility():
self[entry_name + ".Visible." + visi] = "1"
def __setConfigValues(self):
self["Settings.TemplateDir"] = os.path.abspath(self.prefs["Locations"]["TemplateDir"])
def __set_config_values(self):
"""Set some hdf values according to configuration settings.
"""
self["Settings.TemplateDir"] = os.path.abspath(
self.prefs["Locations"]["TemplateDir"])
self["Settings.DocDir"] = os.path.abspath(self.prefs["Locations"]["DocDir"])
self["Settings.Stylesheet"] = self.prefs["WebSettings"]["Stylesheet"]
self["Settings.Language"] = self.prefs["WebSettings"]["Languages"][0]
for num,d in enumerate(self.prefs["Locations"]["PluginDir"]):
self["Settings.PluginDir.%d" % num] = d
for (num, dpath) in enumerate(self.prefs["Locations"]["PluginDir"]):
self["Settings.PluginDir.%d" % num] = dpath
## store the first directory in this settings variable - backward compatibility
self["Settings.PluginDir"] = self.prefs["Locations"]["PluginDir"][0]
self["Settings.SettingsDir"] = self.prefs["Locations"]["SettingsDir"]

View File

@ -22,23 +22,28 @@
"""supply information about existing languages
"""
__revision__ = "$Id"
## every language information should contain (name, pluralformat)
LANGUAGE_INFO = {
"cs": ('Český', ('3', '(n==1) ? 0 : (n>=2 && n< =4) ? 1 : 2')),
"da": ('Dansk', ('2', '(n != 1)')),
"de": ('Deutsch', ('2', '(n != 1)')),
"en": ('English', ('2', '(n != 1)')),
"es": ('Español', ('2', '(n != 1)')),
"fi": ('Suomi', ('2', '(n != 1)')),
"fr": ('Français', ('2', '(n != 1)')),
"hu": ('Magyar', ('1', '0')),
"it": ('Italiano', ('2', '(n != 1)')),
"ja": ('日本語', ('1', '0')),
"nl": ('Nederlands', ('2', '(n != 1)')),
"pl": ('Polski', ('3', '(n==1 ? 0 : n%10>=2 && n%10< =4 && (n%100<10 || n%100>=20) ? 1 : 2)')),
"pt": ('Português', ('2', '(n != 1)')),
"ru": ('Русский', ('3', '(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10< =4 && (n%100<10 || n%100>=20) ? 1 : 2)')),
"sl": ('Slovensko', ('4', '(n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3)')),
"sv": ('Svenska', ('2', '(n != 1)')),
}
"cs": ('Český', ('3', '(n==1) ? 0 : (n>=2 && n< =4) ? 1 : 2')),
"da": ('Dansk', ('2', '(n != 1)')),
"de": ('Deutsch', ('2', '(n != 1)')),
"en": ('English', ('2', '(n != 1)')),
"es": ('Español', ('2', '(n != 1)')),
"fi": ('Suomi', ('2', '(n != 1)')),
"fr": ('Français', ('2', '(n != 1)')),
"hu": ('Magyar', ('1', '0')),
"it": ('Italiano', ('2', '(n != 1)')),
"ja": ('日本語', ('1', '0')),
"nl": ('Nederlands', ('2', '(n != 1)')),
"pl": ('Polski', ('3', '(n==1 ? 0 : n%10>=2 && n%10< =4 '
+ '&& (n%100<10 || n%100>=20) ? 1 : 2)')),
"pt": ('Português', ('2', '(n != 1)')),
"ru": ('Русский', ('3', '(n%10==1 && n%100!=11 ? 0 : '
+ 'n%10>=2 && n%10< =4 && (n%100<10 || n%100>=20) ? 1 : 2)')),
"sl": ('Slovensko', ('4', '(n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || '
+ 'n%100==4 ? 2 : 3)')),
"sv": ('Svenska', ('2', '(n != 1)')),
}

View File

@ -17,103 +17,150 @@
# along with the CryptoBox; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
""" this module handles all http requests and renders a website """
__revision__ = "$Id$"
import cryptobox.core.main
import cryptobox.web.dataset
import cryptobox.plugins.manage
from cryptobox.core.exceptions import *
import cryptobox.core.exceptions
import re
import cherrypy
import types
import os
import sys
try:
import neo_cgi, neo_util, neo_cs
except ImportError:
errorMsg = "Could not import clearsilver module. Try 'apt-get install python-clearsilver'."
self.log.error(errorMsg)
sys.stderr.write(errorMsg)
raise ImportError, errorMsg
_ERRMSG = "Could not import clearsilver module. \
Try 'apt-get install python-clearsilver'."
sys.stderr.write(_ERRMSG)
raise ImportError, _ERRMSG
GETTEXT_DOMAIN = 'cryptobox-server'
class PluginIconHandler:
"""deliver the icons of available plugins via cherrypy"""
def __init__(self, plugins):
for plugin in plugins.getPlugins():
if not plugin: continue
plname = plugin.getName()
## expose the getIcon function of this plugin
setattr(self, plname, plugin.getIcon)
for plugin in plugins.get_plugins():
if not plugin:
continue
plname = plugin.get_name()
## expose the get_icon function of this plugin
setattr(self, plname, plugin.get_icon)
class WebInterfaceSites:
'''
'''
"""handle all http requests and render pages
this includes:
- filtering common arguments
- calling feature actions
- translating content
all available features are dynamically exposed
"""
## this template is used under strange circumstances
defaultTemplate = "empty"
def __init__(self, conf_file=None):
import logging,sys
self.cbox = cryptobox.core.main.CryptoBoxProps(conf_file)
self.log = logging.getLogger("CryptoBox")
self.prefs = self.cbox.prefs
self.__resetDataset()
## we should only use variables preceded by "__" to avoid name conflicts
## when loading features
self.cbox = cryptobox.core.main.CryptoBox(conf_file)
self.__cached_language_data = None
self.__dataset = None
self.icons = None
self.__plugin_manager = None
self.__reset_dataset()
## store the original http error handler
self._cp_on_http_error = self.newHTTPErrorHandler
self._cp_on_http_error = self.new_http_error_handler
## set initial language order
self.langOrder = self.cbox.prefs["WebSettings"]["Languages"][:]
self.lang_order = self.cbox.prefs["WebSettings"]["Languages"][:]
def __resetDataset(self):
def __reset_dataset(self):
"""this method has to be called at the beginning of every "site" action
important: only at the beginning of an action (to not loose information)
important: for _every_ "site" action (cherrypy is stateful)
also take care for the plugins, as they also contain datasets
"""
self.__loadPlugins()
self.dataset = cryptobox.web.dataset.WebInterfaceDataset(self.cbox, self.prefs, self.pluginList.getPlugins())
self.__load_plugins()
self.__dataset = cryptobox.web.dataset.WebInterfaceDataset(
self.cbox, self.cbox.prefs, self.__plugin_manager.get_plugins())
## publish plugin icons
self.icons = PluginIconHandler(self.pluginList)
self.icons = PluginIconHandler(self.__plugin_manager)
self.icons.exposed = True
## check, if a configuration partition has become available
self.cbox.prefs.preparePartition()
self.cbox.prefs.prepare_partition()
def __loadPlugins(self):
self.pluginList = cryptobox.plugins.manage.PluginManager(self.cbox, self.prefs["Locations"]["PluginDir"], self)
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)
def __load_plugins(self):
"""reinitialize the list of available plugins
this includes the following:
- reload all plugins and check their state (disabled or not)
- reinitilize the datasets of all plugins
"""
self.__plugin_manager = cryptobox.plugins.manage.PluginManager(
self.cbox, self.cbox.prefs["Locations"]["PluginDir"], self)
for plugin in self.__plugin_manager.get_plugins():
if not plugin:
continue
plname = plugin.get_name()
## check if there are name conflicts: e.g. a local variable has the
## same name as a plugin to be loaded -> skip these plugins
## if we do not check this here, nasty side effects may occour ...
try:
prev_obj = getattr(self, plname)
if not callable(prev_obj) \
or not prev_obj.exposed:
self.cbox.log.error("Skipped feature (%s) as its name"
+ " conflicts with a local variable - see"
+ " module cryptobox.web.sites" % plname)
## skip this plugin
continue
except (NameError, AttributeError):
## an attribute with the same name does not exist -> ok
if plugin.is_enabled():
self.cbox.log.info("Plugin '%s' loaded" % plname)
## expose all features as URLs
setattr(self, plname, self.return_plugin_action(plugin))
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
## it has to be defined before any page definition requiring authentification
def __requestAuth(self=None):
## sub pages requiring authentication may not be defined above
def __request_auth(self=None):
""" this is a function decorator to check authentication
"""
def check_credentials(site):
""" see description of _inner_wrapper - please simplify this!
"""
def _inner_wrapper(self, *args, **kargs):
"""this function was necessary while trying around with the
function decorator - if someone can implement the decorator
with less effort, then any suggestions are welcome!
"""
import base64
## define a "non-allowed" function
user, password = None, None
try:
resp = cherrypy.request.headers["Authorization"][6:] # ignore "Basic "
(user, password) = base64.b64decode(resp).split(":",1)
## ignore the "Basic " (first six letters) part
resp = cherrypy.request.headers["Authorization"][6:]
(user, password) = base64.b64decode(resp).split(":", 1)
except KeyError:
## no "authorization" header was sent
pass
@ -123,18 +170,20 @@ class WebInterfaceSites:
except AttributeError:
## no cherrypy response header defined
pass
authDict = self.cbox.prefs.userDB["admins"]
if user in authDict.keys():
if self.cbox.prefs.userDB.getDigest(password) == authDict[user]:
auth_dict = self.cbox.prefs.user_db["admins"]
if user in auth_dict.keys():
if self.cbox.prefs.user_db.get_digest(password) == auth_dict[user]:
## ok: return the choosen page
self.cbox.log.info("access granted for: %s" % user)
return site(self, *args, **kargs)
else:
self.cbox.log.info("wrong password supplied for: %s" % user)
self.cbox.log.info(
"wrong password supplied for: %s" % user)
else:
self.cbox.log.info("unknown user: %s" % str(user))
## wrong credentials: return "access denied"
cherrypy.response.headers["WWW-Authenticate"] = '''Basic realm="CryptoBox"'''
cherrypy.response.headers["WWW-Authenticate"] = \
'''Basic realm="CryptoBox"'''
cherrypy.response.status = 401
return self.__render("access_denied")
return _inner_wrapper
@ -147,15 +196,18 @@ class WebInterfaceSites:
@cherrypy.expose
def index(self, weblang=""):
self.__resetDataset()
self.__setWebLang(weblang)
self.__checkEnvironment()
"""the default page on startup - we show the list of available disks
"""
self.__reset_dataset()
self.__set_web_lang(weblang)
self.__check_environment()
## do not forget the language!
param_dict = {"weblang":weblang}
## render "disks" plugin by default
return self.return_plugin_action(self.pluginList.getPlugin("disks"))(**param_dict)
return self.return_plugin_action(
self.__plugin_manager.get_plugin("disks"))(**param_dict)
def newHTTPErrorHandler(self, errorCode, message):
def new_http_error_handler(self, error_code, message):
"""handle http errors gracefully
404 - not found errors: ignored if url is below /cryptobox-misc/
@ -163,148 +215,164 @@ class WebInterfaceSites:
500 - runtime errors: return "ok" exit code and show a polite excuse
others: are there any other possible http errors?
"""
import traceback, sys
import traceback
## we ignore uninteresting not-found errors
if (errorCode == 404) and \
if (error_code == 404) and \
(cherrypy.request.path.startswith("/cryptobox-misc/") or \
cherrypy.request.path in ['/robots.txt','/favicon.ico']):
cherrypy.response.status = errorCode
cherrypy.response.status = error_code
return
## an invalid action was requested
if errorCode == 404:
if error_code == 404:
## we send a not-found error (with the usual interface)
cherrypy.response.status = errorCode
self.dataset["Data.Warning"] = "InvalidAction"
cherrypy.response.status = error_code
self.__dataset["Data.Warning"] = "InvalidAction"
cherrypy.response.body = self.__render("empty")
return
## are there still bugs in the code?
if errorCode == 500:
if error_code == 500:
## we fix the error code (200 is "OK")
cherrypy.response.status = 200
self.cbox.log.error("HTTP-ERROR[500] - a runtime error occoured: %s" % str(message))
self.cbox.log.error(
"HTTP-ERROR[500] - runtime error: %s" % str(message))
## add a traceback and exception information to the lo
for a in traceback.format_exception(*sys.exc_info()):
self.cbox.log.error("\t%s" % a)
self.dataset["Data.Warning"] = "RuntimeError"
for log_line in traceback.format_exception(*sys.exc_info()):
self.cbox.log.error("\t%s" % log_line)
self.__dataset["Data.Warning"] = "RuntimeError"
cherrypy.response.body = self.__render("empty")
return
## unknown error type
cherrypy.response.status = errorCode
self.cbox.log.warn("HTTP-ERROR[%d] - an unknown error occoured: %s" % (errorCode, message))
cherrypy.response.status = error_code
self.cbox.log.warn("HTTP-ERROR[%d] - an unknown error occoured: %s" \
% (error_code, message))
cherrypy.response.body = self.__render("empty")
def return_plugin_action(self, plugin):
""" returns a function that is suitable for handling a cherrypy
page request
"""
def handler(self, **args):
self.__resetDataset()
self.__checkEnvironment()
args_orig = dict(args)
"""this function handles a cherrypy page request
"""
self.__reset_dataset()
self.__check_environment()
## set web interface language
try:
self.__setWebLang(args["weblang"])
self.__set_web_lang(args["weblang"])
del args["weblang"]
except KeyError:
self.__setWebLang("")
## we always read the "device" setting - otherwise volume-plugin links
## would not work easily (see "volume_props" linking to "volume_format_fs")
self.__set_web_lang("")
## we always read the "device" setting - otherwise volume-plugin
## links would not work easily
## (see "volume_props" linking to "volume_format_fs")
## it will get ignored for non-volume plugins
try:
plugin.device = None
if self.__setDevice(args["device"]):
if self.__set_device(args["device"]):
plugin.device = args["device"]
del args["device"]
except KeyError:
pass
plugin.device = None
## check the device argument of volume plugins
if "volume" in plugin.pluginCapabilities:
if "volume" in plugin.plugin_capabilities:
## initialize the dataset of the selected device if necessary
if plugin.device:
self.dataset.setCurrentDiskState(plugin.device)
self.__dataset.set_current_disk_state(plugin.device)
else:
## invalid (or missing) device setting
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)
## check if there is a "redirect" setting - this will override
## the return value of the do_action 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}
override_next_template = { "plugin":args["redirect"] }
if "volume" in plugin.plugin_capabilities:
override_next_template["values"] = {"device":plugin.device}
del args["redirect"]
except KeyError:
override_nextTemplate = None
override_next_template = None
## check for information to be kept after the last call
try:
keep_values = args["message_keep"]
del args["message_keep"]
for key, value in keep_values["dataset"].items():
self.dataset[key] = value
self.__dataset[key] = value
except KeyError:
keep_values = None
## call the plugin handler
nextTemplate = plugin.doAction(**args)
next_template = plugin.do_action(**args)
## for 'volume' plugins: reread the dataset of the current disk
## additionally: set the default template for plugins
if "volume" in plugin.pluginCapabilities:
if "volume" in plugin.plugin_capabilities:
## maybe the state of the current volume was changed?
self.dataset.setCurrentDiskState(plugin.device)
if not nextTemplate: nextTemplate = { "plugin":"volume_mount", "values":{"device":plugin.device}}
self.__dataset.set_current_disk_state(plugin.device)
if not next_template:
next_template = { "plugin":"volume_mount",
"values":{"device":plugin.device}}
else:
## maybe a non-volume plugin changed some plugin settings (e.g. plugin_manager)
self.dataset.setPluginData()
## update the container hdf-dataset (maybe a plugin changed the state of a container)
self.dataset.setContainersState()
## some non-volume plugins change the internal state of other
## plugins - e.g.: plugin_manager
self.__dataset.set_plugin_data()
## some non-volume plugins may change the state of containers
self.__dataset.set_containers_state()
## default page for non-volume plugins is the disk selection
if not nextTemplate: nextTemplate = { "plugin":"disks", "values":{} }
if not next_template:
next_template = { "plugin":"disks", "values":{} }
## was a redirect requested?
if override_nextTemplate:
nextTemplate = override_nextTemplate
## if another plugins was choosen for 'nextTemplate', then do it!
if isinstance(nextTemplate, types.DictType) \
and "plugin" in nextTemplate.keys() \
and "values" in nextTemplate.keys() \
and self.pluginList.getPlugin(nextTemplate["plugin"]):
valueDict = dict(nextTemplate["values"])
if override_next_template:
next_template = override_next_template
## if another plugins was choosen for 'next_template', then do it!
if isinstance(next_template, dict) \
and "plugin" in next_template.keys() \
and "values" in next_template.keys() \
and self.__plugin_manager.get_plugin(next_template["plugin"]):
value_dict = dict(next_template["values"])
## force the current weblang attribute - otherwise it gets lost
valueDict["weblang"] = self.dataset["Settings.Language"]
value_dict["weblang"] = self.__dataset["Settings.Language"]
## check for warnings/success messages, that should be kept
if "Data.Warning" in plugin.hdf.keys() \
or "Data.Success" in plugin.hdf.keys():
self.cbox.log.info("keep warning message")
valueDict["message_keep"] = { "plugin":plugin, "dataset":{}}
if "Data.Success" in plugin.hdf.keys() \
or "Data.Warning" in plugin.hdf.keys():
value_dict["message_keep"] = {"plugin":plugin, "dataset":{}}
for keep_key in ("Data.Warning", "Data.Success"):
if keep_key in plugin.hdf.keys():
valueDict["message_keep"]["dataset"][keep_key] = plugin.hdf[keep_key]
new_plugin = self.pluginList.getPlugin(nextTemplate["plugin"])
return self.return_plugin_action(new_plugin)(**valueDict)
self.cbox.log.info("keeping message: %s" % \
plugin.hdf[keep_key])
value_dict["message_keep"]["dataset"][keep_key] = \
plugin.hdf[keep_key]
new_plugin = self.__plugin_manager.get_plugin(next_template["plugin"])
return self.return_plugin_action(new_plugin)(**value_dict)
## save the currently active plugin name
self.dataset["Data.ActivePlugin"] = plugin.getName()
return self.__render(nextTemplate, plugin)
self.__dataset["Data.ActivePlugin"] = plugin.get_name()
return self.__render(next_template, plugin)
## apply authentication?
if plugin.isAuthRequired():
return lambda **args: self.__requestAuth()(handler)(self, **args)
if plugin.is_auth_required():
return lambda **args: self.__request_auth()(handler)(self, **args)
else:
return lambda **args: handler(self, **args)
## test authentication
@cherrypy.expose
@__requestAuth
@__request_auth
def test(self, weblang=""):
self.__resetDataset()
self.__setWebLang(weblang)
self.__checkEnvironment()
"""test authentication - this function may be safely removed
"""
self.__reset_dataset()
self.__set_web_lang(weblang)
self.__check_environment()
return "test passed"
@cherrypy.expose
def test_stream(self):
"""just for testing purposes - to check if the "stream_response" feature
actually works - for now (September 02006) it does not seem to be ok"""
actually works - for now (September 02006) it does not seem to be ok
"""
import time
yield "<html><head><title>neu</title></head><body><p><ul>"
for a in range(10):
yield "<li>yes: %d - %s</li>" % (a, str(time.time()))
for num in range(10):
yield "<li>yes: %d - %s</li>" % (num, str(time.time()))
time.sleep(1)
yield "</ul></p></html>"
@ -312,69 +380,77 @@ class WebInterfaceSites:
##################### input checker ##########################
def __checkEnvironment(self):
"""here we should place all interesting checks to inform the user of problems
def __check_environment(self):
"""inform the user of suspicious environmental problems
examples are: non-https, readonly-config, ...
"""
## this check is done _after_ "resetDataSet" -> a possible config partition was
## loaded before
if self.cbox.prefs.requiresPartition() and not self.cbox.prefs.getActivePartition():
self.dataset["Data.EnvironmentWarning"] = "ReadOnlyConfig"
# TODO: turn this on soon (add "not") - for now it is annoying
if self.__checkHTTPS():
self.dataset["Data.EnvironmentWarning"] = "NoSSL"
## this check is done _after_ "reset_dataset" -> if there is
## a config partition, then it was loaded before
if self.cbox.prefs.requires_partition() \
and not self.cbox.prefs.get_active_partition():
self.__dataset["Data.EnvironmentWarning"] = "ReadOnlyConfig"
#TODO: turn this on soon (add "not") - for now it is annoying
if self.__check_https():
self.__dataset["Data.EnvironmentWarning"] = "NoSSL"
def __checkHTTPS(self):
## check the request scheme
if cherrypy.request.scheme == "https": return True
def __check_https(self):
"""check the request scheme
"""
if cherrypy.request.scheme == "https":
return True
## check an environment setting - this is quite common behind proxies
try:
if os.environ["HTTPS"]: return True
if os.environ["HTTPS"]:
return True
except KeyError:
pass
## check http header TODO (check pound for the name)
try:
if cherrypy.request.headers["TODO"]: return True
except KeyError:
pass
## the connection seems to be unencrypted
return False
## check http header for ssl information
#TODO: (check pound for the name)
try:
if cherrypy.request.headers["XXX"]:
return True
except KeyError:
## the connection seems to be unencrypted
return False
def __setWebLang(self, value):
"""set the preferred priority of languages according to the following order:
def __set_web_lang(self, value):
"""set the preferred priority of languages according to this order:
1. language selected via web interface
2. preferred browser language setting
3. languages defined in the config file
"""
## start with the configured language order
langOrder = self.cbox.prefs["WebSettings"]["Languages"][:]
self.cbox.log.debug("updating language preferences (default: %s)" % str(langOrder))
lang_order = self.cbox.prefs["WebSettings"]["Languages"][:]
self.cbox.log.debug(
"updating language preferences (default: %s)" % str(lang_order))
## put the preferred browser language in front
guess = self.__getPreferredBrowserLanguage(langOrder)
guess = self.__get_browser_language(lang_order)
if guess:
langOrder.remove(guess)
langOrder.insert(0,guess)
self.cbox.log.debug("raised priority of preferred browser language: %s" % guess)
lang_order.remove(guess)
lang_order.insert(0, guess)
self.cbox.log.debug(
"raised priority of preferred browser language: %s" % guess)
## is the chosen language (via web interface) valid? - put it in front
if value and (value in langOrder) and (not re.search(u'\W',value)):
langOrder.remove(value)
langOrder.insert(0,value)
self.cbox.log.debug("raised priority of selected language: %s" % value)
if value and (value in lang_order) and (not re.search(u'\W', value)):
lang_order.remove(value)
lang_order.insert(0, value)
self.cbox.log.debug(
"raised priority of selected language: %s" % value)
elif value:
self.cbox.log.info("invalid language selected: %s" % value)
## store current language setting
self.cbox.log.debug("current language preference: %s" % str(langOrder))
self.langOrder = langOrder
self.dataset["Settings.Language"] = langOrder[0]
self.dataset["Settings.LinkAttrs.weblang"] = langOrder[0]
self.cbox.log.debug(
"current language preference: %s" % str(lang_order))
self.lang_order = lang_order
self.__dataset["Settings.Language"] = lang_order[0]
self.__dataset["Settings.LinkAttrs.weblang"] = lang_order[0]
def __getPreferredBrowserLanguage(self, availLangs):
def __get_browser_language(self, avail_langs):
"""guess the preferred language of the user (as sent by the browser)
take the first language, that is part of 'availLangs'
take the first language, that is part of 'avail_langs'
"""
try:
pref_lang_header = cherrypy.request.headers["Accept-Language"]
@ -382,56 +458,64 @@ class WebInterfaceSites:
## no language header was specified
return None
## this could be a typical 'Accept-Language' header:
## de-de,de;q=0.8,en-us;q=0.5,en;q=0.3
## de-de,de;q=0.8,en-us;q=0.5,en;q=0.3
regex = re.compile(u"\w+(-\w+)?(;q=[\d\.]+)?$")
pref_langs = [e.split(";",1)[0]
pref_langs = [e.split(";", 1)[0]
for e in pref_lang_header.split(",")
if regex.match(e)]
## is one of these preferred languages available?
for lang in pref_langs:
if lang in availLangs: return lang
if lang in avail_langs:
return lang
## we try to be nice: also look for "de" if "de-de" was specified ...
for lang in pref_langs:
## use only the first part of the language
short_lang = lang.split("-",1)[0]
if short_lang in availLangs: return short_lang
short_lang = lang.split("-", 1)[0]
if short_lang in avail_langs:
return short_lang
## we give up
return None
def __setDevice(self, device):
def __set_device(self, device):
"""check a device name that was chosen via the web interface
issue a warning if the device is invalid"""
if device and re.match(u'[\w /\-]+$', device) and self.cbox.getContainer(device):
self.log.debug("select device: %s" % device)
if device and re.match(u'[\w /\-]+$', device) \
and self.cbox.get_container(device):
self.cbox.log.debug("select device: %s" % device)
return True
else:
self.log.warn("invalid device: %s" % device)
self.dataset["Data.Warning"] = "InvalidDevice"
self.cbox.log.warn("invalid device: %s" % device)
self.__dataset["Data.Warning"] = "InvalidDevice"
return False
def __substituteGettext(self, languages, textDomain, hdf):
def __substitute_gettext(self, languages, text_domain, hdf):
"""substitute all texts in the hdf dataset with their translated
counterparts as returned by gettext
"""
import gettext
try:
translator = gettext.translation(textDomain, languages=languages)
except IOError, errMsg:
translator = gettext.translation(text_domain, languages=languages)
except IOError, err_msg:
## no translation found
self.cbox.log.warn("unable to load language file: %s" % errMsg)
self.cbox.log.warn("unable to load language file: %s" % err_msg)
return hdf
def walk_tree(hdf_node):
"""iterate through all nodes"""
def translate_node(node):
for (key,value) in node.attrs():
if key == 'LINK': return
"""turn one single string into unicode"""
for (key, value) in node.attrs():
if key == "LINK":
return
try:
node.setValue("",translator.ugettext(node.value()))
except UnicodeEncodeError, errMsg:
self.cbox.log.info("Failed unicode encoding for gettext: %s - %s" % (node.value(),errMsg))
node.setValue("", translator.ugettext(node.value()))
except UnicodeEncodeError, err_msg:
self.cbox.log.info(
"Failed unicode encoding for gettext: %s - %s" \
% (node.value(),err_msg))
## fallback to default encoding
node.setValue("",translator.gettext(node.value()))
node.setValue("", translator.gettext(node.value()))
while hdf_node:
translate_node(hdf_node)
walk_tree(hdf_node.child())
@ -439,86 +523,95 @@ class WebInterfaceSites:
walk_tree(hdf)
def __getLanguageData(self):
def __get_language_data(self):
"""return the hdf dataset of the main interface and all plugins
translations are done according to self.langOrder
translations are done according to self.lang_order
"""
## check if the language setting was changed - use cached data if possible
try:
if self.cachedLanguageData["langOrder"] == self.langOrder:
self.cbox.log.debug("using cached language data: %s" % str(self.langOrder))
return self.cachedLanguageData["hdf"]
except AttributeError:
pass
## check if the language setting has changed - use cache if possible
if self.__cached_language_data and \
self.__cached_language_data["lang_order"] == self.lang_order:
self.cbox.log.debug(
"using cached language data: %s" % str(self.lang_order))
return self.__cached_language_data["hdf"]
self.cbox.log.debug("generating language data")
hdf = neo_util.HDF()
hdf.readFile(os.path.join(self.prefs["Locations"]["TemplateDir"],"language.hdf"))
self.__substituteGettext(self.langOrder, GETTEXT_DOMAIN, hdf)
hdf.readFile(os.path.join(
self.cbox.prefs["Locations"]["TemplateDir"],"language.hdf"))
self.__substitute_gettext(self.lang_order, GETTEXT_DOMAIN, hdf)
## load the language data of all plugins
for p in self.pluginList.getPlugins():
pl_lang = p.getLanguageData()
self.__substituteGettext(self.langOrder, "%s-feature-%s" % (GETTEXT_DOMAIN, p.getName()), pl_lang)
hdf.copy("Plugins.%s" % p.getName(), pl_lang)
self.cbox.log.debug("language data for plugin loaded: %s" % p.getName())
for plugin in self.__plugin_manager.get_plugins():
pl_lang = plugin.get_language_data()
self.__substitute_gettext(self.lang_order, "%s-feature-%s" % \
(GETTEXT_DOMAIN, plugin.get_name()), pl_lang)
hdf.copy("Plugins.%s" % plugin.get_name(), pl_lang)
self.cbox.log.debug(
"language data for plugin loaded: %s" % plugin.get_name())
## cache result for later retrieval
self.cachedLanguageData = {"langOrder": self.langOrder, "hdf": hdf}
self.__cached_language_data = \
{"lang_order": self.lang_order, "hdf": hdf}
return hdf
def __render(self, renderInfo, plugin=None):
def __render(self, render_info, plugin=None):
'''renders from clearsilver templates and returns the resulting html
'''
## is renderInfo a string (filename of the template) or a dictionary?
if type(renderInfo) == types.DictType:
template = renderInfo["template"]
if renderInfo.has_key("generator"):
generator = renderInfo["generator"]
## is render_info a string (filename of the template) or a dictionary?
if isinstance(render_info, dict):
template = render_info["template"]
if render_info.has_key("generator"):
generator = render_info["generator"]
else:
generator = None
else:
(template, generator) = (renderInfo, None)
(template, generator) = (render_info, None)
## load the language data
hdf = neo_util.HDF()
hdf.copy("Lang", self.__getLanguageData())
hdf.copy("Lang", self.__get_language_data())
## first: assume, that the template file is in the global template directory
self.dataset["Settings.TemplateFile"] = os.path.abspath(os.path.join(self.prefs["Locations"]["TemplateDir"], template + ".cs"))
## first: assume, that the template file is in the global
## template directory
self.__dataset["Settings.TemplateFile"] = os.path.abspath(os.path.join(
self.cbox.prefs["Locations"]["TemplateDir"],
template + ".cs"))
if plugin:
## check, if the plugin provides the template file -> overriding
plugin_cs_file = plugin.getTemplateFileName(template)
plugin_cs_file = plugin.get_template_filename(template)
if plugin_cs_file:
self.dataset["Settings.TemplateFile"] = plugin_cs_file
self.__dataset["Settings.TemplateFile"] = plugin_cs_file
## add the current state of the plugins to the hdf dataset
self.dataset["Data.Status.Plugins.%s" % plugin.getName()] = plugin.getStatus()
self.__dataset["Data.Status.Plugins.%s" % plugin.get_name()] = \
plugin.get_status()
## load the dataset of the plugin
plugin.loadDataSet(hdf)
plugin.load_dataset(hdf)
self.log.info("rendering site: " + template)
self.cbox.log.info("rendering site: " + template)
cs_path = os.path.abspath(os.path.join(self.prefs["Locations"]["TemplateDir"], "main.cs"))
cs_path = os.path.abspath(os.path.join(
self.cbox.prefs["Locations"]["TemplateDir"], "main.cs"))
if not os.access(cs_path, os.R_OK):
log.error("Couldn't read clearsilver file: %s" % cs_path)
self.cbox.log.error(
"Couldn't read clearsilver file: %s" % cs_path)
yield "Couldn't read clearsilver file: %s" % cs_path
return
self.log.debug(self.dataset)
for key in self.dataset.keys():
hdf.setValue(key,str(self.dataset[key]))
cs = neo_cs.CS(hdf)
cs.parseFile(cs_path)
self.cbox.log.debug(self.__dataset)
for key in self.__dataset.keys():
hdf.setValue(key, str(self.__dataset[key]))
cs_data = neo_cs.CS(hdf)
cs_data.parseFile(cs_path)
## is there a generator containing additional information?
if not generator:
## all content in one flush
yield cs.render()
yield cs_data.render()
else:
content_generate = generator()
dummy_line = """<!-- CONTENT_DUMMY -->"""
## now we do it linewise - checking for the content marker
for line in cs.render().splitlines():
for line in cs_data.render().splitlines():
if line.find(dummy_line) != -1:
yield line.replace(dummy_line, content_generate.next())
else:

View File

@ -24,6 +24,8 @@ super class of all web interface unittests for the cryptobox
just inherit this class and add some test functions
"""
__revision__ = "$Id"
import unittest
import twill
import cherrypy
@ -34,10 +36,12 @@ import os
## we do the following, for easy surfing
## e.g. use: cbx.go(your_url)
## commands api: http://twill.idyll.org/commands.html
CBXHOST="localhost"
CBXPORT=8081
CBX_URL="http://%s:%d/" % (CBXHOST, CBXPORT)
LOG_FILE="/tmp/twill.log"
CBXHOST = "localhost"
CBXPORT = 8081
CBX_URL = "http://%s:%d/" % (CBXHOST, CBXPORT)
LOG_FILE = "/tmp/cryptobox-twill.log"
WEBLOG_FILE = "/tmp/cryptobox-cherrypy.log"
class WebInterfaceTestClass(unittest.TestCase):
'''this class checks the webserver, using "twill"
@ -62,9 +66,12 @@ class WebInterfaceTestClass(unittest.TestCase):
'server.logToScreen' : False,
'autoreload.on': False,
'server.threadPool': 1,
'server.environment': 'production',
'server.environment': 'development',
'server.log_tracebacks': True,
'server.log_file': WEBLOG_FILE,
})
cherrypy.root = cryptobox.web.sites.WebInterfaceSites("cryptobox-unittests.conf")
cherrypy.root = cryptobox.web.sites.WebInterfaceSites(
"cryptobox-unittests.conf")
cherrypy.server.start(initOnly=True, serverClass=None)
from cherrypy._cpwsgi import wsgiApp
@ -74,19 +81,13 @@ class WebInterfaceTestClass(unittest.TestCase):
self.output = open(LOG_FILE,"a")
twill.set_output(self.output)
self.cmd = twill.commands
self.URL = CBX_URL
self.url = CBX_URL
self.cbox = cherrypy.root.cbox
self.globals, self.locals = twill.namespaces.get_twill_glocals()
## search for a usable block device
## use /dev/ubd? if possible - otherwise /dev/hd?
## so it will be possible to use these tests inside of an uml
for d in ["ubdb", "loop", "ubda", "udbc", "ubdd", "hdb", "hda", "hdc", "hdd"]:
if os.path.exists("/dev/%s1" % d):
device = d
break
else:
device = "hda"
self.device = device
self.blockdevice, self.device = self.__find_test_device()
@ -103,6 +104,34 @@ class WebInterfaceTestClass(unittest.TestCase):
browser = twill.commands.get_browser()
soup = BeautifulSoup(browser.get_html())
return soup
def __find_test_device(self):
"""Search for a valid test device - the data will get lost ...
"""
for dev in ["ubdb", "loop", "ubda", "udbc", "ubdd"]:
if os.path.exists("/dev/%s1" % dev) \
and not self.__is_config_partition("/dev/%s1" % dev):
return (dev, dev + "1")
if os.path.exists("/dev/%s2" % dev) \
and not self.__is_config_partition("/dev/%s2" % dev):
return (dev, dev + "2")
else:
raise Exception, "no valid device for testing found"
def __is_config_partition(self, device):
"""Check if the device is a configuration partition.
"""
import subprocess
proc = subprocess.Popen(
shell = False,
stdout = subprocess.PIPE,
stderr = subprocess.PIPE,
args = [ '/sbin/e2label',
device ])
(stdout, stderr) = proc.communicate()
return stdout.strip() == "cbox_config"
def register_auth(self, url, user="admin", password="admin"):

44
src/pylintrc Normal file
View File

@ -0,0 +1,44 @@
# this is a local configuration file for pylint to be used for checking the
# quality of the CryptoBox code
#
# just run:
# bin/do_pylint.sh cryptobox.core.main
# to check the module cryptobox.core.main
[MASTER]
# Add <file or directory> to the black list. It should be a base name, not a
# path. You may set this option multiple times.
ignore=CVS
ignore=.svn
[BASIC]
# Required attributes for module, separated by a comma
required-attributes=__revision__
# Good variable names which should always be accepted, separated by a comma
good-names=i,j,k,ex,Run,_
# Bad variable names which should always be refused, separated by a comma
bad-names=foo,bar,baz,toto,tutu,tata
[FORMAT]
# Maximum number of characters on a single line.
max-line-length=88
# Maximum number of lines in a module
max-module-lines=1000
# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
# tab).
indent-string='\t'
[MISCELLANEOUS]
# List of note tags to take in consideration, separated by a comma.
notes=FIXME,XXX,TODO