diff --git a/orchestra/contrib/lists/backends.py b/orchestra/contrib/lists/backends.py index 92d2b73d..f0eeb937 100644 --- a/orchestra/contrib/lists/backends.py +++ b/orchestra/contrib/lists/backends.py @@ -285,7 +285,10 @@ class MailmanTraffic(ServiceMonitor): try: with open(postlog, 'r') as postlog: for line in postlog.readlines(): - month, day, time, year, __, __, __, list_name, __, addr, size = line.split()[:11] + line = line.split() + if len(line) < 11: + continue + month, day, time, year, __, __, __, list_name, __, addr, size = line[:11] try: list = lists[list_name] except KeyError: diff --git a/orchestra/contrib/payments/admin.py b/orchestra/contrib/payments/admin.py index 74041bb8..37fe7c7d 100644 --- a/orchestra/contrib/payments/admin.py +++ b/orchestra/contrib/payments/admin.py @@ -30,9 +30,10 @@ PROCESS_STATE_COLORS = { } -class PaymentSourceAdmin(SelectPluginAdminMixin, AccountAdminMixin, admin.ModelAdmin): +class PaymentSourceAdmin(SelectPluginAdminMixin, AccountAdminMixin, ExtendedModelAdmin): list_display = ('label', 'method', 'number', 'account_link', 'is_active') list_filter = ('method', 'is_active') + change_readonly_fields = ('method',) search_fields = ('account__username', 'account__full_name', 'data') plugin = PaymentMethod plugin_field = 'method' diff --git a/orchestra/contrib/saas/backends/phplist.py b/orchestra/contrib/saas/backends/phplist.py index 8f4c99ea..a17bc08b 100644 --- a/orchestra/contrib/saas/backends/phplist.py +++ b/orchestra/contrib/saas/backends/phplist.py @@ -77,7 +77,7 @@ class PhpListSaaSController(ServiceController): UPDATE phplist_user_user SET password="%(digest)s" where ID=1;' \\ %(db_name)s""") % context sys.stdout.write('cmd: %s\n' % cmd) - sshrun(server.get_address(), cmd) + sshrun(server.get_address(), cmd, persist=True) def save(self, saas): if hasattr(saas, 'password'): diff --git a/orchestra/contrib/webapps/models.py b/orchestra/contrib/webapps/models.py index 91d7fe88..48e21511 100644 --- a/orchestra/contrib/webapps/models.py +++ b/orchestra/contrib/webapps/models.py @@ -70,12 +70,15 @@ class WebApp(models.Model): def get_directive(self): return self.type_instance.get_directive() - def get_path(self): + def get_base_path(self): context = { 'home': self.get_user().get_home(), 'app_name': self.name, } - path = settings.WEBAPPS_BASE_DIR % context + return settings.WEBAPPS_BASE_DIR % context + + def get_path(self): + path = self.get_base_path() public_root = self.options.filter(name='public-root').first() if public_root: path = os.path.join(path, public_root.value) diff --git a/orchestra/contrib/webapps/options.py b/orchestra/contrib/webapps/options.py index b5e3f0a9..79bd2802 100644 --- a/orchestra/contrib/webapps/options.py +++ b/orchestra/contrib/webapps/options.py @@ -1,3 +1,4 @@ +import os import re from functools import lru_cache @@ -58,7 +59,7 @@ class PHPAppOption(AppOption): abstract = True def validate(self): - super(PHPAppOption, self).validate() + super().validate() if self.deprecated: php_version = self.instance.webapp.type_instance.get_php_version_number() if php_version and self.deprecated and float(php_version) > self.deprecated: @@ -73,6 +74,15 @@ class PublicRoot(AppOption): help_text = _("Document root relative to webapps/<webapp>/") regex = r'[^ ]+' group = AppOption.FILESYSTEM + + def validate(self): + super().validate() + base_path = self.instance.webapp.get_base_path() + path = os.path.join(base_path, self.instance.value) + if not os.path.abspath(path).startswith(base_path): + raise ValidationError( + _("Public root path '%s' outside of webapp base path '%s'") % (path, base_path) + ) class Timeout(AppOption): diff --git a/orchestra/utils/sys.py b/orchestra/utils/sys.py index 84ab5b1e..07b0fcb4 100644 --- a/orchestra/utils/sys.py +++ b/orchestra/utils/sys.py @@ -160,13 +160,13 @@ def run(command, display=False, valid_codes=(0,), silent=False, stdin=b'', async def sshrun(addr, command, *args, executable='bash', persist=False, options=None, **kwargs): + from .. import settings base_options = { 'stricthostkeychecking': 'no', 'BatchMode': 'yes', 'EscapeChar': 'none', } if persist: - from .. import settings base_options.update({ 'ControlMaster': 'auto', 'ControlPersist': 'yes',