Improved webapps and saas validation

This commit is contained in:
Marc Aymerich 2015-03-25 17:04:44 +00:00
parent dd84217320
commit c55cff9a37
8 changed files with 95 additions and 116 deletions

21
TODO.md
View File

@ -195,23 +195,20 @@ Php binaries should have this format: /usr/bin/php5.2-cgi
* Orchestra global search box on the header, based https://github.com/django/django/blob/master/django/contrib/admin/options.py#L866 and iterating over all registered services and inspectin its admin.search_fields * Orchestra global search box on the header, based https://github.com/django/django/blob/master/django/contrib/admin/options.py#L866 and iterating over all registered services and inspectin its admin.search_fields
* contain error on plugin missing key (plugin dissabled): NOP, fail hard is better than silently, perhaps fail at starttime? apploading * contain error on plugin missing key (plugin dissabled): NOP, fail hard is better than silently, perhaps fail at starttime? apploading machinary
* contact.alternative_phone on a phone.tooltip, email:to * contact.alternative_phone on a phone.tooltip, email:to
* better validate options and directives (url locations, filesystem paths, etc..) * better validate options and directives (url locations, filesystem paths, etc..)
* filter php deprecated options out based on version
* make sure that you understand the risks * make sure that you understand the risks
* full support for deactivation of services/accounts * full support for deactivation of services/accounts
* Display admin.is_active (disabled account/order by) * Display admin.is_active (disabled account special icon and order by support)
* lock resource monitoring * lock resource monitoring
* -EXecCGI in common CMS upload locations /wp-upload/upload/uploads * -EXecCGI in common CMS upload locations /wp-upload/upload/uploads
* cgi user / pervent shell access * cgi user / pervent shell access
@ -219,14 +216,6 @@ Php binaries should have this format: /usr/bin/php5.2-cgi
* disable anonymized list options (mailman) * disable anonymized list options (mailman)
* webapps directory protection and disable excecgi
* php-fpm disable execCGI
* SuexecUserGroup needs to be per app othewise wrapper/fpm user can't be correct
* wprdess-mu saas app that create a Website object????
* tags = GenericRelation(TaggedItem, related_query_name='bookmarks') * tags = GenericRelation(TaggedItem, related_query_name='bookmarks')
* make home for all systemusers (/home/username) and fix monitors * make home for all systemusers (/home/username) and fix monitors
@ -243,15 +232,11 @@ require_once(/etc/moodles/.$moodle_host.config.php);``` moodle/drupl
* normurlpath '' return '/' * normurlpath '' return '/'
* rename webapps.type to something more generic
* initial configuration of multisite sas apps with password stored in DATA * initial configuration of multisite sas apps with password stored in DATA
* webapps installation complete, passowrd protected * webapps installation complete, passowrd protected
* saas.initial_password autogenerated (ok because its random and not user provided) vs saas.password /change_Form provided + send email with initial_password * saas.initial_password autogenerated (ok because its random and not user provided) vs saas.password /change_Form provided + send email with initial_password
* disable saas apps
* more robust backend error handling, continue executing but exit code > 0 if failure, replace exit_code=0; do_sometging || exit_code=1 * more robust backend error handling, continue executing but exit code > 0 if failure, replace exit_code=0; do_sometging || exit_code=1
* saas require unique emails? connect to backend server to find out because they change * saas require unique emails? connect to backend server to find out because they change
@ -260,6 +245,4 @@ require_once(/etc/moodles/.$moodle_host.config.php);``` moodle/drupl
* website directives uniquenes validation on serializers * website directives uniquenes validation on serializers
* gitlab store id, username changes

View File

@ -11,8 +11,8 @@ from .services import SoftwareService
class SaaSAdmin(SelectPluginAdminMixin, AccountAdminMixin, ExtendedModelAdmin): class SaaSAdmin(SelectPluginAdminMixin, AccountAdminMixin, ExtendedModelAdmin):
list_display = ('name', 'service', 'display_site_domain', 'account_link') list_display = ('name', 'service', 'display_site_domain', 'account_link', 'is_active')
list_filter = ('service',) list_filter = ('service', 'is_active')
change_readonly_fields = ('service',) change_readonly_fields = ('service',)
plugin = SoftwareService plugin = SoftwareService
plugin_field = 'service' plugin_field = 'service'

View File

@ -22,9 +22,10 @@ class GitLabSaaSBackend(ServiceController):
user_id = saas.data['user_id'] user_id = saas.data['user_id']
return self.get_base_url() + '/users/%i' % user_id return self.get_base_url() + '/users/%i' % user_id
def validate_response(self, response, status_codes): def validate_response(self, response, *status_codes):
if response.status_code not in status_codes: if response.status_code not in status_codes:
raise RuntimeError("[%i] %s" % (response.status_code, response.content)) raise RuntimeError("[%i] %s" % (response.status_code, response.content))
return json.loads(response.content)
def authenticate(self): def authenticate(self):
login_url = self.get_base_url() + '/session' login_url = self.get_base_url() + '/session'
@ -33,8 +34,8 @@ class GitLabSaaSBackend(ServiceController):
'password': settings.SAAS_GITLAB_ROOT_PASSWORD, 'password': settings.SAAS_GITLAB_ROOT_PASSWORD,
} }
response = requests.post(login_url, data=data) response = requests.post(login_url, data=data)
self.validate_response(response, [201]) session = self.validate_response(response, 201)
token = json.loads(response.content)['private_token'] token = session['private_token']
self.headers = { self.headers = {
'PRIVATE-TOKEN': token, 'PRIVATE-TOKEN': token,
} }
@ -49,9 +50,7 @@ class GitLabSaaSBackend(ServiceController):
'name': saas.account.get_full_name(), 'name': saas.account.get_full_name(),
} }
response = requests.post(user_url, data=data, headers=self.headers) response = requests.post(user_url, data=data, headers=self.headers)
self.validate_response(response, [201]) user = self.validate_response(response, 201)
print response.content
user = json.loads(response.content)
saas.data['user_id'] = user['id'] saas.data['user_id'] = user['id']
# Using queryset update to avoid triggering backends with the post_save signal # Using queryset update to avoid triggering backends with the post_save signal
type(saas).objects.filter(pk=saas.pk).update(data=saas.data) type(saas).objects.filter(pk=saas.pk).update(data=saas.data)
@ -60,19 +59,32 @@ class GitLabSaaSBackend(ServiceController):
def change_password(self, saas, server): def change_password(self, saas, server):
self.authenticate() self.authenticate()
user_url = self.get_user_url(saas) user_url = self.get_user_url(saas)
data = { response = requests.get(user_url, headers=self.headers)
'password': saas.password, user = self.validate_response(response, 200)
} user = json.loads(response.content)
response = requests.patch(user_url, data=data, headers=self.headers) user['password'] = saas.password
self.validate_response(response, [200]) response = requests.put(user_url, data=user, headers=self.headers)
print json.dumps(json.loads(response.content), indent=4) user = self.validate_response(response, 200)
print json.dumps(user, indent=4)
def set_state(self, saas, server):
# TODO http://feedback.gitlab.com/forums/176466-general/suggestions/4098632-add-administrative-api-call-to-block-users
return
self.authenticate()
user_url = self.get_user_url(saas)
response = requests.get(user_url, headers=self.headers)
user = self.validate_response(response, 200)
user['state'] = 'active' if saas.active else 'blocked',
response = requests.patch(user_url, data=user, headers=self.headers)
user = self.validate_response(response, 200)
print json.dumps(user, indent=4)
def delete_user(self, saas, server): def delete_user(self, saas, server):
self.authenticate() self.authenticate()
user_url = self.get_user_url(saas) user_url = self.get_user_url(saas)
response = requests.delete(user_url, headers=self.headers) response = requests.delete(user_url, headers=self.headers)
self.validate_response(response, [200, 404]) user = self.validate_response(response, 200, 404)
print json.dumps(json.loads(response.content), indent=4) print json.dumps(user, indent=4)
def _validate_creation(self, saas, server): def _validate_creation(self, saas, server):
""" checks if a saas object is valid for creation on the server side """ """ checks if a saas object is valid for creation on the server side """
@ -96,6 +108,7 @@ class GitLabSaaSBackend(ServiceController):
self.append(self.change_password, saas) self.append(self.change_password, saas)
else: else:
self.append(self.create_user, saas) self.append(self.create_user, saas)
self.append(self.set_state, saas)
def delete(self, saas): def delete(self, saas):
self.append(self.delete_user, saas) self.append(self.delete_user, saas)

View File

@ -19,6 +19,8 @@ class SaaS(models.Model):
validators=[validators.validate_username]) validators=[validators.validate_username])
account = models.ForeignKey('accounts.Account', verbose_name=_("account"), account = models.ForeignKey('accounts.Account', verbose_name=_("account"),
related_name='saas') related_name='saas')
is_active = models.BooleanField(_("active"), default=True,
help_text=_("Designates whether this service should be treated as active. "))
data = JSONField(_("data"), default={}, data = JSONField(_("data"), default={},
help_text=_("Extra information dependent of each service.")) help_text=_("Extra information dependent of each service."))
@ -41,6 +43,10 @@ class SaaS(models.Model):
""" Per request lived service_instance """ """ Per request lived service_instance """
return self.service_class(self) return self.service_class(self)
@cached_property
def active(self):
return self.is_active and self.account.is_active
def clean(self): def clean(self):
self.data = self.service_instance.clean_data() self.data = self.service_instance.clean_data()

View File

@ -48,6 +48,20 @@ class AppOption(Plugin):
}) })
class PHPAppOption(AppOption):
deprecated = None
group = AppOption.PHP
def validate(self):
super(PHPAppOption, self).validate()
if self.deprecated:
php_version = self.instance.webapp.type_instance.get_php_version()
if php_version and php_version > self.deprecated:
raise ValidationError(
_("This option is deprecated since PHP version %s.") % str(self.deprecated)
)
class PublicRoot(AppOption): class PublicRoot(AppOption):
name = 'public-root' name = 'public-root'
verbose_name = _("Public root") verbose_name = _("Public root")
@ -77,193 +91,171 @@ class Processes(AppOption):
group = AppOption.PROCESS group = AppOption.PROCESS
class PHPEnabledFunctions(AppOption): class PHPEnabledFunctions(PHPAppOption):
name = 'enabled_functions' name = 'enabled_functions'
verbose_name = _("Enabled functions") verbose_name = _("Enabled functions")
help_text = ' '.join(settings.WEBAPPS_PHP_DISABLED_FUNCTIONS) help_text = ' '.join(settings.WEBAPPS_PHP_DISABLED_FUNCTIONS)
regex = r'^[\w\.,-]+$' regex = r'^[\w\.,-]+$'
group = AppOption.PHP
class PHPAllowURLInclude(AppOption): class PHPAllowURLInclude(PHPAppOption):
name = 'allow_url_include' name = 'allow_url_include'
verbose_name = _("Allow URL include") verbose_name = _("Allow URL include")
help_text = _("Allows the use of URL-aware fopen wrappers with include, include_once, require, " help_text = _("Allows the use of URL-aware fopen wrappers with include, include_once, require, "
"require_once (On or Off).") "require_once (On or Off).")
regex = r'^(On|Off|on|off)$' regex = r'^(On|Off|on|off)$'
group = AppOption.PHP
class PHPAllowURLFopen(AppOption): class PHPAllowURLFopen(PHPAppOption):
name = 'allow_url_fopen' name = 'allow_url_fopen'
verbose_name = _("Allow URL fopen") verbose_name = _("Allow URL fopen")
help_text = _("Enables the URL-aware fopen wrappers that enable accessing URL object like files (On or Off).") help_text = _("Enables the URL-aware fopen wrappers that enable accessing URL object like files (On or Off).")
regex = r'^(On|Off|on|off)$' regex = r'^(On|Off|on|off)$'
group = AppOption.PHP
class PHPAutoAppendFile(AppOption): class PHPAutoAppendFile(PHPAppOption):
name = 'auto_append_file' name = 'auto_append_file'
verbose_name = _("Auto append file") verbose_name = _("Auto append file")
help_text = _("Specifies the name of a file that is automatically parsed after the main file.") help_text = _("Specifies the name of a file that is automatically parsed after the main file.")
regex = r'^[\w\.,-/]+$' regex = r'^[\w\.,-/]+$'
group = AppOption.PHP
class PHPAutoPrependFile(AppOption): class PHPAutoPrependFile(PHPAppOption):
name = 'auto_prepend_file' name = 'auto_prepend_file'
verbose_name = _("Auto prepend file") verbose_name = _("Auto prepend file")
help_text = _("Specifies the name of a file that is automatically parsed before the main file.") help_text = _("Specifies the name of a file that is automatically parsed before the main file.")
regex = r'^[\w\.,-/]+$' regex = r'^[\w\.,-/]+$'
group = AppOption.PHP
class PHPDateTimeZone(AppOption): class PHPDateTimeZone(PHPAppOption):
name = 'date.timezone' name = 'date.timezone'
verbose_name = _("date.timezone") verbose_name = _("date.timezone")
help_text = _("Sets the default timezone used by all date/time functions (Timezone string 'Europe/London').") help_text = _("Sets the default timezone used by all date/time functions (Timezone string 'Europe/London').")
regex = r'^\w+/\w+$' regex = r'^\w+/\w+$'
group = AppOption.PHP
class PHPDefaultSocketTimeout(AppOption): class PHPDefaultSocketTimeout(PHPAppOption):
name = 'default_socket_timeout' name = 'default_socket_timeout'
verbose_name = _("Default socket timeout") verbose_name = _("Default socket timeout")
help_text = _("Number between 0 and 999.") help_text = _("Number between 0 and 999.")
regex = r'^[0-9]{1,3}$' regex = r'^[0-9]{1,3}$'
group = AppOption.PHP
class PHPDisplayErrors(AppOption): class PHPDisplayErrors(PHPAppOption):
name = 'display_errors' name = 'display_errors'
verbose_name = _("Display errors") verbose_name = _("Display errors")
help_text = _("Determines whether errors should be printed to the screen as part of the output or " help_text = _("Determines whether errors should be printed to the screen as part of the output or "
"if they should be hidden from the user (On or Off).") "if they should be hidden from the user (On or Off).")
regex = r'^(On|Off|on|off)$' regex = r'^(On|Off|on|off)$'
group = AppOption.PHP
class PHPExtension(AppOption): class PHPExtension(PHPAppOption):
name = 'extension' name = 'extension'
verbose_name = _("Extension") verbose_name = _("Extension")
regex = r'^[^ ]+$' regex = r'^[^ ]+$'
group = AppOption.PHP
class PHPMagicQuotesGPC(AppOption): class PHPMagicQuotesGPC(PHPAppOption):
name = 'magic_quotes_gpc' name = 'magic_quotes_gpc'
verbose_name = _("Magic quotes GPC") verbose_name = _("Magic quotes GPC")
help_text = _("Sets the magic_quotes state for GPC (Get/Post/Cookie) operations (On or Off) " help_text = _("Sets the magic_quotes state for GPC (Get/Post/Cookie) operations (On or Off) "
"<b>DEPRECATED as of PHP 5.3.0</b>.") "<b>DEPRECATED as of PHP 5.3.0</b>.")
regex = r'^(On|Off|on|off)$' regex = r'^(On|Off|on|off)$'
deprecated = 5.3 deprecated = 5.3
group = AppOption.PHP
class PHPMagicQuotesRuntime(AppOption): class PHPMagicQuotesRuntime(PHPAppOption):
name = 'magic_quotes_runtime' name = 'magic_quotes_runtime'
verbose_name = _("Magic quotes runtime") verbose_name = _("Magic quotes runtime")
help_text = _("Functions that return data from any sort of external source will have quotes escaped " help_text = _("Functions that return data from any sort of external source will have quotes escaped "
"with a backslash (On or Off) <b>DEPRECATED as of PHP 5.3.0</b>.") "with a backslash (On or Off) <b>DEPRECATED as of PHP 5.3.0</b>.")
regex = r'^(On|Off|on|off)$' regex = r'^(On|Off|on|off)$'
deprecated = 5.3 deprecated = 5.3
group = AppOption.PHP
class PHPMaginQuotesSybase(AppOption): class PHPMaginQuotesSybase(PHPAppOption):
name = 'magic_quotes_sybase' name = 'magic_quotes_sybase'
verbose_name = _("Magic quotes sybase") verbose_name = _("Magic quotes sybase")
help_text = _("Single-quote is escaped with a single-quote instead of a backslash (On or Off).") help_text = _("Single-quote is escaped with a single-quote instead of a backslash (On or Off).")
regex = r'^(On|Off|on|off)$' regex = r'^(On|Off|on|off)$'
group = AppOption.PHP
class PHPMaxExecutonTime(AppOption): class PHPMaxExecutonTime(PHPAppOption):
name = 'max_execution_time' name = 'max_execution_time'
verbose_name = _("Max execution time") verbose_name = _("Max execution time")
help_text = _("Maximum time in seconds a script is allowed to run before it is terminated by " help_text = _("Maximum time in seconds a script is allowed to run before it is terminated by "
"the parser (Integer between 0 and 999).") "the parser (Integer between 0 and 999).")
regex = r'^[0-9]{1,3}$' regex = r'^[0-9]{1,3}$'
group = AppOption.PHP
class PHPMaxInputTime(AppOption): class PHPMaxInputTime(PHPAppOption):
name = 'max_input_time' name = 'max_input_time'
verbose_name = _("Max input time") verbose_name = _("Max input time")
help_text = _("Maximum time in seconds a script is allowed to parse input data, like POST and GET " help_text = _("Maximum time in seconds a script is allowed to parse input data, like POST and GET "
"(Integer between 0 and 999).") "(Integer between 0 and 999).")
regex = r'^[0-9]{1,3}$' regex = r'^[0-9]{1,3}$'
group = AppOption.PHP
class PHPMaxInputVars(AppOption): class PHPMaxInputVars(PHPAppOption):
name = 'max_input_vars' name = 'max_input_vars'
verbose_name = _("Max input vars") verbose_name = _("Max input vars")
help_text = _("How many input variables may be accepted (limit is applied to $_GET, $_POST " help_text = _("How many input variables may be accepted (limit is applied to $_GET, $_POST "
"and $_COOKIE superglobal separately) (Integer between 0 and 9999).") "and $_COOKIE superglobal separately) (Integer between 0 and 9999).")
regex = r'^[0-9]{1,4}$' regex = r'^[0-9]{1,4}$'
group = AppOption.PHP
class PHPMemoryLimit(AppOption): class PHPMemoryLimit(PHPAppOption):
name = 'memory_limit' name = 'memory_limit'
verbose_name = _("Memory limit") verbose_name = _("Memory limit")
help_text = _("This sets the maximum amount of memory in bytes that a script is allowed to allocate " help_text = _("This sets the maximum amount of memory in bytes that a script is allowed to allocate "
"(Value between 0M and 999M).") "(Value between 0M and 999M).")
regex = r'^[0-9]{1,3}M$' regex = r'^[0-9]{1,3}M$'
group = AppOption.PHP
class PHPMySQLConnectTimeout(AppOption): class PHPMySQLConnectTimeout(PHPAppOption):
name = 'mysql.connect_timeout' name = 'mysql.connect_timeout'
verbose_name = _("Mysql connect timeout") verbose_name = _("Mysql connect timeout")
help_text = _("Number between 0 and 999.") help_text = _("Number between 0 and 999.")
regex = r'^([0-9]){1,3}$' regex = r'^([0-9]){1,3}$'
group = AppOption.PHP
class PHPOutputBuffering(AppOption): class PHPOutputBuffering(PHPAppOption):
name = 'output_buffering' name = 'output_buffering'
verbose_name = _("Output buffering") verbose_name = _("Output buffering")
help_text = _("Turn on output buffering (On or Off).") help_text = _("Turn on output buffering (On or Off).")
regex = r'^(On|Off|on|off)$' regex = r'^(On|Off|on|off)$'
group = AppOption.PHP
class PHPRegisterGlobals(AppOption): class PHPRegisterGlobals(PHPAppOption):
name = 'register_globals' name = 'register_globals'
verbose_name = _("Register globals") verbose_name = _("Register globals")
help_text = _("Whether or not to register the EGPCS (Environment, GET, POST, Cookie, Server) " help_text = _("Whether or not to register the EGPCS (Environment, GET, POST, Cookie, Server) "
"variables as global variables (On or Off).") "variables as global variables (On or Off).")
regex = r'^(On|Off|on|off)$' regex = r'^(On|Off|on|off)$'
group = AppOption.PHP
class PHPPostMaxSize(AppOption): class PHPPostMaxSize(PHPAppOption):
name = 'post_max_size' name = 'post_max_size'
verbose_name = _("Post max size") verbose_name = _("Post max size")
help_text = _("Sets max size of post data allowed (Value between 0M and 999M).") help_text = _("Sets max size of post data allowed (Value between 0M and 999M).")
regex = r'^[0-9]{1,3}M$' regex = r'^[0-9]{1,3}M$'
group = AppOption.PHP
class PHPSendmailPath(AppOption): class PHPSendmailPath(PHPAppOption):
name = 'sendmail_path' name = 'sendmail_path'
verbose_name = _("sendmail_path") verbose_name = _("sendmail_path")
help_text = _("Where the sendmail program can be found.") help_text = _("Where the sendmail program can be found.")
regex = r'^[^ ]+$' regex = r'^[^ ]+$'
group = AppOption.PHP
class PHPSessionBugCompatWarn(AppOption): class PHPSessionBugCompatWarn(PHPAppOption):
name = 'session.bug_compat_warn' name = 'session.bug_compat_warn'
verbose_name = _("session.bug_compat_warn") verbose_name = _("session.bug_compat_warn")
help_text = _("Enables an PHP bug on session initialization for legacy behaviour (On or Off).") help_text = _("Enables an PHP bug on session initialization for legacy behaviour (On or Off).")
regex = r'^(On|Off|on|off)$' regex = r'^(On|Off|on|off)$'
group = AppOption.PHP
class PHPSessionAutoStart(AppOption): class PHPSessionAutoStart(PHPAppOption):
name = 'session.auto_start' name = 'session.auto_start'
verbose_name = _("session.auto_start") verbose_name = _("session.auto_start")
help_text = _("Specifies whether the session module starts a session automatically on request " help_text = _("Specifies whether the session module starts a session automatically on request "
@ -272,72 +264,63 @@ class PHPSessionAutoStart(AppOption):
group = AppOption.PHP group = AppOption.PHP
class PHPSafeMode(AppOption): class PHPSafeMode(PHPAppOption):
name = 'safe_mode' name = 'safe_mode'
verbose_name = _("Safe mode") verbose_name = _("Safe mode")
help_text = _("Whether to enable PHP's safe mode (On or Off) <b>DEPRECATED as of PHP 5.3.0</b>") help_text = _("Whether to enable PHP's safe mode (On or Off) <b>DEPRECATED as of PHP 5.3.0</b>")
regex = r'^(On|Off|on|off)$' regex = r'^(On|Off|on|off)$'
deprecated=5.3 deprecated=5.3
group = AppOption.PHP
class PHPSuhosinPostMaxVars(AppOption): class PHPSuhosinPostMaxVars(PHPAppOption):
name = 'suhosin.post.max_vars' name = 'suhosin.post.max_vars'
verbose_name = _("Suhosin POST max vars") verbose_name = _("Suhosin POST max vars")
help_text = _("Number between 0 and 9999.") help_text = _("Number between 0 and 9999.")
regex = r'^[0-9]{1,4}$' regex = r'^[0-9]{1,4}$'
group = AppOption.PHP
class PHPSuhosinGetMaxVars(AppOption): class PHPSuhosinGetMaxVars(PHPAppOption):
name = 'suhosin.get.max_vars' name = 'suhosin.get.max_vars'
verbose_name = _("Suhosin GET max vars") verbose_name = _("Suhosin GET max vars")
help_text = _("Number between 0 and 9999.") help_text = _("Number between 0 and 9999.")
regex = r'^[0-9]{1,4}$' regex = r'^[0-9]{1,4}$'
group = AppOption.PHP
class PHPSuhosinRequestMaxVars(AppOption): class PHPSuhosinRequestMaxVars(PHPAppOption):
name = 'suhosin.request.max_vars' name = 'suhosin.request.max_vars'
verbose_name = _("Suhosin request max vars") verbose_name = _("Suhosin request max vars")
help_text = _("Number between 0 and 9999.") help_text = _("Number between 0 and 9999.")
regex = r'^[0-9]{1,4}$' regex = r'^[0-9]{1,4}$'
group = AppOption.PHP
class PHPSuhosinSessionEncrypt(AppOption): class PHPSuhosinSessionEncrypt(PHPAppOption):
name = 'suhosin.session.encrypt' name = 'suhosin.session.encrypt'
verbose_name = _("suhosin.session.encrypt") verbose_name = _("suhosin.session.encrypt")
help_text = _("On or Off") help_text = _("On or Off")
regex = r'^(On|Off|on|off)$' regex = r'^(On|Off|on|off)$'
group = AppOption.PHP
class PHPSuhosinSimulation(AppOption): class PHPSuhosinSimulation(PHPAppOption):
name = 'suhosin.simulation' name = 'suhosin.simulation'
verbose_name = _("Suhosin simulation") verbose_name = _("Suhosin simulation")
help_text = _("On or Off") help_text = _("On or Off")
regex = r'^(On|Off|on|off)$' regex = r'^(On|Off|on|off)$'
group = AppOption.PHP
class PHPSuhosinExecutorIncludeWhitelist(AppOption): class PHPSuhosinExecutorIncludeWhitelist(PHPAppOption):
name = 'suhosin.executor.include.whitelist' name = 'suhosin.executor.include.whitelist'
verbose_name = _("suhosin.executor.include.whitelist") verbose_name = _("suhosin.executor.include.whitelist")
regex = r'.*$' regex = r'.*$'
group = AppOption.PHP
class PHPUploadMaxFileSize(AppOption): class PHPUploadMaxFileSize(PHPAppOption):
name = 'upload_max_filesize' name = 'upload_max_filesize'
verbose_name = _("upload_max_filesize") verbose_name = _("upload_max_filesize")
help_text = _("Value between 0M and 999M.") help_text = _("Value between 0M and 999M.")
regex = r'^[0-9]{1,3}M$' regex = r'^[0-9]{1,3}M$'
group = AppOption.PHP
class PHPZendExtension(AppOption): class PHPZendExtension(PHPAppOption):
name = 'zend_extension' name = 'zend_extension'
verbose_name = _("Zend extension") verbose_name = _("Zend extension")
regex = r'^[^ ]+$' regex = r'^[^ ]+$'
group = AppOption.PHP

View File

@ -36,14 +36,6 @@ class AppType(plugins.Plugin):
'name': _("A WordPress blog with this name already exists."), 'name': _("A WordPress blog with this name already exists."),
}) })
@classmethod
@cached
def get_php_options(cls):
# TODO validate php options once a php version has been selected (deprecated directives)
php_version = getattr(cls, 'php_version', 1)
php_options = AppOption.get_option_groups()[AppOption.PHP]
return [op for op in php_options if getattr(cls, 'deprecated', 99) > php_version]
@classmethod @classmethod
@cached @cached
def get_options(cls): def get_options(cls):
@ -52,8 +44,6 @@ class AppType(plugins.Plugin):
options = [] options = []
for group in cls.option_groups: for group in cls.option_groups:
group_options = groups[group] group_options = groups[group]
if group == AppOption.PHP:
group_options = cls.get_php_options()
if group is None: if group is None:
options.insert(0, (group, group_options)) options.insert(0, (group, group_options))
else: else:

View File

@ -7,8 +7,10 @@ from rest_framework import serializers
from orchestra.forms import widgets from orchestra.forms import widgets
from orchestra.plugins.forms import PluginDataForm from orchestra.plugins.forms import PluginDataForm
from orchestra.utils.functional import cached
from .. import settings from .. import settings
from ..options import AppOption
from . import AppType from . import AppType
@ -57,6 +59,12 @@ class PHPApp(AppType):
def get_detail(self): def get_detail(self):
return self.instance.data.get('php_version', '') return self.instance.data.get('php_version', '')
@cached
def get_php_options(self):
php_version = self.get_php_version()
php_options = AppOption.get_option_groups()[AppOption.PHP]
return [op for op in php_options if getattr(self, 'deprecated', 999) > php_version]
def get_php_init_vars(self, merge=False): def get_php_init_vars(self, merge=False):
""" """
process php options for inclusion on php.ini process php options for inclusion on php.ini
@ -72,7 +80,7 @@ class PHPApp(AppType):
for webapp in webapps: for webapp in webapps:
if webapp.type_instance.get_php_version == php_version: if webapp.type_instance.get_php_version == php_version:
options += list(webapp.options.all()) options += list(webapp.options.all())
php_options = [option.name for option in type(self).get_php_options()] php_options = [option.name for option in self.get_php_options()]
enabled_functions = set() enabled_functions = set()
for opt in options: for opt in options:
if opt.name in php_options: if opt.name in php_options:

View File

@ -10,7 +10,6 @@ from orchestra.utils.python import import_class
from . import settings from . import settings
# TODO multiple and unique validation support in the formset
class SiteDirective(Plugin): class SiteDirective(Plugin):
HTTPD = 'HTTPD' HTTPD = 'HTTPD'
SEC = 'ModSecurity' SEC = 'ModSecurity'
@ -141,7 +140,6 @@ class WordPressSaaS(SiteDirective):
name = 'wordpress-saas' name = 'wordpress-saas'
verbose_name = "WordPress SaaS" verbose_name = "WordPress SaaS"
help_text = _("URL path for mounting wordpress multisite.") help_text = _("URL path for mounting wordpress multisite.")
# fpm_listen = settings.WEBAPPS_WORDPRESSMU_LISTEN
group = SiteDirective.SAAS group = SiteDirective.SAAS
regex = r'^/[^ ]*$' regex = r'^/[^ ]*$'
unique_value = True unique_value = True
@ -151,7 +149,6 @@ class DokuWikiSaaS(SiteDirective):
name = 'dokuwiki-saas' name = 'dokuwiki-saas'
verbose_name = "DokuWiki SaaS" verbose_name = "DokuWiki SaaS"
help_text = _("URL path for mounting wordpress multisite.") help_text = _("URL path for mounting wordpress multisite.")
# fpm_listen = settings.WEBAPPS_DOKUWIKIMU_LISTEN
group = SiteDirective.SAAS group = SiteDirective.SAAS
regex = r'^/[^ ]*$' regex = r'^/[^ ]*$'
unique_value = True unique_value = True
@ -161,7 +158,6 @@ class DrupalSaaS(SiteDirective):
name = 'drupal-saas' name = 'drupal-saas'
verbose_name = "Drupdal SaaS" verbose_name = "Drupdal SaaS"
help_text = _("URL path for mounting wordpress multisite.") help_text = _("URL path for mounting wordpress multisite.")
# fpm_listen = settings.WEBAPPS_DRUPALMU_LISTEN
group = SiteDirective.SAAS group = SiteDirective.SAAS
regex = r'^/[^ ]*$' regex = r'^/[^ ]*$'
unique_value = True unique_value = True