diff --git a/orchestra/apps/webapps/admin.py b/orchestra/apps/webapps/admin.py index c5370350..7e624fcf 100644 --- a/orchestra/apps/webapps/admin.py +++ b/orchestra/apps/webapps/admin.py @@ -7,6 +7,7 @@ from orchestra.admin import ExtendedModelAdmin from orchestra.admin.utils import change_url from orchestra.apps.accounts.admin import AccountAdminMixin +from . import settings from .models import WebApp, WebAppOption @@ -14,6 +15,11 @@ class WebAppOptionInline(admin.TabularInline): model = WebAppOption extra = 1 + OPTIONS_HELP_TEXT = str({ + k: str(unicode(v[1])) if len(v) == 3 else '' + for k, v in settings.WEBAPPS_OPTIONS.iteritems() + }) + class Media: css = { 'all': ('orchestra/css/hide-inline-id.css',) @@ -23,6 +29,17 @@ class WebAppOptionInline(admin.TabularInline): """ Make value input widget bigger """ if db_field.name == 'value': kwargs['widget'] = forms.TextInput(attrs={'size':'100'}) + if db_field.name == 'name': + kwargs['widget'] = forms.Select(attrs={ + 'onChange': """ + siteoptions = %s; + valueelement = $("#"+this.id.replace("name", "value")); + valueelement.parent().find('p').remove(); + valueelement.parent().append( + "

" + siteoptions[this.options[this.selectedIndex].value] + "

" + ); + """ % self.OPTIONS_HELP_TEXT, + }) return super(WebAppOptionInline, self).formfield_for_dbfield(db_field, **kwargs) diff --git a/orchestra/apps/webapps/models.py b/orchestra/apps/webapps/models.py index 6f113493..2f9280e8 100644 --- a/orchestra/apps/webapps/models.py +++ b/orchestra/apps/webapps/models.py @@ -78,7 +78,7 @@ class WebAppOption(models.Model): def clean(self): """ validates name and value according to WEBAPPS_OPTIONS """ - __, regex = settings.WEBAPPS_OPTIONS[self.name] + regex = settings.WEBAPPS_OPTIONS[self.name][-1] if not re.match(regex, self.value): raise ValidationError({ 'value': ValidationError(_("'%(value)s' does not match %(regex)s."), diff --git a/orchestra/apps/webapps/settings.py b/orchestra/apps/webapps/settings.py index 814fca94..fad8cbc4 100644 --- a/orchestra/apps/webapps/settings.py +++ b/orchestra/apps/webapps/settings.py @@ -77,153 +77,6 @@ WEBAPPS_DEFAULT_HTTPS_CERT = getattr(settings, 'WEBAPPS_DEFAULT_HTTPS_CERT', ) -WEBAPPS_OPTIONS = getattr(settings, 'WEBAPPS_OPTIONS', { - # { name: ( verbose_name, validation_regex ) } - # PHP - 'enabled_functions': ( - _("PHP - Enabled functions"), - r'^[\w\.,-]+$' - ), - 'PHP-allow_url_include': ( - _("PHP - Allow URL include"), - r'^(On|Off|on|off)$' - ), - 'PHP-allow_url_fopen': ( - _("PHP - allow_url_fopen"), - r'^(On|Off|on|off)$' - ), - 'PHP-auto_append_file': ( - _("PHP - Auto append file"), - r'^[\w\.,-/]+$' - ), - 'PHP-auto_prepend_file': ( - _("PHP - Auto prepend file"), - r'^[\w\.,-/]+$' - ), - 'PHP-date.timezone': ( - _("PHP - date.timezone"), - r'^\w+/\w+$' - ), - 'PHP-default_socket_timeout': ( - _("PHP - Default socket timeout"), - r'^[0-9]{1,3}$' - ), - 'PHP-display_errors': ( - _("PHP - Display errors"), - r'^(On|Off|on|off)$' - ), - 'PHP-extension': ( - _("PHP - Extension"), - r'^[^ ]+$' - ), - 'PHP-magic_quotes_gpc': ( - _("PHP - Magic quotes GPC"), - r'^(On|Off|on|off)$' - ), - 'PHP-magic_quotes_runtime': ( - _("PHP - Magic quotes runtime"), - r'^(On|Off|on|off)$' - ), - 'PHP-magic_quotes_sybase': ( - _("PHP - Magic quotes sybase"), - r'^(On|Off|on|off)$' - ), - 'PHP-max_execution_time': ( - _("PHP - Max execution time"), - r'^[0-9]{1,3}$' - ), - 'PHP-max_input_time': ( - _("PHP - Max input time"), - r'^[0-9]{1,3}$' - ), - 'PHP-memory_limit': ( - _("PHP - Memory limit"), - r'^[0-9]{1,3}M$' - ), - 'PHP-mysql.connect_timeout': ( - _("PHP - Mysql connect timeout"), - r'^([0-9]){1,3}$' - ), - 'PHP-output_buffering': ( - _("PHP - output_buffering"), - r'^(On|Off|on|off)$' - ), - 'PHP-register_globals': ( - _("PHP - Register globals"), - r'^(On|Off|on|off)$' - ), - 'PHP-post_max_size': ( - _("PHP - Post max size"), - r'^[0-9]{1,3}M$' - ), - 'PHP-sendmail_path': ( - _("PHP - sendmail_path"), - r'^[^ ]+$' - ), - 'PHP-session.bug_compat_warn': ( - _("PHP - session.bug_compat_warn"), - r'^(On|Off|on|off)$' - ), - 'PHP-session.auto_start': ( - _("PHP - session.auto_start"), - r'^(On|Off|on|off)$' - ), - 'PHP-safe_mode': ( - _("PHP - Safe mode"), - r'^(On|Off|on|off)$' - ), - 'PHP-suhosin.post.max_vars': ( - _("PHP - Suhosin post max vars"), - r'^[0-9]{1,4}$' - ), - 'PHP-suhosin.request.max_vars': ( - _("PHP - Suhosin request max vars"), - r'^[0-9]{1,4}$' - ), - 'PHP-suhosin.session.encrypt': ( - _("PHP - suhosin.session.encrypt"), - r'^(On|Off|on|off)$' - ), - 'PHP-suhosin.simulation': ( - _("PHP - Suhosin simulation"), - r'^(On|Off|on|off)$' - ), - 'PHP-suhosin.executor.include.whitelist': ( - _("PHP - suhosin.executor.include.whitelist"), - r'.*$' - ), - 'PHP-upload_max_filesize': ( - _("PHP - upload_max_filesize"), - r'^[0-9]{1,3}M$' - ), - 'PHP-zend_extension': ( - _("PHP - zend_extension"), - r'^[^ ]+$' - ), - # FCGID - 'FcgidIdleTimeout': ( - _("FCGI - Idle timeout"), - r'^[0-9]{1,3}$' - ), - 'FcgidBusyTimeout': ( - _("FCGI - Busy timeout"), - r'^[0-9]{1,3}$' - ), - 'FcgidConnectTimeout': ( - _("FCGI - Connection timeout"), - r'^[0-9]{1,3}$' - ), - 'FcgidIOTimeout': ( - _("FCGI - IO timeout"), - r'^[0-9]{1,3}$' - ), - 'FcgidProcessLifeTime': ( - _("FCGI - IO timeout"), - r'^[0-9]{1,4}$' - ), -}) - - WEBAPPS_PHP_DISABLED_FUNCTIONS = getattr(settings, 'WEBAPPS_PHP_DISABLED_FUNCTION', [ 'exec', 'passthru', @@ -246,3 +99,179 @@ WEBAPPS_PHP_DISABLED_FUNCTIONS = getattr(settings, 'WEBAPPS_PHP_DISABLED_FUNCTIO 'escapeshellarg', 'dl' ]) + + +WEBAPPS_OPTIONS = getattr(settings, 'WEBAPPS_OPTIONS', { + # { name: ( verbose_name, [help_text], validation_regex ) } + # PHP + 'enabled_functions': ( + _("PHP - Enabled functions"), + ' '.join(WEBAPPS_PHP_DISABLED_FUNCTIONS), + r'^[\w\.,-]+$' + ), + 'PHP-allow_url_include': ( + _("PHP - Allow URL include"), + _("On or Off"), + r'^(On|Off|on|off)$' + ), + 'PHP-allow_url_fopen': ( + _("PHP - allow_url_fopen"), + _("On or Off"), + r'^(On|Off|on|off)$' + ), + 'PHP-auto_append_file': ( + _("PHP - Auto append file"), + r'^[\w\.,-/]+$' + ), + 'PHP-auto_prepend_file': ( + _("PHP - Auto prepend file"), + r'^[\w\.,-/]+$' + ), + 'PHP-date.timezone': ( + _("PHP - date.timezone"), + _("Timezone string 'Europe/London'."), + r'^\w+/\w+$' + ), + 'PHP-default_socket_timeout': ( + _("PHP - Default socket timeout"), + _("Number between 0 and 999."), + r'^[0-9]{1,3}$' + ), + 'PHP-display_errors': ( + _("PHP - Display errors"), + _("On or Off"), + r'^(On|Off|on|off)$' + ), + 'PHP-extension': ( + _("PHP - Extension"), + r'^[^ ]+$' + ), + 'PHP-magic_quotes_gpc': ( + _("PHP - Magic quotes GPC"), + _("On or Off"), + r'^(On|Off|on|off)$' + ), + 'PHP-magic_quotes_runtime': ( + _("PHP - Magic quotes runtime"), + _("On or Off"), + r'^(On|Off|on|off)$' + ), + 'PHP-magic_quotes_sybase': ( + _("PHP - Magic quotes sybase"), + _("On or Off"), + r'^(On|Off|on|off)$' + ), + 'PHP-max_execution_time': ( + _("PHP - Max execution time"), + _("Number between 0 and 999."), + r'^[0-9]{1,3}$' + ), + 'PHP-max_input_time': ( + _("PHP - Max input time"), + _("Number between 0 and 999."), + r'^[0-9]{1,3}$' + ), + 'PHP-memory_limit': ( + _("PHP - Memory limit"), + _("Value between 0M and 999M."), + r'^[0-9]{1,3}M$' + ), + 'PHP-mysql.connect_timeout': ( + _("PHP - Mysql connect timeout"), + _("Number between 0 and 999."), + r'^([0-9]){1,3}$' + ), + 'PHP-output_buffering': ( + _("PHP - output_buffering"), + _("On or Off"), + r'^(On|Off|on|off)$' + ), + 'PHP-register_globals': ( + _("PHP - Register globals"), + _("On or Off"), + r'^(On|Off|on|off)$' + ), + 'PHP-post_max_size': ( + _("PHP - Post max size"), + _("Value between 0M and 999M."), + r'^[0-9]{1,3}M$' + ), + 'PHP-sendmail_path': ( + _("PHP - sendmail_path"), + r'^[^ ]+$' + ), + 'PHP-session.bug_compat_warn': ( + _("PHP - session.bug_compat_warn"), + _("On or Off"), + r'^(On|Off|on|off)$' + ), + 'PHP-session.auto_start': ( + _("PHP - session.auto_start"), + _("On or Off"), + r'^(On|Off|on|off)$' + ), + 'PHP-safe_mode': ( + _("PHP - Safe mode"), + _("On or Off"), + r'^(On|Off|on|off)$' + ), + 'PHP-suhosin.post.max_vars': ( + _("PHP - Suhosin post max vars"), + _("Number between 0 and 9999."), + r'^[0-9]{1,4}$' + ), + 'PHP-suhosin.request.max_vars': ( + _("PHP - Suhosin request max vars"), + _("Number between 0 and 9999."), + r'^[0-9]{1,4}$' + ), + 'PHP-suhosin.session.encrypt': ( + _("PHP - suhosin.session.encrypt"), + _("On or Off"), + r'^(On|Off|on|off)$' + ), + 'PHP-suhosin.simulation': ( + _("PHP - Suhosin simulation"), + _("On or Off"), + r'^(On|Off|on|off)$' + ), + 'PHP-suhosin.executor.include.whitelist': ( + _("PHP - suhosin.executor.include.whitelist"), + r'.*$' + ), + 'PHP-upload_max_filesize': ( + _("PHP - upload_max_filesize"), + _("Value between 0M and 999M."), + r'^[0-9]{1,3}M$' + ), + 'PHP-zend_extension': ( + _("PHP - zend_extension"), + r'^[^ ]+$' + ), + # FCGID + 'FcgidIdleTimeout': ( + _("FCGI - Idle timeout"), + _("Number between 0 and 999."), + r'^[0-9]{1,3}$' + ), + 'FcgidBusyTimeout': ( + _("FCGI - Busy timeout"), + _("Number between 0 and 999."), + r'^[0-9]{1,3}$' + ), + 'FcgidConnectTimeout': ( + _("FCGI - Connection timeout"), + _("Number of seconds between 0 and 999."), + r'^[0-9]{1,3}$' + ), + 'FcgidIOTimeout': ( + _("FCGI - IO timeout"), + _("Number of seconds between 0 and 999."), + r'^[0-9]{1,3}$' + ), + 'FcgidProcessLifeTime': ( + _("FCGI - IO timeout"), + _("Numbe of secondsr between 0 and 9999."), + r'^[0-9]{1,4}$' + ), +}) diff --git a/orchestra/apps/websites/admin.py b/orchestra/apps/websites/admin.py index a0083bdb..36b8ad54 100644 --- a/orchestra/apps/websites/admin.py +++ b/orchestra/apps/websites/admin.py @@ -6,6 +6,7 @@ from orchestra.admin import ExtendedModelAdmin from orchestra.admin.utils import admin_link, change_url from orchestra.apps.accounts.admin import AccountAdminMixin, SelectAccountAdminMixin +from . import settings from .models import Content, Website, WebsiteOption @@ -13,6 +14,11 @@ class WebsiteOptionInline(admin.TabularInline): model = WebsiteOption extra = 1 + OPTIONS_HELP_TEXT = str({ + k: str(unicode(v[1])) if len(v) == 3 else '' + for k, v in settings.WEBSITES_OPTIONS.iteritems() + }) + # class Media: # css = { # 'all': ('orchestra/css/hide-inline-id.css',) @@ -22,6 +28,19 @@ class WebsiteOptionInline(admin.TabularInline): """ Make value input widget bigger """ if db_field.name == 'value': kwargs['widget'] = forms.TextInput(attrs={'size':'100'}) + if db_field.name == 'name': + options = { + } + kwargs['widget'] = forms.Select(attrs={ + 'onChange': """ + siteoptions = %s; + valueelement = $("#"+this.id.replace("name", "value")); + valueelement.parent().find('p').remove(); + valueelement.parent().append( + "

" + siteoptions[this.options[this.selectedIndex].value] + "

" + ); + """ % self.OPTIONS_HELP_TEXT, + }) return super(WebsiteOptionInline, self).formfield_for_dbfield(db_field, **kwargs) @@ -56,6 +75,7 @@ class WebsiteAdmin(SelectAccountAdminMixin, ExtendedModelAdmin): ) filter_by_account_fields = ['domains'] prefetch_related = ('domains', 'content_set__webapp') + search_fields = ('name', 'account__username', 'domains__name') def display_domains(self, website): domains = [] diff --git a/orchestra/apps/websites/backends/apache.py b/orchestra/apps/websites/backends/apache.py index 5d898ddf..9422c9d2 100644 --- a/orchestra/apps/websites/backends/apache.py +++ b/orchestra/apps/websites/backends/apache.py @@ -168,6 +168,19 @@ class Apache2Backend(ServiceController): self.append("if [[ ! $DISABLED ]]; then a2dissite %(site_unique_name)s.conf;\n" "else UPDATED=0; fi" % context) + def get_username(self, site): + option = site.options.filter('user_group') + if option: + return option[0].split()[0] + return site.account.username + + def get_groupname(self, site): + option = site.options.filter('user_group') + if option and ' ' in option: + user, group = option.split() + return group + return site.account.username + def get_context(self, site): base_apache_conf = settings.WEBSITES_BASE_APACHE_CONF sites_available = os.path.join(base_apache_conf, 'sites-available') @@ -177,8 +190,8 @@ class Apache2Backend(ServiceController): 'site_name': site.name, 'ip': settings.WEBSITES_DEFAULT_IP, 'site_unique_name': site.unique_name, - 'user': site.get_username(), - 'group': site.get_groupname(), + 'user': self.get_username(site), + 'group': self.get_groupname(site), 'sites_enabled': sites_enabled, 'sites_available': "%s.conf" % os.path.join(sites_available, site.unique_name), 'logs': site.get_www_log_path(), diff --git a/orchestra/apps/websites/models.py b/orchestra/apps/websites/models.py index 32d5c946..29645152 100644 --- a/orchestra/apps/websites/models.py +++ b/orchestra/apps/websites/models.py @@ -50,12 +50,6 @@ class Website(models.Model): if domain: return '%s://%s' % (self.protocol, domain) - def get_username(self): - return self.account.username - - def get_groupname(self): - return self.get_username() - def get_www_log_path(self): context = { 'unique_name': self.unique_name @@ -80,7 +74,7 @@ class WebsiteOption(models.Model): def clean(self): """ validates name and value according to WEBSITES_WEBSITEOPTIONS """ - __, regex = settings.WEBSITES_OPTIONS[self.name] + regex = settings.WEBSITES_OPTIONS[self.name][-1] if not re.match(regex, self.value): raise ValidationError({ 'value': ValidationError(_("'%(value)s' does not match %(regex)s."), diff --git a/orchestra/apps/websites/settings.py b/orchestra/apps/websites/settings.py index 6d6cc1b8..fc7d0e7f 100644 --- a/orchestra/apps/websites/settings.py +++ b/orchestra/apps/websites/settings.py @@ -18,38 +18,47 @@ WEBSITES_DOMAIN_MODEL = getattr(settings, 'WEBSITES_DOMAIN_MODEL', 'domains.Doma WEBSITES_OPTIONS = getattr(settings, 'WEBSITES_OPTIONS', { - # { name: ( verbose_name, validation_regex ) } + # { name: ( verbose_name, [help_text], validation_regex ) } 'directory_protection': ( _("HTTPD - Directory protection"), - r'^([\w/_]+)\s+(\".*\")\s+([\w/_\.]+)$' + _("Space separated ..."), + r'^([\w/_]+)\s+(\".*\")\s+([\w/_\.]+)$', ), 'redirect': ( _("HTTPD - Redirection"), - r'^(permanent\s[^ ]+|[^ ]+)\s[^ ]+$' + _("[permanent] <website path> <destination URL>"), + r'^(permanent\s[^ ]+|[^ ]+)\s[^ ]+$', ), 'ssl_ca': ( _("HTTPD - SSL CA"), + _("Filesystem path of the CA certificate file."), r'^[^ ]+$' ), 'ssl_cert': ( _("HTTPD - SSL cert"), + _("Filesystem path of the certificate file."), r'^[^ ]+$' ), 'ssl_key': ( _("HTTPD - SSL key"), - r'^[^ ]+$' + _("Filesystem path of the key file."), + r'^[^ ]+$', ), 'sec_rule_remove': ( _("HTTPD - SecRuleRemoveById"), - r'^[0-9\s]+$' + _("Space separated ModSecurity rule IDs."), + r'^[0-9\s]+$', ), 'sec_engine': ( - _("HTTPD - Disable Modsecurity"), - r'^[\w/_]+$' + _("HTTPD - Modsecurity engine"), + _("On or Off, defaults to On"), + r'^(On|Off)$', ), 'user_group': ( _("HTTPD - SuexecUserGroup"), - r'^[\w/_]+\s[\w/_]+$' + _("Username and optional groupname (user [group])"), + # TODO validate existing user/group + r'^[\w/_]+(\s[\w/_]+)*$', ), })